basic find and download ability

This commit is contained in:
Simon Ding
2024-07-01 11:34:54 +08:00
parent ab8e653c5b
commit 3b7a444164
13 changed files with 2304 additions and 0 deletions

206
pkg/jackett/category.go Normal file
View File

@@ -0,0 +1,206 @@
package jackett
import "fmt"
type Category struct {
ID int
Name string
}
func (c Category) String() string {
return fmt.Sprintf("%s[%d]", c.Name, c.ID)
}
const (
CustomCategoryOffset = 100000
)
// Categories from the Newznab spec
// https://github.com/nZEDb/nZEDb/blob/0.x/docs/newznab_api_specification.txt#L627
var (
CategoryOther = Category{0, "Other"}
CategoryOther_Misc = Category{10, "Other/Misc"}
CategoryOther_Hashed = Category{20, "Other/Hashed"}
CategoryConsole = Category{1000, "Console"}
CategoryConsole_NDS = Category{1010, "Console/NDS"}
CategoryConsole_PSP = Category{1020, "Console/PSP"}
CategoryConsole_Wii = Category{1030, "Console/Wii"}
CategoryConsole_XBOX = Category{1040, "Console/Xbox"}
CategoryConsole_XBOX360 = Category{1050, "Console/Xbox360"}
CategoryConsole_WiiwareVC = Category{1060, "Console/Wiiware/V"}
CategoryConsole_XBOX360DLC = Category{1070, "Console/Xbox360"}
CategoryConsole_PS3 = Category{1080, "Console/PS3"}
CategoryConsole_Other = Category{1999, "Console/Other"}
CategoryConsole_3DS = Category{1110, "Console/3DS"}
CategoryConsole_PSVita = Category{1120, "Console/PS Vita"}
CategoryConsole_WiiU = Category{1130, "Console/WiiU"}
CategoryConsole_XBOXOne = Category{1140, "Console/XboxOne"}
CategoryConsole_PS4 = Category{1180, "Console/PS4"}
CategoryMovies = Category{2000, "Movies"}
CategoryMovies_Foreign = Category{2010, "Movies/Foreign"}
CategoryMovies_Other = Category{2020, "Movies/Other"}
CategoryMovies_SD = Category{2030, "Movies/SD"}
CategoryMovies_HD = Category{2040, "Movies/HD"}
CategoryMovies_3D = Category{2050, "Movies/3D"}
CategoryMovies_BluRay = Category{2060, "Movies/BluRay"}
CategoryMovies_DVD = Category{2070, "Movies/DVD"}
CategoryMovies_WEBDL = Category{2080, "Movies/WEBDL"}
CategoryAudio = Category{3000, "Audio"}
CategoryAudio_MP3 = Category{3010, "Audio/MP3"}
CategoryAudio_Video = Category{3020, "Audio/Video"}
CategoryAudio_Audiobook = Category{3030, "Audio/Audiobook"}
CategoryAudio_Lossless = Category{3040, "Audio/Lossless"}
CategoryAudio_Other = Category{3999, "Audio/Other"}
CategoryAudio_Foreign = Category{3060, "Audio/Foreign"}
CategoryPC = Category{4000, "PC"}
CategoryPC_0day = Category{4010, "PC/0day"}
CategoryPC_ISO = Category{4020, "PC/ISO"}
CategoryPC_Mac = Category{4030, "PC/Mac"}
CategoryPC_PhoneOther = Category{4040, "PC/Phone-Other"}
CategoryPC_Games = Category{4050, "PC/Games"}
CategoryPC_PhoneIOS = Category{4060, "PC/Phone-IOS"}
CategoryPC_PhoneAndroid = Category{4070, "PC/Phone-Android"}
CategoryTV = Category{5000, "TV"}
CategoryTV_WEBDL = Category{5010, "TV/WEB-DL"}
CategoryTV_FOREIGN = Category{5020, "TV/Foreign"}
CategoryTV_SD = Category{5030, "TV/SD"}
CategoryTV_HD = Category{5040, "TV/HD"}
CategoryTV_Other = Category{5999, "TV/Other"}
CategoryTV_Sport = Category{5060, "TV/Sport"}
CategoryTV_Anime = Category{5070, "TV/Anime"}
CategoryTV_Documentary = Category{5080, "TV/Documentary"}
CategoryXXX = Category{6000, "XXX"}
CategoryXXX_DVD = Category{6010, "XXX/DVD"}
CategoryXXX_WMV = Category{6020, "XXX/WMV"}
CategoryXXX_XviD = Category{6030, "XXX/XviD"}
CategoryXXX_x264 = Category{6040, "XXX/x264"}
CategoryXXX_Other = Category{6999, "XXX/Other"}
CategoryXXX_Imageset = Category{6060, "XXX/Imageset"}
CategoryXXX_Packs = Category{6070, "XXX/Packs"}
CategoryBooks = Category{7000, "Books"}
CategoryBooks_Magazines = Category{7010, "Books/Magazines"}
CategoryBooks_Ebook = Category{7020, "Books/Ebook"}
CategoryBooks_Comics = Category{7030, "Books/Comics"}
CategoryBooks_Technical = Category{7040, "Books/Technical"}
CategoryBooks_Foreign = Category{7060, "Books/Foreign"}
CategoryBooks_Unknown = Category{7999, "Books/Unknown"}
)
var AllCategories = Categories{
CategoryOther,
CategoryOther_Misc,
CategoryOther_Hashed,
CategoryConsole,
CategoryConsole_NDS,
CategoryConsole_PSP,
CategoryConsole_Wii,
CategoryConsole_XBOX,
CategoryConsole_XBOX360,
CategoryConsole_WiiwareVC,
CategoryConsole_XBOX360DLC,
CategoryConsole_PS3,
CategoryConsole_Other,
CategoryConsole_3DS,
CategoryConsole_PSVita,
CategoryConsole_WiiU,
CategoryConsole_XBOXOne,
CategoryConsole_PS4,
CategoryMovies,
CategoryMovies_Foreign,
CategoryMovies_Other,
CategoryMovies_SD,
CategoryMovies_HD,
CategoryMovies_3D,
CategoryMovies_BluRay,
CategoryMovies_DVD,
CategoryMovies_WEBDL,
CategoryAudio,
CategoryAudio_MP3,
CategoryAudio_Video,
CategoryAudio_Audiobook,
CategoryAudio_Lossless,
CategoryAudio_Other,
CategoryAudio_Foreign,
CategoryPC,
CategoryPC_0day,
CategoryPC_ISO,
CategoryPC_Mac,
CategoryPC_PhoneOther,
CategoryPC_Games,
CategoryPC_PhoneIOS,
CategoryPC_PhoneAndroid,
CategoryTV,
CategoryTV_WEBDL,
CategoryTV_FOREIGN,
CategoryTV_SD,
CategoryTV_HD,
CategoryTV_Other,
CategoryTV_Sport,
CategoryTV_Anime,
CategoryTV_Documentary,
CategoryXXX,
CategoryXXX_DVD,
CategoryXXX_WMV,
CategoryXXX_XviD,
CategoryXXX_x264,
CategoryXXX_Other,
CategoryXXX_Imageset,
CategoryXXX_Packs,
CategoryBooks,
CategoryBooks_Magazines,
CategoryBooks_Ebook,
CategoryBooks_Comics,
CategoryBooks_Technical,
CategoryBooks_Foreign,
CategoryBooks_Unknown,
}
func ParentCategory(c Category) Category {
switch {
case c.ID < 1000:
return CategoryOther
case c.ID < 2000:
return CategoryConsole
case c.ID < 3000:
return CategoryMovies
case c.ID < 4000:
return CategoryAudio
case c.ID < 5000:
return CategoryPC
case c.ID < 6000:
return CategoryTV
case c.ID < 7000:
return CategoryXXX
case c.ID < 8000:
return CategoryBooks
}
return CategoryOther
}
type Categories []Category
func (slice Categories) Subset(ids ...int) Categories {
cats := Categories{}
for _, cat := range AllCategories {
for _, id := range ids {
if cat.ID == id {
cats = append(cats, cat)
}
}
}
return cats
}
func (slice Categories) Len() int {
return len(slice)
}
func (slice Categories) Less(i, j int) bool {
return slice[i].ID < slice[j].ID
}
func (slice Categories) Swap(i, j int) {
slice[i], slice[j] = slice[j], slice[i]
}

