From 73591bbd55b32713dbd8b7011b44e95e725eea43 Mon Sep 17 00:00:00 2001 From: 5rahim Date: Sun, 15 Sep 2024 20:17:01 -0400 Subject: [PATCH] fix semver function, flawed tests refactor: centralize metadata provider --- .golangci.yml | 11 + codegen/generated/public_structs.json | 717 +++++++++++------- internal/api/anizip/anizip_helper.go | 42 - internal/api/metadata/anime.go | 134 ++-- .../anizip_helper_test.go | 6 +- internal/api/metadata/mock.go | 17 + internal/api/metadata/provider.go | 105 ++- internal/api/metadata/test_helpers.go | 22 - internal/api/metadata/types.go | 83 +- internal/api/tvdb/tvdb_test.go | 18 +- internal/core/app.go | 24 +- internal/core/modules.go | 17 +- internal/extension_playground/playground.go | 28 +- .../extension_playground/playground_test.go | 4 +- internal/handlers/anime_collection.go | 6 +- internal/handlers/anime_entries.go | 7 +- internal/handlers/manual_dump.go | 18 +- internal/handlers/metadata.go | 10 +- internal/handlers/scan.go | 1 + internal/handlers/torrentstream.go | 8 +- internal/library/anime/anime_entry.go | 34 +- .../anime/anime_entry_download_info.go | 37 +- .../anime/anime_entry_download_info_test.go | 11 +- internal/library/anime/anime_entry_test.go | 6 +- internal/library/anime/collection.go | 5 - internal/library/anime/collection_test.go | 4 +- internal/library/anime/episode.go | 47 +- internal/library/anime/missing_episodes.go | 10 +- .../library/anime/missing_episodes_test.go | 5 +- .../library/autodownloader/autodownloader.go | 16 +- internal/library/autoscanner/autoscanner.go | 66 +- internal/library/scanner/hydrator.go | 16 +- internal/library/scanner/hydrator_test.go | 7 +- internal/library/scanner/media_fetcher.go | 23 +- .../library/scanner/media_fetcher_test.go | 14 +- .../library/scanner/media_tree_analysis.go | 28 +- .../scanner/media_tree_analysis_test.go | 18 +- internal/library/scanner/scan.go | 23 +- internal/library/scanner/scan_logger_test.go | 12 +- .../mediaplayers/mediaplayer/repository.go | 1 - internal/offline/{test_helper.go => mock.go} | 0 internal/offline/snapshot.go | 4 - internal/onlinestream/repository.go | 28 +- .../torrent_client/repository.go | 15 +- .../torrent_client/smart_select.go | 9 +- internal/torrents/analyzer/analyzer.go | 35 +- internal/torrents/analyzer/analyzer_test.go | 11 +- internal/torrents/animetosho/provider_test.go | 12 +- internal/torrents/torrent/repository.go | 3 - internal/torrents/torrent/repository_test.go | 2 +- internal/torrents/torrent/search.go | 54 +- internal/torrentstream/collection.go | 12 +- internal/torrentstream/collection_test.go | 22 +- internal/torrentstream/finder.go | 18 +- internal/torrentstream/list.go | 4 +- internal/torrentstream/repository.go | 30 +- internal/torrentstream/repository_test.go | 11 +- internal/torrentstream/stream.go | 16 +- internal/updater/check.go | 48 -- internal/updater/check_test.go | 10 +- internal/updater/updater.go | 18 +- internal/util/version.go | 35 +- internal/util/version_test.go | 21 + 63 files changed, 1210 insertions(+), 869 deletions(-) create mode 100644 .golangci.yml rename internal/api/{anizip => metadata}/anizip_helper_test.go (63%) create mode 100644 internal/api/metadata/mock.go delete mode 100644 internal/api/metadata/test_helpers.go rename internal/offline/{test_helper.go => mock.go} (100%) diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 00000000..9ec8cecf --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,11 @@ +run: + concurrency: 4 + timeout: 1m + issues-exit-code: 1 + tests: true + +linters: + disable-all: true + + enable: + - exhaustruct diff --git a/codegen/generated/public_structs.json b/codegen/generated/public_structs.json index c5a378c5..1489e980 100644 --- a/codegen/generated/public_structs.json +++ b/codegen/generated/public_structs.json @@ -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", diff --git a/internal/api/anizip/anizip_helper.go b/internal/api/anizip/anizip_helper.go index 5d63ccbd..106eec24 100644 --- a/internal/api/anizip/anizip_helper.go +++ b/internal/api/anizip/anizip_helper.go @@ -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 -} diff --git a/internal/api/metadata/anime.go b/internal/api/metadata/anime.go index 16ba683a..275b8e3c 100644 --- a/internal/api/metadata/anime.go +++ b/internal/api/metadata/anime.go @@ -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 +} diff --git a/internal/api/anizip/anizip_helper_test.go b/internal/api/metadata/anizip_helper_test.go similarity index 63% rename from internal/api/anizip/anizip_helper_test.go rename to internal/api/metadata/anizip_helper_test.go index 6d167570..ce98aeec 100644 --- a/internal/api/anizip/anizip_helper_test.go +++ b/internal/api/metadata/anizip_helper_test.go @@ -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) } } diff --git a/internal/api/metadata/mock.go b/internal/api/metadata/mock.go new file mode 100644 index 00000000..d63511f0 --- /dev/null +++ b/internal/api/metadata/mock.go @@ -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, + }) +} diff --git a/internal/api/metadata/provider.go b/internal/api/metadata/provider.go index f7a3d429..bf436454 100644 --- a/internal/api/metadata/provider.go +++ b/internal/api/metadata/provider.go @@ -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) diff --git a/internal/api/metadata/test_helpers.go b/internal/api/metadata/test_helpers.go deleted file mode 100644 index e4621ca3..00000000 --- a/internal/api/metadata/test_helpers.go +++ /dev/null @@ -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 -} diff --git a/internal/api/metadata/types.go b/internal/api/metadata/types.go index 84af9340..c5853521 100644 --- a/internal/api/metadata/types.go +++ b/internal/api/metadata/types.go @@ -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 +} diff --git a/internal/api/tvdb/tvdb_test.go b/internal/api/tvdb/tvdb_test.go index e535b986..83e67f5b 100644 --- a/internal/api/tvdb/tvdb_test.go +++ b/internal/api/tvdb/tvdb_test.go @@ -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 diff --git a/internal/core/app.go b/internal/core/app.go index b981d275..780e775a 100644 --- a/internal/core/app.go +++ b/internal/core/app.go @@ -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, diff --git a/internal/core/modules.go b/internal/core/modules.go index 18887d61..e643fcd4 100644 --- a/internal/core/modules.go +++ b/internal/core/modules.go @@ -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) diff --git a/internal/extension_playground/playground.go b/internal/extension_playground/playground.go index 59477c1c..c737e996 100644 --- a/internal/extension_playground/playground.go +++ b/internal/extension_playground/playground.go @@ -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 } diff --git a/internal/extension_playground/playground_test.go b/internal/extension_playground/playground_test.go index c32ad6bd..e13e7d74 100644 --- a/internal/extension_playground/playground_test.go +++ b/internal/extension_playground/playground_test.go @@ -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" diff --git a/internal/handlers/anime_collection.go b/internal/handlers/anime_collection.go index c207846b..45b2d26e 100644 --- a/internal/handlers/anime_collection.go +++ b/internal/handlers/anime_collection.go @@ -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, }) } diff --git a/internal/handlers/anime_entries.go b/internal/handlers/anime_entries.go index 32929e38..7fbce402 100644 --- a/internal/handlers/anime_entries.go +++ b/internal/handlers/anime_entries.go @@ -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, }) diff --git a/internal/handlers/manual_dump.go b/internal/handlers/manual_dump.go index 46688425..8e63c890 100644 --- a/internal/handlers/manual_dump.go +++ b/internal/handlers/manual_dump.go @@ -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 { diff --git a/internal/handlers/metadata.go b/internal/handlers/metadata.go index b932c1b0..7a62b0ab 100644 --- a/internal/handlers/metadata.go +++ b/internal/handlers/metadata.go @@ -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) diff --git a/internal/handlers/scan.go b/internal/handlers/scan.go index 551ee4b0..fd18b1cb 100644 --- a/internal/handlers/scan.go +++ b/internal/handlers/scan.go @@ -72,6 +72,7 @@ func HandleScanLocalFiles(c *RouteCtx) error { SkipIgnoredFiles: b.SkipIgnoredFiles, ScanSummaryLogger: scanSummaryLogger, ScanLogger: scanLogger, + MetadataProvider: c.App.MetadataProvider, } // Scan the library diff --git a/internal/handlers/torrentstream.go b/internal/handlers/torrentstream.go index 72be59cb..4c69b22c 100644 --- a/internal/handlers/torrentstream.go +++ b/internal/handlers/torrentstream.go @@ -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{ diff --git a/internal/library/anime/anime_entry.go b/internal/library/anime/anime_entry.go index f85a6e12..ddff5173 100644 --- a/internal/library/anime/anime_entry.go +++ b/internal/library/anime/anime_entry.go @@ -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 } diff --git a/internal/library/anime/anime_entry_download_info.go b/internal/library/anime/anime_entry_download_info.go index 3ed381de..8874c3d3 100644 --- a/internal/library/anime/anime_entry_download_info.go +++ b/internal/library/anime/anime_entry_download_info.go @@ -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 } diff --git a/internal/library/anime/anime_entry_download_info_test.go b/internal/library/anime/anime_entry_download_info_test.go index e94812fe..ce713aba 100644 --- a/internal/library/anime/anime_entry_download_info_test.go +++ b/internal/library/anime/anime_entry_download_info_test.go @@ -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, diff --git a/internal/library/anime/anime_entry_test.go b/internal/library/anime/anime_entry_test.go index 164f1732..fefd1e09 100644 --- a/internal/library/anime/anime_entry_test.go +++ b/internal/library/anime/anime_entry_test.go @@ -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, diff --git a/internal/library/anime/collection.go b/internal/library/anime/collection.go index 8ec63a76..5e8b54af 100644 --- a/internal/library/anime/collection.go +++ b/internal/library/anime/collection.go @@ -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, }) diff --git a/internal/library/anime/collection_test.go b/internal/library/anime/collection_test.go index ee1927c3..02a3f6c0 100644 --- a/internal/library/anime/collection_test.go +++ b/internal/library/anime/collection_test.go @@ -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, }) diff --git a/internal/library/anime/episode.go b/internal/library/anime/episode.go index 5c8ba577..81f4733f 100644 --- a/internal/library/anime/episode.go +++ b/internal/library/anime/episode.go @@ -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 diff --git a/internal/library/anime/missing_episodes.go b/internal/library/anime/missing_episodes.go index cbea0298..b23880da 100644 --- a/internal/library/anime/missing_episodes.go +++ b/internal/library/anime/missing_episodes.go @@ -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 { diff --git a/internal/library/anime/missing_episodes_test.go b/internal/library/anime/missing_episodes_test.go index 5d5475d6..a17b4d42 100644 --- a/internal/library/anime/missing_episodes_test.go +++ b/internal/library/anime/missing_episodes_test.go @@ -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, }) diff --git a/internal/library/autodownloader/autodownloader.go b/internal/library/autodownloader/autodownloader.go index 52e8b777..3b68130e 100644 --- a/internal/library/autodownloader/autodownloader.go +++ b/internal/library/autodownloader/autodownloader.go @@ -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() } diff --git a/internal/library/autoscanner/autoscanner.go b/internal/library/autoscanner/autoscanner.go index 9067503e..c32fda9e 100644 --- a/internal/library/autoscanner/autoscanner.go +++ b/internal/library/autoscanner/autoscanner.go @@ -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() diff --git a/internal/library/scanner/hydrator.go b/internal/library/scanner/hydrator.go index acaa3cfd..9ee6947d 100644 --- a/internal/library/scanner/hydrator.go +++ b/internal/library/scanner/hydrator.go @@ -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 diff --git a/internal/library/scanner/hydrator_test.go b/internal/library/scanner/hydrator_test.go index 0a43f4c1..06826f90 100644 --- a/internal/library/scanner/hydrator_test.go +++ b/internal/library/scanner/hydrator_test.go @@ -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, } diff --git a/internal/library/scanner/media_fetcher.go b/internal/library/scanner/media_fetcher.go index cd854e86..ee75f030 100644 --- a/internal/library/scanner/media_fetcher.go +++ b/internal/library/scanner/media_fetcher.go @@ -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 }) diff --git a/internal/library/scanner/media_fetcher_test.go b/internal/library/scanner/media_fetcher_test.go index be7d11b9..0e30610d 100644 --- a/internal/library/scanner/media_fetcher_test.go +++ b/internal/library/scanner/media_fetcher_test.go @@ -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, ) diff --git a/internal/library/scanner/media_tree_analysis.go b/internal/library/scanner/media_tree_analysis.go index 2cce65ec..379112bb 100644 --- a/internal/library/scanner/media_tree_analysis.go +++ b/internal/library/scanner/media_tree_analysis.go @@ -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 } diff --git a/internal/library/scanner/media_tree_analysis_test.go b/internal/library/scanner/media_tree_analysis_test.go index 6165a092..47450ea9 100644 --- a/internal/library/scanner/media_tree_analysis_test.go +++ b/internal/library/scanner/media_tree_analysis_test.go @@ -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()) diff --git a/internal/library/scanner/scan.go b/internal/library/scanner/scan.go index dd454665..49706e5a 100644 --- a/internal/library/scanner/scan.go +++ b/internal/library/scanner/scan.go @@ -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, diff --git a/internal/library/scanner/scan_logger_test.go b/internal/library/scanner/scan_logger_test.go index 52378274..04a7d495 100644 --- a/internal/library/scanner/scan_logger_test.go +++ b/internal/library/scanner/scan_logger_test.go @@ -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() diff --git a/internal/mediaplayers/mediaplayer/repository.go b/internal/mediaplayers/mediaplayer/repository.go index 6b94b079..99e95663 100644 --- a/internal/mediaplayers/mediaplayer/repository.go +++ b/internal/mediaplayers/mediaplayer/repository.go @@ -45,7 +45,6 @@ type ( VLC *vlc2.VLC MpcHc *mpchc2.MpcHc Mpv *mpv.Mpv - MpvType string WSEventManager events.WSEventManagerInterface ContinuityManager *continuity.Manager } diff --git a/internal/offline/test_helper.go b/internal/offline/mock.go similarity index 100% rename from internal/offline/test_helper.go rename to internal/offline/mock.go diff --git a/internal/offline/snapshot.go b/internal/offline/snapshot.go index 83f0f261..262c51d2 100644 --- a/internal/offline/snapshot.go +++ b/internal/offline/snapshot.go @@ -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, diff --git a/internal/onlinestream/repository.go b/internal/onlinestream/repository.go index 912a3515..fab8d302 100644 --- a/internal/onlinestream/repository.go +++ b/internal/onlinestream/repository.go @@ -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{ diff --git a/internal/torrent_clients/torrent_client/repository.go b/internal/torrent_clients/torrent_client/repository.go index 382c2c80..866e9e90 100644 --- a/internal/torrent_clients/torrent_client/repository.go +++ b/internal/torrent_clients/torrent_client/repository.go @@ -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{}, } } diff --git a/internal/torrent_clients/torrent_client/smart_select.go b/internal/torrent_clients/torrent_client/smart_select.go index 166d8b5f..3bc6dfc9 100644 --- a/internal/torrent_clients/torrent_client/smart_select.go +++ b/internal/torrent_clients/torrent_client/smart_select.go @@ -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)") diff --git a/internal/torrents/analyzer/analyzer.go b/internal/torrents/analyzer/analyzer.go index 0e9d50dc..b2f73501 100644 --- a/internal/torrents/analyzer/analyzer.go +++ b/internal/torrents/analyzer/analyzer.go @@ -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() diff --git a/internal/torrents/analyzer/analyzer_test.go b/internal/torrents/analyzer/analyzer_test.go index 093b06dc..a5607a68 100644 --- a/internal/torrents/analyzer/analyzer_test.go +++ b/internal/torrents/analyzer/analyzer_test.go @@ -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 diff --git a/internal/torrents/animetosho/provider_test.go b/internal/torrents/animetosho/provider_test.go index e36ebcec..bf90fd06 100644 --- a/internal/torrents/animetosho/provider_test.go +++ b/internal/torrents/animetosho/provider_test.go @@ -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, }) diff --git a/internal/torrents/torrent/repository.go b/internal/torrents/torrent/repository.go index 5135515b..e20af766 100644 --- a/internal/torrents/torrent/repository.go +++ b/internal/torrents/torrent/repository.go @@ -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{}, } diff --git a/internal/torrents/torrent/repository_test.go b/internal/torrents/torrent/repository_test.go index 799aa4af..81bbd0bb 100644 --- a/internal/torrents/torrent/repository_test.go +++ b/internal/torrents/torrent/repository_test.go @@ -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() diff --git a/internal/torrents/torrent/search.go b/internal/torrents/torrent/search.go index 17d2cefd..d77c248e 100644 --- a/internal/torrents/torrent/search.go +++ b/internal/torrents/torrent/search.go @@ -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: "", diff --git a/internal/torrentstream/collection.go b/internal/torrentstream/collection.go index 48b5fdf1..d498ba13 100644 --- a/internal/torrentstream/collection.go +++ b/internal/torrentstream/collection.go @@ -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, diff --git a/internal/torrentstream/collection_test.go b/internal/torrentstream/collection_test.go index 672cfd4b..a42819ec 100644 --- a/internal/torrentstream/collection_test.go +++ b/internal/torrentstream/collection_test.go @@ -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) diff --git a/internal/torrentstream/finder.go b/internal/torrentstream/finder.go index 18ac93bf..066edf1c 100644 --- a/internal/torrentstream/finder.go +++ b/internal/torrentstream/finder.go @@ -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 diff --git a/internal/torrentstream/list.go b/internal/torrentstream/list.go index fa35e6ce..d0df7021 100644 --- a/internal/torrentstream/list.go +++ b/internal/torrentstream/list.go @@ -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(), diff --git a/internal/torrentstream/repository.go b/internal/torrentstream/repository.go index 9a290cb4..6c8395fb 100644 --- a/internal/torrentstream/repository.go +++ b/internal/torrentstream/repository.go @@ -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) diff --git a/internal/torrentstream/repository_test.go b/internal/torrentstream/repository_test.go index 5d29260f..8c2cb01d 100644 --- a/internal/torrentstream/repository_test.go +++ b/internal/torrentstream/repository_test.go @@ -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() diff --git a/internal/torrentstream/stream.go b/internal/torrentstream/stream.go index 8043f62f..306dee8b 100644 --- a/internal/torrentstream/stream.go +++ b/internal/torrentstream/stream.go @@ -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") } diff --git a/internal/updater/check.go b/internal/updater/check.go index 294f1d49..a289116c 100644 --- a/internal/updater/check.go +++ b/internal/updater/check.go @@ -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 -} diff --git a/internal/updater/check_test.go b/internal/updater/check_test.go index 8482731b..ee034d69 100644 --- a/internal/updater/check_test.go +++ b/internal/updater/check_test.go @@ -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) }) diff --git a/internal/updater/updater.go b/internal/updater/updater.go index 0be159c6..5e614eb9 100644 --- a/internal/updater/updater.go +++ b/internal/updater/updater.go @@ -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, diff --git a/internal/util/version.go b/internal/util/version.go index 343edd10..2998bba1 100644 --- a/internal/util/version.go +++ b/internal/util/version.go @@ -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 } diff --git a/internal/util/version_test.go b/internal/util/version_test.go index adce7cb8..8ed6b6c1 100644 --- a/internal/util/version_test.go +++ b/internal/util/version_test.go @@ -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",