From d53a2be0e708d6d48e2b934cc74c258c5a945d3a Mon Sep 17 00:00:00 2001 From: Laurent Schwitter Date: Fri, 26 Jun 2026 17:02:21 +0200 Subject: [PATCH 1/3] updated readme --- README.md | 13 +++++++++---- package.json | 4 ++-- sample.env | 2 +- src/environment.ts | 2 +- src/util/deprecated-warnings.ts | 17 +++++++++++++++++ 5 files changed, 30 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 188c994..db0506a 100644 --- a/README.md +++ b/README.md @@ -86,6 +86,13 @@ It supports the most popular torrenting clients (qBittorrent, deluge and µTorre ## 🧪 Pipeline +> [!IMPORTANT] +> This is currently slightly out of date due to che metadata changes. +> +> I moved to fetching metadata from [one-pace-api](https://github.com/eltharynd/one-pace-api), this means that we get all of the metadata at once and we don't need to check RSS feed anymore. +> +> When I have time to finish [one-pace-api](https://github.com/eltharynd/one-pace-api) and deploy it for the public, I will be able to just connect via WebSocket and get notifications without polling constantly. + The following diagram synthesizes the pipeline: ![pipeline](docs/pipeline.png?cache=2) @@ -253,7 +260,7 @@ services: # Metadata Settings - #- METADATA_URL=https://raw.githubusercontent.com/ladyisatis/one-pace-metadata/refs/heads/v2/metadata/data.json + #- METADATA_URL=https://raw.githubusercontent.com/eltharynd/one-pace-api/refs/heads/main/output/metadata.json #- METADATA_LANGUAGE=en #- METADATA_POSTER_SET=default #- METADATA_CHECK_INTERVAL=3600 @@ -473,7 +480,7 @@ If you're not sure what Mount Path Mappings are you can have a read on [TRaSH Gu | Metadata Variables | Default | Description | | :--- | :--- | :--- | -| `METADATA_URL` | `https://raw.githubusercontent.com/ladyisatis/one-pace-metadata/refs/heads/v2/metadata/data.json` | Metadata url (untested with different ones). | +| `METADATA_URL` | `https://raw.githubusercontent.com/eltharynd/one-pace-api/refs/heads/main/output/metadata.json` | Metadata url (untested with different ones). | | `METADATA_LANGUAGE` | `en` | Currently only language supported. | | `METADATA_POSTER_SET` | `default` | Currently `default` equals `piratezekk`. There are also `official` and `mizzoufan523` available. If a set is missing a poster it uses `default`. | | `METADATA_CHECK_INTERVAL` | 3600 | Seconds between checking for new metadata. | @@ -513,8 +520,6 @@ If you want to contribute to the posters or create an entire new set, first of a This project wouldn't be possible without the incredible work of the community: - **[One Pace](https://onepace.net/en):** The incredible team behind the unofficial fan edits. -- **[Ladyisatis](https://github.com/ladyisatis):** For maintaining the - [one-pace-metadata](https://github.com/ladyisatis/one-pace-metadata) repository. - For the custom poster artwork sets: - `piratezekk` (default) by **[/u/piratezekk](https://reddit.com/user/piratezekk)**. - `mizzoufan523` by **[/u/Mizzoufan523](https://reddit.com/user/Mizzoufan523)**. diff --git a/package.json b/package.json index ff06af3..dbee1fd 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "cors": "^2.8.6", "dotenv": "^17.4.2", "express": "^5.2.1", - "ez-ts-logger": "^1.0.8", + "ez-ts-logger": "^1.0.11", "multer": "^2.2.0", "reflect-metadata": "^0.2.2", "routing-controllers": "^0.11.3", @@ -44,4 +44,4 @@ "typescript": "^6.x", "vitest": "^4.1.9" } -} \ No newline at end of file +} diff --git a/sample.env b/sample.env index 916f939..3c43ede 100644 --- a/sample.env +++ b/sample.env @@ -167,7 +167,7 @@ TORRENT_CHECK_INTERVAL=60 #################################### # Metadata url (untested with different ones). -#METADATA_URL=https://raw.githubusercontent.com/ladyisatis/one-pace-metadata/refs/heads/v2/metadata/data.json +#METADATA_URL=https://raw.githubusercontent.com/eltharynd/one-pace-api/refs/heads/main/output/metadata.json # Currently only language supported. #METADATA_LANGUAGE=en # Currently `default` equals `piratezekk`. There are also `official` and `mizzoufan523` available. If a set is missing a poster it uses `default`. diff --git a/src/environment.ts b/src/environment.ts index ceba0e9..bc614ca 100644 --- a/src/environment.ts +++ b/src/environment.ts @@ -172,7 +172,7 @@ export default { */ METADATA_URL: process.env.METADATA_URL || - `https://raw.githubusercontent.com/ladyisatis/one-pace-metadata/refs/heads/v2/metadata/data.json`, + `https://raw.githubusercontent.com/eltharynd/one-pace-api/refs/heads/main/output/metadata.json`, METADATA_LANGUAGE: process.env.METADATA_LANGUAGE || 'en', METADATA_POSTER_SET: process.env.METADATA_POSTER_SET || 'default', METADATA_CHECK_INTERVAL: diff --git a/src/util/deprecated-warnings.ts b/src/util/deprecated-warnings.ts index d2ee93d..fbaccf2 100644 --- a/src/util/deprecated-warnings.ts +++ b/src/util/deprecated-warnings.ts @@ -47,4 +47,21 @@ export default function deprecatedWarnings() { if (process.env['DEBUGGING']) { Logger.changeConfigs({ LOG_LEVEL: 'debug' }) } + + if ( + process.env['METADATA_URL'] == + 'https://raw.githubusercontent.com/ladyisatis/one-pace-metadata/refs/heads/v2/metadata/data.json' + ) { + const message = `You're using the old url for metadata. That stopped being supported starting with v1.6.0` + + Logger.critical(message) + throw new UnsupportedMetadataError(message) + } +} + +export class UnsupportedMetadataError extends Error { + constructor(message?: string, options?: { cause?: unknown }) { + super(message, options) + this.name = 'UnsupportedMetadataError' + } } From e453a764ba4ba62ef65ffc79d4d070cc9cc3f62e Mon Sep 17 00:00:00 2001 From: Laurent Schwitter Date: Fri, 26 Jun 2026 18:49:40 +0200 Subject: [PATCH 2/3] switched metadata provider --- nodemon.json | 6 - package.json | 4 +- src/library/clients/emby.controller.ts | 5 +- src/library/clients/jellyfin.controller.ts | 5 +- .../clients/local-folder.controller.ts | 10 +- src/library/clients/plex.controller.ts | 11 +- src/metadata/metadata.controller.ts | 300 +++++++----------- src/metadata/metadata.model.ts | 119 +++---- src/pipeline/pipeline.controller.ts | 127 ++++---- src/pipeline/pipeline.model.ts | 4 +- src/torrent/clients/deluge.controller.ts | 8 +- src/torrent/clients/qbittorrent.controller.ts | 6 +- src/torrent/torrent.controller.ts | 12 +- src/torrent/torrent.model.ts | 4 +- 14 files changed, 274 insertions(+), 347 deletions(-) delete mode 100644 nodemon.json diff --git a/nodemon.json b/nodemon.json deleted file mode 100644 index 179818e..0000000 --- a/nodemon.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "enableSourceMaps": true, - "ignore": [ - "rss.json" - ] -} \ No newline at end of file diff --git a/package.json b/package.json index dbee1fd..1970e05 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "scripts": { "build": "tsc", "start": "node --enable-source-maps dist/index.js", - "dev": "nodemon dist/index.js", + "dev": "nodemon --enable-source-maps dist/index.js", "test": "vitest run", "test:dev": "vitest", "test:coverage": "vitest run --coverage" @@ -44,4 +44,4 @@ "typescript": "^6.x", "vitest": "^4.1.9" } -} +} \ No newline at end of file diff --git a/src/library/clients/emby.controller.ts b/src/library/clients/emby.controller.ts index f5ed4a4..4d5d06e 100644 --- a/src/library/clients/emby.controller.ts +++ b/src/library/clients/emby.controller.ts @@ -111,10 +111,7 @@ export class EmbyController implements ILibraryController { episodeDescription?: { title: string; description: string }, ): Promise { if (!episodeDescription) { - episodeDescription = await Context.metadata.getEpisodeDescription( - arc, - episode, - ) + episodeDescription = await Context.metadata.getEpisode(arc, episode) } let embyLibraryPath = await Context.library.getLibraryFolder() diff --git a/src/library/clients/jellyfin.controller.ts b/src/library/clients/jellyfin.controller.ts index b5acb3f..8d049f3 100644 --- a/src/library/clients/jellyfin.controller.ts +++ b/src/library/clients/jellyfin.controller.ts @@ -111,10 +111,7 @@ export class JellyfinController implements ILibraryController { episodeDescription?: { title: string; description: string }, ): Promise { if (!episodeDescription) { - episodeDescription = await Context.metadata.getEpisodeDescription( - arc, - episode, - ) + episodeDescription = await Context.metadata.getEpisode(arc, episode) } let jellyfinLibraryPath = await Context.library.getLibraryFolder() diff --git a/src/library/clients/local-folder.controller.ts b/src/library/clients/local-folder.controller.ts index 282fbe4..6e319b6 100644 --- a/src/library/clients/local-folder.controller.ts +++ b/src/library/clients/local-folder.controller.ts @@ -46,10 +46,7 @@ export class LocalFolderController implements ILibraryController { ) mkdirSync(targetPath, { recursive: true }) - let episodeDescription = await Context.metadata.getEpisodeDescription( - arc, - episode, - ) + let episodeDescription = await Context.metadata.getEpisode(arc, episode) let targetFileName = LibraryController.resolveEpisodeTargetFileName( arc, episode, @@ -93,10 +90,7 @@ export class LocalFolderController implements ILibraryController { episodeDescription?: { title: string; description: string }, ): Promise { if (!episodeDescription) { - episodeDescription = await Context.metadata.getEpisodeDescription( - arc, - episode, - ) + episodeDescription = await Context.metadata.getEpisode(arc, episode) } let targetPath = `${path.resolve( diff --git a/src/library/clients/plex.controller.ts b/src/library/clients/plex.controller.ts index 2fa1c91..b1f609f 100644 --- a/src/library/clients/plex.controller.ts +++ b/src/library/clients/plex.controller.ts @@ -117,10 +117,7 @@ export class PlexController implements ILibraryController { episodeDescription?: { title: string; description: string }, ): Promise { if (!episodeDescription) { - episodeDescription = await Context.metadata.getEpisodeDescription( - arc, - episode, - ) + episodeDescription = await Context.metadata.getEpisode(arc, episode) } let plexLibraryPath = await Context.library.getLibraryFolder() @@ -239,7 +236,7 @@ export class PlexController implements ILibraryController { async updateSeasonMetadata(arc: number) { Logger.debug(`Updating Season ${arc} Metadata in Plex...`) - let description = await Context.metadata.getSeasonDescription(arc) + let description = await Context.metadata.getArc(arc) let season = await this.show.season(arc) @@ -266,10 +263,10 @@ export class PlexController implements ILibraryController { async updateShowMetadata() { Logger.debug(`Updating Show Metadata in Plex...`) - let description = await Context.metadata.getShowDescription() + let show = await Context.metadata.getShow() await this.show.editTitle(environment.LIBRARY_SERIES_NAME) - await this.show.editSummary(description.plot) + await this.show.editSummary(show.description) if (!environment.PIPELINE_SKIP_POSTERS) { Logger.debug(`Updating Show poster in Plex...`) diff --git a/src/metadata/metadata.controller.ts b/src/metadata/metadata.controller.ts index 8c1ff72..60666dd 100644 --- a/src/metadata/metadata.controller.ts +++ b/src/metadata/metadata.controller.ts @@ -7,19 +7,18 @@ import { Filter } from '../util/filters.js' import resolveSeasonPosterFileName from '../util/resolve-season-poster-filename.js' import resolveSeriesRootFolder from '../util/resolve-series-root-folder.js' import { + ArcMetadata, CRCNotInMetadata, - Episode, - EpisodeDescription, - FormattedArc, + EpisodeMetadata, + Metadata, MetadataAbsentError, - RawMetadataJson, } from './metadata.model.js' export class MetadataController { - private metadata: RawMetadataJson + private metadata: Metadata private newMetadata: boolean = false - private monitored: FormattedArc[] + private monitored: ArcMetadata[] private TVShowNFO private seasonNFOs = {} @@ -30,10 +29,8 @@ export class MetadataController { try { let metadata = (await axios.get(environment.METADATA_URL)).data - if ( - !this.metadata || - metadata.status.last_update > this.metadata.status.last_update - ) { + + if (!this.metadata || metadata.lastUpdate > this.metadata.lastUpdate) { Logger.info(`Newer Metadata found!`) this.metadata = metadata this.newMetadata = true @@ -60,81 +57,84 @@ export class MetadataController { } } - getMonitored(): FormattedArc[] { + getMonitored(): ArcMetadata[] { + this.checkMetadataDownloaded() + return this.monitored } getTVShowNFO() { this.checkMetadataDownloaded() + return this.TVShowNFO } getSeasonNFO(arc: number) { this.checkMetadataDownloaded() + return this.seasonNFOs[`${arc}`] } getEpisodeNFO(arc: number, episode: number) { this.checkMetadataDownloaded() + return this.episodesNFOs[`${arc}`][`${episode}`] } - getEpisodeDescription(arc: number, episode: number): EpisodeDescription { + getEpisode(arc: number, episode: number): EpisodeMetadata { this.checkMetadataDownloaded() - return this.metadata.descriptions[environment.METADATA_LANGUAGE].find( - d => d.arc == arc && d.episode == episode, - ) + + return this.metadata.arcs + .find(a => a.arc == arc) + .episodes.find(e => e.episode == episode) } - getSeasonDescription(arc: number) { + getArc(arc: number) { this.checkMetadataDownloaded() - return this.metadata.arcs[environment.METADATA_LANGUAGE].find( - a => a.part === arc, - ) + + return this.metadata.arcs.find(a => a.arc) } - getShowDescription() { + getShow(): Metadata { this.checkMetadataDownloaded() - return this.metadata.tvshow[environment.METADATA_LANGUAGE] + + return this.metadata } findCRC32(arc: number, episode: number): string { this.checkMetadataDownloaded() - let target = this.metadata.arcs[environment.METADATA_LANGUAGE] - .find(a => a.part === arc) - .episodes.find(e => { - return Number.parseInt(e.episode) == episode - }) - - if (arc == 16 && episode == 25) { - if (!environment.PIPELINE_PREFER_G8) { - Logger.debug(`Corrected 16. Skypiea 25 for alternate G-8 cut`) - target.standard = 'C951349C' - } - } - - if (target.standard == '702231E9') { - Logger.debug(`Skypiea 14 manual correction`) - target.standard = '704F68EA' - } + const target = this.metadata.arcs + .find(a => a.arc == arc) + .episodes.find(e => e.episode == episode) - return environment.PIPELINE_PREFER_EXTENDED && !!target.extended - ? target.extended - : target.standard + if (target.files.alternate && environment.PIPELINE_PREFER_G8) { + return target.files.alternate.CRC32 + } else if (target.files.extended && environment.PIPELINE_PREFER_EXTENDED) { + return target.files.extended.CRC32 + } else return target.files.standard.CRC32 } - findEpisode(CRC32: string): Episode { + findEpisodeByCRC32(CRC32: string): EpisodeMetadata | undefined { this.checkMetadataDownloaded() - let episode = - this.metadata.episodes[CRC32 == '704F68EA' ? '702231E9' : CRC32] - if (!episode) { - Logger.debug( - `CRC32 ${CRC32} not in metadata... Probably just an out of date release included in a batch...`, + + let episode: EpisodeMetadata + + for (const arc of this.metadata.arcs) { + let tmp = arc.episodes.find(e => + Object.values(e.files).some( + (variant: any) => + variant.CRC32 === CRC32 || variant.CRC32_inFileName === CRC32, + ), ) - throw new CRCNotInMetadata(`CRC32 ${CRC32} not in metadata...`) + if (tmp) { + episode = tmp + break + } } - return episode + + if (episode) return episode + else throw new CRCNotInMetadata(`CRC32 ${CRC32} not in metadata...`) } getReport() { @@ -148,39 +148,7 @@ export class MetadataController { }, downloaded: false, } - - if (!this.metadata) return base - - return { - ...base, - downloaded: true, - age: new Date(this.metadata?.status.last_update), - - arcs: { - monitored: this.metadata.arcs[environment.METADATA_LANGUAGE].filter( - a => - (a.part != 0 || environment.PIPELINE_INCLUDE_SPECIALS) && - Filter({ arc: a.part }), - ).length, - total: Object.keys(this.metadata.arcs[environment.METADATA_LANGUAGE]) - .length, - }, - episodes: { - monitored: this.metadata.arcs[environment.METADATA_LANGUAGE] - .filter( - a => - (a.part != 0 || environment.PIPELINE_INCLUDE_SPECIALS) && - Filter({ arc: a.part }), - ) - .map(a => { - return a.episodes.filter(e => - Filter({ arc: a.part, episode: e.episode }), - ).length - }) - .reduce((acc, curr) => acc + curr), - total: Object.keys(this.metadata.episodes).length, - }, - } + return base } private async sendToPipeline() { @@ -205,34 +173,19 @@ export class MetadataController { private generateMonitored() { this.checkMetadataDownloaded() - this.monitored = this.metadata.arcs[environment.METADATA_LANGUAGE] + + this.monitored = this.metadata.arcs .filter( a => - (a.part != 0 || environment.PIPELINE_INCLUDE_SPECIALS) && - Filter({ arc: a.part }), + (a.arc != 0 || environment.PIPELINE_INCLUDE_SPECIALS) && + Filter({ arc: a.arc }), ) .map(a => { return { - arc: a.part, - title: a.title, - description: a.description, - episodes: a.episodes - .filter(e => Filter({ arc: a.part, episode: e.episode })) - .map(e => { - const desc = this.getEpisodeDescription( - a.part, - Number.parseInt(e.episode), - ) - return { - episode: Number.parseInt(e.episode), - title: desc?.title, - description: desc?.description, - CRC32: { - standard: e.standard, - extended: e.extended, - }, - } - }), + ...a, + episodes: a.episodes.filter(e => + Filter({ arc: a.arc, episode: e.episode }), + ), } }) } @@ -240,36 +193,32 @@ export class MetadataController { private async generateTVShowNFO() { this.checkMetadataDownloaded() - let tvshow = JSON.parse( - JSON.stringify(this.metadata.tvshow[environment.METADATA_LANGUAGE]), - ) - let namedseason = this.metadata.arcs[environment.METADATA_LANGUAGE].map( - a => { - return { - _attributes: { - number: a.part, - }, - _text: `${a.part > 0 ? `${a.part}. ` : ''}${a.title}`, - } - }, - ) - - delete tvshow.customrating + let namedseason = this.metadata.arcs.map(a => { + return { + _attributes: { + number: a.arc, + }, + _text: `${a.arc > 0 ? `${a.arc}. ` : ''}${a.title}`, + } + }) let path = resolveSeriesRootFolder(await Context.library.getLibraryFolder()) path += path.includes('/') ? '/' : '\\' path += 'poster.png' - tvshow.title = tvshow.title.replace('One Piece', 'One Pace') - tvshow.originaltitle = tvshow.title - tvshow.sorttitle = tvshow.title + const title = this.metadata.title.replace( + 'One Pace', + environment.LIBRARY_SERIES_NAME, + ) + this.TVShowNFO = `\n${js2xml( { tvshow: { - ...tvshow, - outline: tvshow.plot, - customrating: - this.metadata.tvshow[environment.METADATA_LANGUAGE].customrating, + title: title, + originaltitle: title, + sorttitle: title, + outline: this.metadata.description, + customrating: this.metadata.customRating, lockdata: false, namedseason: namedseason, art: { @@ -288,19 +237,15 @@ export class MetadataController { private async generateSeasonNFOs() { this.checkMetadataDownloaded() - let arcs = JSON.parse( - JSON.stringify(this.metadata.arcs[environment.METADATA_LANGUAGE]), - ) - - for (let a of arcs) { + for (let arc of this.metadata.arcs) { let path = resolveSeriesRootFolder( await Context.library.getLibraryFolder(), ) path += path.includes('/') ? '/' : '\\' if (environment.LIBRARY_MEDIA_SERVER === 'none') { - path += resolveSeasonPosterFileName(a.part) + path += resolveSeasonPosterFileName(arc.arc) } else { - path += `Season ${String(a.part).padStart(2, '0')}` + path += `Season ${String(arc.arc).padStart(2, '0')}` path += path.includes('/') ? '/' : '\\' path += `poster.png` } @@ -308,12 +253,12 @@ export class MetadataController { let NFO = `\n${js2xml( { season: { - title: a.part == 0 ? a.title : `${a.part}. ${a.title}`, - sorttitle: a.part == 0 ? a.title : `${a.part}. ${a.title}`, - seasonnumber: a.part, - plot: a.description, - outline: a.description, - overview: a.description, + title: arc.arc == 0 ? arc.title : `${arc.arc}. ${arc.title}`, + sorttitle: arc.arc == 0 ? arc.title : `${arc.arc}. ${arc.title}`, + seasonnumber: arc.arc, + plot: arc.description, + outline: arc.description, + overview: arc.description, customrating: 'TV-14', lockdata: false, art: { @@ -328,53 +273,52 @@ export class MetadataController { }, )}` - this.seasonNFOs[`${a.part}`] = NFO + this.seasonNFOs[`${arc.arc}`] = NFO } } private async generateEpisodeNFOs() { this.checkMetadataDownloaded() - let descriptions = JSON.parse( - JSON.stringify(this.metadata.descriptions[environment.METADATA_LANGUAGE]), - ) this.episodesNFOs = {} - - for (let ed of descriptions) { - let NFO = `\n${js2xml( - { - episodedetails: { - title: ed.title, - ////////////////// - //Implement when Jellyfin updates to allow for multiple versions of an episode - //Not sure if it's gonna be called displayversion, that's the one for movies - //displayversion: Standard/Extended/G-8 - //////////////// - originaltitle: ed.title, - sorttitle: ed.title, - - plot: ed.description, - - showtitle: environment.LIBRARY_SERIES_NAME, - - season: ed.arc, - displayseason: ed.arc, - episode: ed.episode, - displayepisode: ed.episode, - - customrating: 'TV-14', - lockdata: false, + for (let arc of this.metadata.arcs) { + for (let episode of arc.episodes) { + let NFO = `\n${js2xml( + { + episodedetails: { + title: episode.title, + ////////////////// + //Implement when Jellyfin updates to allow for multiple versions of an episode + //Not sure if it's gonna be called displayversion, that's the one for movies + //displayversion: Standard/Extended/G-8 + //////////////// + originaltitle: episode.title, + sorttitle: episode.title, + + plot: episode.description, + + showtitle: environment.LIBRARY_SERIES_NAME, + + season: arc.arc, + displayseason: arc.arc, + episode: episode.episode, + displayepisode: episode.episode, + + customrating: this.metadata.customRating, + lockdata: false, + }, }, - }, - { - compact: true, - ignoreComment: true, - spaces: 2, - }, - )}` + { + compact: true, + ignoreComment: true, + spaces: 2, + }, + )}` - if (!this.episodesNFOs[`${ed.arc}`]) this.episodesNFOs[`${ed.arc}`] = {} - this.episodesNFOs[`${ed.arc}`][`${ed.episode}`] = NFO + if (!this.episodesNFOs[`${arc.arc}`]) + this.episodesNFOs[`${arc.arc}`] = {} + this.episodesNFOs[`${arc.arc}`][`${episode.episode}`] = NFO + } } } diff --git a/src/metadata/metadata.model.ts b/src/metadata/metadata.model.ts index 756c4b3..494232f 100644 --- a/src/metadata/metadata.model.ts +++ b/src/metadata/metadata.model.ts @@ -1,69 +1,82 @@ -export type TorrentInfo = { - magnetURI: string - infoHash: string -} +export type Metadata = { + lastUpdate: string -export type Episode = { - arc: number - episode: number -} -export type EpisodeDescription = { title: string description: string -} -export type RawMetadataJson = { - status: { - last_update: string - last_update_ts: number - } - tvshow: any - arcs: { - [key: string]: { - part: number - saga: string - title: string - description: string - episodes: { - episode: string - standard: string - extended: string - }[] - }[] - } - descriptions: { - [key: string]: { - arc: number - episode: number - title: string - description: string - }[] - } - episodes: { - [key: string]: { - arc: number - episode: number - extended: boolean - } - } - other_edits: any -} + genre: string[] + customRating: string -export type FormattedArc = { + arcs: ArcMetadata[] +} +export type ArcMetadata = { arc: number + + saga: string title: string description: string - episodes: FormattedEpisode[] + + status: 'complete' | 'tbr' | 'wip' + + mangaChapters: string + mangaChaptersCount: number + + animeEpisodes: string + animeEpisodesCount: number + + fillerEpisodes: string + paceEpisodesCount: number + + animeMinutes: number + paceMinutes: number + savedMinutes: number + savedPercentage: number + + audioLanguages: string[] + subLanguages: string[] + subLanguagesPixeldrain: string[] + + resolution: string + + episodes: EpisodeMetadata[] } -export type FormattedEpisode = { +export type EpisodeMetadata = { + arc: number episode: number + title: string description: string - CRC32: { - standard: string - extended: string - } + + audioLanguages: string[] + subLanguages: string[] + subLanguagesPixeldrain: string[] + + mangaChapters: string + animeEpisodes: string + + released: string + + files: EpisodeFilesMetadata +} + +export type EpisodeFilesMetadata = { + standard: FileMetadata + extended?: FileMetadata + alternate?: FileMetadata + + archived?: FileMetadata[] //missing +} +export type FileMetadata = { + CRC32: string + CRC32_inFileName?: string + + hash: string //missing + magnetURI: string //missing + + duration: number + + partOfBundle?: boolean //missing } export class MetadataAbsentError extends Error { diff --git a/src/pipeline/pipeline.controller.ts b/src/pipeline/pipeline.controller.ts index 496ea38..7048e54 100644 --- a/src/pipeline/pipeline.controller.ts +++ b/src/pipeline/pipeline.controller.ts @@ -4,11 +4,7 @@ import { mkdirSync, readdirSync, unlinkSync } from 'node:fs' import path from 'node:path' import environment from '../environment.js' import { TargetLibraryFile } from '../library/library.model' -import { - FormattedArc, - FormattedEpisode, - TorrentInfo, -} from '../metadata/metadata.model.js' +import { ArcMetadata, EpisodeMetadata } from '../metadata/metadata.model.js' import { QueueDownloadResult } from '../torrent/torrent.model.js' import { Context } from '../util/context.js' import getFileCrc32Hash from '../util/crc32.js' @@ -81,7 +77,7 @@ export class PipelineController { } } - addMonitored(monitored: FormattedArc[]) { + addMonitored(monitored: ArcMetadata[]) { if (this.report && this.report.status == 'RUNNING') { throw new PipelineNotDoneError('Pipeline not done') } else if (!this.report || this.report.status != 'PRE') { @@ -157,7 +153,7 @@ export class PipelineController { } } - let formattedFailed: FormattedArc[] = current.monitored.filter(ma => + let formattedFailed: ArcMetadata[] = current.monitored.filter(ma => failedArcs.find(fa => fa.arc == ma.arc), ) for (let a of formattedFailed) { @@ -205,10 +201,7 @@ export class PipelineController { `S${arc}E${String(episode).padStart(2, '0')} - Attempting Metadata Update`, ) - let episodeDescription = await Context.metadata.getEpisodeDescription( - arc, - episode, - ) + let episodeDescription = await Context.metadata.getEpisode(arc, episode) let targetLibraryFile: TargetLibraryFile = await Context.library.getTargetLibraryEpisodeFile( arc, @@ -252,33 +245,16 @@ export class PipelineController { } private async addToDownloadQueue( - arc: FormattedArc, - episode: FormattedEpisode, + episode: EpisodeMetadata, extended?: boolean, ): Promise { - arc.title - - let rsstitle = `${ - arc.title - } ${String(episode.episode).padStart(2, '0')}${extended ? ` Extended Cut` : ''}` - - if (rsstitle.startsWith(`Skypiea 25`)) { - Logger.debug('Manual correction for 16. Skypiea 25 Alternate G-8') - rsstitle = this.config.PIPELINE_PREFER_G8 - ? 'Skypiea 25 Alternate Cut (G-8)' - : 'Skypiea 25' - } - - let torrentInfo: TorrentInfo - try { - torrentInfo = await Context.rss.getTorrentInfo(rsstitle) - } catch (e) { - Logger.debug(`Couldn't find MagnetURI in RSS, refreshing it...`) - await Context.rss.fetch() - torrentInfo = await Context.rss.getTorrentInfo(rsstitle) + if (episode.files.alternate && this.config.PIPELINE_PREFER_G8) { + return await Context.torrent.queueDownload(episode.files.alternate) + } else if (episode.files.extended && extended) { + return await Context.torrent.queueDownload(episode.files.extended) + } else { + return await Context.torrent.queueDownload(episode.files.standard) } - - return await Context.torrent.queueDownload(torrentInfo) } private formatDownloadQueueStatus(result: QueueDownloadResult): string { @@ -304,10 +280,7 @@ export class PipelineController { true, ) - let episodeDescription = await Context.metadata.getEpisodeDescription( - arc, - episode, - ) + let episodeDescription = await Context.metadata.getEpisode(arc, episode) let targetLibraryFile: TargetLibraryFile = await Context.library.getTargetLibraryEpisodeFile( arc, @@ -396,7 +369,7 @@ export class PipelineController { } } - private async process(ma: FormattedArc, me: FormattedEpisode) { + private async process(ma: ArcMetadata, me: EpisodeMetadata) { this.report.processedEpisodes++ Logger.debug( `S${ma.arc}E${String(me.episode).padStart(2, '0')} - Processing`, @@ -404,25 +377,24 @@ export class PipelineController { const skipVerification = this.config.PIPELINE_SKIP_VERIFY_PRESENT_FILES && - !(this.config.PIPELINE_SKIP_VERIFY_NOT_FOR_EXTENDED && me.CRC32.extended) + !(this.config.PIPELINE_SKIP_VERIFY_NOT_FOR_EXTENDED && me.files.extended) - if (me.CRC32.standard == '702231E9') { - Logger.debug(`Skypiea 14 manual correction`) - me.CRC32.standard = '704F68EA' - } + // if (me.CRC32.standard == '702231E9') { + // Logger.debug(`Skypiea 14 manual correction`) + // me.CRC32.standard = '704F68EA' + // } - if (ma.arc == 16 && me.episode == 25) { - if (!this.config.PIPELINE_PREFER_G8) { - Logger.debug(`Corrected 16. Skypiea 25 for alternate G-8 cut`) - me.CRC32.standard = 'C951349C' - } - } + // if (ma.arc == 16 && me.episode == 25) { + // if (!this.config.PIPELINE_PREFER_G8) { + // Logger.debug(`Corrected 16. Skypiea 25 for alternate G-8 cut`) + // me.CRC32.standard = 'C951349C' + // } + // } if (this.config.PIPELINE_FORCE_REDOWNLOAD) { const queueResult = await this.addToDownloadQueue( - ma, me, - !!me.CRC32.extended && this.config.PIPELINE_PREFER_EXTENDED, + !!me.files.extended && this.config.PIPELINE_PREFER_EXTENDED, ) Logger.info( `S${ma.arc}E${String(me.episode).padStart(2, '0')} - Forced re-download from settings [${this.formatDownloadQueueStatus(queueResult)}]`, @@ -461,11 +433,14 @@ export class PipelineController { `S${ma.arc}E${String(me.episode).padStart(2, '0')} - Hash complete (${CRC32})`, ) - if (!!me.CRC32.extended && this.config.PIPELINE_PREFER_EXTENDED) { + if (!!me.files.extended && this.config.PIPELINE_PREFER_EXTENDED) { Logger.debug( `S${ma.arc}E${String(me.episode).padStart(2, '0')} - Extended wanted`, ) - if (CRC32 == me.CRC32.extended) { + if ( + CRC32 == me.files.extended.CRC32 || + me.files.extended.CRC32_inFileName + ) { Logger.debug( `S${ma.arc}E${String(me.episode).padStart(2, '0')} - Extended present`, ) @@ -479,7 +454,10 @@ export class PipelineController { Logger.info( `S${ma.arc}E${String(me.episode).padStart(2, '0')} - Already present`, ) - } else if (CRC32 == me.CRC32.standard) { + } else if ( + CRC32 == me.files.standard.CRC32 || + me.files.standard.CRC32_inFileName + ) { Logger.debug( `S${ma.arc}E${String(me.episode).padStart(2, '0')} - Standard present`, ) @@ -488,20 +466,23 @@ export class PipelineController { `S${ma.arc}E${String(me.episode).padStart(2, '0')} - Standard instead of extended [Download skipped]`, ) } else { - const queueResult = await this.addToDownloadQueue(ma, me, true) + const queueResult = await this.addToDownloadQueue(me, true) Logger.info( `S${ma.arc}E${String(me.episode).padStart(2, '0')} - Standard instead of extended [${this.formatDownloadQueueStatus(queueResult)}]`, ) } } } else if ( - !!me.CRC32.extended && + !!me.files.extended && !this.config.PIPELINE_PREFER_EXTENDED ) { Logger.debug( `S${ma.arc}E${String(me.episode).padStart(2, '0')} - Standard wanted`, ) - if (CRC32 == me.CRC32.standard) { + if ( + CRC32 == me.files.standard.CRC32 || + CRC32 == me.files.standard.CRC32_inFileName + ) { Logger.debug( `S${ma.arc}E${String(me.episode).padStart(2, '0')} - Standard present`, ) @@ -515,7 +496,10 @@ export class PipelineController { Logger.info( `S${ma.arc}E${String(me.episode).padStart(2, '0')} - Already present`, ) - } else if (CRC32 == me.CRC32.extended) { + } else if ( + CRC32 == me.files.extended.CRC32 || + CRC32 == me.files.extended.CRC32_inFileName + ) { Logger.debug( `S${ma.arc}E${String(me.episode).padStart(2, '0')} - Extended present`, ) @@ -524,13 +508,16 @@ export class PipelineController { `S${ma.arc}E${String(me.episode).padStart(2, '0')} - Extended instead of Standard [Download skipped]`, ) } else { - const queueResult = await this.addToDownloadQueue(ma, me) + const queueResult = await this.addToDownloadQueue(me) Logger.info( `S${ma.arc}E${String(me.episode).padStart(2, '0')} - Extended instead of Standard [${this.formatDownloadQueueStatus(queueResult)}]`, ) } } - } else if (CRC32 == me.CRC32.standard) { + } else if ( + CRC32 == me.files.standard.CRC32 || + CRC32 == me.files.standard.CRC32_inFileName + ) { Logger.debug( `S${ma.arc}E${String(me.episode).padStart(2, '0')} - Standard present`, ) @@ -542,7 +529,10 @@ export class PipelineController { Logger.info( `S${ma.arc}E${String(me.episode).padStart(2, '0')} - Already present`, ) - } else if (CRC32 == me.CRC32.extended) { + } else if ( + CRC32 == me.files.extended.CRC32 || + CRC32 == me.files.extended.CRC32_inFileName + ) { Logger.debug( `S${ma.arc}E${String(me.episode).padStart(2, '0')} - Extended present`, ) @@ -551,7 +541,7 @@ export class PipelineController { `S${ma.arc}E${String(me.episode).padStart(2, '0')} - Extended instead of Standard [Download skipped]`, ) } else { - const queueResult = await this.addToDownloadQueue(ma, me) + const queueResult = await this.addToDownloadQueue(me) Logger.info( `S${ma.arc}E${String(me.episode).padStart(2, '0')} - Extended instead of Standard [${this.formatDownloadQueueStatus(queueResult)}]`, ) @@ -563,9 +553,8 @@ export class PipelineController { ) } else { const queueResult = await this.addToDownloadQueue( - ma, me, - this.config.PIPELINE_PREFER_EXTENDED && !!me.CRC32.extended, + this.config.PIPELINE_PREFER_EXTENDED && !!me.files.extended, ) Logger.info( `S${ma.arc}E${String(me.episode).padStart(2, '0')} - CRC32 Mismatch [${this.formatDownloadQueueStatus(queueResult)}]`, @@ -584,10 +573,12 @@ export class PipelineController { ) } else { const queueResult = await this.addToDownloadQueue( - ma, me, - this.config.PIPELINE_PREFER_EXTENDED && !!me.CRC32.extended, + this.config.PIPELINE_PREFER_EXTENDED && !!me.files.extended, ) + if (ma.arc == 16 && me.episode == 14) { + console.log(me) + } Logger.info( `S${ma.arc}E${String(me.episode).padStart(2, '0')} - Missing [${this.formatDownloadQueueStatus(queueResult)}]`, ) diff --git a/src/pipeline/pipeline.model.ts b/src/pipeline/pipeline.model.ts index acf408a..dd17a28 100644 --- a/src/pipeline/pipeline.model.ts +++ b/src/pipeline/pipeline.model.ts @@ -1,4 +1,4 @@ -import { FormattedArc } from '../metadata/metadata.model' +import { ArcMetadata } from '../metadata/metadata.model' export type PipelineControllerConfig = { PIPELINE_SKIP_VERIFY_PRESENT_FILES: boolean @@ -21,7 +21,7 @@ export class PipelineReport { created: Date = new Date() started: Date ended: Date - monitored: FormattedArc[] = [] + monitored: ArcMetadata[] = [] processedEpisodes: number = 0 monitoredEpisodes: number = 0 status: PipelineStatus = 'PRE' diff --git a/src/torrent/clients/deluge.controller.ts b/src/torrent/clients/deluge.controller.ts index 4a52153..fe13c68 100644 --- a/src/torrent/clients/deluge.controller.ts +++ b/src/torrent/clients/deluge.controller.ts @@ -1,7 +1,7 @@ import { Deluge } from '@ctrl/deluge' import { Logger } from 'ez-ts-logger' import environment from '../../environment.js' -import { TorrentInfo } from '../../metadata/metadata.model.js' +import { FileMetadata } from '../../metadata/metadata.model.js' import { ITorrentController, Torrent, @@ -25,11 +25,11 @@ export class DelugeController implements ITorrentController { } public async addTorrent( - torrentInfo: TorrentInfo, + torrentInfo: FileMetadata, category: string, ): Promise { let torrent = (await this.getAllTorrents()).find( - t => t.hash === torrentInfo.infoHash, + t => t.hash === torrentInfo.hash, ) if (torrent) { Logger.debug(`MagnetURI already in qBittorrent...`) @@ -41,7 +41,7 @@ export class DelugeController implements ITorrentController { } } else { let torrent: Partial = { - hash: torrentInfo.infoHash, + hash: torrentInfo.hash, } let added = (await this.client.addTorrentMagnet(torrentInfo.magnetURI)) .result diff --git a/src/torrent/clients/qbittorrent.controller.ts b/src/torrent/clients/qbittorrent.controller.ts index c4efdc2..3d72904 100644 --- a/src/torrent/clients/qbittorrent.controller.ts +++ b/src/torrent/clients/qbittorrent.controller.ts @@ -5,7 +5,7 @@ import { } from '@ctrl/qbittorrent' import { Logger } from 'ez-ts-logger' import environment from '../../environment.js' -import { TorrentInfo } from '../../metadata/metadata.model.js' +import { FileMetadata } from '../../metadata/metadata.model.js' import { ITorrentController, Torrent, @@ -29,11 +29,11 @@ export class qBittorrentController implements ITorrentController { } public async addTorrent( - torrentInfo: TorrentInfo, + torrentInfo: FileMetadata, category: string, ): Promise { let torrent = (await this.getAllTorrents()).find( - t => t.hash === torrentInfo.infoHash, + t => t.hash === torrentInfo.hash, ) if (torrent) { Logger.debug(`MagnetURI already in qBittorrent...`) diff --git a/src/torrent/torrent.controller.ts b/src/torrent/torrent.controller.ts index 93fb4a9..39c04f5 100644 --- a/src/torrent/torrent.controller.ts +++ b/src/torrent/torrent.controller.ts @@ -5,9 +5,9 @@ import environment from '../environment.js' import { TargetLibraryFile } from '../library/library.model.js' import { CRCNotInMetadata, - Episode, + EpisodeMetadata, + FileMetadata, MetadataAbsentError, - TorrentInfo, } from '../metadata/metadata.model.js' import { Context } from '../util/context.js' import { Filter } from '../util/filters.js' @@ -113,7 +113,7 @@ export class TorrentController { } public async queueDownload( - torrentInfo: TorrentInfo, + torrentInfo: FileMetadata, ): Promise { if (environment.PIPELINE_SKIP_DOWNLOADS) { Logger.debug(`Downloads disabled by env vars`) @@ -122,7 +122,7 @@ export class TorrentController { Logger.debug(`Adding magnetURI to ${this.client.torrentClient}...`) let torrents = await this.client.getAllTorrents() - if (torrents.find(t => t.hash === torrentInfo.infoHash)) { + if (torrents.find(t => t.hash === torrentInfo.hash)) { Logger.debug(`Torrent already in ${this.client.torrentClient}...`) return 'already_present' } @@ -222,9 +222,9 @@ export class TorrentController { const CRC32 = match[1].toUpperCase() Logger.debug(`Parsed CRC32: ${CRC32}`) - let episode: Episode + let episode: EpisodeMetadata try { - episode = await Context.metadata.findEpisode(CRC32) + episode = await Context.metadata.findEpisodeByCRC32(CRC32) } catch (e) { if (e instanceof MetadataAbsentError) { throw e diff --git a/src/torrent/torrent.model.ts b/src/torrent/torrent.model.ts index db05022..144edb3 100644 --- a/src/torrent/torrent.model.ts +++ b/src/torrent/torrent.model.ts @@ -1,4 +1,4 @@ -import { TorrentInfo } from '../metadata/metadata.model.js' +import { FileMetadata } from '../metadata/metadata.model' export type TorrentClient = 'qbittorrent' | 'utorrent' | 'deluge' export type QueueDownloadResult = 'added' | 'already_present' | 'skipped' @@ -14,7 +14,7 @@ export type Torrent = { export interface ITorrentController { readonly torrentClient: TorrentClient - addTorrent(torrentInfo: TorrentInfo, category: string): Promise + addTorrent(torrentInfo: FileMetadata, category: string): Promise getAllTorrents(category?: string): Promise getCompletedTorrents(category?: string): Promise updateTorrentCategory(torrent: Torrent, category: string): Promise From 9f9e6cb174b643b9c4231115112be9cd4adbbd65 Mon Sep 17 00:00:00 2001 From: Laurent Schwitter Date: Fri, 26 Jun 2026 18:51:19 +0200 Subject: [PATCH 3/3] done --- .dockerignore | 1 - package.json | 3 +- src/environment.ts | 12 +++-- src/index.ts | 2 - src/rss/rss.controller.ts | 108 -------------------------------------- src/rss/rss.model.ts | 15 ------ src/util/context.ts | 2 - 7 files changed, 8 insertions(+), 135 deletions(-) delete mode 100644 src/rss/rss.controller.ts delete mode 100644 src/rss/rss.model.ts diff --git a/.dockerignore b/.dockerignore index 0710d2f..8ca2150 100644 --- a/.dockerignore +++ b/.dockerignore @@ -10,6 +10,5 @@ node_modules/ package-lock.json generate-poster-previews.js README.md -rss.json sample.env sonar-project.properties \ No newline at end of file diff --git a/package.json b/package.json index 1970e05..c2dcd71 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,6 @@ "multer": "^2.2.0", "reflect-metadata": "^0.2.2", "routing-controllers": "^0.11.3", - "rss-parser": "^3.13.0", "xml-js": "^1.6.11" }, "devDependencies": { @@ -44,4 +43,4 @@ "typescript": "^6.x", "vitest": "^4.1.9" } -} \ No newline at end of file +} diff --git a/src/environment.ts b/src/environment.ts index bc614ca..1152007 100644 --- a/src/environment.ts +++ b/src/environment.ts @@ -52,11 +52,13 @@ export default { process.env.PIPELINE_SKIP_POSTERS || process.env.SKIP_POSTERS || 'false', ), - PIPELINE_INCLUDE_SPECIALS: /true/i.test( - process.env.PIPELINE_INCLUDE_SPECIALS || - process.env.INCLUDE_SPECIALS || - 'false', - ), + // TODO reimplement + // PIPELINE_INCLUDE_SPECIALS: /true/i.test( + // process.env.PIPELINE_INCLUDE_SPECIALS || + // process.env.INCLUDE_SPECIALS || + // 'false', + // ), + PIPELINE_INCLUDE_SPECIALS: false, PIPELINE_PREFER_EXTENDED: /true/i.test( process.env.PIPELINE_PREFER_EXTENDED || process.env.PREFER_EXTENDED || diff --git a/src/index.ts b/src/index.ts index 20ff02c..6270ffb 100644 --- a/src/index.ts +++ b/src/index.ts @@ -7,7 +7,6 @@ import { Express } from './api/express.js' import { LibraryController } from './library/library.controller.js' import { MetadataController } from './metadata/metadata.controller.js' import { PipelineController } from './pipeline/pipeline.controller.js' -import { RSSController } from './rss/rss.controller.js' import { LabelsDisabledInDelugeError } from './torrent/clients/deluge.controller.js' import { TorrentController } from './torrent/torrent.controller.js' import { TorrentConnectionError } from './torrent/torrent.model.js' @@ -67,7 +66,6 @@ const startApp = async () => { PIPELINE_RETRY_INTERVAL: environment.PIPELINE_RETRY_INTERVAL, }) Context.metadata = new MetadataController() - Context.rss = new RSSController() Context.library = new LibraryController() Context.torrent = new TorrentController() diff --git a/src/rss/rss.controller.ts b/src/rss/rss.controller.ts deleted file mode 100644 index ebde834..0000000 --- a/src/rss/rss.controller.ts +++ /dev/null @@ -1,108 +0,0 @@ -import { Logger } from 'ez-ts-logger' -import { readFileSync, writeFileSync } from 'node:fs' -import { stat } from 'node:fs/promises' -import path from 'node:path' -import Parser from 'rss-parser' -import { Feed, Item } from './rss.model.js' - -const RSS_FEED_URL = `https://onepace.net/en/releases/rss.xml` - -export class RSSController { - private readonly parser: Parser = new Parser({ - customFields: { - item: ['torrent:magnetURI', 'torrent:infoHash'], - }, - }) - - private feed: Parser.Output - - constructor() { - stat('./rss.json') - .then(data => { - try { - this.feed = JSON.parse(readFileSync('./rss.json').toString()) - Logger.info('existing rss.json imported') - } catch (e) { - Logger.error('Badly formed rss.json') - } - }) - .catch(e => { - Logger.debug('no rss.json found') - }) - } - - public async fetch() { - Logger.debug(`Fetching OnePace RSS Feed`) - try { - this.feed = await this.parser.parseURL(RSS_FEED_URL) - writeFileSync(path.resolve('./rss.json'), JSON.stringify(this.feed)) - } catch (e) { - Logger.error(`Error while fetching RSS feed...`) - throw e - } - } - - public async getTorrentInfo(title: string): Promise<{ - magnetURI: string - infoHash: string - }> { - if (!this.feed) await this.fetch() - - let rssTitle = title.replace( - 'The Adventures of the Straw Hats', - 'If You Could Go Anywhere... The Adventures of the Straw Hats', - ) - - if (title === 'Skypiea 20') { - Logger.debug(`Manual override for Skzpiea 20 (not in RSS feed)`) - return { - magnetURI: - 'magnet:?xt=urn:btih:f310ad44380a16a0fef792b5738affccbb0fc65c&dn=%5BOne%20Pace%5D%5B290-291%5D%20Skypiea%2020%20%5B1080p%5D%5B481A9A9D%5D.mkv&tr=http%3A%2F%2Fnyaa.tracker.wf%3A7777%2Fannounce&tr=udp%3A%2F%2Fopen.stealth.si%3A80%2Fannounce&tr=udp%3A%2F%2Ftracker.opentrackr.org%3A1337%2Fannounce&tr=udp%3A%2F%2Fexodus.desync.com%3A6969%2Fannounce&tr=udp%3A%2F%2Ftracker.torrent.eu.org%3A451%2Fannounce', - infoHash: 'f310ad44380a16a0fef792b5738affccbb0fc65c', - } - } else if (title.startsWith('Wano')) { - let episode = Number.parseInt(title.replace('Wano ', '')) - if (episode > 4 && episode < 13) { - Logger.debug(`Manual override for Wano 05-12 (Batch Act 1 Download)`) - return { - magnetURI: - 'magnet:?xt=urn:btih:d67ed82392c28cb6c40509383ba70bfb4e6aefdf&dn=%5BOne+Pace%5D%5B909-924%5D+Wano+Act+1&tr=http%3A%2F%2Fnyaa.tracker.wf%3A7777%2Fannounce&tr=udp%3A%2F%2Ftracker.open-internet.nl%3A6969%2Fannounce&tr=udp%3A%2F%2Ftracker.coppersurfer.tk%3A6969%2Fannounce&tr=https%3A%2F%2F1.track.ga%3A443%2Fannounce', - infoHash: 'd67ed82392c28cb6c40509383ba70bfb4e6aefdf', - } - } - } - - let activeItems = this.feed.items.filter(i => { - for (let cat of i.categories) - if (cat._ === 'outdated') { - return false - } - return true - }) - - let item = activeItems.find(i => i.title === rssTitle) - if (item && item['torrent:magnetURI']) { - Logger.debug(`Found magnetURI for '${rssTitle}'...`) - return { - magnetURI: item['torrent:magnetURI'], - infoHash: item['torrent:infoHash'], - } - } else { - Logger.debug( - `Searching magnetURI for '${rssTitle.replace(/\ [0-9]+$/, '')}'...`, - ) - item = activeItems.find( - i => i.title === rssTitle.replace(/\ [0-9]+$/, ''), - ) - if (item && item['torrent:magnetURI']) { - Logger.debug( - `Found magnetURI for '${rssTitle.replace(/\ [0-9]+$/, '')}'...`, - ) - return { - magnetURI: item['torrent:magnetURI'], - infoHash: item['torrent:infoHash'], - } - } else throw new Error('MagnetURI not found...') - } - } -} diff --git a/src/rss/rss.model.ts b/src/rss/rss.model.ts deleted file mode 100644 index fc905fc..0000000 --- a/src/rss/rss.model.ts +++ /dev/null @@ -1,15 +0,0 @@ -export interface Item { - 'torrent:magnetURI'?: string - 'torrent:infoHash'?: string - categories?: Array<{ - _: string - $?: { domain: string } - }> - guid: string - title: string - link: string -} - -export interface Feed { - items: Item[] -} diff --git a/src/util/context.ts b/src/util/context.ts index 2e4969c..4502bac 100644 --- a/src/util/context.ts +++ b/src/util/context.ts @@ -2,7 +2,6 @@ import { Express } from '../api/express.js' import { LibraryController } from '../library/library.controller.js' import { MetadataController } from '../metadata/metadata.controller.js' import { PipelineController } from '../pipeline/pipeline.controller.js' -import { RSSController } from '../rss/rss.controller.js' import { TorrentController } from '../torrent/torrent.controller.js' class ContextContainer { @@ -10,7 +9,6 @@ class ContextContainer { library: LibraryController metadata: MetadataController pipeline: PipelineController - rss: RSSController torrent: TorrentController }