168
pkg/jackett/jackett.go Normal file
View File

@@ -0,0 +1,168 @@
package jackett
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"strings"
"time"
"github.com/pkg/errors"
"golang.org/x/net/context"
)
var (
apiURL string
apiKey string
)
type Settings struct {
ApiURL string
ApiKey string
Client *http.Client
}
type FetchRequest struct {
Query string
Trackers []string
Categories []uint
}
type FetchResponse struct {
Results []Result
Indexers []Indexer
}
type jackettTime struct {
time.Time
}
func (jt *jackettTime) UnmarshalJSON(b []byte) (err error) {
str := strings.Trim(string(b), `"`)
if str == "0001-01-01T00:00:00" {
} else if len(str) == 19 {
jt.Time, err = time.Parse(time.RFC3339, str+"Z")
} else {
jt.Time, err = time.Parse(time.RFC3339, str)
}
return
}
type Result struct {
BannerUrl string
BlackholeLink string
Category []uint
CategoryDesc string
Comments string
Description string
DownloadVolumeFactor float32
Files uint
FirstSeen jackettTime
Gain float32
Grabs uint
Guid string
Imdb uint
InfoHash string
Link string
MagnetUri string
MinimumRatio float32
MinimumSeedTime uint
Peers uint
PublishDate jackettTime
RageID uint
Seeders uint
Size uint
TMDb uint
TVDBId uint
Title string
Tracker string
TrackerId string
UploadVolumeFactor float32
}
type Indexer struct {
Error string
ID string
Name string
Results uint
Status uint
}
type Jackett struct {
settings *Settings
}
func NewJackett(s *Settings) *Jackett {
if s.ApiURL == "" && apiURL != "" {
s.ApiURL = apiURL
}
if s.ApiKey == "" && apiKey != "" {
s.ApiKey = apiKey
}
if s.Client == nil {
s.Client = http.DefaultClient
}
return &Jackett{settings: s}
}
func (j *Jackett) generateFetchURL(fr *FetchRequest) (string, error) {
u, err := url.Parse(j.settings.ApiURL)
if err != nil {
return "", errors.Wrapf(err, "failed to parse apiURL %q", j.settings.ApiURL)
}
u.Path = "/api/v2.0/indexers/all/results"
q := u.Query()
q.Set("apikey", j.settings.ApiKey)
for _, t := range fr.Trackers {
q.Add("Tracker[]", t)
}
for _, c := range fr.Categories {
q.Add("Category[]", fmt.Sprintf("%v", c))
}
if fr.Query != "" {
q.Add("Query", fr.Query)
}
u.RawQuery = q.Encode()
return u.String(), nil
}
func (j *Jackett) Fetch(ctx context.Context, fr *FetchRequest) (*FetchResponse, error) {
u, err := j.generateFetchURL(fr)
if err != nil {
return nil, errors.Wrap(err, "failed to generate fetch url")
}
req, err := http.NewRequestWithContext(ctx, "GET", u, nil)
if err != nil {
return nil, errors.Wrap(err, "failed to make fetch request")
}
res, err := j.settings.Client.Do(req)
if err != nil {
return nil, errors.Wrap(err, "failed to invoke fetch request")
}
defer res.Body.Close()
data, err := ioutil.ReadAll(res.Body)
if err != nil {
return nil, errors.Wrap(err, "failed to read fetch data")
}
var fres FetchResponse
err = json.Unmarshal(data, &fres)
if err != nil {
return nil, errors.Wrapf(err, "failed to unmarshal fetch data with url=%v and data=%v", u, string(data))
}
return &fres, nil
}
func SearchTvWithJacket(q string, url, apiKey string) (*FetchResponse, error) {
j := NewJackett(&Settings{
ApiURL: url,
ApiKey: apiKey,
})
resp, err := j.Fetch(context.TODO(), &FetchRequest{
//Categories: []uint{uint(CategoryTV.ID)},
Query: q,
})
return resp, err
}