initial commit
This commit is contained in:
commit
9934d85022
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
.env
|
39
access_token.go
Normal file
39
access_token.go
Normal file
@ -0,0 +1,39 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func GetAnilistAccessToken() (string, error) {
|
||||
if strings.HasPrefix(AnilistAccessToken, "ey") {
|
||||
return AnilistAccessToken, nil
|
||||
}
|
||||
|
||||
if StoragePath == "" {
|
||||
return "", ErrAnilistTokenNotObtainable.New()
|
||||
} else if StorageUser == "" || StoragePass == "" {
|
||||
return "", ErrInvalidStorageParams.New()
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("GET", StoragePath, nil)
|
||||
if err != nil {
|
||||
return "", ErrStorageRequestFailed.Wrap(err)
|
||||
}
|
||||
|
||||
req.SetBasicAuth(StorageUser, StoragePass)
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return "", ErrStorageRequestFailed.Wrap(err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
data, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", ErrStorageRequestFailed.Wrap(err)
|
||||
}
|
||||
|
||||
return strings.TrimSpace(string(data)), nil
|
||||
}
|
69
anilist.go
Normal file
69
anilist.go
Normal file
@ -0,0 +1,69 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"git.milar.in/milarin/anilist"
|
||||
"git.milar.in/milarin/channel"
|
||||
)
|
||||
|
||||
func GetCurrentlyWatchingAnimesByAnimeID() (map[anilist.MediaID]*anilist.MediaList, error) {
|
||||
watchingAnimesChannel, err := GetCurrentlyWatchingAnimes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
toMapFunc := func(entry *anilist.MediaList) (anilist.MediaID, *anilist.MediaList) { return entry.MediaID, entry }
|
||||
return channel.ToMap(watchingAnimesChannel, toMapFunc), nil
|
||||
}
|
||||
|
||||
func GetCurrentlyWatchingAnimes() (<-chan *anilist.MediaList, error) {
|
||||
token, err := GetAnilistAccessToken()
|
||||
if err != nil {
|
||||
return nil, ErrAnimeListNotObtainable.Wrap(err, "access token acquisition failed")
|
||||
}
|
||||
|
||||
currentMedia := anilist.NewApi(token).GetMediaList(context.Background(), anilist.MediaListQuery{
|
||||
UserName: AnilistUsername,
|
||||
Type: anilist.MediaTypeAnime,
|
||||
Status: anilist.MediaListStatusCurrent,
|
||||
}, nil)
|
||||
|
||||
plannedMedia := anilist.NewApi(token).GetMediaList(context.Background(), anilist.MediaListQuery{
|
||||
UserName: AnilistUsername,
|
||||
Type: anilist.MediaTypeAnime,
|
||||
Status: anilist.MediaListStatusPlanning,
|
||||
}, nil)
|
||||
|
||||
// TODO add completedMedia for re-downloading last episode?
|
||||
|
||||
return channel.Merge(currentMedia.Chan, plannedMedia.Chan), nil
|
||||
}
|
||||
|
||||
var (
|
||||
animeByTitleCache = map[string]Pair[*anilist.Media, error]{}
|
||||
)
|
||||
|
||||
func SearchAnimeByTitle(title string) (anime *anilist.Media, err error) {
|
||||
// caching
|
||||
if cacheEntry, ok := animeByTitleCache[title]; ok {
|
||||
return cacheEntry.First, cacheEntry.Second
|
||||
}
|
||||
defer func() { animeByTitleCache[title] = Pair[*anilist.Media, error]{anime, err} }()
|
||||
|
||||
token, err := GetAnilistAccessToken()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
anime = anilist.NewApi(token).GetMedia(context.Background(), anilist.MediaQuery{
|
||||
Search: title,
|
||||
Type: anilist.MediaTypeAnime,
|
||||
}, nil).First()
|
||||
|
||||
if anime == nil {
|
||||
return nil, ErrAnimeNotFound.New(title)
|
||||
}
|
||||
|
||||
return anime, nil
|
||||
}
|
14
errors.go
Normal file
14
errors.go
Normal file
@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
import "git.tordarus.net/Tordarus/adverr"
|
||||
|
||||
var (
|
||||
ErrNoSuitableParser = adverr.NewErrTmpl("ErrNoSuitableParser", "could not parse torrent with ID %s because no suitable parser found")
|
||||
ErrTorrentParseFailed = adverr.NewErrTmpl("ErrTorrentParseFailed", "could not parse torrent with ID %s (parsed with '%s')")
|
||||
ErrAnimeNotFound = adverr.NewErrTmpl("ErrAnimeNotFound", "could not find anime with name '%s'")
|
||||
ErrAnilistTokenNotObtainable = adverr.NewErrTmpl("ErrAnilistTokenNotObtainable", "neither ANILIST_TOKEN nor STORAGE_PATH provided")
|
||||
ErrInvalidStorageParams = adverr.NewErrTmpl("ErrInvalidStorageParams", "STORAGE_USER or STORAGE_PASS not provided")
|
||||
ErrStorageRequestFailed = adverr.NewErrTmpl("ErrStorageRequestFailed", "request to file storage could not be made")
|
||||
ErrAnimeListNotObtainable = adverr.NewErrTmpl("ErrAnimeListNotObtainable", "anime list from anilist.co not obtainable (reason: %s)")
|
||||
ErrTorrentNotObtainable = adverr.NewErrTmpl("ErrTorrentNotObtainable", "torrents from nyaa.si not obtainable (reason: %s)")
|
||||
)
|
20
go.mod
Normal file
20
go.mod
Normal file
@ -0,0 +1,20 @@
|
||||
module git.milar.in/animan/downloader
|
||||
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
git.milar.in/animan/model v0.0.0-20220815125824-0716848c4a4d
|
||||
git.milar.in/animan/parsers v0.0.0-20220815122943-517518dd3064
|
||||
git.milar.in/milarin/adverr v0.2.1
|
||||
git.milar.in/milarin/anilist v1.5.0
|
||||
git.milar.in/milarin/channel v0.0.7
|
||||
git.milar.in/milarin/envvars v1.0.3
|
||||
git.tordarus.net/Tordarus/adverr v0.2.0
|
||||
github.com/PuerkitoBio/goquery v1.8.0
|
||||
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/andybalholm/cascadia v1.3.1 // indirect
|
||||
golang.org/x/net v0.0.0-20220812174116-3211cb980234 // indirect
|
||||
)
|
63
go.sum
Normal file
63
go.sum
Normal file
@ -0,0 +1,63 @@
|
||||
git.milar.in/animan/model v0.0.0-20220803215306-905563410463 h1:M7Xxqbv7WQ3xwW66APwI7KQ8wD8IN/v6ZZbq1JB1T2M=
|
||||
git.milar.in/animan/model v0.0.0-20220803215306-905563410463/go.mod h1:FmgisUVGJd8EEvGP4uk9pEv6Ic9aiEhAy5eZF8zZsgU=
|
||||
git.milar.in/animan/model v0.0.0-20220803222302-20b8d45495c4 h1:dh98Tu+uSh02ivBsGKPq6klzPTWP4oHieUPYpMHxxtQ=
|
||||
git.milar.in/animan/model v0.0.0-20220803222302-20b8d45495c4/go.mod h1:FmgisUVGJd8EEvGP4uk9pEv6Ic9aiEhAy5eZF8zZsgU=
|
||||
git.milar.in/animan/model v0.0.0-20220804134914-691506ccc5f0 h1:Q3Co0PrCAmPGtBSW/p8j4xns8i7bs8yIxnwqiajfN7Y=
|
||||
git.milar.in/animan/model v0.0.0-20220804134914-691506ccc5f0/go.mod h1:oXDr3slnzXoccIrci2wotX0cWwMVuAQ9dSvmh1buE4c=
|
||||
git.milar.in/animan/model v0.0.0-20220804144536-96f16f9e0584 h1:T7xYzMtu15ZzFM9ERhuN+5V2ZMFBGalb+2/13ShXtOY=
|
||||
git.milar.in/animan/model v0.0.0-20220804144536-96f16f9e0584/go.mod h1:oXDr3slnzXoccIrci2wotX0cWwMVuAQ9dSvmh1buE4c=
|
||||
git.milar.in/animan/model v0.0.0-20220804154959-f8b66e8c9eda h1:9XG7Dn+UtyLwwNcuol10EdHcBQjYD+1WzmnJbRUJQfs=
|
||||
git.milar.in/animan/model v0.0.0-20220804154959-f8b66e8c9eda/go.mod h1:oXDr3slnzXoccIrci2wotX0cWwMVuAQ9dSvmh1buE4c=
|
||||
git.milar.in/animan/model v0.0.0-20220804163811-82537cb1f6e4 h1:fdtvkK2LJ9O3bP20wcqsL8gpOFr5nLoCn+Qb+SbyilU=
|
||||
git.milar.in/animan/model v0.0.0-20220804163811-82537cb1f6e4/go.mod h1:oXDr3slnzXoccIrci2wotX0cWwMVuAQ9dSvmh1buE4c=
|
||||
git.milar.in/animan/model v0.0.0-20220815093549-fe9b565a9dd2 h1:vNbYNp8VJQ6FBENgZMHA1IFOfPkQEA+TIaN8PFF8m48=
|
||||
git.milar.in/animan/model v0.0.0-20220815093549-fe9b565a9dd2/go.mod h1:oXDr3slnzXoccIrci2wotX0cWwMVuAQ9dSvmh1buE4c=
|
||||
git.milar.in/animan/model v0.0.0-20220815103315-0378bec21c68 h1:UiX2wucO/SkWHCuaxFGr7hw5yqhjR8LYk5gxesRDFec=
|
||||
git.milar.in/animan/model v0.0.0-20220815103315-0378bec21c68/go.mod h1:oXDr3slnzXoccIrci2wotX0cWwMVuAQ9dSvmh1buE4c=
|
||||
git.milar.in/animan/model v0.0.0-20220815122339-9ce113fc9042 h1:rYaoxRO2rKuUm199yq/sIhRRZqe/9kXDSRbFHdWKeMM=
|
||||
git.milar.in/animan/model v0.0.0-20220815122339-9ce113fc9042/go.mod h1:oXDr3slnzXoccIrci2wotX0cWwMVuAQ9dSvmh1buE4c=
|
||||
git.milar.in/animan/model v0.0.0-20220815123155-23f657024bd0 h1:lWAANKFbwvZah9HHOE851FXNHbId8tbv7OGwmH+BBrE=
|
||||
git.milar.in/animan/model v0.0.0-20220815123155-23f657024bd0/go.mod h1:oXDr3slnzXoccIrci2wotX0cWwMVuAQ9dSvmh1buE4c=
|
||||
git.milar.in/animan/model v0.0.0-20220815124104-e3f2c831ee81 h1:b6xJxC2GCB0NgVV0Smh5lmhesB1qSen2bje/570P0Wk=
|
||||
git.milar.in/animan/model v0.0.0-20220815124104-e3f2c831ee81/go.mod h1:oXDr3slnzXoccIrci2wotX0cWwMVuAQ9dSvmh1buE4c=
|
||||
git.milar.in/animan/model v0.0.0-20220815125235-67e9015e4253 h1:ZCz7aBf6v6pCVavPsUULsvqi84DAo9PpjafmUjfIH6w=
|
||||
git.milar.in/animan/model v0.0.0-20220815125235-67e9015e4253/go.mod h1:oXDr3slnzXoccIrci2wotX0cWwMVuAQ9dSvmh1buE4c=
|
||||
git.milar.in/animan/model v0.0.0-20220815125506-350681318c43 h1:z6BId1oJWLRj4PalwtsVPG8Ezm2YYot82YFzrd3Iymk=
|
||||
git.milar.in/animan/model v0.0.0-20220815125506-350681318c43/go.mod h1:oXDr3slnzXoccIrci2wotX0cWwMVuAQ9dSvmh1buE4c=
|
||||
git.milar.in/animan/model v0.0.0-20220815125824-0716848c4a4d h1:oy50tLPhaLG0zKrrlQcsPbjfbgtXi7g9LK5NJt+v5qE=
|
||||
git.milar.in/animan/model v0.0.0-20220815125824-0716848c4a4d/go.mod h1:oXDr3slnzXoccIrci2wotX0cWwMVuAQ9dSvmh1buE4c=
|
||||
git.milar.in/animan/parsers v0.0.0-20220803220519-b516177d7cff h1:KN5IznBS1xvqrwjZNmp71FyfqkILvHgwkOreoqQ5zFk=
|
||||
git.milar.in/animan/parsers v0.0.0-20220803220519-b516177d7cff/go.mod h1:2x+xW6lKKkVVG39sbJFR2B7r7WxHkbmwxyVIkj1R1h4=
|
||||
git.milar.in/animan/parsers v0.0.0-20220804161341-2a3194c37eb6 h1:TNXl6LGxACimnllnoBgPEV7PpMv//+Evqte7C8K4X1M=
|
||||
git.milar.in/animan/parsers v0.0.0-20220804161341-2a3194c37eb6/go.mod h1:X2VK8tKVbtA8nhzjJfRjXZycE4HMwDHnAUWQXU7/gIU=
|
||||
git.milar.in/animan/parsers v0.0.0-20220815114013-d4e5e659350a h1:73ELbFmLE3ejVkiTimDzIz2n6FI1K6raMc4KRw1wJOc=
|
||||
git.milar.in/animan/parsers v0.0.0-20220815114013-d4e5e659350a/go.mod h1:ffYwJnlRMtebp4wE01i+Tyz85yVasw0RSUkG38XeKdI=
|
||||
git.milar.in/animan/parsers v0.0.0-20220815122943-517518dd3064 h1:1gbx3Tzrw8zb5lspRG/vzJ1KdosSOR3brP+9IoXII9g=
|
||||
git.milar.in/animan/parsers v0.0.0-20220815122943-517518dd3064/go.mod h1:ffYwJnlRMtebp4wE01i+Tyz85yVasw0RSUkG38XeKdI=
|
||||
git.milar.in/milarin/adverr v0.2.1 h1:eyXFGC+Ui/kcNt2+NqP3HiAplwxzqeNr9DfitsUb3c4=
|
||||
git.milar.in/milarin/adverr v0.2.1/go.mod h1:wwfglcey4R3vqjNL/d8mbnvFJGzETRXzAEolIHZY32w=
|
||||
git.milar.in/milarin/anilist v1.5.0 h1:fSiAXY/topNk4ISEp2QtcG9HHKLJfMc8w05iqc+Paf0=
|
||||
git.milar.in/milarin/anilist v1.5.0/go.mod h1:8PTHXFMA45JpfRFIpcdrKwDHue8fbT/wwV1BuHFn6c0=
|
||||
git.milar.in/milarin/channel v0.0.7 h1:cVKtwgH/EE7U+XTHcoFCClJ4LR349KanzjX9xKwRcNg=
|
||||
git.milar.in/milarin/channel v0.0.7/go.mod h1:We83LTI8S7u7II3pD+A2ChCDWJfCkcBUCUqii9HjTtM=
|
||||
git.milar.in/milarin/envvars v1.0.2 h1:FjN9qczW7u2LVzJv/hYf8a4m1MZMUkFiqvEjzHJDYgc=
|
||||
git.milar.in/milarin/envvars v1.0.2/go.mod h1:rLh/HN6S254h6m2lklnImcpsy4kHFxaOjM6+Nv9GHKI=
|
||||
git.milar.in/milarin/envvars v1.0.3 h1:go6pYExUzPx+aLvJ5BKNkaANkBNmcrlRJ8TtWsk4uWY=
|
||||
git.milar.in/milarin/envvars v1.0.3/go.mod h1:rLh/HN6S254h6m2lklnImcpsy4kHFxaOjM6+Nv9GHKI=
|
||||
git.tordarus.net/Tordarus/adverr v0.2.0 h1:kLYjR2/Vb2GHiSAMvAv+WPNaHR9BRphKanf8H/pCZdA=
|
||||
git.tordarus.net/Tordarus/adverr v0.2.0/go.mod h1:XRf0+7nhOkIEr0gi9DUG4RvV2KaOFB0fYPDaR1KLenw=
|
||||
github.com/PuerkitoBio/goquery v1.8.0 h1:PJTF7AmFCFKk1N6V6jmKfrNH9tV5pNE6lZMkG0gta/U=
|
||||
github.com/PuerkitoBio/goquery v1.8.0/go.mod h1:ypIiRMtY7COPGk+I/YbZLbxsxn9g5ejnI2HSMtkjZvI=
|
||||
github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c=
|
||||
github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA=
|
||||
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e h1:+WEEuIdZHnUeJJmEUjyYC2gfUMj69yZXw17EnHg/otA=
|
||||
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA=
|
||||
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8 h1:/6y1LfuqNuQdHAm0jjtPtgRcxIxjVZgm5OTu8/QhZvk=
|
||||
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220812174116-3211cb980234 h1:RDqmgfe7SvlMWoqC3xwQ2blLO3fcWcxMa3eBLRdRW7E=
|
||||
golang.org/x/net v0.0.0-20220812174116-3211cb980234/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
94
main.go
Normal file
94
main.go
Normal file
@ -0,0 +1,94 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"git.milar.in/animan/model"
|
||||
"git.milar.in/milarin/adverr"
|
||||
"git.milar.in/milarin/envvars"
|
||||
)
|
||||
|
||||
var (
|
||||
PollRate = envvars.Object("POLL_RATE", 30*time.Minute, time.ParseDuration)
|
||||
|
||||
AnilistUsername = envvars.String("ANILIST_USERNAME", "username")
|
||||
AnilistAccessToken = envvars.String("ANILIST_TOKEN", "")
|
||||
|
||||
StoragePath = envvars.String("STORAGE_PATH", "")
|
||||
StorageUser = envvars.String("STORAGE_USER", "")
|
||||
StoragePass = envvars.String("STORAGE_PASS", "")
|
||||
|
||||
// essential torrent properties
|
||||
|
||||
MaxResolution = envvars.Object("MAX_RESOLUTION", model.Resolution4K, model.ParseResolution)
|
||||
MinResolution = envvars.Object("MIN_RESOLUTION", model.ResolutionHD, model.ParseResolution)
|
||||
|
||||
EssentialLanguages = envvars.StringSlice("ESSENTIAL_LANGUAGES", ",")
|
||||
EssentialSubtitles = envvars.StringSlice("ESSENTIAL_SUBTITLES", ",")
|
||||
|
||||
MaxSeeders = envvars.Int("MAX_SEEDERS", math.MaxInt)
|
||||
MinSeeders = envvars.Int("MIN_SEEDERS", 0)
|
||||
|
||||
MaxLeechers = envvars.Int("MAX_LEECHERS", math.MaxInt)
|
||||
MinLeechers = envvars.Int("MIN_LEECHERS", 0)
|
||||
|
||||
MaxDownloads = envvars.Int("MAX_DOWNLOADS", math.MaxInt)
|
||||
MinDownloads = envvars.Int("MIN_DOWNLOADS", 0)
|
||||
|
||||
TrustedOnly = envvars.Bool("TRUSTED_ONLY", false)
|
||||
|
||||
// preferred torrent properties
|
||||
|
||||
PreferredLanguages = envvars.StringSlice("PREFERRED_LANGUAGES", ",")
|
||||
PreferredSubtitles = envvars.StringSlice("PREFERRED_SUBTITLES", ",")
|
||||
|
||||
/*
|
||||
TODO
|
||||
|
||||
PreferMoreLanguages = envvars.Bool("PREFERER_MORE_LANGUAGES", false)
|
||||
PreferMoreSubtitles = envvars.Bool("PREFERER_MORE_SUBTITLES", false)
|
||||
*/
|
||||
)
|
||||
|
||||
func main() {
|
||||
// get access token once at startup to be sure that an access token is obtainable at all
|
||||
if _, err := GetAnilistAccessToken(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
ticker := time.NewTicker(PollRate)
|
||||
defer ticker.Stop()
|
||||
|
||||
checkTorrents()
|
||||
for range ticker.C {
|
||||
checkTorrents()
|
||||
}
|
||||
}
|
||||
|
||||
func checkTorrents() {
|
||||
fmt.Println("check torrents")
|
||||
|
||||
torrents, err := GetLatestNyaaContent()
|
||||
if err != nil {
|
||||
fmt.Println(adverr.Wrap("retrieving torrents failed", err))
|
||||
return
|
||||
}
|
||||
|
||||
/*watchingAnimes, err := GetCurrentlyWatchingAnimesByAnimeID()
|
||||
if err != nil {
|
||||
fmt.Println(adverr.Wrap("retrieving anime list failed", err))
|
||||
return
|
||||
}*/
|
||||
|
||||
parsedTorrentsByAnimeEp := FilterEssentialTorrents(ParseTorrentsByAnimeEp(torrents))
|
||||
|
||||
for _, parsedTorrents := range parsedTorrentsByAnimeEp {
|
||||
for _, parsedTorrent := range parsedTorrents {
|
||||
fmt.Println(parsedTorrent, "|", parsedTorrent.Torrent.StringWithoutTitle())
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Printf("sleep for %s\n", PollRate)
|
||||
}
|
79
nyaa.go
Normal file
79
nyaa.go
Normal file
@ -0,0 +1,79 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.milar.in/animan/model"
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
)
|
||||
|
||||
var torrentLinkRegex = regexp.MustCompile(`https:\/\/nyaa\.si\/download\/(\d+?)\.torrent`)
|
||||
|
||||
func GetLatestNyaaContent() ([]model.Torrent, error) {
|
||||
resp, err := http.Get("https://nyaa.si/?page=rss&f=0&c=0_0&q=Erai-Raws+ayumu") // TODO https://nyaa.si/?page=rss
|
||||
if err != nil {
|
||||
return nil, ErrTorrentNotObtainable.Wrap(err, "torrent data acqusition failed")
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != 200 {
|
||||
return nil, ErrTorrentNotObtainable.New("invalid status code from nyaa.si: " + strconv.Itoa(resp.StatusCode))
|
||||
}
|
||||
|
||||
nyaa, err := goquery.NewDocumentFromReader(resp.Body)
|
||||
if err != nil {
|
||||
return nil, ErrTorrentNotObtainable.Wrap(err, "nyaa.si response parsing failed")
|
||||
}
|
||||
|
||||
torrents := make([]model.Torrent, 0, 75)
|
||||
|
||||
nyaa.Find("item").Each(func(i int, s *goquery.Selection) {
|
||||
time, err := time.Parse(time.RFC1123Z, s.Find("pubDate").Text())
|
||||
if err != nil {
|
||||
fmt.Println("could not parse time:", s.Find("pubDate"))
|
||||
return
|
||||
}
|
||||
|
||||
seeders, err := strconv.Atoi(s.Find("nyaa\\:seeders").Text())
|
||||
if err != nil {
|
||||
fmt.Println("could not parse seeders:", s.Find("nyaa\\:seeders").Text())
|
||||
return
|
||||
}
|
||||
|
||||
leechers, err := strconv.Atoi(s.Find("nyaa\\:leechers").Text())
|
||||
if err != nil {
|
||||
fmt.Println("could not parse leechers:", s.Find("nyaa\\:leechers").Text())
|
||||
return
|
||||
}
|
||||
|
||||
downloads, err := strconv.Atoi(s.Find("nyaa\\:downloads").Text())
|
||||
if err != nil {
|
||||
fmt.Println("could not parse downloads:", s.Find("nyaa\\:downloads").Text())
|
||||
return
|
||||
}
|
||||
|
||||
// goquery can't parse the link tag for some reason
|
||||
// therefore I have to get around this bug by exploiting regex
|
||||
|
||||
matches := torrentLinkRegex.FindStringSubmatch(s.Text())
|
||||
link := matches[0]
|
||||
id := matches[1]
|
||||
|
||||
torrents = append(torrents, model.Torrent{
|
||||
ID: model.TorrentID(id),
|
||||
Title: s.Find("title").Text(),
|
||||
Link: link,
|
||||
Time: time,
|
||||
Seeders: seeders,
|
||||
Leechers: leechers,
|
||||
Downloads: downloads,
|
||||
Trusted: strings.Contains(strings.ToLower(s.Find("nyaa\\:trusted").Text()), "yes"),
|
||||
})
|
||||
})
|
||||
|
||||
return torrents, nil
|
||||
}
|
54
torrent_filter.go
Normal file
54
torrent_filter.go
Normal file
@ -0,0 +1,54 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"git.milar.in/animan/model"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
func FilterEssentialTorrents(parsedTorrentsByAnimeEp map[model.AnimeEpisode][]*model.ParsedTorrent) map[model.AnimeEpisode][]*model.ParsedTorrent {
|
||||
filteredMap := map[model.AnimeEpisode][]*model.ParsedTorrent{}
|
||||
for animeEpisode, parsedTorrents := range parsedTorrentsByAnimeEp {
|
||||
for _, parsedTorrent := range parsedTorrents {
|
||||
if HasEssentialProperties(parsedTorrent) {
|
||||
filteredMap[animeEpisode] = append(filteredMap[animeEpisode], parsedTorrent)
|
||||
}
|
||||
}
|
||||
}
|
||||
return filteredMap
|
||||
}
|
||||
|
||||
func HasEssentialProperties(torrent *model.ParsedTorrent) bool {
|
||||
if torrent.Resolution < MinResolution || torrent.Resolution > MaxResolution {
|
||||
return false
|
||||
}
|
||||
|
||||
if torrent.Torrent.Seeders < MinSeeders || torrent.Torrent.Seeders > MaxSeeders {
|
||||
return false
|
||||
}
|
||||
|
||||
if torrent.Torrent.Leechers < MinLeechers || torrent.Torrent.Leechers > MaxLeechers {
|
||||
return false
|
||||
}
|
||||
|
||||
if torrent.Torrent.Downloads < MinDownloads || torrent.Torrent.Downloads > MaxDownloads {
|
||||
return false
|
||||
}
|
||||
|
||||
if TrustedOnly && torrent.Torrent.Trusted {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, essentialLanguage := range EssentialLanguages {
|
||||
if !slices.Contains(torrent.Languages, essentialLanguage) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
for _, essentialSubtitle := range EssentialSubtitles {
|
||||
if !slices.Contains(torrent.Subtitles, essentialSubtitle) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
53
torrent_parse.go
Normal file
53
torrent_parse.go
Normal file
@ -0,0 +1,53 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"git.milar.in/animan/model"
|
||||
"git.milar.in/animan/parsers"
|
||||
"git.milar.in/milarin/adverr"
|
||||
)
|
||||
|
||||
func ParseTorrent(torrent *model.Torrent) (*model.ParsedTorrent, error) {
|
||||
for _, parser := range parsers.Parsers {
|
||||
parsedTorrent, ok := parser.TorrentParser(&parser, torrent)
|
||||
if ok {
|
||||
anime, err := SearchAnimeByTitle(parsedTorrent.OriginalAnimeTitle)
|
||||
if err != nil {
|
||||
return parsedTorrent, ErrTorrentParseFailed.Wrap(err, torrent.ID, parser.String())
|
||||
}
|
||||
|
||||
parsedTorrent.Anime = anime
|
||||
return parsedTorrent, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, ErrNoSuitableParser.New(torrent.ID)
|
||||
}
|
||||
|
||||
func ParseTorrentsByAnimeEp(torrents []model.Torrent) map[model.AnimeEpisode][]*model.ParsedTorrent {
|
||||
torrentsByAnimeEp := map[model.AnimeEpisode][]*model.ParsedTorrent{}
|
||||
|
||||
for _, torrent := range torrents {
|
||||
torrent := torrent
|
||||
parsedTorrent, err := ParseTorrent(&torrent)
|
||||
if err != nil {
|
||||
if errors.Is(err, ErrAnimeNotFound) {
|
||||
fmt.Printf("anime not found: \"%s\" (torrent ID: %s)\n", parsedTorrent.OriginalAnimeTitle, torrent.ID)
|
||||
} else if !errors.Is(err, ErrNoSuitableParser) {
|
||||
adverr.Println(err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
animeEpisode := model.AnimeEpisode{
|
||||
Anime: parsedTorrent.Anime.Title.Native,
|
||||
Episode: parsedTorrent.Episode,
|
||||
}
|
||||
|
||||
torrentsByAnimeEp[animeEpisode] = append(torrentsByAnimeEp[animeEpisode], parsedTorrent)
|
||||
}
|
||||
|
||||
return torrentsByAnimeEp
|
||||
}
|
9
torrent_sort.go
Normal file
9
torrent_sort.go
Normal file
@ -0,0 +1,9 @@
|
||||
package main
|
||||
|
||||
import "git.milar.in/animan/model"
|
||||
|
||||
func SortTorrentsByPreferredProperties(torrents map[model.AnimeEpisode][]*model.ParsedTorrent) {
|
||||
// TODO
|
||||
// sort torrent slices by preferred properties
|
||||
// store prefered properties in db somewhere
|
||||
}
|
Loading…
Reference in New Issue
Block a user