moved code used by organizer into logic repo
This commit is contained in:
parent
740d58bb28
commit
4d4bab1134
@ -1,39 +0,0 @@
|
||||
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
|
||||
}
|
68
anilist.go
68
anilist.go
@ -1,68 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"git.milar.in/milarin/anilist"
|
||||
"git.milar.in/milarin/channel"
|
||||
)
|
||||
|
||||
type Pair[A, B any] struct {
|
||||
First A
|
||||
Second B
|
||||
}
|
||||
|
||||
func GetAnimeListByAnimeID() (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")
|
||||
}
|
||||
|
||||
media := channel.Map(channel.Of(AnimeStatuses...), func(status anilist.MediaListStatus) <-chan *anilist.MediaList {
|
||||
return anilist.NewApi(token).GetMediaList(context.Background(), anilist.MediaListQuery{
|
||||
UserName: AnilistUsername,
|
||||
Type: anilist.MediaTypeAnime,
|
||||
Status: status,
|
||||
}, nil).Chan
|
||||
})
|
||||
|
||||
return channel.FlatChan(media), 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
|
||||
}
|
@ -2,14 +2,16 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"git.milar.in/milarin/adverr"
|
||||
"git.milar.in/nyaanime/logic"
|
||||
"git.milar.in/nyaanime/model"
|
||||
)
|
||||
|
||||
func checkTorrents() {
|
||||
fmt.Println("checking torrents")
|
||||
func CheckTorrents() {
|
||||
log.Println("checking torrents")
|
||||
start := time.Now()
|
||||
|
||||
torrents, err := GetTorrents()
|
||||
@ -18,7 +20,7 @@ func checkTorrents() {
|
||||
return
|
||||
}
|
||||
|
||||
animeList, err := GetAnimeListByAnimeID()
|
||||
animeList, err := logic.GetAnimeListByAnimeID()
|
||||
if err != nil {
|
||||
fmt.Println(adverr.Wrap("retrieving anime list failed", err))
|
||||
return
|
||||
@ -48,7 +50,7 @@ func checkTorrents() {
|
||||
continue
|
||||
}
|
||||
|
||||
props, inCollection := GetAnimeEpProps(animeEp)
|
||||
props, inCollection := logic.GetAnimeEpProps(animeEp)
|
||||
|
||||
inCollectionEpisodes[animeEp] = inCollection // debug output
|
||||
if inCollection {
|
||||
@ -79,5 +81,5 @@ func checkTorrents() {
|
||||
downloadedTorrents,
|
||||
)
|
||||
|
||||
fmt.Printf("check took %s. sleeping for %s\n", duration.Truncate(time.Millisecond), (PollRate - duration).Truncate(time.Millisecond))
|
||||
log.Printf("check took %s. sleeping for %s\n", duration.Truncate(time.Millisecond), (PollRate - duration).Truncate(time.Millisecond))
|
||||
}
|
||||
|
54
envvars.go
54
envvars.go
@ -2,32 +2,15 @@ package main
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"git.milar.in/milarin/anilist"
|
||||
"git.milar.in/milarin/envvars/v2"
|
||||
"git.milar.in/nyaanime/model"
|
||||
)
|
||||
|
||||
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_USERNAME", "")
|
||||
StoragePass = envvars.String("STORAGE_PASSWORD", "")
|
||||
|
||||
TorrentPath = envvars.String("TORRENT_PATH", "")
|
||||
AnimePath = envvars.String("ANIME_PATH", "")
|
||||
|
||||
AnimeEpFilepathPattern = envvars.Object(
|
||||
"EPISODE_FILEPATH_PATTERN",
|
||||
template.Must(template.New("anime-episode-filepath-pattern").Parse(`{{.Title.UserPreferred}}/{{.Title.UserPreferred}} Episode {{.Episode}}.{{.Ext}}`)),
|
||||
template.New("anime-episode-filepath-pattern").Parse,
|
||||
)
|
||||
|
||||
DebugAnimeEpisodePattern = envvars.Object(
|
||||
"DEBUG_ANIME_LANGUAGE_PATTERN",
|
||||
@ -35,42 +18,5 @@ var (
|
||||
template.New("anime-episode-pattern").Parse,
|
||||
)
|
||||
|
||||
AnimeStatuses = envvars.ObjectSlice("ANIME_STATUSES", ",", []anilist.MediaListStatus{
|
||||
anilist.MediaListStatusCurrent,
|
||||
anilist.MediaListStatusPlanning,
|
||||
}, ParseMediaListStatus)
|
||||
|
||||
DownloadAll = envvars.Bool("DOWNLOAD_ALL_ANIMES", false)
|
||||
|
||||
// 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", "|", []string{})
|
||||
EssentialSubtitles = envvars.StringSlice("ESSENTIAL_SUBTITLES", "|", []string{})
|
||||
|
||||
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 = ParsePreferredStringProps(envvars.StringSlice("PREFERRED_LANGUAGES", "|", []string{}))
|
||||
PreferredSubtitles = ParsePreferredStringProps(envvars.StringSlice("PREFERRED_SUBTITLES", "|", []string{}))
|
||||
PreferredResolutions = ParsePreferredProps(envvars.StringSlice("PREFERRED_RESOLUTIONS", "|", []string{}), model.ParseResolution)
|
||||
|
||||
/*
|
||||
TODO
|
||||
|
||||
PreferMoreLanguages = envvars.Bool("PREFERER_MORE_LANGUAGES", false)
|
||||
PreferMoreSubtitles = envvars.Bool("PREFERER_MORE_SUBTITLES", false)
|
||||
*/
|
||||
)
|
||||
|
@ -1,25 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"git.milar.in/milarin/slices"
|
||||
)
|
||||
|
||||
func HasFileEssentialProperties(props *FileProperties) bool {
|
||||
if props.Resolution < MinResolution || props.Resolution > MaxResolution {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, essentialLanguage := range EssentialLanguages {
|
||||
if !slices.Contains(props.Languages, essentialLanguage) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
for _, essentialSubtitle := range EssentialSubtitles {
|
||||
if !slices.Contains(props.Subtitles, essentialSubtitle) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
package main
|
||||
|
||||
type FilePriority struct {
|
||||
Properties *FileProperties
|
||||
Priority int
|
||||
PreferredProperties map[string]int
|
||||
}
|
||||
|
||||
func NewFilePriority(props *FileProperties) *FilePriority {
|
||||
priority, preferredProperties := DeterminePriority(props)
|
||||
|
||||
return &FilePriority{
|
||||
Properties: props,
|
||||
Priority: priority,
|
||||
PreferredProperties: preferredProperties,
|
||||
}
|
||||
}
|
@ -1,74 +1,65 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
// type FileProperties struct {
|
||||
// Filepath string
|
||||
// Languages []string
|
||||
// Subtitles []string
|
||||
// Resolution model.Resolution
|
||||
// }
|
||||
|
||||
"git.milar.in/nyaanime/model"
|
||||
"git.milar.in/nyaanime/parsers"
|
||||
ffprobe "gopkg.in/vansante/go-ffprobe.v2"
|
||||
)
|
||||
// var _ model.PropertyHolder = &FileProperties{}
|
||||
|
||||
type FileProperties struct {
|
||||
Filepath string
|
||||
Languages []string
|
||||
Subtitles []string
|
||||
Resolution model.Resolution
|
||||
}
|
||||
// // TODO cache
|
||||
// func AnalyzeFile(path string) (*FileProperties, error) {
|
||||
// props := &FileProperties{Filepath: path}
|
||||
|
||||
var _ model.PropertyHolder = &FileProperties{}
|
||||
// file, err := os.Open(path)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// defer file.Close()
|
||||
|
||||
// TODO cache
|
||||
func AnalyzeFile(path string) (*FileProperties, error) {
|
||||
props := &FileProperties{Filepath: path}
|
||||
// data, err := ffprobe.ProbeReader(context.Background(), file)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
// defaultVideoLang := ""
|
||||
// for _, s := range data.StreamType(ffprobe.StreamVideo) {
|
||||
// if s.Disposition.Default > 0 {
|
||||
// props.Resolution = model.Resolution(s.Height)
|
||||
// defaultVideoLang = parsers.ParseLanguage(s.Tags.Language)
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
|
||||
data, err := ffprobe.ProbeReader(context.Background(), file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// for _, s := range data.StreamType(ffprobe.StreamAudio) {
|
||||
// if s.Tags.Language != "" {
|
||||
// props.Languages = append(props.Languages, parsers.ParseLanguage(s.Tags.Language))
|
||||
// } else if s.Disposition.Default > 0 {
|
||||
// props.Languages = append(props.Languages, defaultVideoLang)
|
||||
// }
|
||||
// }
|
||||
|
||||
defaultVideoLang := ""
|
||||
for _, s := range data.StreamType(ffprobe.StreamVideo) {
|
||||
if s.Disposition.Default > 0 {
|
||||
props.Resolution = model.Resolution(s.Height)
|
||||
defaultVideoLang = parsers.ParseLanguage(s.Tags.Language)
|
||||
break
|
||||
}
|
||||
}
|
||||
// for _, s := range data.StreamType(ffprobe.StreamSubtitle) {
|
||||
// if s.Tags.Language != "" {
|
||||
// props.Subtitles = append(props.Subtitles, parsers.ParseLanguage(s.Tags.Language))
|
||||
// } else if s.Disposition.Default > 0 {
|
||||
// props.Subtitles = append(props.Subtitles, defaultVideoLang)
|
||||
// }
|
||||
// }
|
||||
|
||||
for _, s := range data.StreamType(ffprobe.StreamAudio) {
|
||||
if s.Tags.Language != "" {
|
||||
props.Languages = append(props.Languages, parsers.ParseLanguage(s.Tags.Language))
|
||||
} else if s.Disposition.Default > 0 {
|
||||
props.Languages = append(props.Languages, defaultVideoLang)
|
||||
}
|
||||
}
|
||||
// return props, nil
|
||||
// }
|
||||
|
||||
for _, s := range data.StreamType(ffprobe.StreamSubtitle) {
|
||||
if s.Tags.Language != "" {
|
||||
props.Subtitles = append(props.Subtitles, parsers.ParseLanguage(s.Tags.Language))
|
||||
} else if s.Disposition.Default > 0 {
|
||||
props.Subtitles = append(props.Subtitles, defaultVideoLang)
|
||||
}
|
||||
}
|
||||
// func (fp *FileProperties) GetLanguages() []string {
|
||||
// return fp.Languages
|
||||
// }
|
||||
|
||||
return props, nil
|
||||
}
|
||||
// func (fp *FileProperties) GetSubtitles() []string {
|
||||
// return fp.Subtitles
|
||||
// }
|
||||
|
||||
func (fp *FileProperties) GetLanguages() []string {
|
||||
return fp.Languages
|
||||
}
|
||||
|
||||
func (fp *FileProperties) GetSubtitles() []string {
|
||||
return fp.Subtitles
|
||||
}
|
||||
|
||||
func (fp *FileProperties) GetResolution() model.Resolution {
|
||||
return fp.Resolution
|
||||
}
|
||||
// func (fp *FileProperties) GetResolution() model.Resolution {
|
||||
// return fp.Resolution
|
||||
// }
|
||||
|
17
go.mod
17
go.mod
@ -3,21 +3,21 @@ module git.milar.in/nyaanime/downloader
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
git.milar.in/milarin/adverr v1.0.0
|
||||
git.milar.in/milarin/anilist v1.5.0
|
||||
git.milar.in/milarin/channel v0.0.9
|
||||
git.milar.in/milarin/adverr v1.1.0
|
||||
git.milar.in/milarin/anilist v1.5.1
|
||||
git.milar.in/milarin/envvars/v2 v2.0.0
|
||||
git.milar.in/milarin/gmath v0.0.2
|
||||
git.milar.in/milarin/slices v0.0.3
|
||||
git.milar.in/milarin/slices v0.0.6
|
||||
git.milar.in/milarin/tprint v0.0.7
|
||||
git.milar.in/nyaanime/model v0.0.0-20220825203652-2504dcc85f73
|
||||
git.milar.in/nyaanime/parsers v0.0.0-20220825163541-c840c68b761d
|
||||
git.milar.in/nyaanime/logic v0.0.0-20230113101007-e0018688e419
|
||||
git.milar.in/nyaanime/model v0.0.0-20230113095840-5eb2822653c3
|
||||
git.milar.in/nyaanime/parsers v0.0.0-20221207192513-e7bce7c418d8
|
||||
github.com/PuerkitoBio/goquery v1.8.0
|
||||
github.com/fatih/color v1.13.0
|
||||
gopkg.in/vansante/go-ffprobe.v2 v2.1.0
|
||||
)
|
||||
|
||||
require (
|
||||
git.milar.in/milarin/channel v0.0.14 // indirect
|
||||
git.milar.in/milarin/gmath v0.0.3 // indirect
|
||||
github.com/andybalholm/cascadia v1.3.1 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.16 // indirect
|
||||
@ -25,4 +25,5 @@ require (
|
||||
github.com/rivo/uniseg v0.3.4 // indirect
|
||||
golang.org/x/net v0.0.0-20220812174116-3211cb980234 // indirect
|
||||
golang.org/x/sys v0.0.0-20220823224334-20c2bfdbfe24 // indirect
|
||||
gopkg.in/vansante/go-ffprobe.v2 v2.1.1 // indirect
|
||||
)
|
||||
|
34
go.sum
34
go.sum
@ -1,21 +1,23 @@
|
||||
git.milar.in/milarin/adverr v1.0.0 h1:9povP074PjpoZlNuiKPa9w6fDA8a4RSI5hEDo5T9Tqo=
|
||||
git.milar.in/milarin/adverr v1.0.0/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.9 h1:vYJnXOaIn/+mng1+8CyepQgDfRse2s4FSLNc7zv85To=
|
||||
git.milar.in/milarin/channel v0.0.9/go.mod h1:We83LTI8S7u7II3pD+A2ChCDWJfCkcBUCUqii9HjTtM=
|
||||
git.milar.in/milarin/adverr v1.1.0 h1:jD9WnOvs40lfMhvqQ7cllOaRJNBMWr1f07/s9jAadp0=
|
||||
git.milar.in/milarin/adverr v1.1.0/go.mod h1:joU9sBb7ySyNv4SpTXB0Z4o1mjXsArBw4N27wjgzj9E=
|
||||
git.milar.in/milarin/anilist v1.5.1 h1:gW08WaAvXxC5/+P1QCjyfa5YtaiY9XvF5x/8G4orT6I=
|
||||
git.milar.in/milarin/anilist v1.5.1/go.mod h1:8PTHXFMA45JpfRFIpcdrKwDHue8fbT/wwV1BuHFn6c0=
|
||||
git.milar.in/milarin/channel v0.0.14 h1:1jBaKNNOK/mmMMgC7yVW1Cgnbb7q9aKBXZ5WnWptDek=
|
||||
git.milar.in/milarin/channel v0.0.14/go.mod h1:We83LTI8S7u7II3pD+A2ChCDWJfCkcBUCUqii9HjTtM=
|
||||
git.milar.in/milarin/envvars/v2 v2.0.0 h1:DWRQCWaHqzDD8NGpSgv5tYLuF9A/dVFPAtTvz3oiIqE=
|
||||
git.milar.in/milarin/envvars/v2 v2.0.0/go.mod h1:HkdEi+gG2lJSmVq547bTlQV4qQ0hO333bE8IrE0B9yY=
|
||||
git.milar.in/milarin/gmath v0.0.2 h1:avz+75f8XqAYA1wEB6kis0R5xvRuepBKTqBuJBjh6Yw=
|
||||
git.milar.in/milarin/gmath v0.0.2/go.mod h1:HDLftG5RLpiNGKiIWh+O2G1PYkNzyLDADO8Cd/1abiE=
|
||||
git.milar.in/milarin/slices v0.0.3 h1:kzaLrE/G4rO2DQq3nVk2TYbuqOsiauLHClVUpgSZM8s=
|
||||
git.milar.in/milarin/slices v0.0.3/go.mod h1:XRNfE99aNKeaPOY1phjOlpIQqeGCW1LOqqh8UHS+vAk=
|
||||
git.milar.in/milarin/gmath v0.0.3 h1:ii6rKNItS55O/wtIFhD1cTN2BMwDZjTBmiOocKURvxM=
|
||||
git.milar.in/milarin/gmath v0.0.3/go.mod h1:HDLftG5RLpiNGKiIWh+O2G1PYkNzyLDADO8Cd/1abiE=
|
||||
git.milar.in/milarin/slices v0.0.6 h1:AQoSarZ58WHYol9c6woWJSe8wFpPC2RC4cvIlZpfg9s=
|
||||
git.milar.in/milarin/slices v0.0.6/go.mod h1:NOr53AOeur/qscu/FBj3lsFR262PNYBccLYSTCAXRk4=
|
||||
git.milar.in/milarin/tprint v0.0.7 h1:dvm4l4BhXOie4vtnRMZii0WTLTz2wju8wyUPB/lNeVo=
|
||||
git.milar.in/milarin/tprint v0.0.7/go.mod h1:UwW/B+0cTCbN5hi0bQBE9rIRgLkq+x4V751rrS2KVoI=
|
||||
git.milar.in/nyaanime/model v0.0.0-20220825203652-2504dcc85f73 h1:fnHpasTZiZyVrXH6Yt0pARibWUveO8pLO+3upK1tboc=
|
||||
git.milar.in/nyaanime/model v0.0.0-20220825203652-2504dcc85f73/go.mod h1:DqRaTKOh6JRSTVLmoTQ6jrnDvGZvWrfKj3nimr6yuKs=
|
||||
git.milar.in/nyaanime/parsers v0.0.0-20220825163541-c840c68b761d h1:LskYoxsMQ3UgPF/6P7p7qm5xYCoe7GrYH1MZnwMTdEI=
|
||||
git.milar.in/nyaanime/parsers v0.0.0-20220825163541-c840c68b761d/go.mod h1:qm6fIFBFs90uz7IJ8RKgDir0K8Fa8isixGLgrtC6kgU=
|
||||
git.milar.in/nyaanime/logic v0.0.0-20230113101007-e0018688e419 h1:Am9LlW/2UJuUvWsoA2c9FPBJpTZfSWjHCkWSHlAoX8M=
|
||||
git.milar.in/nyaanime/logic v0.0.0-20230113101007-e0018688e419/go.mod h1:SkgdD87uNP60swIBtrDRZPBF5rLIgBLL6OetP1sETLY=
|
||||
git.milar.in/nyaanime/model v0.0.0-20230113095840-5eb2822653c3 h1:mXcEA47FQQzeSDXE3UvhNfIt4fBfpDSq1/f0r+jbHpY=
|
||||
git.milar.in/nyaanime/model v0.0.0-20230113095840-5eb2822653c3/go.mod h1:kPWLDvFrhc1Uf77gxsBOxNeJ5JTVF2HhVs1IdVcw0tg=
|
||||
git.milar.in/nyaanime/parsers v0.0.0-20221207192513-e7bce7c418d8 h1:vb7jasvTdan0E8VY5snnRj1Xe+60NA7Lpn+GSYE6pW0=
|
||||
git.milar.in/nyaanime/parsers v0.0.0-20221207192513-e7bce7c418d8/go.mod h1:GG4vtUIfxopZc/+Y8OAa//vWJw/m6aeoGN7fw6SLiEM=
|
||||
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=
|
||||
@ -48,5 +50,5 @@ golang.org/x/sys v0.0.0-20220823224334-20c2bfdbfe24/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
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=
|
||||
gopkg.in/vansante/go-ffprobe.v2 v2.1.0 h1:Gh8oVkvOSZG/DgEMmBw8h4oLvJhSQHROEGp3TaVGq08=
|
||||
gopkg.in/vansante/go-ffprobe.v2 v2.1.0/go.mod h1:qF0AlAjk7Nqzqf3y333Ly+KxN3cKF2JqA3JT5ZheUGE=
|
||||
gopkg.in/vansante/go-ffprobe.v2 v2.1.1 h1:DIh5fMn+tlBvG7pXyUZdemVmLdERnf2xX6XOFF+0BBU=
|
||||
gopkg.in/vansante/go-ffprobe.v2 v2.1.1/go.mod h1:qF0AlAjk7Nqzqf3y333Ly+KxN3cKF2JqA3JT5ZheUGE=
|
||||
|
@ -4,9 +4,9 @@ import (
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"git.milar.in/milarin/anilist"
|
||||
"git.milar.in/nyaanime/logic"
|
||||
"git.milar.in/nyaanime/model"
|
||||
)
|
||||
|
||||
@ -16,22 +16,8 @@ type AnimePathPatternData struct {
|
||||
Ext string
|
||||
}
|
||||
|
||||
func GetAnimeEpFilepath(animeEp model.AnimeEpisode, ext string) string {
|
||||
tmplData := AnimePathPatternData{
|
||||
Title: animeEp.Anime.Title,
|
||||
Episode: animeEp.Episode,
|
||||
Ext: ext,
|
||||
}
|
||||
|
||||
b := new(strings.Builder)
|
||||
if err := AnimeEpFilepathPattern.Execute(b, tmplData); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return filepath.Join(AnimePath, b.String())
|
||||
}
|
||||
|
||||
func AnimeEpExistsLocally(animeEp model.AnimeEpisode) bool {
|
||||
animeEpPath := GetAnimeEpFilepath(animeEp, "*")
|
||||
animeEpPath := logic.GetAnimeEpFilepath(animeEp, "*")
|
||||
|
||||
files, err := filepath.Glob(animeEpPath)
|
||||
if err != nil {
|
||||
@ -41,43 +27,14 @@ func AnimeEpExistsLocally(animeEp model.AnimeEpisode) bool {
|
||||
return len(files) > 0
|
||||
}
|
||||
|
||||
func GetAnimeEpProps(animeEp model.AnimeEpisode) (*FilePriority, bool) {
|
||||
animeEpPath := GetAnimeEpFilepath(animeEp, "*")
|
||||
files, err := filepath.Glob(animeEpPath)
|
||||
if err != nil {
|
||||
panic(ErrInvalidGlobSyntax.Wrap(err, animeEpPath))
|
||||
}
|
||||
|
||||
var mostPrio *FilePriority
|
||||
|
||||
for _, file := range files {
|
||||
props, err := AnalyzeFile(file)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if !HasFileEssentialProperties(props) {
|
||||
continue
|
||||
}
|
||||
|
||||
fp := NewFilePriority(props)
|
||||
|
||||
if mostPrio == nil || fp.Priority > mostPrio.Priority {
|
||||
mostPrio = fp
|
||||
}
|
||||
}
|
||||
|
||||
return mostPrio, mostPrio != nil
|
||||
}
|
||||
|
||||
func IsCurrentlyDownloading(animeEp model.AnimeEpisode) bool {
|
||||
animeEpPath := GetAnimeEpFilepath(animeEp, "lock")
|
||||
animeEpPath := logic.GetAnimeEpFilepath(animeEp, "lock")
|
||||
_, err := os.Stat(animeEpPath)
|
||||
return !errors.Is(err, os.ErrNotExist)
|
||||
}
|
||||
|
||||
func SetCurrentlyDownloading(animeEp model.AnimeEpisode) error {
|
||||
animeEpPath := GetAnimeEpFilepath(animeEp, "lock")
|
||||
animeEpPath := logic.GetAnimeEpFilepath(animeEp, "lock")
|
||||
|
||||
dir := filepath.Dir(animeEpPath)
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
|
14
main.go
14
main.go
@ -3,25 +3,27 @@ package main
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"git.milar.in/nyaanime/logic"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println("generated priority values:")
|
||||
fmt.Print(Map2Table("language", PreferredLanguages))
|
||||
fmt.Print(Map2Table("subtitle", PreferredSubtitles))
|
||||
fmt.Print(Map2Table("resolution", PreferredResolutions))
|
||||
fmt.Print(Map2Table("language", logic.PreferredLanguages))
|
||||
fmt.Print(Map2Table("subtitle", logic.PreferredSubtitles))
|
||||
fmt.Print(Map2Table("resolution", logic.PreferredResolutions))
|
||||
fmt.Println()
|
||||
|
||||
// get access token once at startup to be sure that an access token is obtainable at all
|
||||
if _, err := GetAnilistAccessToken(); err != nil {
|
||||
if _, err := logic.GetAnilistAccessToken(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
ticker := time.NewTicker(PollRate)
|
||||
defer ticker.Stop()
|
||||
|
||||
checkTorrents()
|
||||
CheckTorrents()
|
||||
for range ticker.C {
|
||||
checkTorrents()
|
||||
CheckTorrents()
|
||||
}
|
||||
}
|
||||
|
@ -1,53 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"git.milar.in/milarin/gmath"
|
||||
"git.milar.in/milarin/slices"
|
||||
)
|
||||
|
||||
// ParsePreferredProps parses properties and its corresponding priority.
|
||||
// priorities are distributed exponentially in reverse order.
|
||||
//
|
||||
// That means the last entry will have priority 1, the second last 2, then 4, 8 and so on.
|
||||
//
|
||||
// Properties with name "_" will be ignored and function as a placeholder to increase the priority
|
||||
// of the properties which comes before them.
|
||||
//
|
||||
// Properties separated by comma will have the same priorities.
|
||||
//
|
||||
// str usually is the return value of a call to strings.Split(str, "|")
|
||||
//
|
||||
// Examples:
|
||||
// str = "a|b|c" -> c:1 b:2 a:4
|
||||
// str = "a|b|_|c" -> c:1 b:4 a:8
|
||||
// str = "a,b|c" -> c:1 b:4 a:4
|
||||
// str = "d|_|a,b|c" -> c:1 b:4 a:4 d:16
|
||||
//
|
||||
// Additionally, properties can be converted to a generic type with the converter function
|
||||
func ParsePreferredProps[T comparable](str []string, converter func(string) (T, error)) map[T]int {
|
||||
props := map[T]int{}
|
||||
|
||||
for i, subProps := range slices.Reverse(str) {
|
||||
if subProps == "_" {
|
||||
continue
|
||||
}
|
||||
|
||||
propPriority := gmath.Pow(2, i)
|
||||
for _, subProp := range strings.Split(subProps, ",") {
|
||||
subPropT, err := converter(subProp)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
props[subPropT] = propPriority
|
||||
}
|
||||
}
|
||||
|
||||
return props
|
||||
}
|
||||
|
||||
func ParsePreferredStringProps(str []string) map[string]int {
|
||||
return ParsePreferredProps(str, func(s string) (string, error) { return s, nil })
|
||||
}
|
@ -3,6 +3,7 @@ package main
|
||||
import (
|
||||
"git.milar.in/milarin/anilist"
|
||||
"git.milar.in/milarin/slices"
|
||||
"git.milar.in/nyaanime/logic"
|
||||
"git.milar.in/nyaanime/model"
|
||||
)
|
||||
|
||||
@ -33,33 +34,33 @@ func FilterEssentialTorrents(allTorrents map[model.AnimeEpisode][]*model.ParsedT
|
||||
}
|
||||
|
||||
func HasEssentialProperties(torrent *model.ParsedTorrent) bool {
|
||||
if torrent.Resolution < MinResolution || torrent.Resolution > MaxResolution {
|
||||
if torrent.Resolution < logic.MinResolution || torrent.Resolution > logic.MaxResolution {
|
||||
return false
|
||||
}
|
||||
|
||||
if torrent.Torrent.Seeders < MinSeeders || torrent.Torrent.Seeders > MaxSeeders {
|
||||
if torrent.Torrent.Seeders < logic.MinSeeders || torrent.Torrent.Seeders > logic.MaxSeeders {
|
||||
return false
|
||||
}
|
||||
|
||||
if torrent.Torrent.Leechers < MinLeechers || torrent.Torrent.Leechers > MaxLeechers {
|
||||
if torrent.Torrent.Leechers < logic.MinLeechers || torrent.Torrent.Leechers > logic.MaxLeechers {
|
||||
return false
|
||||
}
|
||||
|
||||
if torrent.Torrent.Downloads < MinDownloads || torrent.Torrent.Downloads > MaxDownloads {
|
||||
if torrent.Torrent.Downloads < logic.MinDownloads || torrent.Torrent.Downloads > logic.MaxDownloads {
|
||||
return false
|
||||
}
|
||||
|
||||
if TrustedOnly && !torrent.Torrent.Trusted {
|
||||
if logic.TrustedOnly && !torrent.Torrent.Trusted {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, essentialLanguage := range EssentialLanguages {
|
||||
for _, essentialLanguage := range logic.EssentialLanguages {
|
||||
if !slices.Contains(torrent.Languages, essentialLanguage) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
for _, essentialSubtitle := range EssentialSubtitles {
|
||||
for _, essentialSubtitle := range logic.EssentialSubtitles {
|
||||
if !slices.Contains(torrent.Subtitles, essentialSubtitle) {
|
||||
return false
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
"git.milar.in/milarin/adverr"
|
||||
"git.milar.in/nyaanime/logic"
|
||||
"git.milar.in/nyaanime/model"
|
||||
"git.milar.in/nyaanime/parsers"
|
||||
)
|
||||
@ -13,7 +14,7 @@ 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)
|
||||
anime, err := logic.SearchAnimeByTitle(parsedTorrent.OriginalAnimeTitle)
|
||||
if err != nil {
|
||||
return parsedTorrent, ErrTorrentParseFailed.Wrap(err, torrent.ID, parser.String())
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package main
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.milar.in/nyaanime/logic"
|
||||
"git.milar.in/nyaanime/model"
|
||||
)
|
||||
|
||||
@ -13,7 +14,7 @@ type TorrentPriority struct {
|
||||
}
|
||||
|
||||
func NewTorrentPriority(torrent *model.ParsedTorrent) *TorrentPriority {
|
||||
priority, preferredProperties := DeterminePriority(torrent)
|
||||
priority, preferredProperties := logic.DeterminePriority(torrent)
|
||||
|
||||
return &TorrentPriority{
|
||||
ParsedTorrent: torrent,
|
||||
|
@ -26,28 +26,3 @@ func GetTorrentsWithMaxPrioByAnimeEp(torrents map[model.AnimeEpisode][]*model.Pa
|
||||
|
||||
return torrentsWithPrio
|
||||
}
|
||||
|
||||
func DeterminePriority(props model.PropertyHolder) (priority int, preferredProperties map[string]int) {
|
||||
preferredProperties = map[string]int{}
|
||||
|
||||
for _, lang := range props.GetLanguages() {
|
||||
if langPriority, ok := PreferredLanguages[lang]; ok {
|
||||
priority += langPriority
|
||||
preferredProperties["lang/"+lang] = langPriority
|
||||
}
|
||||
}
|
||||
|
||||
for _, sub := range props.GetSubtitles() {
|
||||
if subPriority, ok := PreferredSubtitles[sub]; ok {
|
||||
priority += subPriority
|
||||
preferredProperties["sub/"+sub] = subPriority
|
||||
}
|
||||
}
|
||||
|
||||
if prefRes, ok := PreferredResolutions[props.GetResolution()]; ok {
|
||||
priority += prefRes
|
||||
preferredProperties["res/"+props.GetResolution().String()] = prefRes
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
31
utils.go
31
utils.go
@ -2,42 +2,17 @@ package main
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"git.milar.in/milarin/anilist"
|
||||
"git.milar.in/milarin/slices"
|
||||
"git.milar.in/milarin/tprint"
|
||||
"git.milar.in/nyaanime/model"
|
||||
)
|
||||
|
||||
var AllMediaListStatuses = []anilist.MediaListStatus{
|
||||
anilist.MediaListStatusCurrent,
|
||||
anilist.MediaListStatusPlanning,
|
||||
anilist.MediaListStatusCompleted,
|
||||
anilist.MediaListStatusDropped,
|
||||
anilist.MediaListStatusPaused,
|
||||
anilist.MediaListStatusRepeating,
|
||||
}
|
||||
|
||||
func ParseMediaListStatus(str string) (anilist.MediaListStatus, error) {
|
||||
s := anilist.MediaListStatus(strings.ToUpper(str))
|
||||
|
||||
allStatusesStr := slices.Map(AllMediaListStatuses, func(status anilist.MediaListStatus) string {
|
||||
return string(status)
|
||||
})
|
||||
|
||||
if !slices.Contains(AllMediaListStatuses, s) {
|
||||
return s, ErrInvalidAnimeStatus.New(s, strings.Join(allStatusesStr, ","))
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func Map2Table[K comparable](title string, m map[K]int) string {
|
||||
table := tprint.NewTable(title, "priority")
|
||||
|
||||
entries := make([]Pair[K, int], 0, len(m))
|
||||
entries := make([]model.Pair[K, int], 0, len(m))
|
||||
for name, priority := range m {
|
||||
entries = append(entries, Pair[K, int]{name, priority})
|
||||
entries = append(entries, model.Pair[K, int]{First: name, Second: priority})
|
||||
}
|
||||
sort.Slice(entries, func(i, j int) bool { return entries[i].Second > entries[j].Second })
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user