initial commit

This commit is contained in:
Timon Ringwald 2022-08-15 15:55:27 +02:00
commit 9934d85022
12 changed files with 501 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
.env

39
access_token.go Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
}

6
utils.go Normal file
View File

@ -0,0 +1,6 @@
package main
type Pair[A, B any] struct {
First A
Second B
}