mirror of
https://github.com/5rahim/seanime
synced 2026-04-18 22:24:55 +02:00
fix semver function, flawed tests
refactor: centralize metadata provider
This commit is contained in:
11
.golangci.yml
Normal file
11
.golangci.yml
Normal file
@@ -0,0 +1,11 @@
|
||||
run:
|
||||
concurrency: 4
|
||||
timeout: 1m
|
||||
issues-exit-code: 1
|
||||
tests: true
|
||||
|
||||
linters:
|
||||
disable-all: true
|
||||
|
||||
enable:
|
||||
- exhaustruct
|
||||
@@ -23586,75 +23586,15 @@
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"filepath": "../internal/api/metadata/provider.go",
|
||||
"filename": "provider.go",
|
||||
"name": "Provider",
|
||||
"formattedName": "Metadata_Provider",
|
||||
"filepath": "../internal/api/metadata/anime.go",
|
||||
"filename": "anime.go",
|
||||
"name": "AnimeWrapperImpl",
|
||||
"formattedName": "Metadata_AnimeWrapperImpl",
|
||||
"package": "metadata",
|
||||
"fields": [
|
||||
{
|
||||
"name": "logger",
|
||||
"jsonName": "logger",
|
||||
"goType": "zerolog.Logger",
|
||||
"typescriptType": "Logger",
|
||||
"usedStructName": "zerolog.Logger",
|
||||
"required": false,
|
||||
"public": false,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "fileCacher",
|
||||
"jsonName": "fileCacher",
|
||||
"goType": "filecache.Cacher",
|
||||
"typescriptType": "Filecache_Cacher",
|
||||
"usedStructName": "filecache.Cacher",
|
||||
"required": false,
|
||||
"public": false,
|
||||
"comments": []
|
||||
}
|
||||
],
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"filepath": "../internal/api/metadata/provider.go",
|
||||
"filename": "provider.go",
|
||||
"name": "NewProviderOptions",
|
||||
"formattedName": "Metadata_NewProviderOptions",
|
||||
"package": "metadata",
|
||||
"fields": [
|
||||
{
|
||||
"name": "Logger",
|
||||
"jsonName": "Logger",
|
||||
"goType": "zerolog.Logger",
|
||||
"typescriptType": "Logger",
|
||||
"usedStructName": "zerolog.Logger",
|
||||
"required": false,
|
||||
"public": true,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "FileCacher",
|
||||
"jsonName": "FileCacher",
|
||||
"goType": "filecache.Cacher",
|
||||
"typescriptType": "Filecache_Cacher",
|
||||
"usedStructName": "filecache.Cacher",
|
||||
"required": false,
|
||||
"public": true,
|
||||
"comments": []
|
||||
}
|
||||
],
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"filepath": "../internal/api/metadata/wrapper.go",
|
||||
"filename": "wrapper.go",
|
||||
"name": "MediaWrapper",
|
||||
"formattedName": "Metadata_MediaWrapper",
|
||||
"package": "metadata",
|
||||
"fields": [
|
||||
{
|
||||
"name": "anizipMedia",
|
||||
"jsonName": "anizipMedia",
|
||||
"name": "metadata",
|
||||
"jsonName": "metadata",
|
||||
"goType": "",
|
||||
"typescriptType": "any",
|
||||
"required": true,
|
||||
@@ -23705,22 +23645,61 @@
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"filepath": "../internal/api/metadata/wrapper.go",
|
||||
"filename": "wrapper.go",
|
||||
"name": "NewMediaWrapperOptions",
|
||||
"formattedName": "Metadata_NewMediaWrapperOptions",
|
||||
"filepath": "../internal/api/metadata/provider.go",
|
||||
"filename": "provider.go",
|
||||
"name": "ProviderImpl",
|
||||
"formattedName": "Metadata_ProviderImpl",
|
||||
"package": "metadata",
|
||||
"fields": [
|
||||
{
|
||||
"name": "AnizipMedia",
|
||||
"jsonName": "AnizipMedia",
|
||||
"goType": "anizip.Media",
|
||||
"typescriptType": "Anizip_Media",
|
||||
"usedStructName": "anizip.Media",
|
||||
"name": "logger",
|
||||
"jsonName": "logger",
|
||||
"goType": "zerolog.Logger",
|
||||
"typescriptType": "Logger",
|
||||
"usedStructName": "zerolog.Logger",
|
||||
"required": false,
|
||||
"public": true,
|
||||
"public": false,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "fileCacher",
|
||||
"jsonName": "fileCacher",
|
||||
"goType": "filecache.Cacher",
|
||||
"typescriptType": "Filecache_Cacher",
|
||||
"usedStructName": "filecache.Cacher",
|
||||
"required": false,
|
||||
"public": false,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "animeMetadataCache",
|
||||
"jsonName": "animeMetadataCache",
|
||||
"goType": "",
|
||||
"typescriptType": "any",
|
||||
"required": false,
|
||||
"public": false,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "anizipCache",
|
||||
"jsonName": "anizipCache",
|
||||
"goType": "anizip.Cache",
|
||||
"typescriptType": "Anizip_Cache",
|
||||
"usedStructName": "anizip.Cache",
|
||||
"required": false,
|
||||
"public": false,
|
||||
"comments": []
|
||||
}
|
||||
],
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"filepath": "../internal/api/metadata/provider.go",
|
||||
"filename": "provider.go",
|
||||
"name": "NewProviderImplOptions",
|
||||
"formattedName": "Metadata_NewProviderImplOptions",
|
||||
"package": "metadata",
|
||||
"fields": [
|
||||
{
|
||||
"name": "Logger",
|
||||
"jsonName": "Logger",
|
||||
@@ -23730,15 +23709,217 @@
|
||||
"required": false,
|
||||
"public": true,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "FileCacher",
|
||||
"jsonName": "FileCacher",
|
||||
"goType": "filecache.Cacher",
|
||||
"typescriptType": "Filecache_Cacher",
|
||||
"usedStructName": "filecache.Cacher",
|
||||
"required": false,
|
||||
"public": true,
|
||||
"comments": []
|
||||
}
|
||||
],
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"filepath": "../internal/api/metadata/wrapper.go",
|
||||
"filename": "wrapper.go",
|
||||
"name": "MediaWrapperEpisodeMetadata",
|
||||
"formattedName": "Metadata_MediaWrapperEpisodeMetadata",
|
||||
"filepath": "../internal/api/metadata/types.go",
|
||||
"filename": "types.go",
|
||||
"name": "Platform",
|
||||
"formattedName": "Metadata_Platform",
|
||||
"package": "metadata",
|
||||
"fields": [],
|
||||
"aliasOf": {
|
||||
"goType": "string",
|
||||
"typescriptType": "string",
|
||||
"declaredValues": [
|
||||
"\"anilist\"",
|
||||
"\"mal\""
|
||||
]
|
||||
},
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"filepath": "../internal/api/metadata/types.go",
|
||||
"filename": "types.go",
|
||||
"name": "AnimeMetadata",
|
||||
"formattedName": "Metadata_AnimeMetadata",
|
||||
"package": "metadata",
|
||||
"fields": [
|
||||
{
|
||||
"name": "Titles",
|
||||
"jsonName": "titles",
|
||||
"goType": "map[string]string",
|
||||
"typescriptType": "Record\u003cstring, string\u003e",
|
||||
"required": false,
|
||||
"public": true,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "Episodes",
|
||||
"jsonName": "episodes",
|
||||
"goType": "map[string]EpisodeMetadata",
|
||||
"typescriptType": "Record\u003cstring, Metadata_EpisodeMetadata\u003e",
|
||||
"usedStructName": "metadata.EpisodeMetadata",
|
||||
"required": false,
|
||||
"public": true,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "EpisodeCount",
|
||||
"jsonName": "episodeCount",
|
||||
"goType": "int",
|
||||
"typescriptType": "number",
|
||||
"required": true,
|
||||
"public": true,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "SpecialCount",
|
||||
"jsonName": "specialCount",
|
||||
"goType": "int",
|
||||
"typescriptType": "number",
|
||||
"required": true,
|
||||
"public": true,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "Mappings",
|
||||
"jsonName": "mappings",
|
||||
"goType": "AnimeMappings",
|
||||
"typescriptType": "Metadata_AnimeMappings",
|
||||
"usedStructName": "metadata.AnimeMappings",
|
||||
"required": false,
|
||||
"public": true,
|
||||
"comments": []
|
||||
}
|
||||
],
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"filepath": "../internal/api/metadata/types.go",
|
||||
"filename": "types.go",
|
||||
"name": "AnimeMappings",
|
||||
"formattedName": "Metadata_AnimeMappings",
|
||||
"package": "metadata",
|
||||
"fields": [
|
||||
{
|
||||
"name": "AnimeplanetId",
|
||||
"jsonName": "animeplanetId",
|
||||
"goType": "string",
|
||||
"typescriptType": "string",
|
||||
"required": true,
|
||||
"public": true,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "KitsuId",
|
||||
"jsonName": "kitsuId",
|
||||
"goType": "int",
|
||||
"typescriptType": "number",
|
||||
"required": true,
|
||||
"public": true,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "MalId",
|
||||
"jsonName": "malId",
|
||||
"goType": "int",
|
||||
"typescriptType": "number",
|
||||
"required": true,
|
||||
"public": true,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "Type",
|
||||
"jsonName": "type",
|
||||
"goType": "string",
|
||||
"typescriptType": "string",
|
||||
"required": true,
|
||||
"public": true,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "AnilistId",
|
||||
"jsonName": "anilistId",
|
||||
"goType": "int",
|
||||
"typescriptType": "number",
|
||||
"required": true,
|
||||
"public": true,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "AnisearchId",
|
||||
"jsonName": "anisearchId",
|
||||
"goType": "int",
|
||||
"typescriptType": "number",
|
||||
"required": true,
|
||||
"public": true,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "AnidbId",
|
||||
"jsonName": "anidbId",
|
||||
"goType": "int",
|
||||
"typescriptType": "number",
|
||||
"required": true,
|
||||
"public": true,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "NotifymoeId",
|
||||
"jsonName": "notifymoeId",
|
||||
"goType": "string",
|
||||
"typescriptType": "string",
|
||||
"required": true,
|
||||
"public": true,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "LivechartId",
|
||||
"jsonName": "livechartId",
|
||||
"goType": "int",
|
||||
"typescriptType": "number",
|
||||
"required": true,
|
||||
"public": true,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "ThetvdbId",
|
||||
"jsonName": "thetvdbId",
|
||||
"goType": "int",
|
||||
"typescriptType": "number",
|
||||
"required": true,
|
||||
"public": true,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "ImdbId",
|
||||
"jsonName": "imdbId",
|
||||
"goType": "string",
|
||||
"typescriptType": "string",
|
||||
"required": true,
|
||||
"public": true,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "ThemoviedbId",
|
||||
"jsonName": "themoviedbId",
|
||||
"goType": "string",
|
||||
"typescriptType": "string",
|
||||
"required": true,
|
||||
"public": true,
|
||||
"comments": []
|
||||
}
|
||||
],
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"filepath": "../internal/api/metadata/types.go",
|
||||
"filename": "types.go",
|
||||
"name": "EpisodeMetadata",
|
||||
"formattedName": "Metadata_EpisodeMetadata",
|
||||
"package": "metadata",
|
||||
"fields": [
|
||||
{
|
||||
@@ -23746,40 +23927,34 @@
|
||||
"jsonName": "anidbId",
|
||||
"goType": "int",
|
||||
"typescriptType": "number",
|
||||
"required": false,
|
||||
"required": true,
|
||||
"public": true,
|
||||
"comments": [
|
||||
" Episode AniDB ID"
|
||||
]
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "TvdbId",
|
||||
"jsonName": "tvdbId",
|
||||
"goType": "int64",
|
||||
"goType": "int",
|
||||
"typescriptType": "number",
|
||||
"required": false,
|
||||
"required": true,
|
||||
"public": true,
|
||||
"comments": [
|
||||
" Episode TVDB ID"
|
||||
]
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "Title",
|
||||
"jsonName": "title",
|
||||
"goType": "string",
|
||||
"typescriptType": "string",
|
||||
"required": false,
|
||||
"required": true,
|
||||
"public": true,
|
||||
"comments": [
|
||||
" Episode title"
|
||||
]
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "Image",
|
||||
"jsonName": "image",
|
||||
"goType": "string",
|
||||
"typescriptType": "string",
|
||||
"required": false,
|
||||
"required": true,
|
||||
"public": true,
|
||||
"comments": []
|
||||
},
|
||||
@@ -23788,7 +23963,7 @@
|
||||
"jsonName": "airDate",
|
||||
"goType": "string",
|
||||
"typescriptType": "string",
|
||||
"required": false,
|
||||
"required": true,
|
||||
"public": true,
|
||||
"comments": []
|
||||
},
|
||||
@@ -23797,7 +23972,7 @@
|
||||
"jsonName": "length",
|
||||
"goType": "int",
|
||||
"typescriptType": "number",
|
||||
"required": false,
|
||||
"required": true,
|
||||
"public": true,
|
||||
"comments": []
|
||||
},
|
||||
@@ -23806,7 +23981,7 @@
|
||||
"jsonName": "summary",
|
||||
"goType": "string",
|
||||
"typescriptType": "string",
|
||||
"required": false,
|
||||
"required": true,
|
||||
"public": true,
|
||||
"comments": []
|
||||
},
|
||||
@@ -23815,7 +23990,7 @@
|
||||
"jsonName": "overview",
|
||||
"goType": "string",
|
||||
"typescriptType": "string",
|
||||
"required": false,
|
||||
"required": true,
|
||||
"public": true,
|
||||
"comments": []
|
||||
},
|
||||
@@ -23824,7 +23999,43 @@
|
||||
"jsonName": "episodeNumber",
|
||||
"goType": "int",
|
||||
"typescriptType": "number",
|
||||
"required": false,
|
||||
"required": true,
|
||||
"public": true,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "Episode",
|
||||
"jsonName": "episode",
|
||||
"goType": "string",
|
||||
"typescriptType": "string",
|
||||
"required": true,
|
||||
"public": true,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "SeasonNumber",
|
||||
"jsonName": "seasonNumber",
|
||||
"goType": "int",
|
||||
"typescriptType": "number",
|
||||
"required": true,
|
||||
"public": true,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "AbsoluteEpisodeNumber",
|
||||
"jsonName": "absoluteEpisodeNumber",
|
||||
"goType": "int",
|
||||
"typescriptType": "number",
|
||||
"required": true,
|
||||
"public": true,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "AnidbEid",
|
||||
"jsonName": "anidbEid",
|
||||
"goType": "int",
|
||||
"typescriptType": "number",
|
||||
"required": true,
|
||||
"public": true,
|
||||
"comments": []
|
||||
}
|
||||
@@ -24962,18 +25173,6 @@
|
||||
"public": true,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "AnizipCache",
|
||||
"jsonName": "AnizipCache",
|
||||
"goType": "anizip.Cache",
|
||||
"typescriptType": "Anizip_Cache",
|
||||
"usedStructName": "anizip.Cache",
|
||||
"required": false,
|
||||
"public": true,
|
||||
"comments": [
|
||||
" AnizipCache holds fetched AniZip media for 30 minutes. (used by route handlers)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "AnilistClient",
|
||||
"jsonName": "AnilistClient",
|
||||
@@ -30434,10 +30633,11 @@
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "anizipMediaCache",
|
||||
"jsonName": "anizipMediaCache",
|
||||
"goType": "",
|
||||
"typescriptType": "any",
|
||||
"name": "metadataProvider",
|
||||
"jsonName": "metadataProvider",
|
||||
"goType": "metadata.Provider",
|
||||
"typescriptType": "Metadata_Provider",
|
||||
"usedStructName": "metadata.Provider",
|
||||
"required": false,
|
||||
"public": false,
|
||||
"comments": []
|
||||
@@ -31821,16 +32021,6 @@
|
||||
" All local files"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "AnizipCache",
|
||||
"jsonName": "AnizipCache",
|
||||
"goType": "anizip.Cache",
|
||||
"typescriptType": "Anizip_Cache",
|
||||
"usedStructName": "anizip.Cache",
|
||||
"required": false,
|
||||
"public": true,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "AnimeCollection",
|
||||
"jsonName": "AnimeCollection",
|
||||
@@ -31985,11 +32175,11 @@
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "AnizipMedia",
|
||||
"jsonName": "AnizipMedia",
|
||||
"goType": "anizip.Media",
|
||||
"typescriptType": "Anizip_Media",
|
||||
"usedStructName": "anizip.Media",
|
||||
"name": "AnimeMetadata",
|
||||
"jsonName": "AnimeMetadata",
|
||||
"goType": "metadata.AnimeMetadata",
|
||||
"typescriptType": "Metadata_AnimeMetadata",
|
||||
"usedStructName": "metadata.AnimeMetadata",
|
||||
"required": false,
|
||||
"public": true,
|
||||
"comments": []
|
||||
@@ -32824,16 +33014,6 @@
|
||||
"public": true,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "AnizipCache",
|
||||
"jsonName": "AnizipCache",
|
||||
"goType": "anizip.Cache",
|
||||
"typescriptType": "Anizip_Cache",
|
||||
"usedStructName": "anizip.Cache",
|
||||
"required": false,
|
||||
"public": true,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "Platform",
|
||||
"jsonName": "Platform",
|
||||
@@ -33107,11 +33287,11 @@
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "AnizipMedia",
|
||||
"jsonName": "AnizipMedia",
|
||||
"goType": "anizip.Media",
|
||||
"typescriptType": "Anizip_Media",
|
||||
"usedStructName": "anizip.Media",
|
||||
"name": "AnimeMetadata",
|
||||
"jsonName": "AnimeMetadata",
|
||||
"goType": "metadata.AnimeMetadata",
|
||||
"typescriptType": "Metadata_AnimeMetadata",
|
||||
"usedStructName": "metadata.AnimeMetadata",
|
||||
"required": false,
|
||||
"public": true,
|
||||
"comments": [
|
||||
@@ -33607,16 +33787,6 @@
|
||||
"public": true,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "AnizipCache",
|
||||
"jsonName": "AnizipCache",
|
||||
"goType": "anizip.Cache",
|
||||
"typescriptType": "Anizip_Cache",
|
||||
"usedStructName": "anizip.Cache",
|
||||
"required": false,
|
||||
"public": true,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "SilencedMediaIds",
|
||||
"jsonName": "SilencedMediaIds",
|
||||
@@ -33916,11 +34086,11 @@
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "anizipCache",
|
||||
"jsonName": "anizipCache",
|
||||
"goType": "anizip.Cache",
|
||||
"typescriptType": "Anizip_Cache",
|
||||
"usedStructName": "anizip.Cache",
|
||||
"name": "metadataProvider",
|
||||
"jsonName": "metadataProvider",
|
||||
"goType": "metadata.Provider",
|
||||
"typescriptType": "Metadata_Provider",
|
||||
"usedStructName": "metadata.Provider",
|
||||
"required": false,
|
||||
"public": false,
|
||||
"comments": []
|
||||
@@ -34032,11 +34202,11 @@
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "AnizipCache",
|
||||
"jsonName": "AnizipCache",
|
||||
"goType": "anizip.Cache",
|
||||
"typescriptType": "Anizip_Cache",
|
||||
"usedStructName": "anizip.Cache",
|
||||
"name": "MetadataProvider",
|
||||
"jsonName": "MetadataProvider",
|
||||
"goType": "metadata.Provider",
|
||||
"typescriptType": "Metadata_Provider",
|
||||
"usedStructName": "metadata.Provider",
|
||||
"required": false,
|
||||
"public": true,
|
||||
"comments": []
|
||||
@@ -34211,6 +34381,16 @@
|
||||
"comments": [
|
||||
" AutoDownloader instance is required to refresh queue."
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "metadataProvider",
|
||||
"jsonName": "metadataProvider",
|
||||
"goType": "metadata.Provider",
|
||||
"typescriptType": "Metadata_Provider",
|
||||
"usedStructName": "metadata.Provider",
|
||||
"required": false,
|
||||
"public": false,
|
||||
"comments": []
|
||||
}
|
||||
],
|
||||
"comments": []
|
||||
@@ -34290,6 +34470,16 @@
|
||||
"required": false,
|
||||
"public": true,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "MetadataProvider",
|
||||
"jsonName": "MetadataProvider",
|
||||
"goType": "metadata.Provider",
|
||||
"typescriptType": "Metadata_Provider",
|
||||
"usedStructName": "metadata.Provider",
|
||||
"required": false,
|
||||
"public": true,
|
||||
"comments": []
|
||||
}
|
||||
],
|
||||
"comments": []
|
||||
@@ -35244,16 +35434,6 @@
|
||||
"public": true,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "AnizipCache",
|
||||
"jsonName": "AnizipCache",
|
||||
"goType": "anizip.Cache",
|
||||
"typescriptType": "Anizip_Cache",
|
||||
"usedStructName": "anizip.Cache",
|
||||
"required": false,
|
||||
"public": true,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "Platform",
|
||||
"jsonName": "Platform",
|
||||
@@ -35264,6 +35444,16 @@
|
||||
"public": true,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "MetadataProvider",
|
||||
"jsonName": "MetadataProvider",
|
||||
"goType": "metadata.Provider",
|
||||
"typescriptType": "Metadata_Provider",
|
||||
"usedStructName": "metadata.Provider",
|
||||
"required": false,
|
||||
"public": true,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "AnilistRateLimiter",
|
||||
"jsonName": "AnilistRateLimiter",
|
||||
@@ -35582,6 +35772,16 @@
|
||||
"public": true,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "MetadataProvider",
|
||||
"jsonName": "MetadataProvider",
|
||||
"goType": "metadata.Provider",
|
||||
"typescriptType": "Metadata_Provider",
|
||||
"usedStructName": "metadata.Provider",
|
||||
"required": false,
|
||||
"public": true,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "LocalFiles",
|
||||
"jsonName": "LocalFiles",
|
||||
@@ -35602,16 +35802,6 @@
|
||||
"public": true,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "AnizipCache",
|
||||
"jsonName": "AnizipCache",
|
||||
"goType": "anizip.Cache",
|
||||
"typescriptType": "Anizip_Cache",
|
||||
"usedStructName": "anizip.Cache",
|
||||
"required": false,
|
||||
"public": true,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "Logger",
|
||||
"jsonName": "Logger",
|
||||
@@ -35672,11 +35862,11 @@
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "anizipCache",
|
||||
"jsonName": "anizipCache",
|
||||
"goType": "anizip.Cache",
|
||||
"typescriptType": "Anizip_Cache",
|
||||
"usedStructName": "anizip.Cache",
|
||||
"name": "metadataProvider",
|
||||
"jsonName": "metadataProvider",
|
||||
"goType": "metadata.Provider",
|
||||
"typescriptType": "Metadata_Provider",
|
||||
"usedStructName": "metadata.Provider",
|
||||
"required": false,
|
||||
"public": false,
|
||||
"comments": []
|
||||
@@ -35732,11 +35922,11 @@
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "anizipMedia",
|
||||
"jsonName": "anizipMedia",
|
||||
"goType": "anizip.Media",
|
||||
"typescriptType": "Anizip_Media",
|
||||
"usedStructName": "anizip.Media",
|
||||
"name": "animeMetadata",
|
||||
"jsonName": "animeMetadata",
|
||||
"goType": "metadata.AnimeMetadata",
|
||||
"typescriptType": "Metadata_AnimeMetadata",
|
||||
"usedStructName": "metadata.AnimeMetadata",
|
||||
"required": false,
|
||||
"public": false,
|
||||
"comments": []
|
||||
@@ -35900,6 +36090,16 @@
|
||||
"required": false,
|
||||
"public": true,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "MetadataProvider",
|
||||
"jsonName": "MetadataProvider",
|
||||
"goType": "metadata.Provider",
|
||||
"typescriptType": "Metadata_Provider",
|
||||
"usedStructName": "metadata.Provider",
|
||||
"required": false,
|
||||
"public": true,
|
||||
"comments": []
|
||||
}
|
||||
],
|
||||
"comments": []
|
||||
@@ -39673,15 +39873,6 @@
|
||||
"public": true,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "MpvType",
|
||||
"jsonName": "MpvType",
|
||||
"goType": "string",
|
||||
"typescriptType": "string",
|
||||
"required": true,
|
||||
"public": true,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "WSEventManager",
|
||||
"jsonName": "WSEventManager",
|
||||
@@ -43552,6 +43743,15 @@
|
||||
],
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"filepath": "../internal/offline/mock.go",
|
||||
"filename": "mock.go",
|
||||
"name": "MockHub",
|
||||
"formattedName": "Offline_MockHub",
|
||||
"package": "offline",
|
||||
"fields": [],
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"filepath": "../internal/offline/models.go",
|
||||
"filename": "models.go",
|
||||
@@ -44019,15 +44219,6 @@
|
||||
],
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"filepath": "../internal/offline/test_helper.go",
|
||||
"filename": "test_helper.go",
|
||||
"name": "MockHub",
|
||||
"formattedName": "Offline_MockHub",
|
||||
"package": "offline",
|
||||
"fields": [],
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"filepath": "../internal/onlinestream/providers/gogoanime.go",
|
||||
"filename": "gogoanime.go",
|
||||
@@ -44171,11 +44362,11 @@
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "anizipCache",
|
||||
"jsonName": "anizipCache",
|
||||
"goType": "anizip.Cache",
|
||||
"typescriptType": "Anizip_Cache",
|
||||
"usedStructName": "anizip.Cache",
|
||||
"name": "metadataProvider",
|
||||
"jsonName": "metadataProvider",
|
||||
"goType": "metadata.Provider",
|
||||
"typescriptType": "Metadata_Provider",
|
||||
"usedStructName": "metadata.Provider",
|
||||
"required": false,
|
||||
"public": false,
|
||||
"comments": []
|
||||
@@ -44420,11 +44611,11 @@
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "AnizipCache",
|
||||
"jsonName": "AnizipCache",
|
||||
"goType": "anizip.Cache",
|
||||
"typescriptType": "Anizip_Cache",
|
||||
"usedStructName": "anizip.Cache",
|
||||
"name": "MetadataProvider",
|
||||
"jsonName": "MetadataProvider",
|
||||
"goType": "metadata.Provider",
|
||||
"typescriptType": "Metadata_Provider",
|
||||
"usedStructName": "metadata.Provider",
|
||||
"required": false,
|
||||
"public": true,
|
||||
"comments": []
|
||||
@@ -49224,6 +49415,16 @@
|
||||
"public": false,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "metadataProvider",
|
||||
"jsonName": "metadataProvider",
|
||||
"goType": "metadata.Provider",
|
||||
"typescriptType": "Metadata_Provider",
|
||||
"usedStructName": "metadata.Provider",
|
||||
"required": false,
|
||||
"public": false,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "activeTorrentCountCtxCancel",
|
||||
"jsonName": "activeTorrentCountCtxCancel",
|
||||
@@ -49302,6 +49503,16 @@
|
||||
"required": true,
|
||||
"public": true,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "MetadataProvider",
|
||||
"jsonName": "MetadataProvider",
|
||||
"goType": "metadata.Provider",
|
||||
"typescriptType": "Metadata_Provider",
|
||||
"usedStructName": "metadata.Provider",
|
||||
"required": false,
|
||||
"public": true,
|
||||
"comments": []
|
||||
}
|
||||
],
|
||||
"comments": []
|
||||
@@ -49683,6 +49894,16 @@
|
||||
"required": false,
|
||||
"public": false,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "metadataProvider",
|
||||
"jsonName": "metadataProvider",
|
||||
"goType": "metadata.Provider",
|
||||
"typescriptType": "Metadata_Provider",
|
||||
"usedStructName": "metadata.Provider",
|
||||
"required": false,
|
||||
"public": false,
|
||||
"comments": []
|
||||
}
|
||||
],
|
||||
"comments": []
|
||||
@@ -49818,6 +50039,16 @@
|
||||
"required": false,
|
||||
"public": true,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "MetadataProvider",
|
||||
"jsonName": "MetadataProvider",
|
||||
"goType": "metadata.Provider",
|
||||
"typescriptType": "Metadata_Provider",
|
||||
"usedStructName": "metadata.Provider",
|
||||
"required": false,
|
||||
"public": true,
|
||||
"comments": []
|
||||
}
|
||||
],
|
||||
"comments": []
|
||||
@@ -50837,16 +51068,6 @@
|
||||
"public": false,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "anizipCache",
|
||||
"jsonName": "anizipCache",
|
||||
"goType": "anizip.Cache",
|
||||
"typescriptType": "Anizip_Cache",
|
||||
"usedStructName": "anizip.Cache",
|
||||
"required": false,
|
||||
"public": false,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "settings",
|
||||
"jsonName": "settings",
|
||||
@@ -51368,11 +51589,11 @@
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "AnizipCache",
|
||||
"jsonName": "AnizipCache",
|
||||
"goType": "anizip.Cache",
|
||||
"typescriptType": "Anizip_Cache",
|
||||
"usedStructName": "anizip.Cache",
|
||||
"name": "MetadataProvider",
|
||||
"jsonName": "MetadataProvider",
|
||||
"goType": "metadata.Provider",
|
||||
"typescriptType": "Metadata_Provider",
|
||||
"usedStructName": "metadata.Provider",
|
||||
"required": false,
|
||||
"public": true,
|
||||
"comments": []
|
||||
@@ -51689,16 +51910,6 @@
|
||||
"public": false,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "anizipCache",
|
||||
"jsonName": "anizipCache",
|
||||
"goType": "anizip.Cache",
|
||||
"typescriptType": "Anizip_Cache",
|
||||
"usedStructName": "anizip.Cache",
|
||||
"required": false,
|
||||
"public": false,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "baseAnimeCache",
|
||||
"jsonName": "baseAnimeCache",
|
||||
@@ -51831,16 +52042,6 @@
|
||||
"public": true,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "AnizipCache",
|
||||
"jsonName": "AnizipCache",
|
||||
"goType": "anizip.Cache",
|
||||
"typescriptType": "Anizip_Cache",
|
||||
"usedStructName": "anizip.Cache",
|
||||
"required": false,
|
||||
"public": true,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "TorrentRepository",
|
||||
"jsonName": "TorrentRepository",
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
package anizip
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func (m *Media) GetTitle() string {
|
||||
if m == nil {
|
||||
return ""
|
||||
@@ -68,40 +63,3 @@ func (e *Episode) GetTitle() string {
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func ExtractEpisodeInteger(s string) (int, bool) {
|
||||
pattern := "[0-9]+"
|
||||
regex := regexp.MustCompile(pattern)
|
||||
|
||||
// Find the first match in the input string.
|
||||
match := regex.FindString(s)
|
||||
|
||||
if match != "" {
|
||||
// Convert the matched string to an integer.
|
||||
num, err := strconv.Atoi(match)
|
||||
if err != nil {
|
||||
return 0, false
|
||||
}
|
||||
return num, true
|
||||
}
|
||||
|
||||
return 0, false
|
||||
}
|
||||
|
||||
func OffsetEpisode(s string, offset int) string {
|
||||
pattern := "([0-9]+)"
|
||||
regex := regexp.MustCompile(pattern)
|
||||
|
||||
// Replace the first matched integer with the incremented value.
|
||||
result := regex.ReplaceAllStringFunc(s, func(matched string) string {
|
||||
num, err := strconv.Atoi(matched)
|
||||
if err == nil {
|
||||
num = num + offset
|
||||
return strconv.Itoa(num)
|
||||
} else {
|
||||
return matched
|
||||
}
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -5,78 +5,86 @@ import (
|
||||
"fmt"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/samber/mo"
|
||||
"regexp"
|
||||
"seanime/internal/api/anilist"
|
||||
"seanime/internal/api/anizip"
|
||||
"seanime/internal/api/mappings"
|
||||
"seanime/internal/api/tvdb"
|
||||
"seanime/internal/util"
|
||||
"seanime/internal/util/filecache"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type (
|
||||
AnimeWrapperImpl struct {
|
||||
anizipMedia mo.Option[*anizip.Media]
|
||||
baseAnime *anilist.BaseAnime
|
||||
fileCacher *filecache.Cacher
|
||||
logger *zerolog.Logger
|
||||
metadata mo.Option[*AnimeMetadata]
|
||||
baseAnime *anilist.BaseAnime
|
||||
fileCacher *filecache.Cacher
|
||||
logger *zerolog.Logger
|
||||
// TVDB
|
||||
tvdbEpisodes []*tvdb.Episode
|
||||
}
|
||||
)
|
||||
|
||||
func (aw *AnimeWrapperImpl) GetEpisodeMetadata(epNum int) EpisodeMetadata {
|
||||
meta := EpisodeMetadata{
|
||||
EpisodeNumber: epNum,
|
||||
func (aw *AnimeWrapperImpl) GetEpisodeMetadata(epNum int) (ret EpisodeMetadata) {
|
||||
ret = EpisodeMetadata{
|
||||
AnidbId: 0,
|
||||
TvdbId: 0,
|
||||
Title: "",
|
||||
Image: "",
|
||||
AirDate: "",
|
||||
Length: 0,
|
||||
Summary: "",
|
||||
Overview: "",
|
||||
EpisodeNumber: epNum,
|
||||
Episode: strconv.Itoa(epNum),
|
||||
SeasonNumber: 0,
|
||||
AbsoluteEpisodeNumber: 0,
|
||||
AnidbEid: 0,
|
||||
}
|
||||
|
||||
defer util.HandlePanicInModuleThen("api/metadata/GetEpisodeMetadata", func() {})
|
||||
|
||||
hasTVDBMetadata := aw.tvdbEpisodes != nil && len(aw.tvdbEpisodes) > 0
|
||||
|
||||
anizipEpisode := mo.None[*anizip.Episode]()
|
||||
if aw.anizipMedia.IsAbsent() {
|
||||
meta.Image = aw.baseAnime.GetBannerImageSafe()
|
||||
episode := mo.None[*EpisodeMetadata]()
|
||||
if aw.metadata.IsAbsent() {
|
||||
ret.Image = aw.baseAnime.GetBannerImageSafe()
|
||||
} else {
|
||||
anizipEpisodeF, found := aw.anizipMedia.MustGet().FindEpisode(strconv.Itoa(epNum))
|
||||
episodeF, found := aw.metadata.MustGet().FindEpisode(strconv.Itoa(epNum))
|
||||
if found {
|
||||
meta.AnidbId = anizipEpisodeF.AnidbEid
|
||||
anizipEpisode = mo.Some(anizipEpisodeF)
|
||||
episode = mo.Some(episodeF)
|
||||
}
|
||||
}
|
||||
|
||||
// If we don't have AniZip metadata, just return the metadata containing the image
|
||||
if anizipEpisode.IsAbsent() {
|
||||
return meta
|
||||
if episode.IsAbsent() {
|
||||
return ret
|
||||
}
|
||||
|
||||
// TVDB metadata
|
||||
ret = *episode.MustGet()
|
||||
|
||||
// If TVDB metadata is available, use it to populate the image
|
||||
if hasTVDBMetadata {
|
||||
tvdbEpisode, found := aw.GetTVDBEpisodeByNumber(epNum)
|
||||
if found {
|
||||
meta.Image = tvdbEpisode.Image
|
||||
meta.TvdbId = tvdbEpisode.ID
|
||||
ret.Image = tvdbEpisode.Image
|
||||
ret.TvdbId = int(tvdbEpisode.ID)
|
||||
}
|
||||
}
|
||||
|
||||
if meta.Image == "" {
|
||||
// If TVDB image is not set, use AniZip image, if that is not set, use the AniList banner image
|
||||
if ret.Image == "" {
|
||||
// Set AniZip image if TVDB image is not set
|
||||
if anizipEpisode.MustGet().Image != "" {
|
||||
meta.Image = anizipEpisode.MustGet().Image
|
||||
if episode.MustGet().Image != "" {
|
||||
ret.Image = episode.MustGet().Image
|
||||
} else {
|
||||
// If AniZip image is not set, use the base media image
|
||||
meta.Image = aw.baseAnime.GetBannerImageSafe()
|
||||
ret.Image = aw.baseAnime.GetBannerImageSafe()
|
||||
}
|
||||
}
|
||||
|
||||
meta.AirDate = anizipEpisode.MustGet().AirDate
|
||||
meta.Length = anizipEpisode.MustGet().Length
|
||||
if anizipEpisode.MustGet().Runtime > 0 {
|
||||
meta.Length = anizipEpisode.MustGet().Runtime
|
||||
}
|
||||
meta.Summary = strings.ReplaceAll(anizipEpisode.MustGet().Summary, "`", "'")
|
||||
meta.Overview = strings.ReplaceAll(anizipEpisode.MustGet().Overview, "`", "'")
|
||||
|
||||
return meta
|
||||
return ret
|
||||
}
|
||||
|
||||
func getTvdbIDFromAnimeLists(anidbID int) (tvdbID int, ok bool) {
|
||||
@@ -89,17 +97,17 @@ func getTvdbIDFromAnimeLists(anidbID int) (tvdbID int, ok bool) {
|
||||
|
||||
func (aw *AnimeWrapperImpl) EmptyTVDBEpisodesBucket(mediaId int) error {
|
||||
|
||||
if aw.anizipMedia.IsAbsent() {
|
||||
if aw.metadata.IsAbsent() {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get TVDB ID
|
||||
var tvdbId int
|
||||
tvdbId = aw.anizipMedia.MustGet().Mappings.ThetvdbID
|
||||
tvdbId = aw.metadata.MustGet().Mappings.ThetvdbId
|
||||
if tvdbId == 0 {
|
||||
if aw.anizipMedia.MustGet().Mappings.AnidbID > 0 {
|
||||
if aw.metadata.MustGet().Mappings.AnidbId > 0 {
|
||||
// Try to get it from the mappings
|
||||
tvdbId, _ = getTvdbIDFromAnimeLists(aw.anizipMedia.MustGet().Mappings.AnidbID)
|
||||
tvdbId, _ = getTvdbIDFromAnimeLists(aw.metadata.MustGet().Mappings.AnidbId)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,17 +121,17 @@ func (aw *AnimeWrapperImpl) EmptyTVDBEpisodesBucket(mediaId int) error {
|
||||
func (aw *AnimeWrapperImpl) GetTVDBEpisodes(populate bool) ([]*tvdb.Episode, error) {
|
||||
key := aw.baseAnime.GetID()
|
||||
|
||||
if aw.anizipMedia.IsAbsent() {
|
||||
return nil, errors.New("metadata: anizip media is absent")
|
||||
if aw.metadata.IsAbsent() {
|
||||
return nil, errors.New("metadata: anime metadata is absent")
|
||||
}
|
||||
|
||||
// Get TVDB ID
|
||||
var tvdbId int
|
||||
tvdbId = aw.anizipMedia.MustGet().Mappings.ThetvdbID
|
||||
tvdbId = aw.metadata.MustGet().Mappings.ThetvdbId
|
||||
if tvdbId == 0 {
|
||||
if aw.anizipMedia.MustGet().Mappings.AnidbID > 0 {
|
||||
if aw.metadata.MustGet().Mappings.AnidbId > 0 {
|
||||
// Try to get it from the mappings
|
||||
tvdbId, _ = getTvdbIDFromAnimeLists(aw.anizipMedia.MustGet().Mappings.AnidbID)
|
||||
tvdbId, _ = getTvdbIDFromAnimeLists(aw.metadata.MustGet().Mappings.AnidbId)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,14 +153,15 @@ func (aw *AnimeWrapperImpl) GetTVDBEpisodes(populate bool) ([]*tvdb.Episode, err
|
||||
var err error
|
||||
|
||||
tv := tvdb.NewTVDB(&tvdb.NewTVDBOptions{
|
||||
ApiKey: "", // Empty
|
||||
Logger: aw.logger,
|
||||
})
|
||||
|
||||
episodes, err = tv.FetchSeriesEpisodes(tvdbId, tvdb.FilterEpisodeMediaInfo{
|
||||
Year: aw.baseAnime.GetStartDate().GetYear(),
|
||||
Month: aw.baseAnime.GetStartDate().GetMonth(),
|
||||
TotalEp: aw.anizipMedia.MustGet().GetMainEpisodeCount(),
|
||||
AbsoluteOffset: aw.anizipMedia.MustGet().GetOffset(),
|
||||
TotalEp: aw.metadata.MustGet().GetMainEpisodeCount(),
|
||||
AbsoluteOffset: aw.metadata.MustGet().GetOffset(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -181,3 +190,40 @@ func (aw *AnimeWrapperImpl) GetTVDBEpisodeByNumber(number int) (*tvdb.Episode, b
|
||||
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func ExtractEpisodeInteger(s string) (int, bool) {
|
||||
pattern := "[0-9]+"
|
||||
regex := regexp.MustCompile(pattern)
|
||||
|
||||
// Find the first match in the input string.
|
||||
match := regex.FindString(s)
|
||||
|
||||
if match != "" {
|
||||
// Convert the matched string to an integer.
|
||||
num, err := strconv.Atoi(match)
|
||||
if err != nil {
|
||||
return 0, false
|
||||
}
|
||||
return num, true
|
||||
}
|
||||
|
||||
return 0, false
|
||||
}
|
||||
|
||||
func OffsetAnidbEpisode(s string, offset int) string {
|
||||
pattern := "([0-9]+)"
|
||||
regex := regexp.MustCompile(pattern)
|
||||
|
||||
// Replace the first matched integer with the incremented value.
|
||||
result := regex.ReplaceAllStringFunc(s, func(matched string) string {
|
||||
num, err := strconv.Atoi(matched)
|
||||
if err == nil {
|
||||
num = num + offset
|
||||
return strconv.Itoa(num)
|
||||
} else {
|
||||
return matched
|
||||
}
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package anizip
|
||||
package metadata
|
||||
|
||||
import (
|
||||
"testing"
|
||||
@@ -17,9 +17,9 @@ func TestOffsetEpisode(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
actual := OffsetEpisode(c.input, 1)
|
||||
actual := OffsetAnidbEpisode(c.input, 1)
|
||||
if actual != c.expected {
|
||||
t.Errorf("OffsetEpisode(%s, 1) == %s, expected %s", c.input, actual, c.expected)
|
||||
t.Errorf("OffsetAnidbEpisode(%s, 1) == %s, expected %s", c.input, actual, c.expected)
|
||||
}
|
||||
}
|
||||
|
||||
17
internal/api/metadata/mock.go
Normal file
17
internal/api/metadata/mock.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package metadata
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/require"
|
||||
"seanime/internal/util"
|
||||
"seanime/internal/util/filecache"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func GetMockProvider(t *testing.T) Provider {
|
||||
filecacher, err := filecache.NewCacher(t.TempDir())
|
||||
require.NoError(t, err)
|
||||
return NewProvider(&NewProviderImplOptions{
|
||||
Logger: util.NewLogger(),
|
||||
FileCacher: filecacher,
|
||||
})
|
||||
}
|
||||
@@ -1,18 +1,24 @@
|
||||
package metadata
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/samber/mo"
|
||||
"seanime/internal/api/anilist"
|
||||
"seanime/internal/api/anizip"
|
||||
"seanime/internal/api/tvdb"
|
||||
"seanime/internal/util/filecache"
|
||||
"seanime/internal/util/result"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type (
|
||||
ProviderImpl struct {
|
||||
logger *zerolog.Logger
|
||||
fileCacher *filecache.Cacher
|
||||
logger *zerolog.Logger
|
||||
fileCacher *filecache.Cacher
|
||||
animeMetadataCache *result.Cache[string, *AnimeMetadata]
|
||||
anizipCache *anizip.Cache
|
||||
}
|
||||
|
||||
NewProviderImplOptions struct {
|
||||
@@ -21,31 +27,112 @@ type (
|
||||
}
|
||||
)
|
||||
|
||||
func GetAnimeMetadataCacheKey(platform Platform, mId int) string {
|
||||
return fmt.Sprintf("%s$%d", platform, mId)
|
||||
}
|
||||
|
||||
// NewProvider creates a new metadata provider.
|
||||
func NewProvider(options *NewProviderImplOptions) Provider {
|
||||
return &ProviderImpl{
|
||||
logger: options.Logger,
|
||||
fileCacher: options.FileCacher,
|
||||
logger: options.Logger,
|
||||
fileCacher: options.FileCacher,
|
||||
animeMetadataCache: result.NewCache[string, *AnimeMetadata](),
|
||||
anizipCache: anizip.NewCache(),
|
||||
}
|
||||
}
|
||||
|
||||
// GetCache returns the anime metadata cache.
|
||||
func (p *ProviderImpl) GetCache() *result.Cache[string, *AnimeMetadata] {
|
||||
return p.animeMetadataCache
|
||||
}
|
||||
|
||||
// GetAnimeMetadata fetches anime metadata from api.ani.zip.
|
||||
func (p *ProviderImpl) GetAnimeMetadata(platform Platform, mId int) (ret *AnimeMetadata, err error) {
|
||||
|
||||
ret, ok := p.animeMetadataCache.Get(GetAnimeMetadataCacheKey(platform, mId))
|
||||
if ok {
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
anizipMedia, err := anizip.FetchAniZipMediaC(string(platform), mId, p.anizipCache)
|
||||
if err != nil || anizipMedia == nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret = &AnimeMetadata{
|
||||
Titles: make(map[string]string),
|
||||
Episodes: make(map[string]*EpisodeMetadata),
|
||||
EpisodeCount: 0,
|
||||
SpecialCount: 0,
|
||||
Mappings: &AnimeMappings{},
|
||||
}
|
||||
|
||||
ret.Titles = anizipMedia.Titles
|
||||
ret.EpisodeCount = anizipMedia.EpisodeCount
|
||||
ret.SpecialCount = anizipMedia.SpecialCount
|
||||
ret.Mappings.AnimeplanetId = anizipMedia.Mappings.AnimeplanetID
|
||||
ret.Mappings.KitsuId = anizipMedia.Mappings.KitsuID
|
||||
ret.Mappings.MalId = anizipMedia.Mappings.MalID
|
||||
ret.Mappings.Type = anizipMedia.Mappings.Type
|
||||
ret.Mappings.AnilistId = anizipMedia.Mappings.AnilistID
|
||||
ret.Mappings.AnisearchId = anizipMedia.Mappings.AnisearchID
|
||||
ret.Mappings.AnidbId = anizipMedia.Mappings.AnidbID
|
||||
ret.Mappings.NotifymoeId = anizipMedia.Mappings.NotifymoeID
|
||||
ret.Mappings.LivechartId = anizipMedia.Mappings.LivechartID
|
||||
ret.Mappings.ThetvdbId = anizipMedia.Mappings.ThetvdbID
|
||||
ret.Mappings.ImdbId = anizipMedia.Mappings.ImdbID
|
||||
ret.Mappings.ThemoviedbId = anizipMedia.Mappings.ThemoviedbID
|
||||
|
||||
for key, anizipEp := range anizipMedia.Episodes {
|
||||
em := &EpisodeMetadata{
|
||||
AnidbId: anizipEp.AnidbEid,
|
||||
TvdbId: anizipEp.TvdbEid,
|
||||
Title: anizipEp.GetTitle(),
|
||||
Image: anizipEp.Image,
|
||||
AirDate: anizipEp.AirDate,
|
||||
Length: anizipEp.Runtime,
|
||||
Summary: strings.ReplaceAll(anizipEp.Summary, "`", "'"),
|
||||
Overview: strings.ReplaceAll(anizipEp.Overview, "`", "'"),
|
||||
EpisodeNumber: anizipEp.EpisodeNumber,
|
||||
Episode: anizipEp.Episode,
|
||||
SeasonNumber: anizipEp.SeasonNumber,
|
||||
AbsoluteEpisodeNumber: anizipEp.AbsoluteEpisodeNumber,
|
||||
AnidbEid: anizipEp.AnidbEid,
|
||||
}
|
||||
if em.Length == 0 && anizipEp.Length > 0 {
|
||||
em.Length = anizipEp.Length
|
||||
}
|
||||
if em.Summary == "" && anizipEp.Overview != "" {
|
||||
em.Summary = anizipEp.Overview
|
||||
}
|
||||
if em.Overview == "" && anizipEp.Summary != "" {
|
||||
em.Overview = anizipEp.Summary
|
||||
}
|
||||
ret.Episodes[key] = em
|
||||
}
|
||||
|
||||
p.animeMetadataCache.SetT(GetAnimeMetadataCacheKey(platform, mId), ret, 1*time.Hour)
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// GetAnimeMetadataWrapper creates a new anime wrapper.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// metadataProvider.GetAnimeMetadataWrapper(media, anizipMedia)
|
||||
// metadataProvider.GetAnimeMetadataWrapper(media, metadata)
|
||||
// metadataProvider.GetAnimeMetadataWrapper(media, nil)
|
||||
func (p *ProviderImpl) GetAnimeMetadataWrapper(media *anilist.BaseAnime, anizipMedia *anizip.Media) AnimeMetadataWrapper {
|
||||
func (p *ProviderImpl) GetAnimeMetadataWrapper(media *anilist.BaseAnime, metadata *AnimeMetadata) AnimeMetadataWrapper {
|
||||
aw := &AnimeWrapperImpl{
|
||||
anizipMedia: mo.None[*anizip.Media](),
|
||||
metadata: mo.None[*AnimeMetadata](),
|
||||
baseAnime: media,
|
||||
fileCacher: p.fileCacher,
|
||||
logger: p.logger,
|
||||
tvdbEpisodes: make([]*tvdb.Episode, 0),
|
||||
}
|
||||
|
||||
if anizipMedia != nil {
|
||||
aw.anizipMedia = mo.Some(anizipMedia)
|
||||
if metadata != nil {
|
||||
aw.metadata = mo.Some(metadata)
|
||||
}
|
||||
|
||||
episodes, err := aw.GetTVDBEpisodes(false)
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
package metadata
|
||||
|
||||
import (
|
||||
"seanime/internal/util"
|
||||
"seanime/internal/util/filecache"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGetMockProvider(t *testing.T) Provider {
|
||||
tempDir := t.TempDir()
|
||||
fileCacher, err := filecache.NewCacher(tempDir)
|
||||
if err != nil {
|
||||
t.Fatalf("could not create filecacher: %v", err)
|
||||
}
|
||||
|
||||
metadataProvider := NewProvider(&NewProviderImplOptions{
|
||||
Logger: util.NewLogger(),
|
||||
FileCacher: fileCacher,
|
||||
})
|
||||
|
||||
return metadataProvider
|
||||
}
|
||||
@@ -2,25 +2,42 @@ package metadata
|
||||
|
||||
import (
|
||||
"seanime/internal/api/anilist"
|
||||
"seanime/internal/api/anizip"
|
||||
"seanime/internal/api/tvdb"
|
||||
"seanime/internal/util/result"
|
||||
)
|
||||
|
||||
const (
|
||||
AnilistPlatform Platform = "anilist"
|
||||
MalPlatform Platform = "mal"
|
||||
)
|
||||
|
||||
type (
|
||||
Platform string
|
||||
|
||||
Provider interface {
|
||||
GetAnimeMetadataWrapper(anime *anilist.BaseAnime, anizipMedia *anizip.Media) AnimeMetadataWrapper
|
||||
// GetAnimeMetadata fetches anime metadata for the given platform from a source.
|
||||
// In this case, the source is api.ani.zip.
|
||||
GetAnimeMetadata(platform Platform, mId int) (*AnimeMetadata, error)
|
||||
GetCache() *result.Cache[string, *AnimeMetadata]
|
||||
// GetAnimeMetadataWrapper creates a wrapper for anime metadata.
|
||||
GetAnimeMetadataWrapper(anime *anilist.BaseAnime, metadata *AnimeMetadata) AnimeMetadataWrapper
|
||||
}
|
||||
|
||||
// AnimeMetadataWrapper is a container for anime metadata.
|
||||
// This wrapper is used to get a more complete metadata object by getting data from multiple sources in the Provider.
|
||||
// The user can request metadata to be fetched from TVDB as well, which will be stored in the cache.
|
||||
AnimeMetadataWrapper interface {
|
||||
// GetEpisodeMetadata combines metadata from multiple sources to create a single EpisodeMetadata object.
|
||||
GetEpisodeMetadata(episodeNumber int) EpisodeMetadata
|
||||
|
||||
EmptyTVDBEpisodesBucket(mediaId int) error
|
||||
GetTVDBEpisodes(populate bool) ([]*tvdb.Episode, error)
|
||||
GetTVDBEpisodeByNumber(episodeNumber int) (*tvdb.Episode, bool)
|
||||
}
|
||||
)
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
type (
|
||||
AnimeMetadata struct {
|
||||
Titles map[string]string `json:"titles"`
|
||||
@@ -47,7 +64,7 @@ type (
|
||||
|
||||
EpisodeMetadata struct {
|
||||
AnidbId int `json:"anidbId"`
|
||||
TvdbId int64 `json:"tvdbId"`
|
||||
TvdbId int `json:"tvdbId"`
|
||||
Title string `json:"title"`
|
||||
Image string `json:"image"`
|
||||
AirDate string `json:"airDate"`
|
||||
@@ -55,8 +72,68 @@ type (
|
||||
Summary string `json:"summary"`
|
||||
Overview string `json:"overview"`
|
||||
EpisodeNumber int `json:"episodeNumber"`
|
||||
Episode string `json:"episode"`
|
||||
SeasonNumber int `json:"seasonNumber"`
|
||||
AbsoluteEpisodeNumber int `json:"absoluteEpisodeNumber"`
|
||||
AnidbEid int `json:"anidbEid"`
|
||||
}
|
||||
)
|
||||
|
||||
func (m *AnimeMetadata) GetTitle() string {
|
||||
if m == nil {
|
||||
return ""
|
||||
}
|
||||
if len(m.Titles["en"]) > 0 {
|
||||
return m.Titles["en"]
|
||||
}
|
||||
return m.Titles["ro"]
|
||||
}
|
||||
|
||||
func (m *AnimeMetadata) GetMappings() *AnimeMappings {
|
||||
if m == nil {
|
||||
return &AnimeMappings{}
|
||||
}
|
||||
return m.Mappings
|
||||
}
|
||||
|
||||
func (m *AnimeMetadata) FindEpisode(ep string) (*EpisodeMetadata, bool) {
|
||||
if m.Episodes == nil {
|
||||
return nil, false
|
||||
}
|
||||
episode, found := m.Episodes[ep]
|
||||
if !found {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
return episode, true
|
||||
}
|
||||
|
||||
func (m *AnimeMetadata) GetMainEpisodeCount() int {
|
||||
if m == nil {
|
||||
return 0
|
||||
}
|
||||
return m.EpisodeCount
|
||||
}
|
||||
|
||||
// GetOffset returns the offset of the first episode relative to the absolute episode number.
|
||||
// e.g, if the first episode's absolute number is 13, then the offset is 12.
|
||||
func (m *AnimeMetadata) GetOffset() int {
|
||||
if m == nil {
|
||||
return 0
|
||||
}
|
||||
firstEp, found := m.FindEpisode("1")
|
||||
if !found {
|
||||
return 0
|
||||
}
|
||||
if firstEp.AbsoluteEpisodeNumber == 0 {
|
||||
return 0
|
||||
}
|
||||
return firstEp.AbsoluteEpisodeNumber - 1
|
||||
}
|
||||
|
||||
func (e *EpisodeMetadata) GetTitle() string {
|
||||
if e == nil {
|
||||
return ""
|
||||
}
|
||||
return e.Title
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ func TestTVDB_FetchSeriesEpisodes(t *testing.T) {
|
||||
|
||||
anizipMedia, err := anizip.FetchAniZipMedia("anilist", tt.anilistId)
|
||||
if err != nil {
|
||||
t.Fatalf("could not fetch anizip media for %s", tt.name)
|
||||
t.Fatalf("could not fetch anime metadata for %s", tt.name)
|
||||
}
|
||||
|
||||
tvdbId := anizipMedia.Mappings.ThetvdbID
|
||||
@@ -124,7 +124,7 @@ func TestTVDB_FetchSeasons(t *testing.T) {
|
||||
|
||||
anizipMedia, err := anizip.FetchAniZipMedia("anilist", tt.anilistId)
|
||||
if err != nil {
|
||||
t.Fatalf("could not fetch anizip media for %s", tt.name)
|
||||
t.Fatalf("could not fetch anime metadata for %s", tt.name)
|
||||
}
|
||||
|
||||
tvdbId := anizipMedia.Mappings.ThetvdbID
|
||||
@@ -174,14 +174,12 @@ func TestTVDB_fetchEpisodes(t *testing.T) {
|
||||
anilistClient := anilist.TestGetMockAnilistClient()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
anilistId int
|
||||
episodeNumber int
|
||||
name string
|
||||
anilistId int
|
||||
}{
|
||||
{
|
||||
name: "Dungeon Meshi",
|
||||
anilistId: 153518,
|
||||
episodeNumber: 1,
|
||||
name: "Dungeon Meshi",
|
||||
anilistId: 153518,
|
||||
},
|
||||
{
|
||||
name: "Boku no Kokoro no Yabai Yatsu 2nd Season",
|
||||
@@ -226,7 +224,7 @@ func TestTVDB_fetchEpisodes(t *testing.T) {
|
||||
|
||||
anizipMedia, err := anizip.FetchAniZipMedia("anilist", tt.anilistId)
|
||||
if err != nil {
|
||||
t.Fatalf("could not fetch anizip media for %s", tt.name)
|
||||
t.Fatalf("could not fetch anime metadata for %s", tt.name)
|
||||
}
|
||||
|
||||
tvdbId := anizipMedia.Mappings.ThetvdbID
|
||||
@@ -351,7 +349,7 @@ func TestTVDB_fetchEpisodesAbsolute(t *testing.T) {
|
||||
|
||||
anizipMedia, err := anizip.FetchAniZipMedia("anilist", tt.anilistId)
|
||||
if err != nil {
|
||||
t.Fatalf("could not fetch anizip media for %s", tt.name)
|
||||
t.Fatalf("could not fetch anime metadata for %s", tt.name)
|
||||
}
|
||||
|
||||
tvdbId := anizipMedia.Mappings.ThetvdbID
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"github.com/rs/zerolog"
|
||||
"runtime"
|
||||
"seanime/internal/api/anilist"
|
||||
"seanime/internal/api/anizip"
|
||||
"seanime/internal/api/metadata"
|
||||
"seanime/internal/constants"
|
||||
"seanime/internal/continuity"
|
||||
@@ -48,7 +47,6 @@ type (
|
||||
TorrentClientRepository *torrent_client.Repository
|
||||
TorrentRepository *torrent.Repository
|
||||
Watcher *scanner.Watcher
|
||||
AnizipCache *anizip.Cache // AnizipCache holds fetched AniZip media for 30 minutes. (used by route handlers)
|
||||
AnilistClient anilist.AnilistClient
|
||||
AnilistPlatform platform.Platform
|
||||
FillerManager *fillermanager.FillerManager
|
||||
@@ -155,29 +153,26 @@ func NewApp(configOpts *ConfigOptions, selfupdater *updater.SelfUpdater) *App {
|
||||
// Anilist Platform
|
||||
anilistPlatform := anilist_platform.NewAnilistPlatform(anilistCW, logger)
|
||||
|
||||
// AniZip Cache
|
||||
anizipCache := anizip.NewCache()
|
||||
|
||||
// File Cacher
|
||||
fileCacher, err := filecache.NewCacher(cfg.Cache.Dir)
|
||||
if err != nil {
|
||||
logger.Fatal().Err(err).Msgf("app: Failed to initialize file cacher")
|
||||
}
|
||||
|
||||
// Online Stream
|
||||
onlinestreamRepository := onlinestream.NewRepository(&onlinestream.NewRepositoryOptions{
|
||||
Logger: logger,
|
||||
FileCacher: fileCacher,
|
||||
AnizipCache: anizipCache,
|
||||
Platform: anilistPlatform,
|
||||
})
|
||||
|
||||
// Metadata Provider
|
||||
metadataProvider := metadata.NewProvider(&metadata.NewProviderImplOptions{
|
||||
Logger: logger,
|
||||
FileCacher: fileCacher,
|
||||
})
|
||||
|
||||
// Online Stream
|
||||
onlinestreamRepository := onlinestream.NewRepository(&onlinestream.NewRepositoryOptions{
|
||||
Logger: logger,
|
||||
FileCacher: fileCacher,
|
||||
MetadataProvider: metadataProvider,
|
||||
Platform: anilistPlatform,
|
||||
})
|
||||
|
||||
// Manga Repository
|
||||
mangaRepository := manga.NewRepository(&manga.NewRepositoryOptions{
|
||||
Logger: logger,
|
||||
@@ -195,14 +190,13 @@ func NewApp(configOpts *ConfigOptions, selfupdater *updater.SelfUpdater) *App {
|
||||
WSEventManager: wsEventManager,
|
||||
})
|
||||
|
||||
extensionPlaygroundRepository := extension_playground.NewPlaygroundRepository(logger, anilistPlatform)
|
||||
extensionPlaygroundRepository := extension_playground.NewPlaygroundRepository(logger, anilistPlatform, metadataProvider)
|
||||
|
||||
app := &App{
|
||||
Config: cfg,
|
||||
Database: database,
|
||||
AnilistClient: anilistCW,
|
||||
AnilistPlatform: anilistPlatform,
|
||||
AnizipCache: anizipCache,
|
||||
WSEventManager: wsEventManager,
|
||||
Logger: logger,
|
||||
Version: constants.Version,
|
||||
|
||||
@@ -119,7 +119,7 @@ func (a *App) initModulesOnce() {
|
||||
TorrentRepository: a.TorrentRepository,
|
||||
Database: a.Database,
|
||||
WSEventManager: a.WSEventManager,
|
||||
AnizipCache: a.AnizipCache,
|
||||
MetadataProvider: a.MetadataProvider,
|
||||
})
|
||||
|
||||
if !a.IsOffline() {
|
||||
@@ -132,12 +132,13 @@ func (a *App) initModulesOnce() {
|
||||
// +---------------------+
|
||||
|
||||
a.AutoScanner = autoscanner.New(&autoscanner.NewAutoScannerOptions{
|
||||
Database: a.Database,
|
||||
Enabled: false, // Will be set in InitOrRefreshModules
|
||||
AutoDownloader: a.AutoDownloader,
|
||||
Platform: a.AnilistPlatform,
|
||||
Logger: a.Logger,
|
||||
WSEventManager: a.WSEventManager,
|
||||
Database: a.Database,
|
||||
Platform: a.AnilistPlatform,
|
||||
Logger: a.Logger,
|
||||
WSEventManager: a.WSEventManager,
|
||||
Enabled: false, // Will be set in InitOrRefreshModules
|
||||
AutoDownloader: a.AutoDownloader,
|
||||
MetadataProvider: a.MetadataProvider,
|
||||
})
|
||||
|
||||
// This is run in a goroutine
|
||||
@@ -180,7 +181,6 @@ func (a *App) initModulesOnce() {
|
||||
|
||||
a.TorrentstreamRepository = torrentstream.NewRepository(&torrentstream.NewRepositoryOptions{
|
||||
Logger: a.Logger,
|
||||
AnizipCache: a.AnizipCache,
|
||||
BaseAnimeCache: anilist.NewBaseAnimeCache(),
|
||||
CompleteAnimeCache: anilist.NewCompleteAnimeCache(),
|
||||
MetadataProvider: a.MetadataProvider,
|
||||
@@ -332,6 +332,7 @@ func (a *App) InitOrRefreshModules() {
|
||||
Transmission: trans,
|
||||
TorrentRepository: a.TorrentRepository,
|
||||
Provider: settings.Torrent.Default,
|
||||
MetadataProvider: a.MetadataProvider,
|
||||
})
|
||||
|
||||
a.TorrentClientRepository.InitActiveTorrentCount(settings.Torrent.ShowActiveTorrentCount, a.WSEventManager)
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
"github.com/rs/zerolog"
|
||||
"runtime"
|
||||
"seanime/internal/api/anilist"
|
||||
"seanime/internal/api/anizip"
|
||||
"seanime/internal/api/metadata"
|
||||
"seanime/internal/extension"
|
||||
"seanime/internal/extension_repo"
|
||||
"seanime/internal/manga"
|
||||
@@ -30,7 +30,7 @@ type (
|
||||
platform platform.Platform
|
||||
baseAnimeCache *result.Cache[int, *anilist.BaseAnime]
|
||||
baseMangaCache *result.Cache[int, *anilist.BaseManga]
|
||||
anizipMediaCache *result.Cache[int, *anizip.Media]
|
||||
metadataProvider metadata.Provider
|
||||
}
|
||||
|
||||
RunPlaygroundCodeResponse struct {
|
||||
@@ -47,13 +47,13 @@ type (
|
||||
}
|
||||
)
|
||||
|
||||
func NewPlaygroundRepository(logger *zerolog.Logger, platform platform.Platform) *PlaygroundRepository {
|
||||
func NewPlaygroundRepository(logger *zerolog.Logger, platform platform.Platform, metadataProvider metadata.Provider) *PlaygroundRepository {
|
||||
return &PlaygroundRepository{
|
||||
logger: logger,
|
||||
platform: platform,
|
||||
metadataProvider: metadataProvider,
|
||||
baseAnimeCache: result.NewCache[int, *anilist.BaseAnime](),
|
||||
baseMangaCache: result.NewCache[int, *anilist.BaseManga](),
|
||||
anizipMediaCache: result.NewCache[int, *anizip.Media](),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,7 +148,7 @@ func newPlaygroundResponse(logger *PlaygroundDebugLogger, value interface{}) *Ru
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
func (r *PlaygroundRepository) getAnime(mediaId int) (anime *anilist.BaseAnime, am *anizip.Media, err error) {
|
||||
func (r *PlaygroundRepository) getAnime(mediaId int) (anime *anilist.BaseAnime, am *metadata.AnimeMetadata, err error) {
|
||||
var ok bool
|
||||
anime, ok = r.baseAnimeCache.Get(mediaId)
|
||||
if !ok {
|
||||
@@ -159,11 +159,7 @@ func (r *PlaygroundRepository) getAnime(mediaId int) (anime *anilist.BaseAnime,
|
||||
r.baseAnimeCache.SetT(mediaId, anime, 24*time.Hour)
|
||||
}
|
||||
|
||||
am, ok = r.anizipMediaCache.Get(mediaId)
|
||||
if !ok {
|
||||
am, _ = anizip.FetchAniZipMedia("anilist", mediaId)
|
||||
r.anizipMediaCache.SetT(mediaId, am, 24*time.Hour)
|
||||
}
|
||||
am, _ = r.metadataProvider.GetAnimeMetadata(metadata.AnilistPlatform, mediaId)
|
||||
return anime, am, nil
|
||||
}
|
||||
|
||||
@@ -196,7 +192,7 @@ func (r *PlaygroundRepository) runPlaygroundCodeAnimeTorrentProvider(ext *extens
|
||||
}
|
||||
|
||||
// Fetch the anime
|
||||
anime, anizipMedia, err := r.getAnime(int(mediaId))
|
||||
anime, animeMetadata, err := r.getAnime(int(mediaId))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -256,15 +252,15 @@ func (r *PlaygroundRepository) runPlaygroundCodeAnimeTorrentProvider(ext *extens
|
||||
anidbEID := 0
|
||||
|
||||
// Get the AniDB Anime ID and Episode ID
|
||||
if anizipMedia != nil {
|
||||
if animeMetadata != nil {
|
||||
// Override absolute offset value of queryMedia
|
||||
queryMedia.AbsoluteSeasonOffset = anizipMedia.GetOffset()
|
||||
queryMedia.AbsoluteSeasonOffset = animeMetadata.GetOffset()
|
||||
|
||||
if anizipMedia.GetMappings() != nil {
|
||||
if animeMetadata.GetMappings() != nil {
|
||||
|
||||
anidbAID = anizipMedia.GetMappings().AnidbID
|
||||
anidbAID = animeMetadata.GetMappings().AnidbId
|
||||
// Find Anizip Episode based on inputted episode number
|
||||
anizipEpisode, found := anizipMedia.FindEpisode(strconv.Itoa(options.EpisodeNumber))
|
||||
anizipEpisode, found := animeMetadata.FindEpisode(strconv.Itoa(options.EpisodeNumber))
|
||||
if found {
|
||||
anidbEID = anizipEpisode.AnidbEid
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
"os"
|
||||
"seanime/internal/api/anilist"
|
||||
"seanime/internal/api/metadata"
|
||||
"seanime/internal/extension"
|
||||
"seanime/internal/platforms/anilist_platform"
|
||||
"seanime/internal/test_utils"
|
||||
@@ -19,8 +20,9 @@ func TestGojaAnimeTorrentProvider(t *testing.T) {
|
||||
|
||||
anilistClient := anilist.TestGetMockAnilistClient()
|
||||
platform := anilist_platform.NewAnilistPlatform(anilistClient, logger)
|
||||
metadataProvider := metadata.GetMockProvider(t)
|
||||
|
||||
repo := NewPlaygroundRepository(logger, platform)
|
||||
repo := NewPlaygroundRepository(logger, platform, metadataProvider)
|
||||
|
||||
// Get the script
|
||||
filepath := "../extension_repo/goja_torrent_test/my-torrent-provider.ts"
|
||||
|
||||
@@ -6,11 +6,8 @@ import (
|
||||
"seanime/internal/database/db_bridge"
|
||||
"seanime/internal/library/anime"
|
||||
"seanime/internal/torrentstream"
|
||||
"seanime/internal/util/result"
|
||||
)
|
||||
|
||||
var libraryCollectionCache = result.NewResultMap[int, *anime.LibraryCollection]()
|
||||
|
||||
// HandleGetLibraryCollection
|
||||
//
|
||||
// @summary returns the main local anime collection.
|
||||
@@ -39,7 +36,6 @@ func HandleGetLibraryCollection(c *RouteCtx) error {
|
||||
libraryCollection, err := anime.NewLibraryCollection(&anime.NewLibraryCollectionOptions{
|
||||
AnimeCollection: animeCollection,
|
||||
Platform: c.App.AnilistPlatform,
|
||||
AnizipCache: c.App.AnizipCache,
|
||||
LocalFiles: lfs,
|
||||
MetadataProvider: c.App.MetadataProvider,
|
||||
})
|
||||
@@ -51,7 +47,7 @@ func HandleGetLibraryCollection(c *RouteCtx) error {
|
||||
c.App.TorrentstreamRepository.HydrateStreamCollection(&torrentstream.HydrateStreamCollectionOptions{
|
||||
AnimeCollection: animeCollection,
|
||||
LibraryCollection: libraryCollection,
|
||||
AnizipCache: c.App.AnizipCache,
|
||||
MetadataProvider: c.App.MetadataProvider,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -10,8 +10,6 @@ import (
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"seanime/internal/api/anilist"
|
||||
"seanime/internal/api/anizip"
|
||||
"seanime/internal/api/mal"
|
||||
"seanime/internal/database/db_bridge"
|
||||
"seanime/internal/library/anime"
|
||||
"seanime/internal/library/scanner"
|
||||
@@ -59,7 +57,6 @@ func HandleGetAnimeEntry(c *RouteCtx) error {
|
||||
entry, err := anime.NewAnimeEntry(&anime.NewAnimeEntryOptions{
|
||||
MediaId: mId,
|
||||
LocalFiles: lfs,
|
||||
AnizipCache: c.App.AnizipCache,
|
||||
AnimeCollection: animeCollection,
|
||||
Platform: c.App.AnilistPlatform,
|
||||
MetadataProvider: c.App.MetadataProvider,
|
||||
@@ -202,7 +199,6 @@ func HandleOpenAnimeEntryInExplorer(c *RouteCtx) error {
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
var (
|
||||
entriesMalCache = result.NewCache[string, []*mal.SearchResultAnime]()
|
||||
entriesSuggestionsCache = result.NewCache[string, []*anilist.BaseAnime]()
|
||||
)
|
||||
|
||||
@@ -353,8 +349,8 @@ func HandleAnimeEntryManualMatch(c *RouteCtx) error {
|
||||
fh := scanner.FileHydrator{
|
||||
LocalFiles: selectedLfs,
|
||||
CompleteAnimeCache: anilist.NewCompleteAnimeCache(),
|
||||
AnizipCache: anizip.NewCache(),
|
||||
Platform: c.App.AnilistPlatform,
|
||||
MetadataProvider: c.App.MetadataProvider,
|
||||
AnilistRateLimiter: limiter.NewAnilistLimiter(),
|
||||
Logger: c.App.Logger,
|
||||
ScanLogger: scanLogger,
|
||||
@@ -427,7 +423,6 @@ func HandleGetMissingEpisodes(c *RouteCtx) error {
|
||||
missingEps := anime.NewMissingEpisodes(&anime.NewMissingEpisodesOptions{
|
||||
AnimeCollection: animeCollection,
|
||||
LocalFiles: lfs,
|
||||
AnizipCache: c.App.AnizipCache,
|
||||
SilencedMediaIds: silencedMediaIds,
|
||||
MetadataProvider: c.App.MetadataProvider,
|
||||
})
|
||||
|
||||
@@ -2,8 +2,8 @@ package handlers
|
||||
|
||||
import (
|
||||
"seanime/internal/api/anilist"
|
||||
"seanime/internal/api/anizip"
|
||||
"seanime/internal/library/scanner"
|
||||
"seanime/internal/util/limiter"
|
||||
)
|
||||
|
||||
// DUMMY HANDLER
|
||||
@@ -32,15 +32,17 @@ func HandleTestDump(c *RouteCtx) error {
|
||||
}
|
||||
|
||||
completeAnimeCache := anilist.NewCompleteAnimeCache()
|
||||
anizipCache := anizip.NewCache()
|
||||
|
||||
mc, err := scanner.NewMediaFetcher(&scanner.MediaFetcherOptions{
|
||||
Enhanced: false,
|
||||
Platform: c.App.AnilistPlatform,
|
||||
LocalFiles: localFiles,
|
||||
CompleteAnimeCache: completeAnimeCache,
|
||||
AnizipCache: anizipCache,
|
||||
Logger: c.App.Logger,
|
||||
Enhanced: false,
|
||||
Platform: c.App.AnilistPlatform,
|
||||
MetadataProvider: c.App.MetadataProvider,
|
||||
LocalFiles: localFiles,
|
||||
CompleteAnimeCache: completeAnimeCache,
|
||||
Logger: c.App.Logger,
|
||||
AnilistRateLimiter: limiter.NewAnilistLimiter(),
|
||||
DisableAnimeCollection: false,
|
||||
ScanLogger: nil,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"seanime/internal/api/anizip"
|
||||
"seanime/internal/api/metadata"
|
||||
)
|
||||
|
||||
// HandlePopulateTVDBEpisodes
|
||||
@@ -20,7 +20,7 @@ func HandlePopulateTVDBEpisodes(c *RouteCtx) error {
|
||||
return c.RespondWithError(err)
|
||||
}
|
||||
|
||||
anizipMedia, err := anizip.FetchAniZipMedia("anilist", b.MediaId)
|
||||
animeMetadata, err := c.App.MetadataProvider.GetAnimeMetadata(metadata.AnilistPlatform, b.MediaId)
|
||||
if err != nil {
|
||||
return c.RespondWithError(err)
|
||||
}
|
||||
@@ -31,7 +31,7 @@ func HandlePopulateTVDBEpisodes(c *RouteCtx) error {
|
||||
}
|
||||
|
||||
// Create media wrapper
|
||||
aw := c.App.MetadataProvider.GetAnimeMetadataWrapper(media, anizipMedia)
|
||||
aw := c.App.MetadataProvider.GetAnimeMetadataWrapper(media, animeMetadata)
|
||||
|
||||
// Fetch episodes
|
||||
episodes, err := aw.GetTVDBEpisodes(true)
|
||||
@@ -59,7 +59,7 @@ func HandleEmptyTVDBEpisodes(c *RouteCtx) error {
|
||||
return c.RespondWithError(err)
|
||||
}
|
||||
|
||||
anizipMedia, err := anizip.FetchAniZipMedia("anilist", b.MediaId)
|
||||
animeMetadata, err := c.App.MetadataProvider.GetAnimeMetadata(metadata.AnilistPlatform, b.MediaId)
|
||||
if err != nil {
|
||||
return c.RespondWithError(err)
|
||||
}
|
||||
@@ -70,7 +70,7 @@ func HandleEmptyTVDBEpisodes(c *RouteCtx) error {
|
||||
}
|
||||
|
||||
// Create media wrapper
|
||||
aw := c.App.MetadataProvider.GetAnimeMetadataWrapper(media, anizipMedia)
|
||||
aw := c.App.MetadataProvider.GetAnimeMetadataWrapper(media, animeMetadata)
|
||||
|
||||
// Empty TVDB episodes bucket
|
||||
err = aw.EmptyTVDBEpisodesBucket(b.MediaId)
|
||||
|
||||
@@ -72,6 +72,7 @@ func HandleScanLocalFiles(c *RouteCtx) error {
|
||||
SkipIgnoredFiles: b.SkipIgnoredFiles,
|
||||
ScanSummaryLogger: scanSummaryLogger,
|
||||
ScanLogger: scanLogger,
|
||||
MetadataProvider: c.App.MetadataProvider,
|
||||
}
|
||||
|
||||
// Scan the library
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
hibiketorrent "github.com/5rahim/hibike/pkg/extension/torrent"
|
||||
lop "github.com/samber/lo/parallel"
|
||||
"seanime/internal/api/anilist"
|
||||
"seanime/internal/api/anizip"
|
||||
"seanime/internal/api/metadata"
|
||||
"seanime/internal/database/models"
|
||||
"seanime/internal/library/anime"
|
||||
"seanime/internal/torrentstream"
|
||||
@@ -107,10 +107,10 @@ func HandleGetTorrentstreamTorrentFilePreviews(c *RouteCtx) error {
|
||||
}
|
||||
|
||||
// Get the media
|
||||
anizipMedia, _ := anizip.FetchAniZipMediaC("anilist", b.Media.GetID(), c.App.AnizipCache)
|
||||
animeMetadata, _ := c.App.MetadataProvider.GetAnimeMetadata(metadata.AnilistPlatform, b.Media.ID)
|
||||
absoluteOffset := 0
|
||||
if anizipMedia != nil {
|
||||
absoluteOffset = anizipMedia.GetOffset()
|
||||
if animeMetadata != nil {
|
||||
absoluteOffset = animeMetadata.GetOffset()
|
||||
}
|
||||
|
||||
files, err := c.App.TorrentstreamRepository.GetTorrentFilePreviewsFromManualSelection(&torrentstream.GetTorrentFilePreviewsOptions{
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"github.com/samber/lo"
|
||||
"github.com/sourcegraph/conc/pool"
|
||||
"seanime/internal/api/anilist"
|
||||
"seanime/internal/api/anizip"
|
||||
"seanime/internal/api/metadata"
|
||||
"seanime/internal/platforms/platform"
|
||||
"sort"
|
||||
@@ -42,7 +41,6 @@ type (
|
||||
NewAnimeEntryOptions struct {
|
||||
MediaId int
|
||||
LocalFiles []*LocalFile // All local files
|
||||
AnizipCache *anizip.Cache
|
||||
AnimeCollection *anilist.AnimeCollection
|
||||
Platform platform.Platform
|
||||
MetadataProvider metadata.Provider
|
||||
@@ -117,7 +115,7 @@ func NewAnimeEntry(opts *NewAnimeEntryOptions) (*AnimeEntry, error) {
|
||||
// +---------------------+
|
||||
|
||||
// Fetch AniDB data and cache it for 30 minutes
|
||||
anizipData, err := anizip.FetchAniZipMediaC("anilist", opts.MediaId, opts.AnizipCache)
|
||||
animeMetadata, err := opts.MetadataProvider.GetAnimeMetadata(metadata.AnilistPlatform, opts.MediaId)
|
||||
if err != nil {
|
||||
|
||||
// +---------------- Start
|
||||
@@ -151,7 +149,7 @@ func NewAnimeEntry(opts *NewAnimeEntryOptions) (*AnimeEntry, error) {
|
||||
// +--------------- End
|
||||
|
||||
}
|
||||
entry.AnidbId = anizipData.GetMappings().AnidbID
|
||||
entry.AnidbId = animeMetadata.GetMappings().AnidbId
|
||||
|
||||
// Instantiate AnimeEntryListData
|
||||
// If the media exist in the user's anime list, add the details
|
||||
@@ -170,7 +168,7 @@ func NewAnimeEntry(opts *NewAnimeEntryOptions) (*AnimeEntry, error) {
|
||||
// +---------------------+
|
||||
|
||||
// Create episode entities
|
||||
entry.hydrateEntryEpisodeData(anilistEntry, anizipData, opts.MetadataProvider)
|
||||
entry.hydrateEntryEpisodeData(anilistEntry, animeMetadata, opts.MetadataProvider)
|
||||
|
||||
return entry, nil
|
||||
|
||||
@@ -182,11 +180,11 @@ func NewAnimeEntry(opts *NewAnimeEntryOptions) (*AnimeEntry, error) {
|
||||
// AniZipData, Media and LocalFiles should be defined
|
||||
func (e *AnimeEntry) hydrateEntryEpisodeData(
|
||||
anilistEntry *anilist.MediaListEntry,
|
||||
anizipData *anizip.Media,
|
||||
animeMetadata *metadata.AnimeMetadata,
|
||||
metadataProvider metadata.Provider,
|
||||
) {
|
||||
|
||||
if anizipData.Episodes == nil && len(anizipData.Episodes) == 0 {
|
||||
if animeMetadata.Episodes == nil && len(animeMetadata.Episodes) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -196,7 +194,7 @@ func (e *AnimeEntry) hydrateEntryEpisodeData(
|
||||
|
||||
// We offset the progress number by 1 if there is a discrepancy
|
||||
progressOffset := 0
|
||||
if HasDiscrepancy(e.Media, anizipData) {
|
||||
if HasDiscrepancy(e.Media, animeMetadata) {
|
||||
progressOffset = 1
|
||||
}
|
||||
|
||||
@@ -224,7 +222,7 @@ func (e *AnimeEntry) hydrateEntryEpisodeData(
|
||||
return NewEpisode(&NewEpisodeOptions{
|
||||
LocalFile: lf,
|
||||
OptionalAniDBEpisode: "",
|
||||
AnizipMedia: anizipData,
|
||||
AnimeMetadata: animeMetadata,
|
||||
Media: e.Media,
|
||||
ProgressOffset: progressOffset,
|
||||
IsDownloaded: true,
|
||||
@@ -245,7 +243,7 @@ func (e *AnimeEntry) hydrateEntryEpisodeData(
|
||||
|
||||
info, err := NewAnimeEntryDownloadInfo(&NewAnimeEntryDownloadInfoOptions{
|
||||
LocalFiles: e.LocalFiles,
|
||||
AnizipMedia: anizipData,
|
||||
AnimeMetadata: animeMetadata,
|
||||
Progress: anilistEntry.Progress,
|
||||
Status: anilistEntry.Status,
|
||||
Media: e.Media,
|
||||
@@ -270,10 +268,10 @@ func (e *AnimeEntry) hydrateEntryEpisodeData(
|
||||
func detectDiscrepancy(
|
||||
mediaLfs []*LocalFile, // Media's local files
|
||||
media *anilist.BaseAnime,
|
||||
anizipData *anizip.Media,
|
||||
animeMetadata *metadata.AnimeMetadata,
|
||||
) (possibleSpecialInclusion bool, hasDiscrepancy bool) {
|
||||
|
||||
if anizipData.Episodes == nil && len(anizipData.Episodes) == 0 {
|
||||
if animeMetadata.Episodes == nil && len(animeMetadata.Episodes) == 0 {
|
||||
return false, false
|
||||
}
|
||||
|
||||
@@ -293,18 +291,18 @@ func detectDiscrepancy(
|
||||
// e.g, epCeiling = 13 AND downloaded episodes = [0,...,13] //=> false
|
||||
possibleSpecialInclusion = hasEpisodeZero && noEpisodeCeiling
|
||||
|
||||
_, aniDBHasS1 := anizipData.Episodes["S1"]
|
||||
_, aniDBHasS1 := animeMetadata.Episodes["S1"]
|
||||
// AniList episode count > Anizip episode count
|
||||
// This means that there is a discrepancy and AniList is most likely including episode 0 as part of main episodes
|
||||
hasDiscrepancy = media.GetCurrentEpisodeCount() > anizipData.GetMainEpisodeCount() && aniDBHasS1
|
||||
hasDiscrepancy = media.GetCurrentEpisodeCount() > animeMetadata.GetMainEpisodeCount() && aniDBHasS1
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func HasDiscrepancy(media *anilist.BaseAnime, anizipData *anizip.Media) bool {
|
||||
if media == nil || anizipData == nil || anizipData.Episodes == nil {
|
||||
func HasDiscrepancy(media *anilist.BaseAnime, animeMetadata *metadata.AnimeMetadata) bool {
|
||||
if media == nil || animeMetadata == nil || animeMetadata.Episodes == nil {
|
||||
return false
|
||||
}
|
||||
_, aniDBHasS1 := anizipData.Episodes["S1"]
|
||||
return media.GetCurrentEpisodeCount() > anizipData.GetMainEpisodeCount() && aniDBHasS1
|
||||
_, aniDBHasS1 := animeMetadata.Episodes["S1"]
|
||||
return media.GetCurrentEpisodeCount() > animeMetadata.GetMainEpisodeCount() && aniDBHasS1
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"github.com/samber/lo"
|
||||
"github.com/sourcegraph/conc/pool"
|
||||
"seanime/internal/api/anilist"
|
||||
"seanime/internal/api/anizip"
|
||||
"seanime/internal/api/metadata"
|
||||
"slices"
|
||||
"strconv"
|
||||
@@ -33,7 +32,7 @@ type (
|
||||
NewAnimeEntryDownloadInfoOptions struct {
|
||||
// Media's local files
|
||||
LocalFiles []*LocalFile
|
||||
AnizipMedia *anizip.Media
|
||||
AnimeMetadata *metadata.AnimeMetadata
|
||||
Media *anilist.BaseAnime
|
||||
Progress *int
|
||||
Status *anilist.MediaListStatus
|
||||
@@ -47,8 +46,8 @@ func NewAnimeEntryDownloadInfo(opts *NewAnimeEntryDownloadInfoOptions) (*AnimeEn
|
||||
if *opts.Media.Status == anilist.MediaStatusNotYetReleased {
|
||||
return &AnimeEntryDownloadInfo{}, nil
|
||||
}
|
||||
if opts.AnizipMedia == nil {
|
||||
return nil, errors.New("could not get anizip media")
|
||||
if opts.AnimeMetadata == nil {
|
||||
return nil, errors.New("could not get anime metadata")
|
||||
}
|
||||
if opts.Media.GetCurrentEpisodeCount() == -1 {
|
||||
return nil, errors.New("could not get current media episode count")
|
||||
@@ -59,7 +58,7 @@ func NewAnimeEntryDownloadInfo(opts *NewAnimeEntryDownloadInfoOptions) (*AnimeEn
|
||||
// +---------------------+
|
||||
|
||||
// Whether AniList includes episode 0 as part of main episodes, but Anizip does not, however Anizip has "S1"
|
||||
_, hasDiscrepancy := detectDiscrepancy(opts.LocalFiles, opts.Media, opts.AnizipMedia)
|
||||
_, hasDiscrepancy := detectDiscrepancy(opts.LocalFiles, opts.Media, opts.AnimeMetadata)
|
||||
|
||||
// I - Progress
|
||||
// Get progress, if the media isn't in the user's list, progress is 0
|
||||
@@ -79,8 +78,8 @@ func NewAnimeEntryDownloadInfo(opts *NewAnimeEntryDownloadInfoOptions) (*AnimeEn
|
||||
mediaEpSlice := generateEpSlice(opts.Media.GetCurrentEpisodeCount()) // e.g, [1,2,3,4]
|
||||
unwatchedEpSlice := lo.Filter(mediaEpSlice, func(i int, _ int) bool { return i > progress }) // e.g, progress = 1: [1,2,3,4] -> [2,3,4]
|
||||
|
||||
anizipEpSlice := generateEpSlice(opts.AnizipMedia.GetMainEpisodeCount()) // e.g, [1,2,3,4]
|
||||
unwatchedAnizipEpSlice := lo.Filter(anizipEpSlice, func(i int, _ int) bool { return i > progress }) // e.g, progress = 1: [1,2,3,4] -> [2,3,4]
|
||||
metadataEpSlice := generateEpSlice(opts.AnimeMetadata.GetMainEpisodeCount()) // e.g, [1,2,3,4]
|
||||
unwatchedAnizipEpSlice := lo.Filter(metadataEpSlice, func(i int, _ int) bool { return i > progress }) // e.g, progress = 1: [1,2,3,4] -> [2,3,4]
|
||||
|
||||
// +---------------------+
|
||||
// | Anizip has more |
|
||||
@@ -88,11 +87,11 @@ func NewAnimeEntryDownloadInfo(opts *NewAnimeEntryDownloadInfoOptions) (*AnimeEn
|
||||
|
||||
// If Anizip has more episodes
|
||||
// e.g, Anizip: 2, Anilist: 1
|
||||
if opts.AnizipMedia.GetMainEpisodeCount() > opts.Media.GetCurrentEpisodeCount() {
|
||||
diff := opts.AnizipMedia.GetMainEpisodeCount() - opts.Media.GetCurrentEpisodeCount()
|
||||
if opts.AnimeMetadata.GetMainEpisodeCount() > opts.Media.GetCurrentEpisodeCount() {
|
||||
diff := opts.AnimeMetadata.GetMainEpisodeCount() - opts.Media.GetCurrentEpisodeCount()
|
||||
// Remove the extra episode number from the Anizip slice
|
||||
anizipEpSlice = anizipEpSlice[:len(anizipEpSlice)-diff] // e.g, [1,2] -> [1]
|
||||
unwatchedAnizipEpSlice = lo.Filter(anizipEpSlice, func(i int, _ int) bool { return i > progress }) // e.g, [1,2] -> [1]
|
||||
metadataEpSlice = metadataEpSlice[:len(metadataEpSlice)-diff] // e.g, [1,2] -> [1]
|
||||
unwatchedAnizipEpSlice = lo.Filter(metadataEpSlice, func(i int, _ int) bool { return i > progress }) // e.g, [1,2] -> [1]
|
||||
}
|
||||
|
||||
// +---------------------+
|
||||
@@ -100,14 +99,14 @@ func NewAnimeEntryDownloadInfo(opts *NewAnimeEntryDownloadInfoOptions) (*AnimeEn
|
||||
// +---------------------+
|
||||
|
||||
// III - Handle discrepancy (inclusion of episode 0 by AniList)
|
||||
// If there Anilist has more episodes than Anizip
|
||||
// If Anilist has more episodes than Anizip
|
||||
// e.g, Anilist: 13, Anizip: 12
|
||||
if hasDiscrepancy {
|
||||
// Add -1 to Anizip slice, -1 is "S1"
|
||||
anizipEpSlice = append([]int{-1}, anizipEpSlice...) // e.g, [-1,1,2,...,12]
|
||||
unwatchedAnizipEpSlice = anizipEpSlice // e.g, [-1,1,2,...,12]
|
||||
metadataEpSlice = append([]int{-1}, metadataEpSlice...) // e.g, [-1,1,2,...,12]
|
||||
unwatchedAnizipEpSlice = metadataEpSlice // e.g, [-1,1,2,...,12]
|
||||
if progress > 0 {
|
||||
unwatchedAnizipEpSlice = lo.Filter(anizipEpSlice, func(i int, _ int) bool { return i > progress-1 })
|
||||
unwatchedAnizipEpSlice = lo.Filter(metadataEpSlice, func(i int, _ int) bool { return i > progress-1 })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,7 +124,7 @@ func NewAnimeEntryDownloadInfo(opts *NewAnimeEntryDownloadInfoOptions) (*AnimeEn
|
||||
hasInaccurateSchedule := false
|
||||
if opts.Media.NextAiringEpisode == nil && *opts.Media.Status == anilist.MediaStatusReleasing {
|
||||
if !hasDiscrepancy {
|
||||
if progress+1 < opts.AnizipMedia.GetMainEpisodeCount() {
|
||||
if progress+1 < opts.AnimeMetadata.GetMainEpisodeCount() {
|
||||
unwatchedEpSlice = lo.Filter(unwatchedEpSlice, func(i int, _ int) bool { return i > progress && i <= progress+1 })
|
||||
unwatchedAnizipEpSlice = lo.Filter(unwatchedAnizipEpSlice, func(i int, _ int) bool { return i > progress && i <= progress+1 })
|
||||
} else {
|
||||
@@ -133,7 +132,7 @@ func NewAnimeEntryDownloadInfo(opts *NewAnimeEntryDownloadInfoOptions) (*AnimeEn
|
||||
unwatchedAnizipEpSlice = lo.Filter(unwatchedAnizipEpSlice, func(i int, _ int) bool { return i > progress && i <= progress })
|
||||
}
|
||||
} else {
|
||||
if progress+1 < opts.AnizipMedia.GetMainEpisodeCount() {
|
||||
if progress+1 < opts.AnimeMetadata.GetMainEpisodeCount() {
|
||||
unwatchedEpSlice = lo.Filter(unwatchedEpSlice, func(i int, _ int) bool { return i > progress && i <= progress })
|
||||
unwatchedAnizipEpSlice = lo.Filter(unwatchedAnizipEpSlice, func(i int, _ int) bool { return i > progress && i <= progress })
|
||||
} else {
|
||||
@@ -196,7 +195,7 @@ func NewAnimeEntryDownloadInfo(opts *NewAnimeEntryDownloadInfoOptions) (*AnimeEn
|
||||
str.Episode = NewEpisode(&NewEpisodeOptions{
|
||||
LocalFile: nil,
|
||||
OptionalAniDBEpisode: str.AniDBEpisode,
|
||||
AnizipMedia: opts.AnizipMedia,
|
||||
AnimeMetadata: opts.AnimeMetadata,
|
||||
Media: opts.Media,
|
||||
ProgressOffset: 0,
|
||||
IsDownloaded: false,
|
||||
@@ -228,7 +227,7 @@ func NewAnimeEntryDownloadInfo(opts *NewAnimeEntryDownloadInfoOptions) (*AnimeEn
|
||||
BatchAll: batchAll,
|
||||
Rewatch: rewatch,
|
||||
HasInaccurateSchedule: hasInaccurateSchedule,
|
||||
AbsoluteOffset: opts.AnizipMedia.GetOffset(),
|
||||
AbsoluteOffset: opts.AnimeMetadata.GetOffset(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"seanime/internal/api/anilist"
|
||||
"seanime/internal/api/anizip"
|
||||
"seanime/internal/api/metadata"
|
||||
"seanime/internal/test_utils"
|
||||
"testing"
|
||||
@@ -13,7 +12,7 @@ import (
|
||||
func TestNewAnimeEntryDownloadInfo(t *testing.T) {
|
||||
test_utils.InitTestProvider(t, test_utils.Anilist())
|
||||
|
||||
metadataProvider := metadata.TestGetMockProvider(t)
|
||||
metadataProvider := metadata.GetMockProvider(t)
|
||||
|
||||
anilistClient := anilist.TestGetMockAnilistClient()
|
||||
animeCollection, err := anilistClient.AnimeCollection(context.Background(), nil)
|
||||
@@ -86,22 +85,14 @@ func TestNewAnimeEntryDownloadInfo(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
anizipCache := anizip.NewCache()
|
||||
|
||||
for _, tt := range tests {
|
||||
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
||||
anizipData, err := anizip.FetchAniZipMediaC("anilist", tt.mediaId, anizipCache)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
anilistEntry, _ := animeCollection.GetListEntryFromAnimeId(tt.mediaId)
|
||||
|
||||
info, err := NewAnimeEntryDownloadInfo(&NewAnimeEntryDownloadInfoOptions{
|
||||
LocalFiles: tt.localFiles,
|
||||
AnizipMedia: anizipData,
|
||||
Progress: &tt.currentProgress,
|
||||
Status: &tt.status,
|
||||
Media: anilistEntry.Media,
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"github.com/samber/lo"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"seanime/internal/api/anilist"
|
||||
"seanime/internal/api/anizip"
|
||||
"seanime/internal/api/metadata"
|
||||
"seanime/internal/platforms/anilist_platform"
|
||||
"seanime/internal/test_utils"
|
||||
@@ -17,7 +16,7 @@ import (
|
||||
func TestNewAnimeEntry(t *testing.T) {
|
||||
test_utils.InitTestProvider(t, test_utils.Anilist())
|
||||
|
||||
metadataProvider := metadata.TestGetMockProvider(t)
|
||||
metadataProvider := metadata.GetMockProvider(t)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -76,8 +75,6 @@ func TestNewAnimeEntry(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
aniZipCache := anizip.NewCache()
|
||||
|
||||
for _, tt := range tests {
|
||||
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
@@ -89,7 +86,6 @@ func TestNewAnimeEntry(t *testing.T) {
|
||||
entry, err := NewAnimeEntry(&NewAnimeEntryOptions{
|
||||
MediaId: tt.mediaId,
|
||||
LocalFiles: tt.localFiles,
|
||||
AnizipCache: aniZipCache,
|
||||
AnimeCollection: animeCollection,
|
||||
Platform: anilistPlatform,
|
||||
MetadataProvider: metadataProvider,
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"github.com/sourcegraph/conc/pool"
|
||||
"path/filepath"
|
||||
"seanime/internal/api/anilist"
|
||||
"seanime/internal/api/anizip"
|
||||
"seanime/internal/api/metadata"
|
||||
"seanime/internal/platforms/platform"
|
||||
"seanime/internal/util"
|
||||
@@ -86,7 +85,6 @@ type (
|
||||
NewLibraryCollectionOptions struct {
|
||||
AnimeCollection *anilist.AnimeCollection
|
||||
LocalFiles []*LocalFile
|
||||
AnizipCache *anizip.Cache
|
||||
Platform platform.Platform
|
||||
MetadataProvider metadata.Provider
|
||||
}
|
||||
@@ -114,7 +112,6 @@ func NewLibraryCollection(opts *NewLibraryCollectionOptions) (lc *LibraryCollect
|
||||
lc.hydrateContinueWatchingList(
|
||||
opts.LocalFiles,
|
||||
opts.AnimeCollection,
|
||||
opts.AnizipCache,
|
||||
opts.Platform,
|
||||
opts.MetadataProvider,
|
||||
)
|
||||
@@ -315,7 +312,6 @@ func (lc *LibraryCollection) hydrateStats(lfs []*LocalFile) {
|
||||
func (lc *LibraryCollection) hydrateContinueWatchingList(
|
||||
localFiles []*LocalFile,
|
||||
animeCollection *anilist.AnimeCollection,
|
||||
anizipCache *anizip.Cache,
|
||||
platform platform.Platform,
|
||||
metadataProvider metadata.Provider,
|
||||
) {
|
||||
@@ -344,7 +340,6 @@ func (lc *LibraryCollection) hydrateContinueWatchingList(
|
||||
MediaId: mId,
|
||||
LocalFiles: localFiles,
|
||||
AnimeCollection: animeCollection,
|
||||
AnizipCache: anizipCache,
|
||||
Platform: platform,
|
||||
MetadataProvider: metadataProvider,
|
||||
})
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"github.com/samber/lo"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"seanime/internal/api/anilist"
|
||||
"seanime/internal/api/anizip"
|
||||
"seanime/internal/api/metadata"
|
||||
"seanime/internal/platforms/anilist_platform"
|
||||
"seanime/internal/test_utils"
|
||||
@@ -15,7 +14,7 @@ import (
|
||||
func TestNewLibraryCollection(t *testing.T) {
|
||||
test_utils.InitTestProvider(t, test_utils.Anilist())
|
||||
|
||||
metadataProvider := metadata.TestGetMockProvider(t)
|
||||
metadataProvider := metadata.GetMockProvider(t)
|
||||
|
||||
anilistClient := anilist.TestGetMockAnilistClient()
|
||||
anilistPlatform := anilist_platform.NewAnilistPlatform(anilistClient, util.NewLogger())
|
||||
@@ -79,7 +78,6 @@ func TestNewLibraryCollection(t *testing.T) {
|
||||
libraryCollection, err := NewLibraryCollection(&NewLibraryCollectionOptions{
|
||||
AnimeCollection: animeCollection,
|
||||
LocalFiles: lfs,
|
||||
AnizipCache: anizip.NewCache(),
|
||||
Platform: anilistPlatform,
|
||||
MetadataProvider: metadataProvider,
|
||||
})
|
||||
|
||||
@@ -2,7 +2,6 @@ package anime
|
||||
|
||||
import (
|
||||
"seanime/internal/api/anilist"
|
||||
"seanime/internal/api/anizip"
|
||||
"seanime/internal/api/metadata"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -27,7 +26,7 @@ type (
|
||||
BaseAnime *anilist.BaseAnime `json:"baseAnime,omitempty"`
|
||||
}
|
||||
|
||||
// EpisodeMetadata represents the metadata of a Episode.
|
||||
// EpisodeMetadata represents the metadata of an Episode.
|
||||
// Metadata is fetched from AniZip (AniDB) and, optionally, AniList (if AniZip is not available).
|
||||
EpisodeMetadata struct {
|
||||
AnidbId int `json:"anidbId,omitempty"`
|
||||
@@ -44,7 +43,7 @@ type (
|
||||
// NewEpisodeOptions hold data used to create a new Episode.
|
||||
NewEpisodeOptions struct {
|
||||
LocalFile *LocalFile
|
||||
AnizipMedia *anizip.Media // optional
|
||||
AnimeMetadata *metadata.AnimeMetadata // optional
|
||||
Media *anilist.BaseAnime
|
||||
OptionalAniDBEpisode string
|
||||
// ProgressOffset will offset the ProgressNumber for a specific MAIN file
|
||||
@@ -69,7 +68,7 @@ type (
|
||||
// It is used to list existing local files as episodes
|
||||
// OR list non-downloaded episodes by passing the `OptionalAniDBEpisode` parameter.
|
||||
//
|
||||
// `AnizipMedia` should be defined, but this is not always the case.
|
||||
// `AnimeMetadata` should be defined, but this is not always the case.
|
||||
// `LocalFile` is optional.
|
||||
func NewEpisode(opts *NewEpisodeOptions) *Episode {
|
||||
entryEp := new(Episode)
|
||||
@@ -94,16 +93,16 @@ func NewEpisode(opts *NewEpisodeOptions) *Episode {
|
||||
opts.ProgressOffset = 0
|
||||
} else {
|
||||
// e.g, "1" -> "2" etc...
|
||||
aniDBEp = anizip.OffsetEpisode(aniDBEp, opts.ProgressOffset)
|
||||
aniDBEp = metadata.OffsetAnidbEpisode(aniDBEp, opts.ProgressOffset)
|
||||
}
|
||||
entryEp.MetadataIssue = "forced_remapping"
|
||||
}
|
||||
|
||||
// Get the AniZip episode
|
||||
foundAnizipEpisode := false
|
||||
var anizipEpisode *anizip.Episode
|
||||
if opts.AnizipMedia != nil {
|
||||
anizipEpisode, foundAnizipEpisode = opts.AnizipMedia.FindEpisode(aniDBEp)
|
||||
var episodeMetadata *metadata.EpisodeMetadata
|
||||
if opts.AnimeMetadata != nil {
|
||||
episodeMetadata, foundAnizipEpisode = opts.AnimeMetadata.FindEpisode(aniDBEp)
|
||||
}
|
||||
|
||||
entryEp.IsDownloaded = true
|
||||
@@ -118,7 +117,7 @@ func NewEpisode(opts *NewEpisodeOptions) *Episode {
|
||||
entryEp.ProgressNumber = opts.LocalFile.GetEpisodeNumber() + opts.ProgressOffset
|
||||
if foundAnizipEpisode {
|
||||
entryEp.AniDBEpisode = aniDBEp
|
||||
entryEp.AbsoluteEpisodeNumber = entryEp.EpisodeNumber + opts.AnizipMedia.GetOffset()
|
||||
entryEp.AbsoluteEpisodeNumber = entryEp.EpisodeNumber + opts.AnimeMetadata.GetOffset()
|
||||
}
|
||||
case LocalFileTypeSpecial:
|
||||
entryEp.EpisodeNumber = opts.LocalFile.GetEpisodeNumber()
|
||||
@@ -139,7 +138,7 @@ func NewEpisode(opts *NewEpisodeOptions) *Episode {
|
||||
entryEp.EpisodeTitle = "Complete Movie"
|
||||
} else {
|
||||
entryEp.DisplayTitle = "Episode " + strconv.Itoa(opts.LocalFile.GetEpisodeNumber())
|
||||
entryEp.EpisodeTitle = anizipEpisode.GetTitle()
|
||||
entryEp.EpisodeTitle = episodeMetadata.GetTitle()
|
||||
}
|
||||
} else {
|
||||
if *opts.Media.GetFormat() == anilist.MediaFormatMovie {
|
||||
@@ -154,13 +153,13 @@ func NewEpisode(opts *NewEpisodeOptions) *Episode {
|
||||
case LocalFileTypeSpecial:
|
||||
if foundAnizipEpisode {
|
||||
entryEp.AniDBEpisode = aniDBEp
|
||||
episodeInt, found := anizip.ExtractEpisodeInteger(aniDBEp)
|
||||
episodeInt, found := metadata.ExtractEpisodeInteger(aniDBEp)
|
||||
if found {
|
||||
entryEp.DisplayTitle = "Special " + strconv.Itoa(episodeInt)
|
||||
} else {
|
||||
entryEp.DisplayTitle = "Special " + aniDBEp
|
||||
}
|
||||
entryEp.EpisodeTitle = anizipEpisode.GetTitle()
|
||||
entryEp.EpisodeTitle = episodeMetadata.GetTitle()
|
||||
} else {
|
||||
entryEp.DisplayTitle = "Special " + strconv.Itoa(opts.LocalFile.GetEpisodeNumber())
|
||||
}
|
||||
@@ -168,7 +167,7 @@ func NewEpisode(opts *NewEpisodeOptions) *Episode {
|
||||
case LocalFileTypeNC:
|
||||
if foundAnizipEpisode {
|
||||
entryEp.AniDBEpisode = aniDBEp
|
||||
entryEp.DisplayTitle = anizipEpisode.GetTitle()
|
||||
entryEp.DisplayTitle = episodeMetadata.GetTitle()
|
||||
entryEp.EpisodeTitle = ""
|
||||
} else {
|
||||
entryEp.DisplayTitle = opts.LocalFile.GetParsedTitle()
|
||||
@@ -181,13 +180,13 @@ func NewEpisode(opts *NewEpisodeOptions) *Episode {
|
||||
}
|
||||
|
||||
// Set episode metadata
|
||||
entryEp.EpisodeMetadata = NewEpisodeMetadata(opts.AnizipMedia, anizipEpisode, opts.Media, opts.MetadataProvider)
|
||||
entryEp.EpisodeMetadata = NewEpisodeMetadata(opts.AnimeMetadata, episodeMetadata, opts.Media, opts.MetadataProvider)
|
||||
|
||||
} else if len(opts.OptionalAniDBEpisode) > 0 && opts.AnizipMedia != nil {
|
||||
} else if len(opts.OptionalAniDBEpisode) > 0 && opts.AnimeMetadata != nil {
|
||||
// No LocalFile, but AniDB episode is provided
|
||||
|
||||
// Get the AniZip episode
|
||||
if anizipEpisode, foundAnizipEpisode := opts.AnizipMedia.FindEpisode(opts.OptionalAniDBEpisode); foundAnizipEpisode {
|
||||
if episodeMetadata, foundAnizipEpisode := opts.AnimeMetadata.FindEpisode(opts.OptionalAniDBEpisode); foundAnizipEpisode {
|
||||
|
||||
entryEp.IsDownloaded = false
|
||||
entryEp.Type = LocalFileTypeMain
|
||||
@@ -199,12 +198,12 @@ func NewEpisode(opts *NewEpisodeOptions) *Episode {
|
||||
entryEp.EpisodeNumber = 0
|
||||
entryEp.ProgressNumber = 0
|
||||
|
||||
if episodeInt, ok := anizip.ExtractEpisodeInteger(opts.OptionalAniDBEpisode); ok {
|
||||
if episodeInt, ok := metadata.ExtractEpisodeInteger(opts.OptionalAniDBEpisode); ok {
|
||||
entryEp.EpisodeNumber = episodeInt
|
||||
entryEp.ProgressNumber = episodeInt
|
||||
if foundAnizipEpisode {
|
||||
entryEp.AniDBEpisode = opts.OptionalAniDBEpisode
|
||||
entryEp.AbsoluteEpisodeNumber = entryEp.EpisodeNumber + opts.AnizipMedia.GetOffset()
|
||||
entryEp.AbsoluteEpisodeNumber = entryEp.EpisodeNumber + opts.AnimeMetadata.GetOffset()
|
||||
}
|
||||
switch entryEp.Type {
|
||||
case LocalFileTypeMain:
|
||||
@@ -213,11 +212,11 @@ func NewEpisode(opts *NewEpisodeOptions) *Episode {
|
||||
entryEp.EpisodeTitle = "Complete Movie"
|
||||
} else {
|
||||
entryEp.DisplayTitle = "Episode " + strconv.Itoa(episodeInt)
|
||||
entryEp.EpisodeTitle = anizipEpisode.GetTitle()
|
||||
entryEp.EpisodeTitle = episodeMetadata.GetTitle()
|
||||
}
|
||||
case LocalFileTypeSpecial:
|
||||
entryEp.DisplayTitle = "Special " + strconv.Itoa(episodeInt)
|
||||
entryEp.EpisodeTitle = anizipEpisode.GetTitle()
|
||||
entryEp.EpisodeTitle = episodeMetadata.GetTitle()
|
||||
case LocalFileTypeNC:
|
||||
entryEp.DisplayTitle = opts.OptionalAniDBEpisode
|
||||
entryEp.EpisodeTitle = ""
|
||||
@@ -226,7 +225,7 @@ func NewEpisode(opts *NewEpisodeOptions) *Episode {
|
||||
}
|
||||
|
||||
// Set episode metadata
|
||||
entryEp.EpisodeMetadata = NewEpisodeMetadata(opts.AnizipMedia, anizipEpisode, opts.Media, opts.MetadataProvider)
|
||||
entryEp.EpisodeMetadata = NewEpisodeMetadata(opts.AnimeMetadata, episodeMetadata, opts.Media, opts.MetadataProvider)
|
||||
} else {
|
||||
// No Local file, no AniZip data
|
||||
// DEVNOTE: Non-downloaded, without any AniDB data. Don't handle this case.
|
||||
@@ -251,8 +250,8 @@ func NewEpisode(opts *NewEpisodeOptions) *Episode {
|
||||
// NewEpisodeMetadata creates a new EpisodeMetadata from an AniZip episode and AniList media.
|
||||
// If the AniZip episode is nil, it will just set the image from the media.
|
||||
func NewEpisodeMetadata(
|
||||
anizipMedia *anizip.Media,
|
||||
episode *anizip.Episode,
|
||||
animeMetadata *metadata.AnimeMetadata,
|
||||
episode *metadata.EpisodeMetadata,
|
||||
media *anilist.BaseAnime,
|
||||
metadataProvider metadata.Provider,
|
||||
) *EpisodeMetadata {
|
||||
@@ -266,7 +265,7 @@ func NewEpisodeMetadata(
|
||||
epInt, err := strconv.Atoi(episode.Episode)
|
||||
|
||||
if err == nil {
|
||||
aw := metadataProvider.GetAnimeMetadataWrapper(media, anizipMedia)
|
||||
aw := metadataProvider.GetAnimeMetadataWrapper(media, animeMetadata)
|
||||
epMetadata := aw.GetEpisodeMetadata(epInt)
|
||||
md.AnidbId = epMetadata.AnidbId
|
||||
md.Image = epMetadata.Image
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
lop "github.com/samber/lo/parallel"
|
||||
"github.com/sourcegraph/conc/pool"
|
||||
"seanime/internal/api/anilist"
|
||||
"seanime/internal/api/anizip"
|
||||
"seanime/internal/api/metadata"
|
||||
"seanime/internal/util/limiter"
|
||||
"sort"
|
||||
@@ -22,7 +21,6 @@ type (
|
||||
NewMissingEpisodesOptions struct {
|
||||
AnimeCollection *anilist.AnimeCollection
|
||||
LocalFiles []*LocalFile
|
||||
AnizipCache *anizip.Cache
|
||||
SilencedMediaIds []int
|
||||
MetadataProvider metadata.Provider
|
||||
}
|
||||
@@ -52,8 +50,8 @@ func NewMissingEpisodes(opts *NewMissingEpisodesOptions) *MissingEpisodes {
|
||||
return nil
|
||||
}
|
||||
rateLimiter.Wait()
|
||||
// Fetch anizip media
|
||||
anizipMedia, err := anizip.FetchAniZipMediaC("anilist", entry.Media.ID, opts.AnizipCache)
|
||||
// Fetch anime metadata
|
||||
animeMetadata, err := opts.MetadataProvider.GetAnimeMetadata(metadata.AnilistPlatform, entry.Media.ID)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
@@ -61,10 +59,10 @@ func NewMissingEpisodes(opts *NewMissingEpisodesOptions) *MissingEpisodes {
|
||||
// Get download info
|
||||
downloadInfo, err := NewAnimeEntryDownloadInfo(&NewAnimeEntryDownloadInfoOptions{
|
||||
LocalFiles: lfs,
|
||||
AnizipMedia: anizipMedia,
|
||||
AnimeMetadata: animeMetadata,
|
||||
Media: entry.Media,
|
||||
Progress: entry.Progress,
|
||||
Status: entry.Status,
|
||||
Media: entry.Media,
|
||||
MetadataProvider: opts.MetadataProvider,
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"github.com/samber/lo"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"seanime/internal/api/anilist"
|
||||
"seanime/internal/api/anizip"
|
||||
"seanime/internal/api/metadata"
|
||||
"seanime/internal/test_utils"
|
||||
"testing"
|
||||
@@ -14,9 +13,10 @@ import (
|
||||
// Test to retrieve accurate missing episodes
|
||||
// DEPRECATED
|
||||
func TestNewMissingEpisodes(t *testing.T) {
|
||||
t.Skip("Outdated test")
|
||||
test_utils.InitTestProvider(t, test_utils.Anilist())
|
||||
|
||||
metadataProvider := metadata.TestGetMockProvider(t)
|
||||
metadataProvider := metadata.GetMockProvider(t)
|
||||
|
||||
anilistClient := anilist.TestGetMockAnilistClient()
|
||||
animeCollection, err := anilistClient.AnimeCollection(context.Background(), nil)
|
||||
@@ -73,7 +73,6 @@ func TestNewMissingEpisodes(t *testing.T) {
|
||||
missingData := NewMissingEpisodes(&NewMissingEpisodesOptions{
|
||||
AnimeCollection: animeCollection,
|
||||
LocalFiles: tt.localFiles,
|
||||
AnizipCache: anizip.NewCache(),
|
||||
MetadataProvider: metadataProvider,
|
||||
})
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"github.com/samber/mo"
|
||||
"github.com/sourcegraph/conc/pool"
|
||||
"seanime/internal/api/anilist"
|
||||
"seanime/internal/api/anizip"
|
||||
"seanime/internal/api/metadata"
|
||||
"seanime/internal/database/db"
|
||||
"seanime/internal/database/db_bridge"
|
||||
"seanime/internal/database/models"
|
||||
@@ -38,7 +38,7 @@ type (
|
||||
animeCollection mo.Option[*anilist.AnimeCollection]
|
||||
wsEventManager events.WSEventManagerInterface
|
||||
settings *models.AutoDownloaderSettings
|
||||
anizipCache *anizip.Cache
|
||||
metadataProvider metadata.Provider
|
||||
settingsUpdatedCh chan struct{}
|
||||
stopCh chan struct{}
|
||||
startCh chan struct{}
|
||||
@@ -52,7 +52,7 @@ type (
|
||||
TorrentRepository *torrent.Repository
|
||||
WSEventManager events.WSEventManagerInterface
|
||||
Database *db.Database
|
||||
AnizipCache *anizip.Cache
|
||||
MetadataProvider metadata.Provider
|
||||
}
|
||||
|
||||
tmpTorrentToDownload struct {
|
||||
@@ -69,7 +69,7 @@ func New(opts *NewAutoDownloaderOptions) *AutoDownloader {
|
||||
database: opts.Database,
|
||||
wsEventManager: opts.WSEventManager,
|
||||
animeCollection: mo.None[*anilist.AnimeCollection](),
|
||||
anizipCache: opts.AnizipCache,
|
||||
metadataProvider: opts.MetadataProvider,
|
||||
settings: &models.AutoDownloaderSettings{
|
||||
Provider: torrent.ProviderAnimeTosho, // Default provider, will be updated after the settings are fetched
|
||||
Interval: 10,
|
||||
@@ -480,8 +480,8 @@ func (ad *AutoDownloader) downloadTorrent(t *NormalizedTorrent, rule *anime.Auto
|
||||
Episode: episode,
|
||||
Link: t.Link,
|
||||
Hash: t.InfoHash,
|
||||
TorrentName: t.Name,
|
||||
Magnet: magnet,
|
||||
TorrentName: t.Name,
|
||||
Downloaded: downloaded,
|
||||
}
|
||||
_ = ad.database.InsertAutoDownloaderItem(item)
|
||||
@@ -668,10 +668,10 @@ func (ad *AutoDownloader) isEpisodeMatch(
|
||||
if listEntry.GetMedia().GetCurrentEpisodeCount() != -1 && episode > listEntry.GetMedia().GetCurrentEpisodeCount() {
|
||||
// Fetch the AniZip media in order to normalize the episode number
|
||||
ad.mu.Lock()
|
||||
anizipMedia, err := anizip.FetchAniZipMediaC("anilist", listEntry.GetMedia().GetID(), ad.anizipCache)
|
||||
animeMetadata, err := ad.metadataProvider.GetAnimeMetadata(metadata.AnilistPlatform, listEntry.GetMedia().GetID())
|
||||
// If the media is found and the offset is greater than 0
|
||||
if err == nil && anizipMedia.GetOffset() > 0 {
|
||||
episode = episode - anizipMedia.GetOffset()
|
||||
if err == nil && animeMetadata.GetOffset() > 0 {
|
||||
episode = episode - animeMetadata.GetOffset()
|
||||
}
|
||||
ad.mu.Unlock()
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package autoscanner
|
||||
import (
|
||||
"errors"
|
||||
"github.com/rs/zerolog"
|
||||
"seanime/internal/api/metadata"
|
||||
"seanime/internal/database/db"
|
||||
"seanime/internal/database/db_bridge"
|
||||
"seanime/internal/events"
|
||||
@@ -18,27 +19,29 @@ import (
|
||||
|
||||
type (
|
||||
AutoScanner struct {
|
||||
fileActionCh chan struct{} // Used to notify the scanner that a file action has occurred.
|
||||
waiting bool // Used to prevent multiple scans from occurring at the same time.
|
||||
missedAction bool // Used to indicate that a file action was missed while scanning.
|
||||
mu sync.Mutex
|
||||
scannedCh chan struct{}
|
||||
waitTime time.Duration // Wait time to listen to additional changes before triggering a scan.
|
||||
enabled bool
|
||||
platform platform.Platform
|
||||
logger *zerolog.Logger
|
||||
wsEventManager events.WSEventManagerInterface
|
||||
db *db.Database // Database instance is required to update the local files.
|
||||
autoDownloader *autodownloader.AutoDownloader // AutoDownloader instance is required to refresh queue.
|
||||
fileActionCh chan struct{} // Used to notify the scanner that a file action has occurred.
|
||||
waiting bool // Used to prevent multiple scans from occurring at the same time.
|
||||
missedAction bool // Used to indicate that a file action was missed while scanning.
|
||||
mu sync.Mutex
|
||||
scannedCh chan struct{}
|
||||
waitTime time.Duration // Wait time to listen to additional changes before triggering a scan.
|
||||
enabled bool
|
||||
platform platform.Platform
|
||||
logger *zerolog.Logger
|
||||
wsEventManager events.WSEventManagerInterface
|
||||
db *db.Database // Database instance is required to update the local files.
|
||||
autoDownloader *autodownloader.AutoDownloader // AutoDownloader instance is required to refresh queue.
|
||||
metadataProvider metadata.Provider
|
||||
}
|
||||
NewAutoScannerOptions struct {
|
||||
Database *db.Database
|
||||
Platform platform.Platform
|
||||
Logger *zerolog.Logger
|
||||
WSEventManager events.WSEventManagerInterface
|
||||
Enabled bool
|
||||
AutoDownloader *autodownloader.AutoDownloader
|
||||
WaitTime time.Duration
|
||||
Database *db.Database
|
||||
Platform platform.Platform
|
||||
Logger *zerolog.Logger
|
||||
WSEventManager events.WSEventManagerInterface
|
||||
Enabled bool
|
||||
AutoDownloader *autodownloader.AutoDownloader
|
||||
WaitTime time.Duration
|
||||
MetadataProvider metadata.Provider
|
||||
}
|
||||
)
|
||||
|
||||
@@ -49,17 +52,19 @@ func New(opts *NewAutoScannerOptions) *AutoScanner {
|
||||
}
|
||||
|
||||
return &AutoScanner{
|
||||
fileActionCh: make(chan struct{}, 1),
|
||||
scannedCh: make(chan struct{}, 1),
|
||||
waiting: false,
|
||||
missedAction: false,
|
||||
waitTime: wt,
|
||||
enabled: opts.Enabled,
|
||||
autoDownloader: opts.AutoDownloader,
|
||||
platform: opts.Platform,
|
||||
logger: opts.Logger,
|
||||
wsEventManager: opts.WSEventManager,
|
||||
db: opts.Database,
|
||||
fileActionCh: make(chan struct{}, 1),
|
||||
waiting: false,
|
||||
missedAction: false,
|
||||
mu: sync.Mutex{},
|
||||
scannedCh: make(chan struct{}, 1),
|
||||
waitTime: wt,
|
||||
enabled: opts.Enabled,
|
||||
platform: opts.Platform,
|
||||
logger: opts.Logger,
|
||||
wsEventManager: opts.WSEventManager,
|
||||
db: opts.Database,
|
||||
autoDownloader: opts.AutoDownloader,
|
||||
metadataProvider: opts.MetadataProvider,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,6 +208,7 @@ func (as *AutoScanner) scan() {
|
||||
SkipLockedFiles: true, // Skip locked files by default.
|
||||
SkipIgnoredFiles: true,
|
||||
ScanSummaryLogger: scanSummaryLogger,
|
||||
MetadataProvider: as.metadataProvider,
|
||||
}
|
||||
|
||||
allLfs, err := sc.Scan()
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
lop "github.com/samber/lo/parallel"
|
||||
"github.com/sourcegraph/conc/pool"
|
||||
"seanime/internal/api/anilist"
|
||||
"seanime/internal/api/anizip"
|
||||
"seanime/internal/api/metadata"
|
||||
"seanime/internal/library/anime"
|
||||
"seanime/internal/library/summary"
|
||||
"seanime/internal/platforms/platform"
|
||||
@@ -24,8 +24,8 @@ type FileHydrator struct {
|
||||
LocalFiles []*anime.LocalFile // Local files to hydrate
|
||||
AllMedia []*anime.NormalizedMedia // All media used to hydrate local files
|
||||
CompleteAnimeCache *anilist.CompleteAnimeCache
|
||||
AnizipCache *anizip.Cache
|
||||
Platform platform.Platform
|
||||
MetadataProvider metadata.Provider
|
||||
AnilistRateLimiter *limiter.Limiter
|
||||
Logger *zerolog.Logger
|
||||
ScanLogger *ScanLogger // optional
|
||||
@@ -267,9 +267,9 @@ func (fh *FileHydrator) hydrateGroupMetadata(
|
||||
if err := media.FetchMediaTree(anilist.FetchMediaTreeAll, fh.Platform.GetAnilistClient(), fh.AnilistRateLimiter, tree, fh.CompleteAnimeCache); err == nil {
|
||||
// Create a new media tree analysis that will be used for episode normalization
|
||||
mta, _ := NewMediaTreeAnalysis(&MediaTreeAnalysisOptions{
|
||||
tree: tree,
|
||||
anizipCache: fh.AnizipCache,
|
||||
rateLimiter: rateLimiter,
|
||||
tree: tree,
|
||||
metadataProvider: fh.MetadataProvider,
|
||||
rateLimiter: rateLimiter,
|
||||
})
|
||||
// Hoist the media tree analysis, so it will be used by other files
|
||||
// We don't care if it's nil because [normalizeEpisodeNumberAndHydrate] will handle it
|
||||
@@ -335,7 +335,7 @@ func (fh *FileHydrator) hydrateGroupMetadata(
|
||||
|
||||
// When we encounter a file with an episode number higher than the media's episode count
|
||||
// we have a forced media ID, we will fetch the media from AniList and get the offset
|
||||
azm, err := anizip.FetchAniZipMediaC("anilist", fh.ForceMediaId, fh.AnizipCache)
|
||||
animeMetadata, err := fh.MetadataProvider.GetAnimeMetadata(metadata.AnilistPlatform, fh.ForceMediaId)
|
||||
if err != nil {
|
||||
/*Log */
|
||||
if fh.ScanLogger != nil {
|
||||
@@ -351,7 +351,7 @@ func (fh *FileHydrator) hydrateGroupMetadata(
|
||||
}
|
||||
|
||||
// Get the first episode to calculate the offset
|
||||
firstEp, ok := azm.Episodes["1"]
|
||||
firstEp, ok := animeMetadata.Episodes["1"]
|
||||
if !ok {
|
||||
/*Log */
|
||||
if fh.ScanLogger != nil {
|
||||
@@ -371,7 +371,7 @@ func (fh *FileHydrator) hydrateGroupMetadata(
|
||||
maxPartAbsoluteEpisodeNumber := 0
|
||||
if usePartEpisodeNumber {
|
||||
minPartAbsoluteEpisodeNumber = firstEp.EpisodeNumber
|
||||
maxPartAbsoluteEpisodeNumber = minPartAbsoluteEpisodeNumber + azm.GetMainEpisodeCount() - 1
|
||||
maxPartAbsoluteEpisodeNumber = minPartAbsoluteEpisodeNumber + animeMetadata.GetMainEpisodeCount() - 1
|
||||
}
|
||||
|
||||
absoluteEpisodeNumber := firstEp.AbsoluteEpisodeNumber
|
||||
|
||||
@@ -2,7 +2,7 @@ package scanner
|
||||
|
||||
import (
|
||||
"seanime/internal/api/anilist"
|
||||
"seanime/internal/api/anizip"
|
||||
"seanime/internal/api/metadata"
|
||||
"seanime/internal/library/anime"
|
||||
"seanime/internal/platforms/anilist_platform"
|
||||
"seanime/internal/util"
|
||||
@@ -13,10 +13,9 @@ import (
|
||||
func TestFileHydrator_HydrateMetadata(t *testing.T) {
|
||||
|
||||
completeAnimeCache := anilist.NewCompleteAnimeCache()
|
||||
anizipCache := anizip.NewCache()
|
||||
anilistRateLimiter := limiter.NewAnilistLimiter()
|
||||
logger := util.NewLogger()
|
||||
|
||||
metadataProvider := metadata.GetMockProvider(t)
|
||||
anilistClient := anilist.TestGetMockAnilistClient()
|
||||
anilistPlatform := anilist_platform.NewAnilistPlatform(anilistClient, util.NewLogger())
|
||||
animeCollection, err := anilistPlatform.GetAnimeCollectionWithRelations()
|
||||
@@ -100,9 +99,9 @@ func TestFileHydrator_HydrateMetadata(t *testing.T) {
|
||||
LocalFiles: lfs,
|
||||
AllMedia: mc.NormalizedMedia,
|
||||
CompleteAnimeCache: completeAnimeCache,
|
||||
AnizipCache: anizipCache,
|
||||
Platform: anilistPlatform,
|
||||
AnilistRateLimiter: anilistRateLimiter,
|
||||
MetadataProvider: metadataProvider,
|
||||
Logger: logger,
|
||||
ScanLogger: scanLogger,
|
||||
}
|
||||
|
||||
@@ -7,8 +7,8 @@ import (
|
||||
"github.com/samber/lo"
|
||||
lop "github.com/samber/lo/parallel"
|
||||
"seanime/internal/api/anilist"
|
||||
"seanime/internal/api/anizip"
|
||||
"seanime/internal/api/mal"
|
||||
"seanime/internal/api/metadata"
|
||||
"seanime/internal/library/anime"
|
||||
"seanime/internal/platforms/platform"
|
||||
"seanime/internal/util"
|
||||
@@ -29,9 +29,9 @@ type MediaFetcher struct {
|
||||
type MediaFetcherOptions struct {
|
||||
Enhanced bool
|
||||
Platform platform.Platform
|
||||
MetadataProvider metadata.Provider
|
||||
LocalFiles []*anime.LocalFile
|
||||
CompleteAnimeCache *anilist.CompleteAnimeCache
|
||||
AnizipCache *anizip.Cache
|
||||
Logger *zerolog.Logger
|
||||
AnilistRateLimiter *limiter.Limiter
|
||||
DisableAnimeCollection bool
|
||||
@@ -49,7 +49,7 @@ func NewMediaFetcher(opts *MediaFetcherOptions) (ret *MediaFetcher, retErr error
|
||||
if opts.Platform == nil ||
|
||||
opts.LocalFiles == nil ||
|
||||
opts.CompleteAnimeCache == nil ||
|
||||
opts.AnizipCache == nil ||
|
||||
opts.MetadataProvider == nil ||
|
||||
opts.Logger == nil ||
|
||||
opts.AnilistRateLimiter == nil {
|
||||
return nil, errors.New("missing options")
|
||||
@@ -122,7 +122,7 @@ func NewMediaFetcher(opts *MediaFetcherOptions) (ret *MediaFetcher, retErr error
|
||||
opts.Platform,
|
||||
opts.LocalFiles,
|
||||
opts.CompleteAnimeCache, // CompleteAnimeCache will be populated on success
|
||||
opts.AnizipCache,
|
||||
opts.MetadataProvider,
|
||||
opts.AnilistRateLimiter,
|
||||
mf.ScanLogger,
|
||||
)
|
||||
@@ -166,7 +166,7 @@ func NewMediaFetcher(opts *MediaFetcherOptions) (ret *MediaFetcher, retErr error
|
||||
// FetchMediaFromLocalFiles gets media and their relations from local file titles.
|
||||
// It retrieves unique titles from local files,
|
||||
// fetches mal.SearchResultAnime from MAL,
|
||||
// uses these search results to get AniList IDs using anizip.Media mappings,
|
||||
// uses these search results to get AniList IDs using metadata.AnimeMetadata mappings,
|
||||
// queries AniList to retrieve all anilist.BaseAnime using anilist.GetBaseAnimeById and their relations using anilist.FetchMediaTree.
|
||||
// It does not return an error if one of the steps fails.
|
||||
// It returns the scanned media and a boolean indicating whether the process was successful.
|
||||
@@ -174,7 +174,7 @@ func FetchMediaFromLocalFiles(
|
||||
platform platform.Platform,
|
||||
localFiles []*anime.LocalFile,
|
||||
completeAnime *anilist.CompleteAnimeCache,
|
||||
anizipCache *anizip.Cache,
|
||||
metadataProvider metadata.Provider,
|
||||
anilistRateLimiter *limiter.Limiter,
|
||||
scanLogger *ScanLogger,
|
||||
) ([]*anilist.CompleteAnime, bool) {
|
||||
@@ -231,12 +231,13 @@ func FetchMediaFromLocalFiles(
|
||||
// | AniZip |
|
||||
// +---------------------+
|
||||
|
||||
// Get AniZip mappings for each MAL ID and store them in `anizipCache`
|
||||
// Get AniZip mappings for each MAL ID and store them in `metadataProvider`
|
||||
// This step is necessary because MAL doesn't provide AniList IDs and some MAL media don't exist on AniList
|
||||
lop.ForEach(malIds, func(id int, index int) {
|
||||
rateLimiter2.Wait()
|
||||
_, _ = anizipCache.GetOrSet(anizip.GetCacheKey("mal", id), func() (*anizip.Media, error) {
|
||||
res, err := anizip.FetchAniZipMedia("mal", id)
|
||||
//_, _ = metadataProvider.GetAnimeMetadata(metadata.MalPlatform, id)
|
||||
_, _ = metadataProvider.GetCache().GetOrSet(metadata.GetAnimeMetadataCacheKey(metadata.MalPlatform, id), func() (*metadata.AnimeMetadata, error) {
|
||||
res, err := metadataProvider.GetAnimeMetadata(metadata.MalPlatform, id)
|
||||
return res, err
|
||||
})
|
||||
})
|
||||
@@ -247,9 +248,9 @@ func FetchMediaFromLocalFiles(
|
||||
|
||||
// Retrieve the AniList IDs from the AniZip mappings stored in the cache
|
||||
anilistIds := make([]int, 0)
|
||||
anizipCache.Range(func(key string, value *anizip.Media) bool {
|
||||
metadataProvider.GetCache().Range(func(key string, value *metadata.AnimeMetadata) bool {
|
||||
if value != nil {
|
||||
anilistIds = append(anilistIds, value.GetMappings().AnilistID)
|
||||
anilistIds = append(anilistIds, value.GetMappings().AnilistId)
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"github.com/samber/lo"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"seanime/internal/api/anilist"
|
||||
"seanime/internal/api/anizip"
|
||||
"seanime/internal/api/metadata"
|
||||
"seanime/internal/library/anime"
|
||||
"seanime/internal/platforms/anilist_platform"
|
||||
"seanime/internal/test_utils"
|
||||
@@ -18,7 +18,7 @@ func TestNewMediaFetcher(t *testing.T) {
|
||||
|
||||
anilistClient := anilist.TestGetMockAnilistClient()
|
||||
anilistPlatform := anilist_platform.NewAnilistPlatform(anilistClient, util.NewLogger())
|
||||
anizipCache := anizip.NewCache()
|
||||
metadataProvider := metadata.GetMockProvider(t)
|
||||
completeAnimeCache := anilist.NewCompleteAnimeCache()
|
||||
anilistRateLimiter := limiter.NewAnilistLimiter()
|
||||
|
||||
@@ -82,7 +82,7 @@ func TestNewMediaFetcher(t *testing.T) {
|
||||
Platform: anilistPlatform,
|
||||
LocalFiles: lfs,
|
||||
CompleteAnimeCache: completeAnimeCache,
|
||||
AnizipCache: anizipCache,
|
||||
MetadataProvider: metadataProvider,
|
||||
Logger: util.NewLogger(),
|
||||
AnilistRateLimiter: anilistRateLimiter,
|
||||
ScanLogger: scanLogger,
|
||||
@@ -111,7 +111,7 @@ func TestNewEnhancedMediaFetcher(t *testing.T) {
|
||||
|
||||
anilistClient := anilist.TestGetMockAnilistClient()
|
||||
anilistPlatform := anilist_platform.NewAnilistPlatform(anilistClient, util.NewLogger())
|
||||
anizipCache := anizip.NewCache()
|
||||
metaProvider := metadata.GetMockProvider(t)
|
||||
completeAnimeCache := anilist.NewCompleteAnimeCache()
|
||||
anilistRateLimiter := limiter.NewAnilistLimiter()
|
||||
|
||||
@@ -162,7 +162,7 @@ func TestNewEnhancedMediaFetcher(t *testing.T) {
|
||||
Platform: anilistPlatform,
|
||||
LocalFiles: lfs,
|
||||
CompleteAnimeCache: completeAnimeCache,
|
||||
AnizipCache: anizipCache,
|
||||
MetadataProvider: metaProvider,
|
||||
Logger: util.NewLogger(),
|
||||
AnilistRateLimiter: anilistRateLimiter,
|
||||
ScanLogger: scanLogger,
|
||||
@@ -190,7 +190,7 @@ func TestFetchMediaFromLocalFiles(t *testing.T) {
|
||||
|
||||
anilistClient := anilist.TestGetMockAnilistClient()
|
||||
anilistPlatform := anilist_platform.NewAnilistPlatform(anilistClient, util.NewLogger())
|
||||
anizipCache := anizip.NewCache()
|
||||
metaProvider := metadata.GetMockProvider(t)
|
||||
completeAnimeCache := anilist.NewCompleteAnimeCache()
|
||||
anilistRateLimiter := limiter.NewAnilistLimiter()
|
||||
|
||||
@@ -240,7 +240,7 @@ func TestFetchMediaFromLocalFiles(t *testing.T) {
|
||||
anilistPlatform,
|
||||
lfs,
|
||||
completeAnimeCache,
|
||||
anizipCache,
|
||||
metaProvider,
|
||||
anilistRateLimiter,
|
||||
scanLogger,
|
||||
)
|
||||
|
||||
@@ -6,15 +6,15 @@ import (
|
||||
"github.com/samber/lo"
|
||||
"github.com/sourcegraph/conc/pool"
|
||||
"seanime/internal/api/anilist"
|
||||
"seanime/internal/api/anizip"
|
||||
"seanime/internal/api/metadata"
|
||||
"seanime/internal/util/limiter"
|
||||
)
|
||||
|
||||
type (
|
||||
MediaTreeAnalysisOptions struct {
|
||||
tree *anilist.CompleteAnimeRelationTree
|
||||
anizipCache *anizip.Cache
|
||||
rateLimiter *limiter.Limiter
|
||||
tree *anilist.CompleteAnimeRelationTree
|
||||
metadataProvider metadata.Provider
|
||||
rateLimiter *limiter.Limiter
|
||||
}
|
||||
|
||||
MediaTreeAnalysis struct {
|
||||
@@ -22,8 +22,8 @@ type (
|
||||
}
|
||||
|
||||
MediaTreeAnalysisBranch struct {
|
||||
media *anilist.CompleteAnime
|
||||
anizipMedia *anizip.Media
|
||||
media *anilist.CompleteAnime
|
||||
animeMetadata *metadata.AnimeMetadata
|
||||
// The second absolute episode number of the first episode
|
||||
// Sometimes, the metadata provider may have a 'true' absolute episode number and a 'part' absolute episode number
|
||||
// 'part' absolute episode numbers might be used for "Part 2s" of a season
|
||||
@@ -53,12 +53,14 @@ func NewMediaTreeAnalysis(opts *MediaTreeAnalysisOptions) (*MediaTreeAnalysis, e
|
||||
for _, rel := range relations {
|
||||
p.Go(func() (*MediaTreeAnalysisBranch, error) {
|
||||
opts.rateLimiter.Wait()
|
||||
azm, err := anizip.FetchAniZipMediaC("anilist", rel.ID, opts.anizipCache)
|
||||
|
||||
animeMetadata, err := opts.metadataProvider.GetAnimeMetadata(metadata.AnilistPlatform, rel.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get the first episode
|
||||
firstEp, ok := azm.Episodes["1"]
|
||||
firstEp, ok := animeMetadata.Episodes["1"]
|
||||
if !ok {
|
||||
return nil, errors.New("no first episode")
|
||||
}
|
||||
@@ -72,22 +74,22 @@ func NewMediaTreeAnalysis(opts *MediaTreeAnalysisOptions) (*MediaTreeAnalysis, e
|
||||
maxPartAbsoluteEpisodeNumber := 0
|
||||
if usePartEpisodeNumber {
|
||||
partAbsoluteEpisodeNumber = firstEp.EpisodeNumber
|
||||
maxPartAbsoluteEpisodeNumber = partAbsoluteEpisodeNumber + azm.GetMainEpisodeCount() - 1
|
||||
maxPartAbsoluteEpisodeNumber = partAbsoluteEpisodeNumber + animeMetadata.GetMainEpisodeCount() - 1
|
||||
}
|
||||
|
||||
// If the first episode exists and has a valid absolute episode number, create a new MediaTreeAnalysisBranch
|
||||
if azm.Episodes != nil && firstEp.AbsoluteEpisodeNumber > 0 {
|
||||
if animeMetadata.Episodes != nil && firstEp.AbsoluteEpisodeNumber > 0 {
|
||||
return &MediaTreeAnalysisBranch{
|
||||
media: rel,
|
||||
anizipMedia: azm,
|
||||
animeMetadata: animeMetadata,
|
||||
minPartAbsoluteEpisodeNumber: partAbsoluteEpisodeNumber,
|
||||
maxPartAbsoluteEpisodeNumber: maxPartAbsoluteEpisodeNumber,
|
||||
minAbsoluteEpisode: firstEp.AbsoluteEpisodeNumber,
|
||||
// The max absolute episode number is the first episode's absolute episode number plus the total episode count minus 1
|
||||
// We subtract 1 because the first episode's absolute episode number is already included in the total episode count
|
||||
// e.g, if the first episode's absolute episode number is 13 and the total episode count is 12, the max absolute episode number is 24
|
||||
maxAbsoluteEpisode: firstEp.AbsoluteEpisodeNumber + (azm.GetMainEpisodeCount() - 1),
|
||||
totalEpisodeCount: azm.GetMainEpisodeCount(),
|
||||
maxAbsoluteEpisode: firstEp.AbsoluteEpisodeNumber + (animeMetadata.GetMainEpisodeCount() - 1),
|
||||
totalEpisodeCount: animeMetadata.GetMainEpisodeCount(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"seanime/internal/api/anilist"
|
||||
"seanime/internal/api/anizip"
|
||||
"seanime/internal/api/metadata"
|
||||
"seanime/internal/test_utils"
|
||||
"seanime/internal/util/limiter"
|
||||
"testing"
|
||||
@@ -20,6 +20,8 @@ func TestMediaTreeAnalysis(t *testing.T) {
|
||||
anilistRateLimiter := limiter.NewAnilistLimiter()
|
||||
tree := anilist.NewCompleteAnimeRelationTree()
|
||||
|
||||
metadataProvider := metadata.GetMockProvider(t)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
mediaId int
|
||||
@@ -71,9 +73,9 @@ func TestMediaTreeAnalysis(t *testing.T) {
|
||||
// +---------------------+
|
||||
|
||||
mta, err := NewMediaTreeAnalysis(&MediaTreeAnalysisOptions{
|
||||
tree: tree,
|
||||
anizipCache: anizip.NewCache(),
|
||||
rateLimiter: limiter.NewLimiter(time.Minute, 25),
|
||||
tree: tree,
|
||||
metadataProvider: metadataProvider,
|
||||
rateLimiter: limiter.NewLimiter(time.Minute, 25),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal("expected media tree analysis, got error:", err.Error())
|
||||
@@ -103,6 +105,8 @@ func TestMediaTreeAnalysis2(t *testing.T) {
|
||||
anilistRateLimiter := limiter.NewAnilistLimiter()
|
||||
tree := anilist.NewCompleteAnimeRelationTree()
|
||||
|
||||
metadataProvider := metadata.GetMockProvider(t)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
mediaId int
|
||||
@@ -143,9 +147,9 @@ func TestMediaTreeAnalysis2(t *testing.T) {
|
||||
// +---------------------+
|
||||
|
||||
mta, err := NewMediaTreeAnalysis(&MediaTreeAnalysisOptions{
|
||||
tree: tree,
|
||||
anizipCache: anizip.NewCache(),
|
||||
rateLimiter: limiter.NewLimiter(time.Minute, 25),
|
||||
tree: tree,
|
||||
metadataProvider: metadataProvider,
|
||||
rateLimiter: limiter.NewLimiter(time.Minute, 25),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal("expected media tree analysis, got error:", err.Error())
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/samber/lo"
|
||||
"seanime/internal/api/anilist"
|
||||
"seanime/internal/api/anizip"
|
||||
"seanime/internal/api/metadata"
|
||||
"seanime/internal/events"
|
||||
"seanime/internal/library/anime"
|
||||
"seanime/internal/library/filesystem"
|
||||
@@ -30,6 +30,7 @@ type Scanner struct {
|
||||
SkipIgnoredFiles bool
|
||||
ScanSummaryLogger *summary.ScanSummaryLogger
|
||||
ScanLogger *ScanLogger
|
||||
MetadataProvider metadata.Provider
|
||||
}
|
||||
|
||||
// Scan will scan the directory and return a list of anime.LocalFile.
|
||||
@@ -38,7 +39,6 @@ func (scn *Scanner) Scan() (lfs []*anime.LocalFile, err error) {
|
||||
defer util.HandlePanicWithError(&err)
|
||||
|
||||
completeAnimeCache := anilist.NewCompleteAnimeCache()
|
||||
anizipCache := anizip.NewCache()
|
||||
|
||||
// Create a new Anilist rate limiter
|
||||
anilistRateLimiter := limiter.NewAnilistLimiter()
|
||||
@@ -177,14 +177,15 @@ func (scn *Scanner) Scan() (lfs []*anime.LocalFile, err error) {
|
||||
|
||||
// Fetch media needed for matching
|
||||
mf, err := NewMediaFetcher(&MediaFetcherOptions{
|
||||
Enhanced: scn.Enhanced,
|
||||
Platform: scn.Platform,
|
||||
LocalFiles: localFiles,
|
||||
CompleteAnimeCache: completeAnimeCache,
|
||||
AnizipCache: anizipCache,
|
||||
Logger: scn.Logger,
|
||||
AnilistRateLimiter: anilistRateLimiter,
|
||||
ScanLogger: scn.ScanLogger,
|
||||
Enhanced: scn.Enhanced,
|
||||
Platform: scn.Platform,
|
||||
MetadataProvider: scn.MetadataProvider,
|
||||
LocalFiles: localFiles,
|
||||
CompleteAnimeCache: completeAnimeCache,
|
||||
Logger: scn.Logger,
|
||||
AnilistRateLimiter: anilistRateLimiter,
|
||||
DisableAnimeCollection: false,
|
||||
ScanLogger: scn.ScanLogger,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -245,7 +246,7 @@ func (scn *Scanner) Scan() (lfs []*anime.LocalFile, err error) {
|
||||
hydrator := &FileHydrator{
|
||||
AllMedia: mc.NormalizedMedia,
|
||||
LocalFiles: localFiles,
|
||||
AnizipCache: anizipCache,
|
||||
MetadataProvider: scn.MetadataProvider,
|
||||
Platform: scn.Platform,
|
||||
CompleteAnimeCache: completeAnimeCache,
|
||||
AnilistRateLimiter: anilistRateLimiter,
|
||||
|
||||
@@ -2,7 +2,7 @@ package scanner
|
||||
|
||||
import (
|
||||
"seanime/internal/api/anilist"
|
||||
"seanime/internal/api/anizip"
|
||||
"seanime/internal/api/metadata"
|
||||
"seanime/internal/library/anime"
|
||||
"seanime/internal/platforms/anilist_platform"
|
||||
"seanime/internal/util"
|
||||
@@ -19,9 +19,8 @@ func TestScanLogger(t *testing.T) {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
allMedia := animeCollection.GetAllAnime()
|
||||
|
||||
metadataProvider := metadata.GetMockProvider(t)
|
||||
completeAnimeCache := anilist.NewCompleteAnimeCache()
|
||||
anizipCache := anizip.NewCache()
|
||||
anilistRateLimiter := limiter.NewAnilistLimiter()
|
||||
logger := util.NewLogger()
|
||||
|
||||
@@ -81,9 +80,10 @@ func TestScanLogger(t *testing.T) {
|
||||
matcher := &Matcher{
|
||||
LocalFiles: lfs,
|
||||
MediaContainer: mc,
|
||||
CompleteAnimeCache: nil,
|
||||
CompleteAnimeCache: completeAnimeCache,
|
||||
Logger: util.NewLogger(),
|
||||
ScanLogger: scanLogger,
|
||||
ScanSummaryLogger: nil,
|
||||
}
|
||||
|
||||
err = matcher.MatchLocalFilesWithMedia()
|
||||
@@ -99,11 +99,13 @@ func TestScanLogger(t *testing.T) {
|
||||
LocalFiles: lfs,
|
||||
AllMedia: mc.NormalizedMedia,
|
||||
CompleteAnimeCache: completeAnimeCache,
|
||||
AnizipCache: anizipCache,
|
||||
Platform: anilistPlatform,
|
||||
MetadataProvider: metadataProvider,
|
||||
AnilistRateLimiter: anilistRateLimiter,
|
||||
Logger: logger,
|
||||
ScanLogger: scanLogger,
|
||||
ScanSummaryLogger: nil,
|
||||
ForceMediaId: 0,
|
||||
}
|
||||
|
||||
fh.HydrateMetadata()
|
||||
|
||||
@@ -45,7 +45,6 @@ type (
|
||||
VLC *vlc2.VLC
|
||||
MpcHc *mpchc2.MpcHc
|
||||
Mpv *mpv.Mpv
|
||||
MpvType string
|
||||
WSEventManager events.WSEventManagerInterface
|
||||
ContinuityManager *continuity.Manager
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"github.com/goccy/go-json"
|
||||
"github.com/samber/lo"
|
||||
"seanime/internal/api/anilist"
|
||||
"seanime/internal/api/anizip"
|
||||
"seanime/internal/database/db_bridge"
|
||||
"seanime/internal/library/anime"
|
||||
"seanime/internal/manga"
|
||||
@@ -75,8 +74,6 @@ func (h *Hub) CreateSnapshot(opts *NewSnapshotOptions) error {
|
||||
lfWrapper := anime.NewLocalFileWrapper(lfs)
|
||||
lfEntries := lfWrapper.GetLocalEntries()
|
||||
|
||||
anizipCache := anizip.NewCache()
|
||||
|
||||
rateLimiter := limiter.NewLimiter(1*time.Second, 5)
|
||||
for _, lfEntry := range lfEntries {
|
||||
if !slices.Contains(opts.AnimeToDownload, lfEntry.GetMediaId()) {
|
||||
@@ -100,7 +97,6 @@ func (h *Hub) CreateSnapshot(opts *NewSnapshotOptions) error {
|
||||
_mediaEntry, err := anime.NewAnimeEntry(&anime.NewAnimeEntryOptions{
|
||||
MediaId: lfEntry.GetMediaId(),
|
||||
LocalFiles: lfs,
|
||||
AnizipCache: anizipCache,
|
||||
AnimeCollection: animeCollection,
|
||||
Platform: h.platform,
|
||||
MetadataProvider: h.metadataProvider,
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/samber/lo"
|
||||
"seanime/internal/api/anilist"
|
||||
"seanime/internal/api/anizip"
|
||||
"seanime/internal/api/metadata"
|
||||
"seanime/internal/extension"
|
||||
"seanime/internal/platforms/platform"
|
||||
"seanime/internal/util/filecache"
|
||||
@@ -19,7 +19,7 @@ type (
|
||||
logger *zerolog.Logger
|
||||
providerExtensionBank *extension.UnifiedBank
|
||||
fileCacher *filecache.Cacher
|
||||
anizipCache *anizip.Cache
|
||||
metadataProvider metadata.Provider
|
||||
platform platform.Platform
|
||||
anilistBaseAnimeCache *anilist.BaseAnimeCache
|
||||
}
|
||||
@@ -63,17 +63,17 @@ type (
|
||||
|
||||
type (
|
||||
NewRepositoryOptions struct {
|
||||
Logger *zerolog.Logger
|
||||
FileCacher *filecache.Cacher
|
||||
AnizipCache *anizip.Cache
|
||||
Platform platform.Platform
|
||||
Logger *zerolog.Logger
|
||||
FileCacher *filecache.Cacher
|
||||
MetadataProvider metadata.Provider
|
||||
Platform platform.Platform
|
||||
}
|
||||
)
|
||||
|
||||
func NewRepository(opts *NewRepositoryOptions) *Repository {
|
||||
return &Repository{
|
||||
logger: opts.Logger,
|
||||
anizipCache: opts.AnizipCache,
|
||||
metadataProvider: opts.MetadataProvider,
|
||||
fileCacher: opts.FileCacher,
|
||||
providerExtensionBank: extension.NewUnifiedBank(),
|
||||
anilistBaseAnimeCache: anilist.NewBaseAnimeCache(),
|
||||
@@ -145,8 +145,8 @@ func (r *Repository) GetMediaEpisodes(provider string, media *anilist.BaseAnime,
|
||||
// | Anizip |
|
||||
// +---------------------+
|
||||
|
||||
anizipMedia, err := anizip.FetchAniZipMediaC("anilist", mId, r.anizipCache)
|
||||
foundAnizipMedia := err == nil && anizipMedia != nil
|
||||
animeMetadata, err := r.metadataProvider.GetAnimeMetadata(metadata.AnilistPlatform, mId)
|
||||
foundAnimeMetadata := err == nil && animeMetadata != nil
|
||||
|
||||
// +---------------------+
|
||||
// | Episode list |
|
||||
@@ -160,18 +160,18 @@ func (r *Repository) GetMediaEpisodes(provider string, media *anilist.BaseAnime,
|
||||
}
|
||||
|
||||
for _, episodeDetails := range ec.ProviderEpisodeList {
|
||||
if foundAnizipMedia {
|
||||
anizipEpisode, found := anizipMedia.Episodes[strconv.Itoa(episodeDetails.Number)]
|
||||
if foundAnimeMetadata {
|
||||
episodeMetadata, found := animeMetadata.Episodes[strconv.Itoa(episodeDetails.Number)]
|
||||
if found {
|
||||
img := anizipEpisode.Image
|
||||
img := episodeMetadata.Image
|
||||
if img == "" {
|
||||
img = media.GetCoverImageSafe()
|
||||
}
|
||||
episodes = append(episodes, &Episode{
|
||||
Number: episodeDetails.Number,
|
||||
Title: anizipEpisode.GetTitle(),
|
||||
Title: episodeMetadata.GetTitle(),
|
||||
Image: img,
|
||||
Description: anizipEpisode.Summary,
|
||||
Description: episodeMetadata.Summary,
|
||||
})
|
||||
} else {
|
||||
episodes = append(episodes, &Episode{
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"errors"
|
||||
"github.com/hekmon/transmissionrpc/v3"
|
||||
"github.com/rs/zerolog"
|
||||
"seanime/internal/api/metadata"
|
||||
"seanime/internal/events"
|
||||
"seanime/internal/torrent_clients/qbittorrent"
|
||||
"seanime/internal/torrent_clients/qbittorrent/model"
|
||||
@@ -21,12 +22,12 @@ const (
|
||||
|
||||
type (
|
||||
Repository struct {
|
||||
logger *zerolog.Logger
|
||||
qBittorrentClient *qbittorrent.Client
|
||||
transmission *transmission.Transmission
|
||||
torrentRepository *torrent.Repository
|
||||
provider string
|
||||
|
||||
logger *zerolog.Logger
|
||||
qBittorrentClient *qbittorrent.Client
|
||||
transmission *transmission.Transmission
|
||||
torrentRepository *torrent.Repository
|
||||
provider string
|
||||
metadataProvider metadata.Provider
|
||||
activeTorrentCountCtxCancel context.CancelFunc
|
||||
activeTorrentCount *ActiveCount
|
||||
}
|
||||
@@ -37,6 +38,7 @@ type (
|
||||
Transmission *transmission.Transmission
|
||||
TorrentRepository *torrent.Repository
|
||||
Provider string
|
||||
MetadataProvider metadata.Provider
|
||||
}
|
||||
|
||||
ActiveCount struct {
|
||||
@@ -56,6 +58,7 @@ func NewRepository(opts *NewRepositoryOptions) *Repository {
|
||||
transmission: opts.Transmission,
|
||||
torrentRepository: opts.TorrentRepository,
|
||||
provider: opts.Provider,
|
||||
metadataProvider: opts.MetadataProvider,
|
||||
activeTorrentCount: &ActiveCount{},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,10 +77,11 @@ func (r *Repository) SmartSelect(p *SmartSelectParams) error {
|
||||
|
||||
// AnalyzeTorrentFiles the torrent files
|
||||
analyzer := torrent_analyzer.NewAnalyzer(&torrent_analyzer.NewAnalyzerOptions{
|
||||
Logger: r.logger,
|
||||
Filepaths: filepaths,
|
||||
Media: p.Media,
|
||||
Platform: p.Platform,
|
||||
Logger: r.logger,
|
||||
Filepaths: filepaths,
|
||||
Media: p.Media,
|
||||
Platform: p.Platform,
|
||||
MetadataProvider: r.metadataProvider,
|
||||
})
|
||||
|
||||
r.logger.Debug().Msg("torrent client: analyzing torrent files (smart select)")
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
lop "github.com/samber/lo/parallel"
|
||||
"path/filepath"
|
||||
"seanime/internal/api/anilist"
|
||||
"seanime/internal/api/anizip"
|
||||
"seanime/internal/api/metadata"
|
||||
"seanime/internal/library/anime"
|
||||
"seanime/internal/library/scanner"
|
||||
"seanime/internal/platforms/platform"
|
||||
@@ -18,10 +18,11 @@ type (
|
||||
// Analyzer is a service similar to the scanner, but it is used to analyze torrent files.
|
||||
// i.e. torrent files instead of local files.
|
||||
Analyzer struct {
|
||||
files []*File
|
||||
media *anilist.CompleteAnime
|
||||
platform platform.Platform
|
||||
logger *zerolog.Logger
|
||||
files []*File
|
||||
media *anilist.CompleteAnime
|
||||
platform platform.Platform
|
||||
logger *zerolog.Logger
|
||||
metadataProvider metadata.Provider
|
||||
}
|
||||
|
||||
// Analysis contains the results of the analysis.
|
||||
@@ -41,10 +42,11 @@ type (
|
||||
|
||||
type (
|
||||
NewAnalyzerOptions struct {
|
||||
Logger *zerolog.Logger
|
||||
Filepaths []string // Filepath of the torrent files
|
||||
Media *anilist.CompleteAnime // The media to compare the files with
|
||||
Platform platform.Platform
|
||||
Logger *zerolog.Logger
|
||||
Filepaths []string // Filepath of the torrent files
|
||||
Media *anilist.CompleteAnime // The media to compare the files with
|
||||
Platform platform.Platform
|
||||
MetadataProvider metadata.Provider
|
||||
}
|
||||
)
|
||||
|
||||
@@ -53,10 +55,11 @@ func NewAnalyzer(opts *NewAnalyzerOptions) *Analyzer {
|
||||
return newFile(idx, filepath)
|
||||
})
|
||||
return &Analyzer{
|
||||
files: files,
|
||||
media: opts.Media,
|
||||
platform: opts.Platform,
|
||||
logger: opts.Logger,
|
||||
files: files,
|
||||
media: opts.Media,
|
||||
platform: opts.Platform,
|
||||
logger: opts.Logger,
|
||||
metadataProvider: opts.MetadataProvider,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,7 +191,6 @@ func (f *File) GetIndex() int {
|
||||
func (a *Analyzer) scanFiles() error {
|
||||
|
||||
completeAnimeCache := anilist.NewCompleteAnimeCache()
|
||||
anizipCache := anizip.NewCache()
|
||||
anilistRateLimiter := limiter.NewAnilistLimiter()
|
||||
|
||||
lfs := a.getLocalFiles() // Extract local files from the Files
|
||||
@@ -232,10 +234,13 @@ func (a *Analyzer) scanFiles() error {
|
||||
LocalFiles: lfs,
|
||||
AllMedia: mc.NormalizedMedia,
|
||||
CompleteAnimeCache: completeAnimeCache,
|
||||
AnizipCache: anizipCache,
|
||||
Platform: a.platform,
|
||||
MetadataProvider: a.metadataProvider,
|
||||
AnilistRateLimiter: anilistRateLimiter,
|
||||
Logger: a.logger,
|
||||
ScanLogger: nil,
|
||||
ScanSummaryLogger: nil,
|
||||
ForceMediaId: 0,
|
||||
}
|
||||
|
||||
fh.HydrateMetadata()
|
||||
|
||||
@@ -3,6 +3,7 @@ package torrent_analyzer
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"seanime/internal/api/anilist"
|
||||
"seanime/internal/api/metadata"
|
||||
"seanime/internal/platforms/anilist_platform"
|
||||
"seanime/internal/test_utils"
|
||||
"seanime/internal/util"
|
||||
@@ -16,6 +17,7 @@ func TestSelectFilesFromSeason(t *testing.T) {
|
||||
logger := util.NewLogger()
|
||||
anilistClient := anilist.TestGetMockAnilistClient()
|
||||
anilistPlatform := anilist_platform.NewAnilistPlatform(anilistClient, logger)
|
||||
metadataProvider := metadata.GetMockProvider(t)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -80,10 +82,11 @@ func TestSelectFilesFromSeason(t *testing.T) {
|
||||
}
|
||||
|
||||
analyzer := NewAnalyzer(&NewAnalyzerOptions{
|
||||
Logger: logger,
|
||||
Filepaths: tt.filepaths,
|
||||
Media: media,
|
||||
Platform: anilistPlatform,
|
||||
Logger: logger,
|
||||
Filepaths: tt.filepaths,
|
||||
Media: media,
|
||||
Platform: anilistPlatform,
|
||||
MetadataProvider: metadataProvider,
|
||||
})
|
||||
|
||||
// AnalyzeTorrentFiles
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"seanime/internal/api/anilist"
|
||||
"seanime/internal/api/anizip"
|
||||
"seanime/internal/api/metadata"
|
||||
"seanime/internal/platforms/anilist_platform"
|
||||
"seanime/internal/test_utils"
|
||||
"seanime/internal/util"
|
||||
@@ -21,6 +21,8 @@ func TestSmartSearch(t *testing.T) {
|
||||
|
||||
toshoPlatform := NewProvider(util.NewLogger())
|
||||
|
||||
metadataProvider := metadata.GetMockProvider(t)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
mId int
|
||||
@@ -84,7 +86,7 @@ func TestSmartSearch(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
||||
media, err := anilistPlatform.GetAnime(tt.mId)
|
||||
anizipMedia, err := anizip.FetchAniZipMedia("anilist", media.GetID())
|
||||
animeMetadata, err := metadataProvider.GetAnimeMetadata(metadata.AnilistPlatform, tt.mId)
|
||||
require.NoError(t, err)
|
||||
|
||||
queryMedia := hibiketorrent.Media{
|
||||
@@ -107,7 +109,7 @@ func TestSmartSearch(t *testing.T) {
|
||||
|
||||
if assert.NoError(t, err) {
|
||||
|
||||
anizipEpisode, ok := anizipMedia.FindEpisode(strconv.Itoa(tt.episodeNumber))
|
||||
episodeMetadata, ok := animeMetadata.FindEpisode(strconv.Itoa(tt.episodeNumber))
|
||||
require.True(t, ok)
|
||||
|
||||
torrents, err := toshoPlatform.SmartSearch(hibiketorrent.AnimeSmartSearchOptions{
|
||||
@@ -116,8 +118,8 @@ func TestSmartSearch(t *testing.T) {
|
||||
Batch: tt.batch,
|
||||
EpisodeNumber: tt.episodeNumber,
|
||||
Resolution: tt.resolution,
|
||||
AnidbAID: anizipMedia.Mappings.AnidbID,
|
||||
AnidbEID: anizipEpisode.AnidbEid,
|
||||
AnidbAID: animeMetadata.Mappings.AnidbId,
|
||||
AnidbEID: episodeMetadata.AnidbEid,
|
||||
BestReleases: false,
|
||||
})
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ package torrent
|
||||
|
||||
import (
|
||||
"github.com/rs/zerolog"
|
||||
"seanime/internal/api/anizip"
|
||||
"seanime/internal/api/metadata"
|
||||
"seanime/internal/extension"
|
||||
"seanime/internal/util/result"
|
||||
@@ -15,7 +14,6 @@ type (
|
||||
extensionBank *extension.UnifiedBank
|
||||
animeProviderSearchCaches *result.Map[string, *result.Cache[string, *SearchData]]
|
||||
animeProviderSmartSearchCaches *result.Map[string, *result.Cache[string, *SearchData]]
|
||||
anizipCache *anizip.Cache
|
||||
settings RepositorySettings
|
||||
metadataProvider metadata.Provider
|
||||
mu sync.Mutex
|
||||
@@ -38,7 +36,6 @@ func NewRepository(opts *NewRepositoryOptions) *Repository {
|
||||
extensionBank: extension.NewUnifiedBank(),
|
||||
animeProviderSearchCaches: result.NewResultMap[string, *result.Cache[string, *SearchData]](),
|
||||
animeProviderSmartSearchCaches: result.NewResultMap[string, *result.Cache[string, *SearchData]](),
|
||||
anizipCache: anizip.NewCache(),
|
||||
settings: RepositorySettings{},
|
||||
mu: sync.Mutex{},
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
|
||||
func getTestRepo(t *testing.T) *Repository {
|
||||
logger := util.NewLogger()
|
||||
metadataProvider := metadata.TestGetMockProvider(t)
|
||||
metadataProvider := metadata.GetMockProvider(t)
|
||||
|
||||
extensionBank := extension.NewUnifiedBank()
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"github.com/samber/lo"
|
||||
"github.com/samber/mo"
|
||||
"seanime/internal/api/anilist"
|
||||
"seanime/internal/api/anizip"
|
||||
"seanime/internal/api/metadata"
|
||||
"seanime/internal/extension"
|
||||
"seanime/internal/library/anime"
|
||||
"seanime/internal/util"
|
||||
@@ -76,10 +76,10 @@ func (r *Repository) SearchAnime(opts AnimeSearchOptions) (ret *SearchData, err
|
||||
var torrents []*hibiketorrent.AnimeTorrent
|
||||
|
||||
// Fetch Anizip media
|
||||
anizipMedia := mo.None[*anizip.Media]()
|
||||
anizipMediaF, err := anizip.FetchAniZipMediaC("anilist", opts.Media.ID, r.anizipCache)
|
||||
animeMetadata := mo.None[*metadata.AnimeMetadata]()
|
||||
animeMetadataF, err := r.metadataProvider.GetAnimeMetadata(metadata.AnilistPlatform, opts.Media.GetID())
|
||||
if err == nil {
|
||||
anizipMedia = mo.Some(anizipMediaF)
|
||||
animeMetadata = mo.Some(animeMetadataF)
|
||||
}
|
||||
|
||||
queryMedia := hibiketorrent.Media{
|
||||
@@ -101,7 +101,7 @@ func (r *Repository) SearchAnime(opts AnimeSearchOptions) (ret *SearchData, err
|
||||
}
|
||||
|
||||
//// Force simple search if AniZip media is absent
|
||||
//if opts.Type == AnimeSearchTypeSmart && anizipMedia.IsAbsent() {
|
||||
//if opts.Type == AnimeSearchTypeSmart && animeMetadata.IsAbsent() {
|
||||
// opts.Type = AnimeSearchTypeSimple
|
||||
//}
|
||||
|
||||
@@ -113,17 +113,17 @@ func (r *Repository) SearchAnime(opts AnimeSearchOptions) (ret *SearchData, err
|
||||
anidbEID := 0
|
||||
|
||||
// Get the AniDB Anime ID and Episode ID
|
||||
if anizipMedia.IsPresent() {
|
||||
if animeMetadata.IsPresent() {
|
||||
// Override absolute offset value of queryMedia
|
||||
queryMedia.AbsoluteSeasonOffset = anizipMedia.MustGet().GetOffset()
|
||||
queryMedia.AbsoluteSeasonOffset = animeMetadata.MustGet().GetOffset()
|
||||
|
||||
if anizipMedia.MustGet().GetMappings() != nil {
|
||||
if animeMetadata.MustGet().GetMappings() != nil {
|
||||
|
||||
anidbAID = anizipMedia.MustGet().GetMappings().AnidbID
|
||||
anidbAID = animeMetadata.MustGet().GetMappings().AnidbId
|
||||
// Find Anizip Episode based on inputted episode number
|
||||
anizipEpisode, found := anizipMedia.MustGet().FindEpisode(strconv.Itoa(opts.EpisodeNumber))
|
||||
episodeMetadata, found := animeMetadata.MustGet().FindEpisode(strconv.Itoa(opts.EpisodeNumber))
|
||||
if found {
|
||||
anidbEID = anizipEpisode.AnidbEid
|
||||
anidbEID = episodeMetadata.AnidbEid
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -182,10 +182,10 @@ func (r *Repository) SearchAnime(opts AnimeSearchOptions) (ret *SearchData, err
|
||||
defer wg.Done()
|
||||
|
||||
preview := r.createAnimeTorrentPreview(createAnimeTorrentPreviewOptions{
|
||||
torrent: t,
|
||||
media: opts.Media,
|
||||
anizipMedia: anizipMedia,
|
||||
searchOpts: &opts,
|
||||
torrent: t,
|
||||
media: opts.Media,
|
||||
animeMetadata: animeMetadata,
|
||||
searchOpts: &opts,
|
||||
})
|
||||
if preview != nil {
|
||||
previews = append(previews, preview)
|
||||
@@ -228,10 +228,10 @@ func (r *Repository) SearchAnime(opts AnimeSearchOptions) (ret *SearchData, err
|
||||
}
|
||||
|
||||
type createAnimeTorrentPreviewOptions struct {
|
||||
torrent *hibiketorrent.AnimeTorrent
|
||||
media *anilist.BaseAnime
|
||||
anizipMedia mo.Option[*anizip.Media]
|
||||
searchOpts *AnimeSearchOptions
|
||||
torrent *hibiketorrent.AnimeTorrent
|
||||
media *anilist.BaseAnime
|
||||
animeMetadata mo.Option[*metadata.AnimeMetadata]
|
||||
searchOpts *AnimeSearchOptions
|
||||
}
|
||||
|
||||
func (r *Repository) createAnimeTorrentPreview(opts createAnimeTorrentPreviewOptions) *Preview {
|
||||
@@ -280,26 +280,26 @@ func (r *Repository) createAnimeTorrentPreview(opts createAnimeTorrentPreviewOpt
|
||||
opts.torrent.EpisodeNumber = 1
|
||||
}
|
||||
|
||||
if opts.anizipMedia.IsPresent() {
|
||||
if opts.animeMetadata.IsPresent() {
|
||||
|
||||
// normalize episode number
|
||||
if opts.torrent.EpisodeNumber >= 0 && opts.torrent.EpisodeNumber > opts.media.GetCurrentEpisodeCount() {
|
||||
opts.torrent.EpisodeNumber = opts.torrent.EpisodeNumber - opts.anizipMedia.MustGet().GetOffset()
|
||||
opts.torrent.EpisodeNumber = opts.torrent.EpisodeNumber - opts.animeMetadata.MustGet().GetOffset()
|
||||
}
|
||||
|
||||
anizipMedia := opts.anizipMedia.MustGet()
|
||||
_, foundEp := anizipMedia.FindEpisode(strconv.Itoa(opts.searchOpts.EpisodeNumber))
|
||||
animeMetadata := opts.animeMetadata.MustGet()
|
||||
_, foundEp := animeMetadata.FindEpisode(strconv.Itoa(opts.searchOpts.EpisodeNumber))
|
||||
|
||||
if foundEp {
|
||||
var episode *anime.Episode
|
||||
|
||||
// Remove the episode if the parsed episode number is not the same as the search option
|
||||
if isProbablySameEpisode(parsedData.EpisodeNumber, opts.searchOpts.EpisodeNumber, opts.anizipMedia.MustGet().GetOffset()) {
|
||||
if isProbablySameEpisode(parsedData.EpisodeNumber, opts.searchOpts.EpisodeNumber, opts.animeMetadata.MustGet().GetOffset()) {
|
||||
ep := opts.searchOpts.EpisodeNumber
|
||||
episode = anime.NewEpisode(&anime.NewEpisodeOptions{
|
||||
LocalFile: nil,
|
||||
OptionalAniDBEpisode: strconv.Itoa(ep),
|
||||
AnizipMedia: anizipMedia,
|
||||
AnimeMetadata: animeMetadata,
|
||||
Media: opts.media,
|
||||
ProgressOffset: 0,
|
||||
IsDownloaded: false,
|
||||
@@ -321,7 +321,7 @@ func (r *Repository) createAnimeTorrentPreview(opts createAnimeTorrentPreviewOpt
|
||||
var episode *anime.Episode
|
||||
|
||||
// Remove the episode if the parsed episode number is not the same as the search option
|
||||
if isProbablySameEpisode(parsedData.EpisodeNumber, opts.searchOpts.EpisodeNumber, opts.anizipMedia.MustGet().GetOffset()) {
|
||||
if isProbablySameEpisode(parsedData.EpisodeNumber, opts.searchOpts.EpisodeNumber, opts.animeMetadata.MustGet().GetOffset()) {
|
||||
displayTitle := ""
|
||||
if len(parsedData.EpisodeNumber) == 1 && parsedData.EpisodeNumber[0] != strconv.Itoa(opts.searchOpts.EpisodeNumber) {
|
||||
displayTitle = fmt.Sprintf("Episode %s", parsedData.EpisodeNumber[0])
|
||||
@@ -337,7 +337,7 @@ func (r *Repository) createAnimeTorrentPreview(opts createAnimeTorrentPreviewOpt
|
||||
AbsoluteEpisodeNumber: 0,
|
||||
LocalFile: nil,
|
||||
IsDownloaded: false,
|
||||
EpisodeMetadata: anime.NewEpisodeMetadata(opts.anizipMedia.MustGet(), nil, opts.media, r.metadataProvider),
|
||||
EpisodeMetadata: anime.NewEpisodeMetadata(opts.animeMetadata.MustGet(), nil, opts.media, r.metadataProvider),
|
||||
FileMetadata: nil,
|
||||
IsInvalid: false,
|
||||
MetadataIssue: "",
|
||||
|
||||
@@ -3,7 +3,7 @@ package torrentstream
|
||||
import (
|
||||
"fmt"
|
||||
"seanime/internal/api/anilist"
|
||||
"seanime/internal/api/anizip"
|
||||
"seanime/internal/api/metadata"
|
||||
"seanime/internal/library/anime"
|
||||
"strconv"
|
||||
"sync"
|
||||
@@ -21,7 +21,7 @@ type (
|
||||
HydrateStreamCollectionOptions struct {
|
||||
AnimeCollection *anilist.AnimeCollection
|
||||
LibraryCollection *anime.LibraryCollection
|
||||
AnizipCache *anizip.Cache
|
||||
MetadataProvider metadata.Provider
|
||||
}
|
||||
)
|
||||
|
||||
@@ -93,13 +93,13 @@ func (r *Repository) HydrateStreamCollection(opts *HydrateStreamCollectionOption
|
||||
}
|
||||
|
||||
// Get the media info
|
||||
anizipMedia, err := anizip.FetchAniZipMediaC("anilist", mediaId, r.anizipCache)
|
||||
animeMetadata, err := opts.MetadataProvider.GetAnimeMetadata(metadata.AnilistPlatform, mediaId)
|
||||
if err != nil {
|
||||
r.logger.Error().Err(err).Msg("torrentstream: could not fetch AniDB media")
|
||||
return
|
||||
}
|
||||
|
||||
_, found := anizipMedia.FindEpisode(strconv.Itoa(nextEpisodeToWatch))
|
||||
_, found := animeMetadata.FindEpisode(strconv.Itoa(nextEpisodeToWatch))
|
||||
//if !found {
|
||||
// r.logger.Error().Msg("torrentstream: could not find episode in AniDB")
|
||||
// return
|
||||
@@ -107,7 +107,7 @@ func (r *Repository) HydrateStreamCollection(opts *HydrateStreamCollectionOption
|
||||
|
||||
progressOffset := 0
|
||||
anidbEpisode := strconv.Itoa(nextEpisodeToWatch)
|
||||
if anime.HasDiscrepancy(entry.GetMedia(), anizipMedia) {
|
||||
if anime.HasDiscrepancy(entry.GetMedia(), animeMetadata) {
|
||||
progressOffset = 1
|
||||
if nextEpisodeToWatch == 1 {
|
||||
anidbEpisode = "S1"
|
||||
@@ -118,7 +118,7 @@ func (r *Repository) HydrateStreamCollection(opts *HydrateStreamCollectionOption
|
||||
episode := anime.NewEpisode(&anime.NewEpisodeOptions{
|
||||
LocalFile: nil,
|
||||
OptionalAniDBEpisode: anidbEpisode,
|
||||
AnizipMedia: anizipMedia,
|
||||
AnimeMetadata: animeMetadata,
|
||||
Media: entry.GetMedia(),
|
||||
ProgressOffset: progressOffset,
|
||||
IsDownloaded: false,
|
||||
|
||||
@@ -5,28 +5,24 @@ import (
|
||||
"github.com/samber/lo"
|
||||
"github.com/stretchr/testify/require"
|
||||
"seanime/internal/api/anilist"
|
||||
"seanime/internal/api/anizip"
|
||||
"seanime/internal/api/metadata"
|
||||
"seanime/internal/events"
|
||||
"seanime/internal/library/anime"
|
||||
"seanime/internal/platforms/anilist_platform"
|
||||
"seanime/internal/test_utils"
|
||||
"seanime/internal/util"
|
||||
"seanime/internal/util/filecache"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestStreamCollection(t *testing.T) {
|
||||
t.Skip("Incomplete")
|
||||
test_utils.SetTwoLevelDeep()
|
||||
test_utils.InitTestProvider(t, test_utils.Anilist())
|
||||
|
||||
logger := util.NewLogger()
|
||||
filecacher, err := filecache.NewCacher(t.TempDir())
|
||||
require.NoError(t, err)
|
||||
metadataProvider := metadata.TestGetMockProvider(t)
|
||||
metadataProvider := metadata.GetMockProvider(t)
|
||||
anilistClient := anilist.TestGetMockAnilistClient()
|
||||
anilistPlatform := anilist_platform.NewAnilistPlatform(anilistClient, util.NewLogger())
|
||||
anizipCache := anizip.NewCache()
|
||||
anilistPlatform.SetUsername(test_utils.ConfigData.Provider.AnilistUsername)
|
||||
animeCollection, err := anilistPlatform.GetAnimeCollection(false)
|
||||
require.NoError(t, err)
|
||||
@@ -34,16 +30,14 @@ func TestStreamCollection(t *testing.T) {
|
||||
|
||||
repo := NewRepository(&NewRepositoryOptions{
|
||||
Logger: logger,
|
||||
AnizipCache: anizip.NewCache(),
|
||||
BaseAnimeCache: anilist.NewBaseAnimeCache(),
|
||||
CompleteAnimeCache: anilist.NewCompleteAnimeCache(),
|
||||
Platform: anilistPlatform,
|
||||
MetadataProvider: metadata.NewProvider(&metadata.NewProviderImplOptions{
|
||||
Logger: logger,
|
||||
FileCacher: filecacher,
|
||||
}),
|
||||
PlaybackManager: nil,
|
||||
WSEventManager: events.NewMockWSEventManager(logger),
|
||||
MetadataProvider: metadataProvider,
|
||||
WSEventManager: events.NewMockWSEventManager(logger),
|
||||
TorrentRepository: nil,
|
||||
PlaybackManager: nil,
|
||||
Database: nil,
|
||||
})
|
||||
|
||||
// Mock Anilist collection and local files
|
||||
@@ -72,7 +66,6 @@ func TestStreamCollection(t *testing.T) {
|
||||
libraryCollection, err := anime.NewLibraryCollection(&anime.NewLibraryCollectionOptions{
|
||||
AnimeCollection: animeCollection,
|
||||
LocalFiles: lfs,
|
||||
AnizipCache: anizipCache,
|
||||
Platform: anilistPlatform,
|
||||
MetadataProvider: metadataProvider,
|
||||
})
|
||||
@@ -82,7 +75,6 @@ func TestStreamCollection(t *testing.T) {
|
||||
repo.HydrateStreamCollection(&HydrateStreamCollectionOptions{
|
||||
AnimeCollection: animeCollection,
|
||||
LibraryCollection: libraryCollection,
|
||||
AnizipCache: anizipCache,
|
||||
})
|
||||
spew.Dump(libraryCollection)
|
||||
|
||||
|
||||
@@ -172,10 +172,11 @@ searchLoop:
|
||||
|
||||
// Create a new Torrent Analyzer
|
||||
analyzer := torrentanalyzer.NewAnalyzer(&torrentanalyzer.NewAnalyzerOptions{
|
||||
Logger: r.logger,
|
||||
Filepaths: filepaths,
|
||||
Media: media,
|
||||
Platform: r.platform,
|
||||
Logger: r.logger,
|
||||
Filepaths: filepaths,
|
||||
Media: media,
|
||||
Platform: r.platform,
|
||||
MetadataProvider: r.metadataProvider,
|
||||
})
|
||||
|
||||
r.logger.Debug().Msgf("torrentstream: Analyzing torrent %s", searchT.Link)
|
||||
@@ -296,10 +297,11 @@ func (r *Repository) findBestTorrentFromManualSelection(t *hibiketorrent.AnimeTo
|
||||
|
||||
// Create a new Torrent Analyzer
|
||||
analyzer := torrentanalyzer.NewAnalyzer(&torrentanalyzer.NewAnalyzerOptions{
|
||||
Logger: r.logger,
|
||||
Filepaths: filepaths,
|
||||
Media: media,
|
||||
Platform: r.platform,
|
||||
Logger: r.logger,
|
||||
Filepaths: filepaths,
|
||||
Media: media,
|
||||
Platform: r.platform,
|
||||
MetadataProvider: r.metadataProvider,
|
||||
})
|
||||
|
||||
// Analyze torrent files
|
||||
|
||||
@@ -24,7 +24,7 @@ func (r *Repository) NewEpisodeCollection(mId int) (ec *EpisodeCollection, err e
|
||||
}
|
||||
|
||||
// Get the media info, this is cached
|
||||
completeAnime, anizipMedia, err := r.getMediaInfo(mId)
|
||||
completeAnime, animeMetadata, err := r.getMediaInfo(mId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -40,7 +40,7 @@ func (r *Repository) NewEpisodeCollection(mId int) (ec *EpisodeCollection, err e
|
||||
|
||||
info, err := anime.NewAnimeEntryDownloadInfo(&anime.NewAnimeEntryDownloadInfoOptions{
|
||||
LocalFiles: nil,
|
||||
AnizipMedia: anizipMedia,
|
||||
AnimeMetadata: animeMetadata,
|
||||
Progress: lo.ToPtr(0), // Progress is 0 because we want the entire list
|
||||
Status: lo.ToPtr(anilist.MediaListStatusCurrent),
|
||||
Media: completeAnime.ToBaseAnime(),
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"seanime/internal/api/anilist"
|
||||
"seanime/internal/api/anizip"
|
||||
"seanime/internal/api/metadata"
|
||||
"seanime/internal/database/db"
|
||||
"seanime/internal/database/models"
|
||||
@@ -33,7 +32,6 @@ type (
|
||||
|
||||
// Injected dependencies
|
||||
torrentRepository *torrent.Repository
|
||||
anizipCache *anizip.Cache
|
||||
baseAnimeCache *anilist.BaseAnimeCache
|
||||
completeAnimeCache *anilist.CompleteAnimeCache
|
||||
platform platform.Platform
|
||||
@@ -52,7 +50,6 @@ type (
|
||||
|
||||
NewRepositoryOptions struct {
|
||||
Logger *zerolog.Logger
|
||||
AnizipCache *anizip.Cache
|
||||
TorrentRepository *torrent.Repository
|
||||
BaseAnimeCache *anilist.BaseAnimeCache
|
||||
CompleteAnimeCache *anilist.CompleteAnimeCache
|
||||
@@ -67,17 +64,22 @@ type (
|
||||
// NewRepository creates a new injectable Repository instance
|
||||
func NewRepository(opts *NewRepositoryOptions) *Repository {
|
||||
ret := &Repository{
|
||||
logger: opts.Logger,
|
||||
anizipCache: opts.AnizipCache,
|
||||
baseAnimeCache: opts.BaseAnimeCache,
|
||||
completeAnimeCache: opts.CompleteAnimeCache,
|
||||
platform: opts.Platform,
|
||||
metadataProvider: opts.MetadataProvider,
|
||||
playbackManager: opts.PlaybackManager,
|
||||
wsEventManager: opts.WSEventManager,
|
||||
torrentRepository: opts.TorrentRepository,
|
||||
selectionHistoryMap: result.NewResultMap[int, *hibiketorrent.AnimeTorrent](),
|
||||
db: opts.Database,
|
||||
client: nil,
|
||||
serverManager: nil,
|
||||
settings: mo.Option[Settings]{},
|
||||
currentEpisodeCollection: mo.Option[*EpisodeCollection]{},
|
||||
selectionHistoryMap: result.NewResultMap[int, *hibiketorrent.AnimeTorrent](),
|
||||
torrentRepository: opts.TorrentRepository,
|
||||
baseAnimeCache: opts.BaseAnimeCache,
|
||||
completeAnimeCache: opts.CompleteAnimeCache,
|
||||
platform: opts.Platform,
|
||||
wsEventManager: opts.WSEventManager,
|
||||
metadataProvider: opts.MetadataProvider,
|
||||
playbackManager: opts.PlaybackManager,
|
||||
mediaPlayerRepository: nil,
|
||||
mediaPlayerRepositorySubscriber: nil,
|
||||
logger: opts.Logger,
|
||||
db: opts.Database,
|
||||
}
|
||||
ret.client = NewClient(ret)
|
||||
ret.serverManager = newServerManager(ret)
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"fmt"
|
||||
"github.com/samber/lo"
|
||||
"seanime/internal/api/anilist"
|
||||
"seanime/internal/api/anizip"
|
||||
"seanime/internal/api/metadata"
|
||||
"seanime/internal/continuity"
|
||||
"seanime/internal/database/db"
|
||||
@@ -21,7 +20,7 @@ import (
|
||||
)
|
||||
|
||||
func TestTorrentstream(t *testing.T) {
|
||||
t.Skip()
|
||||
t.Skip("Incomplete")
|
||||
test_utils.SetTwoLevelDeep()
|
||||
test_utils.InitTestProvider(t, test_utils.Anilist(), test_utils.MediaPlayer(), test_utils.Torrentstream())
|
||||
|
||||
@@ -74,7 +73,6 @@ func TestTorrentstream(t *testing.T) {
|
||||
|
||||
repo := NewRepository(&NewRepositoryOptions{
|
||||
Logger: logger,
|
||||
AnizipCache: anizip.NewCache(),
|
||||
BaseAnimeCache: anilist.NewBaseAnimeCache(),
|
||||
CompleteAnimeCache: anilist.NewCompleteAnimeCache(),
|
||||
Platform: anilistPlatform,
|
||||
@@ -82,9 +80,10 @@ func TestTorrentstream(t *testing.T) {
|
||||
Logger: logger,
|
||||
FileCacher: filecacher,
|
||||
}),
|
||||
PlaybackManager: playbackManager,
|
||||
WSEventManager: wsEventManager,
|
||||
Database: database,
|
||||
PlaybackManager: playbackManager,
|
||||
WSEventManager: wsEventManager,
|
||||
Database: database,
|
||||
TorrentRepository: nil,
|
||||
})
|
||||
repo.SetMediaPlayerRepository(mediaPlayerRepo)
|
||||
defer repo.Shutdown()
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"github.com/anacrolix/torrent"
|
||||
"github.com/samber/mo"
|
||||
"seanime/internal/api/anilist"
|
||||
"seanime/internal/api/anizip"
|
||||
"seanime/internal/api/metadata"
|
||||
"seanime/internal/events"
|
||||
"seanime/internal/library/playbackmanager"
|
||||
"strconv"
|
||||
@@ -213,8 +213,8 @@ func (r *Repository) DropTorrent() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, torrent := range r.client.torrentClient.MustGet().Torrents() {
|
||||
torrent.Drop()
|
||||
for _, t := range r.client.torrentClient.MustGet().Torrents() {
|
||||
t.Drop()
|
||||
}
|
||||
|
||||
// Also stop the server, since it's dropped
|
||||
@@ -228,7 +228,7 @@ func (r *Repository) DropTorrent() error {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
func (r *Repository) getMediaInfo(mediaId int) (media *anilist.CompleteAnime, anizipMedia *anizip.Media, err error) {
|
||||
func (r *Repository) getMediaInfo(mediaId int) (media *anilist.CompleteAnime, animeMetadata *metadata.AnimeMetadata, err error) {
|
||||
// Get the media
|
||||
var found bool
|
||||
media, found = r.completeAnimeCache.Get(mediaId)
|
||||
@@ -241,7 +241,7 @@ func (r *Repository) getMediaInfo(mediaId int) (media *anilist.CompleteAnime, an
|
||||
}
|
||||
|
||||
// Get the media
|
||||
anizipMedia, err = anizip.FetchAniZipMediaC("anilist", mediaId, r.anizipCache)
|
||||
animeMetadata, err = r.metadataProvider.GetAnimeMetadata(metadata.AnilistPlatform, mediaId)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("torrentstream: Could not fetch AniDB media: %w", err)
|
||||
}
|
||||
@@ -249,14 +249,14 @@ func (r *Repository) getMediaInfo(mediaId int) (media *anilist.CompleteAnime, an
|
||||
return
|
||||
}
|
||||
|
||||
func (r *Repository) getEpisodeInfo(anizipMedia *anizip.Media, aniDBEpisode string) (episode *anizip.Episode, err error) {
|
||||
if anizipMedia == nil {
|
||||
func (r *Repository) getEpisodeInfo(animeMetadata *metadata.AnimeMetadata, aniDBEpisode string) (episode *metadata.EpisodeMetadata, err error) {
|
||||
if animeMetadata == nil {
|
||||
return nil, fmt.Errorf("torrentstream: Anizip media is nil")
|
||||
}
|
||||
|
||||
// Get the episode
|
||||
var found bool
|
||||
episode, found = anizipMedia.FindEpisode(aniDBEpisode)
|
||||
episode, found = animeMetadata.FindEpisode(aniDBEpisode)
|
||||
if !found {
|
||||
return nil, fmt.Errorf("torrentstream: Episode not found in the Anizip media")
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@@ -82,50 +81,3 @@ func (u *Updater) fetchLatestRelease() (*Release, error) {
|
||||
|
||||
return &res.Release, nil
|
||||
}
|
||||
|
||||
// compareVersion compares current and latest version is returns true if the latest version is newer than the current version.
|
||||
// It also returns the update type (patch, minor, major) if the latest version is newer than the current version.
|
||||
func (u *Updater) compareVersion(currVersion string, tagName string) (string, bool) {
|
||||
tagName = strings.TrimPrefix(tagName, "v")
|
||||
|
||||
currVParts := strings.Split(currVersion, ".")
|
||||
latestVParts := strings.Split(tagName, ".")
|
||||
|
||||
if len(currVParts) != 3 || len(latestVParts) != 3 {
|
||||
return "", false
|
||||
}
|
||||
|
||||
currMajor, _ := strconv.Atoi(currVParts[0])
|
||||
currMinor, _ := strconv.Atoi(currVParts[1])
|
||||
currPatch, _ := strconv.Atoi(currVParts[2])
|
||||
|
||||
latestMajor, _ := strconv.Atoi(latestVParts[0])
|
||||
latestMinor, _ := strconv.Atoi(latestVParts[1])
|
||||
latestPatch, _ := strconv.Atoi(latestVParts[2])
|
||||
|
||||
if currMajor > latestMajor {
|
||||
return "", false
|
||||
}
|
||||
|
||||
if currMajor < latestMajor {
|
||||
return MajorRelease, true
|
||||
}
|
||||
|
||||
if currMinor > latestMinor {
|
||||
return "", false
|
||||
}
|
||||
|
||||
if currMinor < latestMinor {
|
||||
return MinorRelease, true
|
||||
}
|
||||
|
||||
if currPatch > latestPatch {
|
||||
return "", false
|
||||
}
|
||||
|
||||
if currPatch < latestPatch {
|
||||
return PatchRelease, true
|
||||
}
|
||||
|
||||
return "", false
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"seanime/internal/constants"
|
||||
"seanime/internal/util"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@@ -30,8 +31,6 @@ func TestUpdater_FetchLatestRelease(t *testing.T) {
|
||||
|
||||
func TestUpdater_CompareVersion(t *testing.T) {
|
||||
|
||||
updater := Updater{}
|
||||
|
||||
tests := []struct {
|
||||
currVersion string
|
||||
latestVersion string
|
||||
@@ -62,11 +61,16 @@ func TestUpdater_CompareVersion(t *testing.T) {
|
||||
latestVersion: "0.2.1",
|
||||
shouldUpdate: false,
|
||||
},
|
||||
{
|
||||
currVersion: "1.0.0",
|
||||
latestVersion: "0.2.1",
|
||||
shouldUpdate: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.latestVersion, func(t *testing.T) {
|
||||
updateType, shouldUpdate := updater.compareVersion(tt.currVersion, tt.latestVersion)
|
||||
updateType, shouldUpdate := util.CompareVersion(tt.currVersion, tt.latestVersion)
|
||||
assert.Equal(t, tt.shouldUpdate, shouldUpdate)
|
||||
t.Log(tt.latestVersion, updateType)
|
||||
})
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
package updater
|
||||
|
||||
import "github.com/rs/zerolog"
|
||||
import (
|
||||
"github.com/rs/zerolog"
|
||||
"seanime/internal/util"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
PatchRelease = "patch"
|
||||
@@ -68,11 +72,21 @@ func (u *Updater) GetLatestUpdate() (*Update, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
updateType, shouldUpdate := u.compareVersion(u.CurrentVersion, rl.TagName)
|
||||
newV := strings.TrimPrefix(rl.TagName, "v")
|
||||
updateTypeI, shouldUpdate := util.CompareVersion(u.CurrentVersion, newV)
|
||||
if !shouldUpdate {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
updateType := ""
|
||||
if updateTypeI == -1 {
|
||||
updateType = MinorRelease
|
||||
} else if updateTypeI == -2 {
|
||||
updateType = PatchRelease
|
||||
} else if updateTypeI == -3 {
|
||||
updateType = MajorRelease
|
||||
}
|
||||
|
||||
return &Update{
|
||||
Release: rl,
|
||||
Type: updateType,
|
||||
|
||||
@@ -46,25 +46,26 @@ func CompareVersion(current string, b string) (comp int, shouldUpdate bool) {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
if currV.Major() > otherV.Major() {
|
||||
comp *= 3
|
||||
} else if currV.Minor() > otherV.Minor() {
|
||||
comp *= 2
|
||||
} else if currV.Patch() > otherV.Patch() {
|
||||
comp *= 1
|
||||
} else if currV.GreaterThan(otherV) {
|
||||
if currV.GreaterThan(otherV) {
|
||||
shouldUpdate = false
|
||||
|
||||
} else if currV.Major() < otherV.Major() {
|
||||
comp *= 3
|
||||
shouldUpdate = true
|
||||
} else if currV.Minor() < otherV.Minor() {
|
||||
comp *= 2
|
||||
shouldUpdate = true
|
||||
} else if currV.Patch() < otherV.Patch() {
|
||||
comp *= 1
|
||||
shouldUpdate = true
|
||||
if currV.Major() > otherV.Major() {
|
||||
comp *= 3
|
||||
} else if currV.Minor() > otherV.Minor() {
|
||||
comp *= 2
|
||||
} else if currV.Patch() > otherV.Patch() {
|
||||
comp *= 1
|
||||
}
|
||||
} else if currV.LessThan(otherV) {
|
||||
shouldUpdate = true
|
||||
|
||||
if currV.Major() < otherV.Major() {
|
||||
comp *= 3
|
||||
} else if currV.Minor() < otherV.Minor() {
|
||||
comp *= 2
|
||||
} else if currV.Patch() < otherV.Patch() {
|
||||
comp *= 1
|
||||
}
|
||||
}
|
||||
|
||||
return comp, shouldUpdate
|
||||
@@ -73,5 +74,5 @@ func CompareVersion(current string, b string) (comp int, shouldUpdate bool) {
|
||||
func VersionIsOlderThan(version string, compare string) bool {
|
||||
comp, shouldUpdate := CompareVersion(version, compare)
|
||||
// shouldUpdate is false means the current version is newer
|
||||
return comp < 0 && !shouldUpdate
|
||||
return comp < 0 && shouldUpdate
|
||||
}
|
||||
|
||||
@@ -28,6 +28,27 @@ func TestCompareVersion(t *testing.T) {
|
||||
expectedOutput: -3,
|
||||
shouldUpdate: true,
|
||||
},
|
||||
{
|
||||
name: "Current version is older by minor version",
|
||||
currVersion: "0.2.2",
|
||||
otherVersion: "0.3.0",
|
||||
expectedOutput: -2,
|
||||
shouldUpdate: true,
|
||||
},
|
||||
{
|
||||
name: "Current version is older by major version",
|
||||
currVersion: "0.2.2",
|
||||
otherVersion: "3.0.0",
|
||||
expectedOutput: -3,
|
||||
shouldUpdate: true,
|
||||
},
|
||||
{
|
||||
name: "Current version is older by minor version",
|
||||
currVersion: "0.2.2",
|
||||
otherVersion: "0.2.3",
|
||||
expectedOutput: -1,
|
||||
shouldUpdate: true,
|
||||
},
|
||||
{
|
||||
name: "Current version is newer by minor version",
|
||||
currVersion: "1.2.0",
|
||||
|
||||
Reference in New Issue
Block a user