From 3c9ed469a89cfe416306549186043ee162e3da9b Mon Sep 17 00:00:00 2001 From: TOomaAh Date: Mon, 20 Jan 2025 01:48:11 +0100 Subject: [PATCH] refactor(tvdb): use tvdb api --- server/api/metadata.ts | 39 +++++ server/api/tvdb/index.ts | 124 ++++++++++---- server/api/tvdb/interfaces.ts | 157 +++++++++++++----- server/lib/scanners/baseScanner.ts | 3 +- server/lib/scanners/jellyfin/index.ts | 3 +- server/lib/scanners/plex/index.ts | 6 +- server/lib/scanners/sonarr/index.ts | 4 +- server/lib/settings/index.ts | 62 +++++-- server/routes/settings/index.ts | 2 +- .../routes/settings/{tvdb.ts => metadata.ts} | 11 +- server/routes/tv.ts | 25 ++- src/components/MetadataSelector/index.ts | 0 src/components/Settings/SettingsLayout.tsx | 10 +- ...{SettingsTvdb.tsx => SettingsMetadata.tsx} | 16 +- src/pages/settings/{tvdb.tsx => metadata.tsx} | 8 +- 15 files changed, 342 insertions(+), 128 deletions(-) create mode 100644 server/api/metadata.ts rename server/routes/settings/{tvdb.ts => metadata.ts} (80%) create mode 100644 src/components/MetadataSelector/index.ts rename src/components/Settings/{SettingsTvdb.tsx => SettingsMetadata.tsx} (94%) rename src/pages/settings/{tvdb.tsx => metadata.tsx} (62%) diff --git a/server/api/metadata.ts b/server/api/metadata.ts new file mode 100644 index 00000000..ab5df3dc --- /dev/null +++ b/server/api/metadata.ts @@ -0,0 +1,39 @@ +import type { TvShowIndexer } from '@server/api/indexer'; +import TheMovieDb from '@server/api/themoviedb'; +import Tvdb from '@server/api/tvdb'; +import { getSettings, IndexerType } from '@server/lib/settings'; +import logger from '@server/logger'; + +export const getMetadataProvider = async ( + mediaType: 'movie' | 'tv' | 'anime' +): Promise => { + try { + const settings = await getSettings(); + + if (!settings.tvdb.apiKey || mediaType == 'movie') { + return new TheMovieDb(); + } + + if ( + mediaType == 'tv' && + settings.metadataSettings.tvShow == IndexerType.TVDB + ) { + return await Tvdb.getInstance(); + } + + if ( + mediaType == 'anime' && + settings.metadataSettings.anime == IndexerType.TVDB + ) { + return await Tvdb.getInstance(); + } + + return new TheMovieDb(); + } catch (e) { + logger.error('Failed to get metadata provider', { + label: 'Metadata', + message: e.message, + }); + return new TheMovieDb(); + } +}; diff --git a/server/api/tvdb/index.ts b/server/api/tvdb/index.ts index bb3f64ee..0ff79bcd 100644 --- a/server/api/tvdb/index.ts +++ b/server/api/tvdb/index.ts @@ -10,10 +10,11 @@ import type { import type { TvdbEpisode, TvdbLoginResponse, - TvdbSeason, - TvdbTvShowDetail, + TvdbSeasonDetails, + TvdbTvDetails, } from '@server/api/tvdb/interfaces'; import cacheManager, { type AvailableCacheIds } from '@server/lib/cache'; +import { getSettings } from '@server/lib/settings'; import logger from '@server/logger'; interface TvdbConfig { @@ -36,16 +37,21 @@ type TvdbId = number; type ValidTvdbId = Exclude; class Tvdb extends ExternalAPI implements TvShowIndexer { + static instance: Tvdb; private readonly tmdb: TheMovieDb; private static readonly DEFAULT_CACHE_TTL = 43200; - private static readonly DEFAULT_LANGUAGE = 'en'; - - constructor(config: Partial = {}) { - const finalConfig = { ...DEFAULT_CONFIG, ...config }; + private static readonly DEFAULT_LANGUAGE = 'eng'; + private token: string; + private apiKey?: string; + private pin?: string; + constructor(apiKey: string, pin?: string) { + const finalConfig = { ...DEFAULT_CONFIG }; super( finalConfig.baseUrl, - {}, + { + apiKey: apiKey, + }, { nodeCache: cacheManager.getCache(finalConfig.cachePrefix).data, rateLimit: { @@ -54,9 +60,33 @@ class Tvdb extends ExternalAPI implements TvShowIndexer { }, } ); + this.apiKey = apiKey; + this.pin = pin; this.tmdb = new TheMovieDb(); } + public static async getInstance(): Promise { + if (!this.instance) { + const settings = await getSettings(); + + if (!settings.tvdb.apiKey) { + throw new Error('TVDB API key is not set'); + } + + try { + this.instance = new Tvdb(settings.tvdb.apiKey, settings.tvdb.pin); + await this.instance.login(); + } catch (error) { + logger.error(`Failed to login to TVDB: ${error.message}`); + throw new Error('TVDB API key is not set'); + } + + this.instance = new Tvdb(settings.tvdb.apiKey, settings.tvdb.pin); + } + + return this.instance; + } + public async test(): Promise { try { return await this.get('/en/445009', {}); @@ -66,6 +96,21 @@ class Tvdb extends ExternalAPI implements TvShowIndexer { } } + async handleRenewToken(): Promise { + throw new Error('Method not implemented.'); + } + + async login(): Promise { + const response = await this.post('/login', { + apiKey: process.env.TVDB_API_KEY, + }); + + this.defaultHeaders.Authorization = `Bearer ${response.token}`; + this.token = response.token; + + return response; + } + public async getShowByTvdbId({ tvdbId, }: { @@ -152,29 +197,34 @@ class Tvdb extends ExternalAPI implements TvShowIndexer { } } - private async fetchTvdbShowData(tvdbId: number): Promise { - return await this.get( - `/en/${tvdbId}`, - {}, + private async fetchTvdbShowData(tvdbId: number): Promise { + return await this.get( + `/series/${tvdbId}/extended?meta=episodes`, + { + short: 'true', + }, Tvdb.DEFAULT_CACHE_TTL ); } - private processSeasons(tvdbData: TvdbTvShowDetail): TmdbTvSeasonResult[] { - if (!tvdbData || !tvdbData.seasons) { + private processSeasons(tvdbData: TvdbTvDetails): TmdbTvSeasonResult[] { + if (!tvdbData || !tvdbData.seasons || !tvdbData.episodes) { return []; } return tvdbData.seasons - .filter((season) => season.seasonNumber !== 0) + .filter( + (season) => + season.number > 0 && season.type && season.type.type === 'official' + ) .map((season) => this.createSeasonData(season, tvdbData)); } private createSeasonData( - season: TvdbSeason, - tvdbData: TvdbTvShowDetail + season: TvdbSeasonDetails, + tvdbData: TvdbTvDetails ): TmdbTvSeasonResult { - if (!season.seasonNumber) { + if (!season.number) { return { id: 0, episode_count: 0, @@ -187,15 +237,15 @@ class Tvdb extends ExternalAPI implements TvShowIndexer { } const episodeCount = tvdbData.episodes.filter( - (episode) => episode.seasonNumber === season.seasonNumber + (episode) => episode.seasonNumber === season.number ).length; return { - id: tvdbData.tvdbId, + id: tvdbData.id, episode_count: episodeCount, - name: `${season.seasonNumber}`, + name: `${season.number}`, overview: '', - season_number: season.seasonNumber, + season_number: season.number, poster_path: '', air_date: '', }; @@ -204,25 +254,35 @@ class Tvdb extends ExternalAPI implements TvShowIndexer { private async getTvdbSeasonData( tvdbId: number, seasonNumber: number, - tvId: number + tvId: number, + language: string = Tvdb.DEFAULT_LANGUAGE ): Promise { - const tvdbSeason = await this.fetchTvdbShowData(tvdbId); + const tvdbData = await this.fetchTvdbShowData(tvdbId); - const episodes = this.processEpisodes(tvdbSeason, seasonNumber, tvId); + if (!tvdbData) { + return this.createEmptySeasonResponse(tvId); + } + + const seasons = await this.get( + `/series/${tvdbId}/episodes/official/${language}`, + {} + ); + + const episodes = this.processEpisodes(seasons, seasonNumber, tvId); return { episodes, - external_ids: { tvdb_id: tvdbSeason.tvdbId }, + external_ids: { tvdb_id: tvdbId }, name: '', overview: '', - id: tvdbSeason.tvdbId, - air_date: tvdbSeason.firstAired, + id: seasons.id, + air_date: seasons.firstAired, season_number: episodes.length, }; } private processEpisodes( - tvdbSeason: TvdbTvShowDetail, + tvdbSeason: TvdbSeasonDetails, seasonNumber: number, tvId: number ): TmdbTvEpisodeResult[] { @@ -241,10 +301,10 @@ class Tvdb extends ExternalAPI implements TvShowIndexer { tvId: number ): TmdbTvEpisodeResult { return { - id: episode.tvdbId, - air_date: episode.airDate, - episode_number: episode.episodeNumber, - name: episode.title || `Episode ${index + 1}`, + id: episode.id, + air_date: episode.aired, + episode_number: episode.number, + name: episode.name || `Episode ${index + 1}`, overview: episode.overview || '', season_number: episode.seasonNumber, production_code: '', diff --git a/server/api/tvdb/interfaces.ts b/server/api/tvdb/interfaces.ts index 3e9007e2..532ff0e6 100644 --- a/server/api/tvdb/interfaces.ts +++ b/server/api/tvdb/interfaces.ts @@ -3,73 +3,142 @@ export interface TvdbBaseResponse { errors: string; } +export interface TvdbLoginResponse { + token: string; +} + export interface TvdbLoginResponse extends TvdbBaseResponse<{ token: string }> { data: { token: string }; } -export interface TvdbTvShowDetail { - tvdbId: number; - title: string; - overview: string; +interface TvDetailsAliases { + language: string; + name: string; +} + +interface TvDetailsStatus { + id: number; + name: string; + recordType: string; + keepUpdated: boolean; +} + +export interface TvdbTvDetails extends TvdbBaseResponse { + id: number; + name: string; slug: string; + image: string; + nameTranslations: string[]; + overwiewTranslations: string[]; + aliases: TvDetailsAliases[]; + firstAired: Date; + lastAired: Date; + nextAired: Date | string; + score: number; + status: TvDetailsStatus; originalCountry: string; originalLanguage: string; - language: string; - firstAired: string; - lastAired: string; - tvMazeId: number; - tmdbId: number; - imdbId: string; - lastUpdated: string; - status: string; - runtime: number; - timeOfDay: TvdbTimeOfDay; - originalNetwork: string; - network: string; - genres: string[]; - alternativeTitles: TvdbAlternativeTitle[]; - actors: TvdbActor[]; - images: TvdbImage[]; - seasons: TvdbSeason[]; + defaultSeasonType: string; + isOrderRandomized: boolean; + lastUpdated: Date; + averageRuntime: number; + seasons: TvdbSeasonDetails[]; episodes: TvdbEpisode[]; } -export interface TvdbTimeOfDay { - hours: number; - minutes: number; +interface TvdbCompanyType { + companyTypeId: number; + companyTypeName: string; } -export interface TvdbAlternativeTitle { - title: string; +interface TvdbParentCompany { + id?: number; + name?: string; + relation?: { + id?: number; + typeName?: string; + }; } -export interface TvdbActor { +interface TvdbCompany { + id: number; name: string; - character: string; - image?: string; + slug: string; + nameTranslations?: string[]; + overviewTranslations?: string[]; + aliases?: string[]; + country: string; + primaryCompanyType: number; + activeDate: string; + inactiveDate?: string; + companyType: TvdbCompanyType; + parentCompany: TvdbParentCompany; + tagOptions?: string[]; } -export interface TvdbImage { - coverType: string; - url: string; +interface TvdbType { + id: number; + name: string; + type: string; + alternateName?: string; } -export interface TvdbSeason { - seasonNumber: number; +interface TvdbArtwork { + id: number; + image: string; + thumbnail: string; + language: string; + type: number; + score: number; + width: number; + height: number; + includesText: boolean; } export interface TvdbEpisode { - tvdbShowId: number; - tvdbId: number; - seasonNumber: number; - episodeNumber: number; - absoluteEpisodeNumber: number; - title?: string; - airDate: string; - airDateUtc: string; - runtime?: number; + id: number; + seriesId: number; + name: string; + aired: string; + runtime: number; + nameTranslations: string[]; overview?: string; - image?: string; + overviewTranslations: string[]; + image: string; + imageType: number; + isMovie: number; + seasons?: string[]; + number: number; + absoluteNumber: number; + seasonNumber: number; + lastUpdated: string; + finaleType?: string; + year: string; +} + +export interface TvdbSeasonDetails extends TvdbBaseResponse { + id: number; + seriesId: number; + type: TvdbType; + number: number; + nameTranslations: string[]; + overviewTranslations: string[]; + image: string; + imageType: number; + companies: { + studio: TvdbCompany[]; + network: TvdbCompany[]; + production: TvdbCompany[]; + distributor: TvdbCompany[]; + special_effects: TvdbCompany[]; + }; + lastUpdated: string; + year: string; + episodes: TvdbEpisode[]; + trailers: string[]; + artwork: TvdbArtwork[]; + tagOptions?: string[]; + firstAired: string; } export interface TvdbEpisodeTranslation diff --git a/server/lib/scanners/baseScanner.ts b/server/lib/scanners/baseScanner.ts index 5a06c330..b78ea811 100644 --- a/server/lib/scanners/baseScanner.ts +++ b/server/lib/scanners/baseScanner.ts @@ -3,7 +3,7 @@ import { MediaStatus, MediaType } from '@server/constants/media'; import { getRepository } from '@server/datasource'; import Media from '@server/entity/Media'; import Season from '@server/entity/Season'; -import { getIndexer, getSettings } from '@server/lib/settings'; +import { getSettings } from '@server/lib/settings'; import logger from '@server/logger'; import AsyncLock from '@server/utils/asyncLock'; import { randomUUID } from 'crypto'; @@ -62,7 +62,6 @@ class BaseScanner { protected sessionId: string; protected running = false; readonly asyncLock = new AsyncLock(); - readonly tvShowIndexer = getIndexer(); readonly tmdb = new TheMovieDb(); protected constructor( diff --git a/server/lib/scanners/jellyfin/index.ts b/server/lib/scanners/jellyfin/index.ts index d535d632..0b79ef84 100644 --- a/server/lib/scanners/jellyfin/index.ts +++ b/server/lib/scanners/jellyfin/index.ts @@ -10,7 +10,7 @@ import Media from '@server/entity/Media'; import Season from '@server/entity/Season'; import { User } from '@server/entity/User'; import type { Library } from '@server/lib/settings'; -import { getIndexer, getSettings } from '@server/lib/settings'; +import { getSettings } from '@server/lib/settings'; import logger from '@server/logger'; import AsyncLock from '@server/utils/asyncLock'; import { getHostname } from '@server/utils/getHostname'; @@ -45,7 +45,6 @@ class JellyfinScanner { constructor({ isRecentOnly }: { isRecentOnly?: boolean } = {}) { this.tmdb = new TheMovieDb(); - this.tvShowIndexer = getIndexer(); this.isRecentOnly = isRecentOnly ?? false; } diff --git a/server/lib/scanners/plex/index.ts b/server/lib/scanners/plex/index.ts index 16cd9522..a4fcfcf5 100644 --- a/server/lib/scanners/plex/index.ts +++ b/server/lib/scanners/plex/index.ts @@ -273,7 +273,7 @@ class PlexScanner await this.processHamaSpecials(metadata, mediaIds.tvdbId); } - const tvShow = await this.tvShowIndexer.getTvShow({ + const tvShow = await this.tmdb.getTvShow({ tvId: mediaIds.tmdbId, }); @@ -431,7 +431,7 @@ class PlexScanner const matchedtvdb = plexitem.guid.match(hamaTvdbRegex); if (matchedtvdb) { - const show = await this.tvShowIndexer.getShowByTvdbId({ + const show = await this.tmdb.getShowByTvdbId({ tvdbId: Number(matchedtvdb[1]), }); @@ -465,7 +465,7 @@ class PlexScanner type: 'tvdb', }); if (extResponse.tv_results[0]) { - tvShow = await this.tvShowIndexer.getTvShow({ + tvShow = await this.tmdb.getTvShow({ tvId: extResponse.tv_results[0].id, }); mediaIds.tvdbId = result.tvdbId; diff --git a/server/lib/scanners/sonarr/index.ts b/server/lib/scanners/sonarr/index.ts index 99982dc2..7a6e95c0 100644 --- a/server/lib/scanners/sonarr/index.ts +++ b/server/lib/scanners/sonarr/index.ts @@ -94,11 +94,11 @@ class SonarrScanner }); if (!media || !media.tmdbId) { - tvShow = await this.tvShowIndexer.getShowByTvdbId({ + tvShow = await this.tmdb.getShowByTvdbId({ tvdbId: sonarrSeries.tvdbId, }); } else { - tvShow = await this.tvShowIndexer.getTvShow({ tvId: media.tmdbId }); + tvShow = await this.tmdb.getTvShow({ tvId: media.tmdbId }); } const tmdbId = tvShow.id; diff --git a/server/lib/settings/index.ts b/server/lib/settings/index.ts index 331462f6..8f8ce2be 100644 --- a/server/lib/settings/index.ts +++ b/server/lib/settings/index.ts @@ -1,6 +1,3 @@ -import type { TvShowIndexer } from '@server/api/indexer'; -import TheMovieDb from '@server/api/themoviedb'; -import Tvdb from '@server/api/tvdb'; import { MediaServerType } from '@server/constants/server'; import { Permission } from '@server/lib/permissions'; import { runMigrations } from '@server/lib/settings/migrator'; @@ -103,6 +100,32 @@ interface Quota { quotaDays?: number; } +export enum IndexerType { + TMDB, + TVDB, +} + +export interface MetadataSettings { + tvShow: IndexerType; + anime: IndexerType; +} + +export interface TvdbSettings { + apiKey?: string; + pin?: string; +} + +export interface ProxySettings { + enabled: boolean; + hostname: string; + port: number; + useSsl: boolean; + user: string; + password: string; + bypassFilter: string; + bypassLocalAddresses: boolean; +} + export interface MainSettings { apiKey: string; applicationTitle: string; @@ -342,7 +365,8 @@ export interface AllSettings { notifications: NotificationSettings; jobs: Record; network: NetworkSettings; - tvdb: boolean; + tvdb: TvdbSettings; + metadataSettings: MetadataSettings; } const SETTINGS_PATH = process.env.CONFIG_DIRECTORY @@ -403,7 +427,14 @@ class Settings { apiKey: '', }, tautulli: {}, - tvdb: false, + metadataSettings: { + tvShow: IndexerType.TMDB, + anime: IndexerType.TVDB, + }, + tvdb: { + apiKey: '', + pin: '', + }, radarr: [], sonarr: [], public: { @@ -598,11 +629,19 @@ class Settings { this.data.tautulli = data; } - get tvdb(): boolean { + get metadataSettings(): MetadataSettings { + return this.data.metadataSettings; + } + + set metadataSettings(data: MetadataSettings) { + this.data.metadataSettings = data; + } + + get tvdb(): TvdbSettings { return this.data.tvdb; } - set tvdb(data: boolean) { + set tvdb(data: TvdbSettings) { this.data.tvdb = data; } @@ -786,13 +825,4 @@ export const getSettings = (initialSettings?: AllSettings): Settings => { return settings; }; -export const getIndexer = (): TvShowIndexer => { - const settings = getSettings(); - if (settings.tvdb) { - return new Tvdb(); - } else { - return new TheMovieDb(); - } -}; - export default Settings; diff --git a/server/routes/settings/index.ts b/server/routes/settings/index.ts index f1c1580f..96e6a7ee 100644 --- a/server/routes/settings/index.ts +++ b/server/routes/settings/index.ts @@ -39,10 +39,10 @@ import { rescheduleJob } from 'node-schedule'; import path from 'path'; import semver from 'semver'; import { URL } from 'url'; +import tvdbRoutes from './metadata'; import notificationRoutes from './notifications'; import radarrRoutes from './radarr'; import sonarrRoutes from './sonarr'; -import tvdbRoutes from './tvdb'; const settingsRoutes = Router(); diff --git a/server/routes/settings/tvdb.ts b/server/routes/settings/metadata.ts similarity index 80% rename from server/routes/settings/tvdb.ts rename to server/routes/settings/metadata.ts index 79c669fd..e93152b8 100644 --- a/server/routes/settings/tvdb.ts +++ b/server/routes/settings/metadata.ts @@ -1,11 +1,11 @@ import Tvdb from '@server/api/tvdb'; -import { getSettings } from '@server/lib/settings'; +import { getSettings, type TvdbSettings } from '@server/lib/settings'; import logger from '@server/logger'; import { Router } from 'express'; const tvdbRoutes = Router(); -export interface TvdbSettings { +export interface MetadataSettings { tvdb: boolean; } @@ -22,7 +22,10 @@ tvdbRoutes.put('/', (req, res) => { const body = req.body as TvdbSettings; - settings.tvdb = body.tvdb ?? settings.tvdb ?? false; + settings.tvdb = { + apiKey: body.apiKey, + pin: body.pin, + }; settings.save(); return res.status(200).json({ @@ -32,7 +35,7 @@ tvdbRoutes.put('/', (req, res) => { tvdbRoutes.post('/test', async (req, res, next) => { try { - const tvdb = new Tvdb(); + const tvdb = await Tvdb.getInstance(); await tvdb.test(); return res.status(200).json({ message: 'Successfully connected to Tvdb' }); } catch (e) { diff --git a/server/routes/tv.ts b/server/routes/tv.ts index 50ddd847..d49a01e0 100644 --- a/server/routes/tv.ts +++ b/server/routes/tv.ts @@ -1,10 +1,12 @@ +import { getMetadataProvider } from '@server/api/metadata'; import RottenTomatoes from '@server/api/rating/rottentomatoes'; import TheMovieDb from '@server/api/themoviedb'; +import { ANIME_KEYWORD_ID } from '@server/api/themoviedb/constants'; +import type { TmdbKeyword } from '@server/api/themoviedb/interfaces'; import { MediaType } from '@server/constants/media'; import { getRepository } from '@server/datasource'; import Media from '@server/entity/Media'; import { Watchlist } from '@server/entity/Watchlist'; -import { getIndexer } from '@server/lib/settings'; import logger from '@server/logger'; import { mapTvResult } from '@server/models/Search'; import { mapSeasonWithEpisodes, mapTvDetails } from '@server/models/Tv'; @@ -13,14 +15,21 @@ import { Router } from 'express'; const tvRoutes = Router(); tvRoutes.get('/:id', async (req, res, next) => { - const indexer = getIndexer(); + const tmdb = new TheMovieDb(); try { + const tmdbTv = await tmdb.getTvShow({ + tvId: Number(req.params.id), + }); + const indexer = tmdbTv.keywords.results.some( + (keyword: TmdbKeyword) => keyword.id === ANIME_KEYWORD_ID + ) + ? await getMetadataProvider('anime') + : await getMetadataProvider('tv'); const tv = await indexer.getTvShow({ tvId: Number(req.params.id), language: (req.query.language as string) ?? req.locale, }); - const media = await Media.getMedia(tv.id, MediaType.TV); const onUserWatchlist = await getRepository(Watchlist).exist({ @@ -58,7 +67,15 @@ tvRoutes.get('/:id', async (req, res, next) => { tvRoutes.get('/:id/season/:seasonNumber', async (req, res, next) => { try { - const indexer = getIndexer(); + const tmdb = new TheMovieDb(); + const tmdbTv = await tmdb.getTvShow({ + tvId: Number(req.params.id), + }); + const indexer = tmdbTv.keywords.results.some( + (keyword: TmdbKeyword) => keyword.id === ANIME_KEYWORD_ID + ) + ? await getMetadataProvider('anime') + : await getMetadataProvider('tv'); const season = await indexer.getTvSeason({ tvId: Number(req.params.id), diff --git a/src/components/MetadataSelector/index.ts b/src/components/MetadataSelector/index.ts new file mode 100644 index 00000000..e69de29b diff --git a/src/components/Settings/SettingsLayout.tsx b/src/components/Settings/SettingsLayout.tsx index 3d5f5e74..a0fabbd1 100644 --- a/src/components/Settings/SettingsLayout.tsx +++ b/src/components/Settings/SettingsLayout.tsx @@ -38,11 +38,6 @@ const SettingsLayout = ({ children }: SettingsLayoutProps) => { route: '/settings/users', regex: /^\/settings\/users/, }, - { - text: 'Tvdb', - route: '/settings/tvdb', - regex: /^\/settings\/tvdb/, - }, settings.currentSettings.mediaServerType === MediaServerType.PLEX ? { text: intl.formatMessage(messages.menuPlexSettings), @@ -64,6 +59,11 @@ const SettingsLayout = ({ children }: SettingsLayoutProps) => { route: '/settings/network', regex: /^\/settings\/network/, }, + { + text: 'Metadata', + route: '/settings/metadata', + regex: /^\/settings\/metadata/, + }, { text: intl.formatMessage(messages.menuNotifications), route: '/settings/notifications/email', diff --git a/src/components/Settings/SettingsTvdb.tsx b/src/components/Settings/SettingsMetadata.tsx similarity index 94% rename from src/components/Settings/SettingsTvdb.tsx rename to src/components/Settings/SettingsMetadata.tsx index 4931d306..55c1f914 100644 --- a/src/components/Settings/SettingsTvdb.tsx +++ b/src/components/Settings/SettingsMetadata.tsx @@ -5,7 +5,7 @@ import SettingsBadge from '@app/components/Settings/SettingsBadge'; import globalMessages from '@app/i18n/globalMessages'; import defineMessages from '@app/utils/defineMessages'; import { ArrowDownOnSquareIcon, BeakerIcon } from '@heroicons/react/24/outline'; -import type { TvdbSettings } from '@server/routes/settings/tvdb'; +import type { MetadataSettings } from '@server/routes/settings/metadata'; import { Field, Form, Formik } from 'formik'; import { useState } from 'react'; import { useIntl } from 'react-intl'; @@ -15,15 +15,13 @@ import useSWR from 'swr'; const messages = defineMessages('components.Settings', { general: 'General', settings: 'Settings', - apikey: 'API Key', - pin: 'PIN', enable: 'Enable', enableTip: 'Enable Tvdb (only for season and episode).' + ' Due to a limitation of the api used, only English is available.', }); -const SettingsTvdb = () => { +const SettingsMetadata = () => { const intl = useIntl(); const [isTesting, setIsTesting] = useState(false); @@ -42,7 +40,7 @@ const SettingsTvdb = () => { } }; - const saveSettings = async (value: TvdbSettings) => { + const saveSettings = async (value: MetadataSettings) => { const response = await fetch('/api/v1/settings/tvdb', { method: 'PUT', headers: { @@ -58,7 +56,7 @@ const SettingsTvdb = () => { } }; - const { data, error } = useSWR('/api/v1/settings/tvdb'); + const { data, error } = useSWR('/api/v1/settings/tvdb'); if (!data && !error) { return ; @@ -73,8 +71,8 @@ const SettingsTvdb = () => { ]} />
-

{'Tvdb'}

-

{'Settings for Tvdb'}

+

{'Metadata'}

+

{'Settings for metadata indexer'}

{ ); }; -export default SettingsTvdb; +export default SettingsMetadata; diff --git a/src/pages/settings/tvdb.tsx b/src/pages/settings/metadata.tsx similarity index 62% rename from src/pages/settings/tvdb.tsx rename to src/pages/settings/metadata.tsx index e4142d40..e248cfab 100644 --- a/src/pages/settings/tvdb.tsx +++ b/src/pages/settings/metadata.tsx @@ -1,16 +1,16 @@ import SettingsLayout from '@app/components/Settings/SettingsLayout'; -import SettingsTvdb from '@app/components/Settings/SettingsTvdb'; +import SettingsMetadata from '@app/components/Settings/SettingsMetadata'; import useRouteGuard from '@app/hooks/useRouteGuard'; import { Permission } from '@app/hooks/useUser'; import type { NextPage } from 'next'; -const TvdbSettingsPage: NextPage = () => { +const MetadataSettingsPage: NextPage = () => { useRouteGuard(Permission.ADMIN); return ( - + ); }; -export default TvdbSettingsPage; +export default MetadataSettingsPage;