From b95f4333159afe936e59e32e1c2880c3b436b86e Mon Sep 17 00:00:00 2001 From: iPromKnight Date: Wed, 7 Feb 2024 09:48:32 +0000 Subject: [PATCH] interfaces normalized and extracted for services --- .../consumer/src/jobs/process_torrents_job.ts | 12 +-- .../src/lib/helpers/extension_helpers.ts | 66 +++++++++++++ .../src/lib/interfaces/cache_options.ts | 2 +- .../src/lib/interfaces/cache_service.ts | 8 ++ .../src/lib/interfaces/cinemeta_metadata.ts | 32 +++--- .../lib/interfaces/common_video_metadata.ts | 2 +- .../lib/interfaces/ingested_rabbit_message.ts | 6 +- .../lib/interfaces/kitsu_catalog_metadata.ts | 12 +-- .../src/lib/interfaces/kitsu_metadata.ts | 20 ++-- .../src/lib/interfaces/logging_service.ts | 6 ++ .../src/lib/interfaces/metadata_query.ts | 2 +- .../src/lib/interfaces/metadata_response.ts | 6 +- .../src/lib/interfaces/metadata_service.ts | 14 +++ .../interfaces/parse_torrent_title_result.ts | 2 +- .../src/lib/interfaces/parsed_torrent.ts | 8 +- .../interfaces/torrent_download_service.ts | 6 ++ .../lib/interfaces/torrent_entries_service.ts | 18 ++++ .../lib/interfaces/torrent_file_collection.ts | 14 +-- .../lib/interfaces/torrent_file_service.ts | 8 ++ .../interfaces/torrent_processing_service.ts | 5 + .../interfaces/torrent_subtitle_service.ts | 5 + .../src/lib/interfaces/tracker_service.ts | 3 + .../src/lib/services/cache_service.ts | 25 ++--- .../src/lib/services/extension_service.ts | 68 ------------- .../src/lib/services/logging_service.ts | 32 +++--- .../src/lib/services/metadata_service.ts | 47 ++++----- .../lib/services/torrent_download_service.ts | 55 ++++++----- .../lib/services/torrent_entries_service.ts | 29 +++--- .../src/lib/services/torrent_file_service.ts | 99 ++++++++++--------- .../services/torrent_processing_service.ts | 17 ++-- .../lib/services/torrent_subtitle_service.ts | 22 +++-- .../src/lib/services/tracker_service.ts | 15 +-- .../src/repository/database_repository.ts | 12 +-- .../interfaces/content_attributes.ts | 4 +- .../repository/interfaces/file_attributes.ts | 10 +- .../interfaces/ingested_page_attributes.ts | 4 +- .../interfaces/ingested_torrent_attributes.ts | 4 +- .../interfaces/provider_attributes.ts | 4 +- .../interfaces/skip_torrent_attributes.ts | 4 +- .../interfaces/subtitle_attributes.ts | 4 +- .../interfaces/torrent_attributes.ts | 16 +-- .../consumer/src/repository/models/content.ts | 4 +- .../consumer/src/repository/models/file.ts | 6 +- .../src/repository/models/ingestedPage.ts | 4 +- .../src/repository/models/ingestedTorrent.ts | 4 +- .../src/repository/models/provider.ts | 4 +- .../src/repository/models/skipTorrent.ts | 4 +- .../src/repository/models/subtitle.ts | 4 +- .../consumer/src/repository/models/torrent.ts | 4 +- 49 files changed, 424 insertions(+), 338 deletions(-) create mode 100644 src/node/consumer/src/lib/helpers/extension_helpers.ts create mode 100644 src/node/consumer/src/lib/interfaces/logging_service.ts create mode 100644 src/node/consumer/src/lib/interfaces/metadata_service.ts create mode 100644 src/node/consumer/src/lib/interfaces/torrent_download_service.ts create mode 100644 src/node/consumer/src/lib/interfaces/torrent_entries_service.ts create mode 100644 src/node/consumer/src/lib/interfaces/torrent_file_service.ts create mode 100644 src/node/consumer/src/lib/interfaces/torrent_processing_service.ts create mode 100644 src/node/consumer/src/lib/interfaces/torrent_subtitle_service.ts create mode 100644 src/node/consumer/src/lib/interfaces/tracker_service.ts delete mode 100644 src/node/consumer/src/lib/services/extension_service.ts diff --git a/src/node/consumer/src/jobs/process_torrents_job.ts b/src/node/consumer/src/jobs/process_torrents_job.ts index 635df58..93a6f24 100644 --- a/src/node/consumer/src/jobs/process_torrents_job.ts +++ b/src/node/consumer/src/jobs/process_torrents_job.ts @@ -1,6 +1,6 @@ import client, {Channel, Connection, ConsumeMessage, Options} from 'amqplib' -import {IngestedRabbitMessage, IngestedRabbitTorrent} from "../lib/interfaces/ingested_rabbit_message"; -import {IngestedTorrentAttributes} from "../repository/interfaces/ingested_torrent_attributes"; +import {IIngestedRabbitMessage, IIngestedRabbitTorrent} from "../lib/interfaces/ingested_rabbit_message"; +import {IIngestedTorrentAttributes} from "../repository/interfaces/ingested_torrent_attributes"; import {configurationService} from '../lib/services/configuration_service'; import {torrentProcessingService} from '../lib/services/torrent_processing_service'; import {logger} from '../lib/services/logging_service'; @@ -23,13 +23,13 @@ class ProcessTorrentsJob { } } private processMessage = (msg: ConsumeMessage) => { - const ingestedTorrent: IngestedTorrentAttributes = this.getMessageAsJson(msg); + const ingestedTorrent: IIngestedTorrentAttributes = this.getMessageAsJson(msg); return torrentProcessingService.processTorrentRecord(ingestedTorrent); }; - private getMessageAsJson = (msg: ConsumeMessage): IngestedTorrentAttributes => { + private getMessageAsJson = (msg: ConsumeMessage): IIngestedTorrentAttributes => { const content = msg?.content.toString('utf8') ?? "{}"; - const receivedObject: IngestedRabbitMessage = JSON.parse(content); - const receivedTorrent: IngestedRabbitTorrent = receivedObject.message; + const receivedObject: IIngestedRabbitMessage = JSON.parse(content); + const receivedTorrent: IIngestedRabbitTorrent = receivedObject.message; return {...receivedTorrent, info_hash: receivedTorrent.infoHash}; }; private async assertAndConsumeQueue(channel: Channel) { diff --git a/src/node/consumer/src/lib/helpers/extension_helpers.ts b/src/node/consumer/src/lib/helpers/extension_helpers.ts new file mode 100644 index 0000000..b74933b --- /dev/null +++ b/src/node/consumer/src/lib/helpers/extension_helpers.ts @@ -0,0 +1,66 @@ +const VIDEO_EXTENSIONS = [ + "3g2", + "3gp", + "avi", + "flv", + "mkv", + "mk3d", + "mov", + "mp2", + "mp4", + "m4v", + "mpe", + "mpeg", + "mpg", + "mpv", + "webm", + "wmv", + "ogm", + "divx" +]; + +const SUBTITLE_EXTENSIONS = [ + "aqt", + "gsub", + "jss", + "sub", + "ttxt", + "pjs", + "psb", + "rt", + "smi", + "slt", + "ssf", + "srt", + "ssa", + "ass", + "usf", + "idx", + "vtt" +]; + +const DISK_EXTENSIONS = [ + "iso", + "m2ts", + "ts", + "vob" +]; + +export const ExtensionHelpers = { + isVideo(filename: string) { + return this.isExtension(filename, VIDEO_EXTENSIONS); + }, + + isSubtitle(filename: string) { + return this.isExtension(filename, SUBTITLE_EXTENSIONS); + }, + + isDisk(filename: string) { + return this.isExtension(filename, DISK_EXTENSIONS); + }, + + isExtension(filename: string, extensions: string[]) { + const extensionMatch = filename.match(/\.(\w{2,4})$/); + return extensionMatch !== null && extensions.includes(extensionMatch[1].toLowerCase()); + } +} \ No newline at end of file diff --git a/src/node/consumer/src/lib/interfaces/cache_options.ts b/src/node/consumer/src/lib/interfaces/cache_options.ts index f8b25e6..8613c41 100644 --- a/src/node/consumer/src/lib/interfaces/cache_options.ts +++ b/src/node/consumer/src/lib/interfaces/cache_options.ts @@ -1,3 +1,3 @@ -export interface CacheOptions { +export interface ICacheOptions { ttl: number; } \ No newline at end of file diff --git a/src/node/consumer/src/lib/interfaces/cache_service.ts b/src/node/consumer/src/lib/interfaces/cache_service.ts index e69de29..3a94858 100644 --- a/src/node/consumer/src/lib/interfaces/cache_service.ts +++ b/src/node/consumer/src/lib/interfaces/cache_service.ts @@ -0,0 +1,8 @@ +import {CacheMethod} from "../services/cache_service"; + +export interface ICacheService { + cacheWrapImdbId: (key: string, method: CacheMethod) => Promise; + cacheWrapKitsuId: (key: string, method: CacheMethod) => Promise; + cacheWrapMetadata: (id: string, method: CacheMethod) => Promise; + cacheTrackers: (method: CacheMethod) => Promise; +} \ No newline at end of file diff --git a/src/node/consumer/src/lib/interfaces/cinemeta_metadata.ts b/src/node/consumer/src/lib/interfaces/cinemeta_metadata.ts index fc32250..ad3fc1f 100644 --- a/src/node/consumer/src/lib/interfaces/cinemeta_metadata.ts +++ b/src/node/consumer/src/lib/interfaces/cinemeta_metadata.ts @@ -1,12 +1,12 @@ -import {CommonVideoMetadata} from "./common_video_metadata"; +import {ICommonVideoMetadata} from "./common_video_metadata"; -export interface CinemetaJsonResponse { - meta?: CinemetaMetaData; - trailerStreams?: CinemetaTrailerStream[]; - links?: CinemetaLink[]; - behaviorHints?: CinemetaBehaviorHints; +export interface ICinemetaJsonResponse { + meta?: ICinemetaMetaData; + trailerStreams?: ICinemetaTrailerStream[]; + links?: ICinemetaLink[]; + behaviorHints?: ICinemetaBehaviorHints; } -export interface CinemetaMetaData { +export interface ICinemetaMetaData { awards?: string; cast?: string[]; country?: string; @@ -28,16 +28,16 @@ export interface CinemetaMetaData { year?: string; background?: string; logo?: string; - popularities?: CinemetaPopularities; + popularities?: ICinemetaPopularities; moviedb_id?: number; slug?: string; - trailers?: CinemetaTrailer[]; + trailers?: ICinemetaTrailer[]; id?: string; genres?: string[]; releaseInfo?: string; - videos?: CinemetaVideo[]; + videos?: ICinemetaVideo[]; } -export interface CinemetaPopularities { +export interface ICinemetaPopularities { PXS_TEST?: number; PXS?: number; SCM?: number; @@ -49,11 +49,11 @@ export interface CinemetaPopularities { stremio?: number; stremio_lib?: number; } -export interface CinemetaTrailer { +export interface ICinemetaTrailer { source?: string; type?: string; } -export interface CinemetaVideo extends CommonVideoMetadata { +export interface ICinemetaVideo extends ICommonVideoMetadata { name?: string; number?: number; firstAired?: string; @@ -63,16 +63,16 @@ export interface CinemetaVideo extends CommonVideoMetadata { thumbnail?: string; description?: string; } -export interface CinemetaTrailerStream { +export interface ICinemetaTrailerStream { title?: string; ytId?: string; } -export interface CinemetaLink { +export interface ICinemetaLink { name?: string; category?: string; url?: string; } -export interface CinemetaBehaviorHints { +export interface ICinemetaBehaviorHints { defaultVideoId?: null; hasScheduledVideos?: boolean; } \ No newline at end of file diff --git a/src/node/consumer/src/lib/interfaces/common_video_metadata.ts b/src/node/consumer/src/lib/interfaces/common_video_metadata.ts index 16df177..1a0faf7 100644 --- a/src/node/consumer/src/lib/interfaces/common_video_metadata.ts +++ b/src/node/consumer/src/lib/interfaces/common_video_metadata.ts @@ -1,4 +1,4 @@ -export interface CommonVideoMetadata { +export interface ICommonVideoMetadata { season?: number; episode?: number; released?: string; diff --git a/src/node/consumer/src/lib/interfaces/ingested_rabbit_message.ts b/src/node/consumer/src/lib/interfaces/ingested_rabbit_message.ts index 28ef21c..db25a2f 100644 --- a/src/node/consumer/src/lib/interfaces/ingested_rabbit_message.ts +++ b/src/node/consumer/src/lib/interfaces/ingested_rabbit_message.ts @@ -1,4 +1,4 @@ -export interface IngestedRabbitTorrent { +export interface IIngestedRabbitTorrent { name: string; source: string; category: string; @@ -10,6 +10,6 @@ export interface IngestedRabbitTorrent { processed: boolean; } -export interface IngestedRabbitMessage { - message: IngestedRabbitTorrent; +export interface IIngestedRabbitMessage { + message: IIngestedRabbitTorrent; } \ No newline at end of file diff --git a/src/node/consumer/src/lib/interfaces/kitsu_catalog_metadata.ts b/src/node/consumer/src/lib/interfaces/kitsu_catalog_metadata.ts index 42ac1bd..a29f798 100644 --- a/src/node/consumer/src/lib/interfaces/kitsu_catalog_metadata.ts +++ b/src/node/consumer/src/lib/interfaces/kitsu_catalog_metadata.ts @@ -1,10 +1,10 @@ -import {KitsuLink, KitsuTrailer} from "./kitsu_metadata"; +import {IKitsuLink, IKitsuTrailer} from "./kitsu_metadata"; -export interface KitsuCatalogJsonResponse { - metas: KitsuCatalogMetaData[]; +export interface IKitsuCatalogJsonResponse { + metas: IKitsuCatalogMetaData[]; } -export interface KitsuCatalogMetaData { +export interface IKitsuCatalogMetaData { id: string; type: string; animeType: string; @@ -18,6 +18,6 @@ export interface KitsuCatalogMetaData { logo?: string; poster: string; background: string; - trailers: KitsuTrailer[]; - links: KitsuLink[]; + trailers: IKitsuTrailer[]; + links: IKitsuLink[]; } \ No newline at end of file diff --git a/src/node/consumer/src/lib/interfaces/kitsu_metadata.ts b/src/node/consumer/src/lib/interfaces/kitsu_metadata.ts index e789340..ea3f5d5 100644 --- a/src/node/consumer/src/lib/interfaces/kitsu_metadata.ts +++ b/src/node/consumer/src/lib/interfaces/kitsu_metadata.ts @@ -1,10 +1,10 @@ -import {CommonVideoMetadata} from "./common_video_metadata"; +import {ICommonVideoMetadata} from "./common_video_metadata"; -export interface KitsuJsonResponse { +export interface IKitsuJsonResponse { cacheMaxAge?: number; - meta?: KitsuMeta; + meta?: IKitsuMeta; } -export interface KitsuMeta { +export interface IKitsuMeta { aliases?: string[]; animeType?: string; background?: string; @@ -15,7 +15,7 @@ export interface KitsuMeta { imdbRating?: string; imdb_id?: string; kitsu_id?: string; - links?: KitsuLink[]; + links?: IKitsuLink[]; logo?: string; name?: string; poster?: string; @@ -23,23 +23,23 @@ export interface KitsuMeta { runtime?: string; slug?: string; status?: string; - trailers?: KitsuTrailer[]; + trailers?: IKitsuTrailer[]; type?: string; userCount?: number; - videos?: KitsuVideo[]; + videos?: IKitsuVideo[]; year?: string; } -export interface KitsuVideo extends CommonVideoMetadata { +export interface IKitsuVideo extends ICommonVideoMetadata { imdbEpisode?: number; imdbSeason?: number; imdb_id?: string; thumbnail?: string; } -export interface KitsuTrailer { +export interface IKitsuTrailer { source?: string; type?: string; } -export interface KitsuLink { +export interface IKitsuLink { name?: string; category?: string; url?: string; diff --git a/src/node/consumer/src/lib/interfaces/logging_service.ts b/src/node/consumer/src/lib/interfaces/logging_service.ts new file mode 100644 index 0000000..44e7c49 --- /dev/null +++ b/src/node/consumer/src/lib/interfaces/logging_service.ts @@ -0,0 +1,6 @@ +export interface ILoggingService { + info(message: string, ...args: any[]): void; + error(message: string, ...args: any[]): void; + debug(message: string, ...args: any[]): void; + warn(message: string, ...args: any[]): void; +} \ No newline at end of file diff --git a/src/node/consumer/src/lib/interfaces/metadata_query.ts b/src/node/consumer/src/lib/interfaces/metadata_query.ts index 2ae9c4e..3d6932a 100644 --- a/src/node/consumer/src/lib/interfaces/metadata_query.ts +++ b/src/node/consumer/src/lib/interfaces/metadata_query.ts @@ -1,4 +1,4 @@ -export interface MetaDataQuery { +export interface IMetaDataQuery { title?: string type?: string year?: number | string diff --git a/src/node/consumer/src/lib/interfaces/metadata_response.ts b/src/node/consumer/src/lib/interfaces/metadata_response.ts index ce9670e..c3d481f 100644 --- a/src/node/consumer/src/lib/interfaces/metadata_response.ts +++ b/src/node/consumer/src/lib/interfaces/metadata_response.ts @@ -1,6 +1,6 @@ -import {CommonVideoMetadata} from "./common_video_metadata"; +import {ICommonVideoMetadata} from "./common_video_metadata"; -export interface MetadataResponse { +export interface IMetadataResponse { kitsuId?: number; imdbId?: number; type?: string; @@ -9,7 +9,7 @@ export interface MetadataResponse { country?: string; genres?: string[]; status?: string; - videos?: CommonVideoMetadata[]; + videos?: ICommonVideoMetadata[]; episodeCount?: number[]; totalCount?: number; } \ No newline at end of file diff --git a/src/node/consumer/src/lib/interfaces/metadata_service.ts b/src/node/consumer/src/lib/interfaces/metadata_service.ts new file mode 100644 index 0000000..71a1a2a --- /dev/null +++ b/src/node/consumer/src/lib/interfaces/metadata_service.ts @@ -0,0 +1,14 @@ +import {IMetaDataQuery} from "./metadata_query"; +import {IMetadataResponse} from "./metadata_response"; + +export interface IMetadataService { + getKitsuId(info: IMetaDataQuery): Promise; + + getImdbId(info: IMetaDataQuery): Promise; + + getMetadata(query: IMetaDataQuery): Promise; + + isEpisodeImdbId(imdbId: string | undefined): Promise; + + escapeTitle(title: string): string; +} \ No newline at end of file diff --git a/src/node/consumer/src/lib/interfaces/parse_torrent_title_result.ts b/src/node/consumer/src/lib/interfaces/parse_torrent_title_result.ts index 0cc445b..fcb9a61 100644 --- a/src/node/consumer/src/lib/interfaces/parse_torrent_title_result.ts +++ b/src/node/consumer/src/lib/interfaces/parse_torrent_title_result.ts @@ -1,4 +1,4 @@ -export interface ParseTorrentTitleResult { +export interface IParseTorrentTitleResult { title?: string; date?: string; year?: number | string; diff --git a/src/node/consumer/src/lib/interfaces/parsed_torrent.ts b/src/node/consumer/src/lib/interfaces/parsed_torrent.ts index fad6616..8e227ef 100644 --- a/src/node/consumer/src/lib/interfaces/parsed_torrent.ts +++ b/src/node/consumer/src/lib/interfaces/parsed_torrent.ts @@ -1,8 +1,8 @@ -import {ParseTorrentTitleResult} from "./parse_torrent_title_result"; +import {IParseTorrentTitleResult} from "./parse_torrent_title_result"; import {TorrentType} from "../enums/torrent_types"; -import {TorrentFileCollection} from "./torrent_file_collection"; +import {ITorrentFileCollection} from "./torrent_file_collection"; -export interface ParsedTorrent extends ParseTorrentTitleResult { +export interface IParsedTorrent extends IParseTorrentTitleResult { size?: number; isPack?: boolean; imdbId?: string | number; @@ -14,5 +14,5 @@ export interface ParsedTorrent extends ParseTorrentTitleResult { uploadDate?: Date; seeders?: number; torrentId?: string; - fileCollection?: TorrentFileCollection; + fileCollection?: ITorrentFileCollection; } \ No newline at end of file diff --git a/src/node/consumer/src/lib/interfaces/torrent_download_service.ts b/src/node/consumer/src/lib/interfaces/torrent_download_service.ts new file mode 100644 index 0000000..980b354 --- /dev/null +++ b/src/node/consumer/src/lib/interfaces/torrent_download_service.ts @@ -0,0 +1,6 @@ +import {IParsedTorrent} from "./parsed_torrent"; +import {ITorrentFileCollection} from "./torrent_file_collection"; + +export interface ITorrentDownloadService { + getTorrentFiles(torrent: IParsedTorrent, timeout: number): Promise; +} \ No newline at end of file diff --git a/src/node/consumer/src/lib/interfaces/torrent_entries_service.ts b/src/node/consumer/src/lib/interfaces/torrent_entries_service.ts new file mode 100644 index 0000000..4d4696d --- /dev/null +++ b/src/node/consumer/src/lib/interfaces/torrent_entries_service.ts @@ -0,0 +1,18 @@ +import {IParsedTorrent} from "./parsed_torrent"; +import {Torrent} from "../../repository/models/torrent"; +import {ITorrentAttributes} from "../../repository/interfaces/torrent_attributes"; +import {SkipTorrent} from "../../repository/models/skipTorrent"; + +export interface ITorrentEntriesService { + createTorrentEntry(torrent: IParsedTorrent, overwrite): Promise; + + createSkipTorrentEntry(torrent: Torrent): Promise<[SkipTorrent, boolean]>; + + getStoredTorrentEntry(torrent: Torrent): Promise; + + checkAndUpdateTorrent(torrent: IParsedTorrent): Promise; + + createTorrentContents(torrent: Torrent): Promise; + + updateTorrentSeeders(torrent: ITorrentAttributes): Promise<[number] | ITorrentAttributes>; +} \ No newline at end of file diff --git a/src/node/consumer/src/lib/interfaces/torrent_file_collection.ts b/src/node/consumer/src/lib/interfaces/torrent_file_collection.ts index 1e00090..5b9f824 100644 --- a/src/node/consumer/src/lib/interfaces/torrent_file_collection.ts +++ b/src/node/consumer/src/lib/interfaces/torrent_file_collection.ts @@ -1,9 +1,9 @@ -import {ContentAttributes} from "../../repository/interfaces/content_attributes"; -import {FileAttributes} from "../../repository/interfaces/file_attributes"; -import {SubtitleAttributes} from "../../repository/interfaces/subtitle_attributes"; +import {IContentAttributes} from "../../repository/interfaces/content_attributes"; +import {IFileAttributes} from "../../repository/interfaces/file_attributes"; +import {ISubtitleAttributes} from "../../repository/interfaces/subtitle_attributes"; -export interface TorrentFileCollection { - contents?: ContentAttributes[]; - videos?: FileAttributes[]; - subtitles?: SubtitleAttributes[]; +export interface ITorrentFileCollection { + contents?: IContentAttributes[]; + videos?: IFileAttributes[]; + subtitles?: ISubtitleAttributes[]; } \ No newline at end of file diff --git a/src/node/consumer/src/lib/interfaces/torrent_file_service.ts b/src/node/consumer/src/lib/interfaces/torrent_file_service.ts new file mode 100644 index 0000000..f34218c --- /dev/null +++ b/src/node/consumer/src/lib/interfaces/torrent_file_service.ts @@ -0,0 +1,8 @@ +import {IParsedTorrent} from "./parsed_torrent"; +import {ITorrentFileCollection} from "./torrent_file_collection"; + +export interface ITorrentFileService { + parseTorrentFiles(torrent: IParsedTorrent): Promise; + + isPackTorrent(torrent: IParsedTorrent): boolean; +} \ No newline at end of file diff --git a/src/node/consumer/src/lib/interfaces/torrent_processing_service.ts b/src/node/consumer/src/lib/interfaces/torrent_processing_service.ts new file mode 100644 index 0000000..f4a1db7 --- /dev/null +++ b/src/node/consumer/src/lib/interfaces/torrent_processing_service.ts @@ -0,0 +1,5 @@ +import {IIngestedTorrentAttributes} from "../../repository/interfaces/ingested_torrent_attributes"; + +export interface ITorrentProcessingService { + processTorrentRecord(torrent: IIngestedTorrentAttributes): Promise; +} \ No newline at end of file diff --git a/src/node/consumer/src/lib/interfaces/torrent_subtitle_service.ts b/src/node/consumer/src/lib/interfaces/torrent_subtitle_service.ts new file mode 100644 index 0000000..66648b5 --- /dev/null +++ b/src/node/consumer/src/lib/interfaces/torrent_subtitle_service.ts @@ -0,0 +1,5 @@ +import {ITorrentFileCollection} from "./torrent_file_collection"; + +export interface ITorrentSubtitleService { + assignSubtitles(fileCollection: ITorrentFileCollection): ITorrentFileCollection; +} \ No newline at end of file diff --git a/src/node/consumer/src/lib/interfaces/tracker_service.ts b/src/node/consumer/src/lib/interfaces/tracker_service.ts new file mode 100644 index 0000000..cc15453 --- /dev/null +++ b/src/node/consumer/src/lib/interfaces/tracker_service.ts @@ -0,0 +1,3 @@ +export interface ITrackerService { + getTrackers(): Promise; +} \ No newline at end of file diff --git a/src/node/consumer/src/lib/services/cache_service.ts b/src/node/consumer/src/lib/services/cache_service.ts index 54117e4..144000e 100644 --- a/src/node/consumer/src/lib/services/cache_service.ts +++ b/src/node/consumer/src/lib/services/cache_service.ts @@ -1,9 +1,10 @@ import {Cache, createCache, memoryStore} from 'cache-manager'; -import { mongoDbStore } from '@tirke/node-cache-manager-mongodb' -import { configurationService } from './configuration_service'; -import { logger } from './logging_service'; -import { CacheType } from "../enums/cache_types"; -import {CacheOptions} from "../interfaces/cache_options"; +import {mongoDbStore} from '@tirke/node-cache-manager-mongodb' +import {configurationService} from './configuration_service'; +import {logger} from './logging_service'; +import {CacheType} from "../enums/cache_types"; +import {ICacheOptions} from "../interfaces/cache_options"; +import {ICacheService} from "../interfaces/cache_service"; const GLOBAL_KEY_PREFIX = 'knightcrawler-consumer'; const IMDB_ID_PREFIX = `${GLOBAL_KEY_PREFIX}|imdb_id`; @@ -17,7 +18,7 @@ const TRACKERS_TTL: number = 2 * 24 * 60 * 60; // 2 days export type CacheMethod = () => any; -class CacheService { +class CacheService implements ICacheService { constructor() { if (!configurationService.cacheConfig.NO_CACHE) { logger.info('Cache is disabled'); @@ -27,18 +28,18 @@ class CacheService { this.memoryCache = this.initiateMemoryCache(); this.remoteCache = this.initiateRemoteCache(); } - + public cacheWrapImdbId = (key: string, method: CacheMethod): Promise => - this.cacheWrap(CacheType.MongoDb, `${IMDB_ID_PREFIX}:${key}`, method, { ttl: GLOBAL_TTL }); + this.cacheWrap(CacheType.MongoDb, `${IMDB_ID_PREFIX}:${key}`, method, {ttl: GLOBAL_TTL}); public cacheWrapKitsuId = (key: string, method: CacheMethod): Promise => - this.cacheWrap(CacheType.MongoDb, `${KITSU_ID_PREFIX}:${key}`, method, { ttl: GLOBAL_TTL }); + this.cacheWrap(CacheType.MongoDb, `${KITSU_ID_PREFIX}:${key}`, method, {ttl: GLOBAL_TTL}); public cacheWrapMetadata = (id: string, method: CacheMethod): Promise => - this.cacheWrap(CacheType.Memory, `${METADATA_PREFIX}:${id}`, method, { ttl: MEMORY_TTL }); + this.cacheWrap(CacheType.Memory, `${METADATA_PREFIX}:${id}`, method, {ttl: MEMORY_TTL}); public cacheTrackers = (method: CacheMethod): Promise => - this.cacheWrap(CacheType.Memory, `${TRACKERS_KEY_PREFIX}`, method, { ttl: TRACKERS_TTL }); + this.cacheWrap(CacheType.Memory, `${TRACKERS_KEY_PREFIX}`, method, {ttl: TRACKERS_TTL}); private initiateMemoryCache = () => createCache(memoryStore(), { @@ -85,7 +86,7 @@ class CacheService { private readonly remoteCache: Cache; private cacheWrap = async ( - cacheType: CacheType, key: string, method: CacheMethod, options: CacheOptions): Promise => { + cacheType: CacheType, key: string, method: CacheMethod, options: ICacheOptions): Promise => { const cache = this.getCacheType(cacheType); if (configurationService.cacheConfig.NO_CACHE || !cache) { diff --git a/src/node/consumer/src/lib/services/extension_service.ts b/src/node/consumer/src/lib/services/extension_service.ts deleted file mode 100644 index f80db1e..0000000 --- a/src/node/consumer/src/lib/services/extension_service.ts +++ /dev/null @@ -1,68 +0,0 @@ -class ExtensionService { - private readonly VIDEO_EXTENSIONS: string[] = [ - "3g2", - "3gp", - "avi", - "flv", - "mkv", - "mk3d", - "mov", - "mp2", - "mp4", - "m4v", - "mpe", - "mpeg", - "mpg", - "mpv", - "webm", - "wmv", - "ogm", - "divx" - ]; - - private readonly SUBTITLE_EXTENSIONS: string[] = [ - "aqt", - "gsub", - "jss", - "sub", - "ttxt", - "pjs", - "psb", - "rt", - "smi", - "slt", - "ssf", - "srt", - "ssa", - "ass", - "usf", - "idx", - "vtt" - ]; - - private readonly DISK_EXTENSIONS: string[] = [ - "iso", - "m2ts", - "ts", - "vob" - ] - - public isVideo(filename: string): boolean { - return this.isExtension(filename, this.VIDEO_EXTENSIONS); - } - - public isSubtitle(filename: string): boolean { - return this.isExtension(filename, this.SUBTITLE_EXTENSIONS); - } - - public isDisk(filename: string): boolean { - return this.isExtension(filename, this.DISK_EXTENSIONS); - } - - public isExtension(filename: string, extensions: string[]): boolean { - const extensionMatch = filename.match(/\.(\w{2,4})$/); - return extensionMatch !== null && extensions.includes(extensionMatch[1].toLowerCase()); - } -} - -export const extensionService = new ExtensionService(); \ No newline at end of file diff --git a/src/node/consumer/src/lib/services/logging_service.ts b/src/node/consumer/src/lib/services/logging_service.ts index ac23ed8..2d67911 100644 --- a/src/node/consumer/src/lib/services/logging_service.ts +++ b/src/node/consumer/src/lib/services/logging_service.ts @@ -1,26 +1,30 @@ import {Logger, pino} from "pino"; +import {ILoggingService} from "../interfaces/logging_service"; + +class LoggingService implements ILoggingService { + private readonly logger: Logger; + + constructor() { + this.logger = pino({ + level: process.env.LOG_LEVEL || 'info' + }); + } -class LoggingService { - public readonly logger: Logger = pino({ - level: process.env.LOG_LEVEL || 'info' - }); - public info(message: string, ...args: any[]): void { - this.logger.info(message); + this.logger.info(message, args); } - + public error(message: string, ...args: any[]): void { - this.logger.error(message); + this.logger.error(message, args); } - + public debug(message: string, ...args: any[]): void { - this.logger.debug(message); + this.logger.debug(message, args); } - + public warn(message: string, ...args: any[]): void { - this.logger.warn(message); + this.logger.warn(message, args); } } -export const logger = new LoggingService(); - +export const logger = new LoggingService(); \ No newline at end of file diff --git a/src/node/consumer/src/lib/services/metadata_service.ts b/src/node/consumer/src/lib/services/metadata_service.ts index 3a14b23..3b52d4f 100644 --- a/src/node/consumer/src/lib/services/metadata_service.ts +++ b/src/node/consumer/src/lib/services/metadata_service.ts @@ -1,21 +1,22 @@ import axios, {AxiosResponse} from 'axios'; -import {search, ResultTypes} from 'google-sr'; +import {ResultTypes, search} from 'google-sr'; import nameToImdb from 'name-to-imdb'; -import { cacheService } from './cache_service'; -import { TorrentType } from '../enums/torrent_types'; -import {MetadataResponse} from "../interfaces/metadata_response"; -import {CinemetaJsonResponse} from "../interfaces/cinemeta_metadata"; -import {CommonVideoMetadata} from "../interfaces/common_video_metadata"; -import {KitsuJsonResponse} from "../interfaces/kitsu_metadata"; -import {MetaDataQuery} from "../interfaces/metadata_query"; -import {KitsuCatalogJsonResponse} from "../interfaces/kitsu_catalog_metadata"; +import {cacheService} from './cache_service'; +import {TorrentType} from '../enums/torrent_types'; +import {IMetadataResponse} from "../interfaces/metadata_response"; +import {ICinemetaJsonResponse} from "../interfaces/cinemeta_metadata"; +import {ICommonVideoMetadata} from "../interfaces/common_video_metadata"; +import {IKitsuJsonResponse} from "../interfaces/kitsu_metadata"; +import {IMetaDataQuery} from "../interfaces/metadata_query"; +import {IKitsuCatalogJsonResponse} from "../interfaces/kitsu_catalog_metadata"; +import {IMetadataService} from "../interfaces/metadata_service"; const CINEMETA_URL = 'https://v3-cinemeta.strem.io'; const KITSU_URL = 'https://anime-kitsu.strem.fun'; const TIMEOUT = 20000; -class MetadataService { - public async getKitsuId(info: MetaDataQuery): Promise { +class MetadataService implements IMetadataService { + public async getKitsuId(info: IMetaDataQuery): Promise { const title = this.escapeTitle(info.title.replace(/\s\|\s.*/, '')); const year = info.year ? ` ${info.year}` : ''; const season = info.season > 1 ? ` S${info.season}` : ''; @@ -23,9 +24,9 @@ class MetadataService { const query = encodeURIComponent(key); return cacheService.cacheWrapKitsuId(key, - () => axios.get(`${KITSU_URL}/catalog/series/kitsu-anime-list/search=${query}.json`, { timeout: 60000 }) + () => axios.get(`${KITSU_URL}/catalog/series/kitsu-anime-list/search=${query}.json`, {timeout: 60000}) .then((response) => { - const body = response.data as KitsuCatalogJsonResponse; + const body = response.data as IKitsuCatalogJsonResponse; if (body && body.metas && body.metas.length) { return body.metas[0].id.replace('kitsu:', ''); } else { @@ -34,7 +35,7 @@ class MetadataService { })); } - public async getImdbId(info: MetaDataQuery): Promise { + public async getImdbId(info: IMetaDataQuery): Promise { const name = this.escapeTitle(info.title); const year = info.year || (info.date && info.date.slice(0, 4)); const key = `${name}_${year || 'NA'}_${info.type}`; @@ -53,7 +54,7 @@ class MetadataService { } } - public getMetadata(query: MetaDataQuery): Promise { + public getMetadata(query: IMetaDataQuery): Promise { if (!query.id) { return Promise.reject("no valid id provided"); } @@ -93,14 +94,14 @@ class MetadataService { .trim(); } - private async requestMetadata(url: string): Promise { + private async requestMetadata(url: string): Promise { let response: AxiosResponse = await axios.get(url, {timeout: TIMEOUT}); - let result: MetadataResponse; + let result: IMetadataResponse; const body = response.data; if ('kitsu_id' in body.meta) { - result = this.handleKitsuResponse(body as KitsuJsonResponse); + result = this.handleKitsuResponse(body as IKitsuJsonResponse); } else if ('imdb_id' in body.meta) { - result = this.handleCinemetaResponse(body as CinemetaJsonResponse); + result = this.handleCinemetaResponse(body as ICinemetaJsonResponse); } else { throw new Error('No valid metadata'); } @@ -108,7 +109,7 @@ class MetadataService { return result; } - private handleCinemetaResponse(body: CinemetaJsonResponse): MetadataResponse { + private handleCinemetaResponse(body: ICinemetaJsonResponse): IMetadataResponse { return { imdbId: parseInt(body.meta.imdb_id), type: body.meta.type, @@ -137,7 +138,7 @@ class MetadataService { }; } - private handleKitsuResponse(body: KitsuJsonResponse): MetadataResponse { + private handleKitsuResponse(body: IKitsuJsonResponse): IMetadataResponse { return { kitsuId: parseInt(body.meta.kitsu_id), type: body.meta.type, @@ -167,7 +168,7 @@ class MetadataService { }; } - private getEpisodeCount(videos: CommonVideoMetadata[]) { + private getEpisodeCount(videos: ICommonVideoMetadata[]) { return Object.values( videos .filter(entry => entry.season !== 0 && entry.episode !== 0) @@ -179,7 +180,7 @@ class MetadataService { ); } - private getIMDbIdFromNameToImdb(name: string, info: MetaDataQuery): Promise { + private getIMDbIdFromNameToImdb(name: string, info: IMetaDataQuery): Promise { const year = info.year; const type = info.type; return new Promise((resolve, reject) => { diff --git a/src/node/consumer/src/lib/services/torrent_download_service.ts b/src/node/consumer/src/lib/services/torrent_download_service.ts index dae27e1..dffcce2 100644 --- a/src/node/consumer/src/lib/services/torrent_download_service.ts +++ b/src/node/consumer/src/lib/services/torrent_download_service.ts @@ -1,22 +1,23 @@ import {encode} from 'magnet-uri'; import torrentStream from 'torrent-stream'; import {configurationService} from './configuration_service'; -import {extensionService} from './extension_service'; -import {TorrentFileCollection} from "../interfaces/torrent_file_collection"; -import {ParsedTorrent} from "../interfaces/parsed_torrent"; -import {FileAttributes} from "../../repository/interfaces/file_attributes"; -import {SubtitleAttributes} from "../../repository/interfaces/subtitle_attributes"; -import {ContentAttributes} from "../../repository/interfaces/content_attributes"; +import {ExtensionHelpers} from '../helpers/extension_helpers'; +import {ITorrentFileCollection} from "../interfaces/torrent_file_collection"; +import {IParsedTorrent} from "../interfaces/parsed_torrent"; +import {IFileAttributes} from "../../repository/interfaces/file_attributes"; +import {ISubtitleAttributes} from "../../repository/interfaces/subtitle_attributes"; +import {IContentAttributes} from "../../repository/interfaces/content_attributes"; import {parse} from "parse-torrent-title"; +import {ITorrentDownloadService} from "../interfaces/torrent_download_service"; -interface TorrentFile { +interface ITorrentFile { name: string; path: string; length: number; fileIndex: number; } -class TorrentDownloadService { +class TorrentDownloadService implements ITorrentDownloadService { private engineOptions: TorrentStream.TorrentEngineOptions = { connections: configurationService.torrentConfig.MAX_CONNECTIONS_PER_TORRENT, uploads: 0, @@ -25,8 +26,8 @@ class TorrentDownloadService { tracker: true, }; - public async getTorrentFiles(torrent: ParsedTorrent, timeout: number = 30000): Promise { - const torrentFiles: TorrentFile[] = await this.filesFromTorrentStream(torrent, timeout); + public async getTorrentFiles(torrent: IParsedTorrent, timeout: number = 30000): Promise { + const torrentFiles: ITorrentFile[] = await this.filesFromTorrentStream(torrent, timeout); const videos = this.filterVideos(torrent, torrentFiles); const subtitles = this.filterSubtitles(torrent, torrentFiles); @@ -39,7 +40,7 @@ class TorrentDownloadService { }; } - private async filesFromTorrentStream(torrent: ParsedTorrent, timeout: number): Promise { + private async filesFromTorrentStream(torrent: IParsedTorrent, timeout: number): Promise { if (!torrent.infoHash) { return Promise.reject(new Error("No infoHash...")); } @@ -57,7 +58,7 @@ class TorrentDownloadService { engine = torrentStream(magnet, this.engineOptions); engine.on("ready", () => { - const files: TorrentFile[] = engine.files.map((file, fileId) => ({ + const files: ITorrentFile[] = engine.files.map((file, fileId) => ({ ...file, fileIndex: fileId, size: file.length, @@ -73,22 +74,22 @@ class TorrentDownloadService { }); } - private filterVideos(torrent: ParsedTorrent, torrentFiles: TorrentFile[]): FileAttributes[] { + private filterVideos(torrent: IParsedTorrent, torrentFiles: ITorrentFile[]): IFileAttributes[] { if (torrentFiles.length === 1 && !Number.isInteger(torrentFiles[0].fileIndex)) { return [this.mapTorrentFileToFileAttributes(torrent, torrentFiles[0])]; } - const videos = torrentFiles.filter(file => extensionService.isVideo(file.path || '')); - const maxSize = Math.max(...videos.map((video: TorrentFile) => video.length)); + const videos = torrentFiles.filter(file => ExtensionHelpers.isVideo(file.path || '')); + const maxSize = Math.max(...videos.map((video: ITorrentFile) => video.length)); const minSampleRatio = videos.length <= 3 ? 3 : 10; const minAnimeExtraRatio = 5; const minRedundantRatio = videos.length <= 3 ? 30 : Number.MAX_VALUE; - const isSample = (video: TorrentFile) => video.path?.match(/sample|bonus|promo/i) && maxSize / parseInt(video.path.toString()) > minSampleRatio; - const isRedundant = (video: TorrentFile) => maxSize / parseInt(video.path.toString()) > minRedundantRatio; - const isExtra = (video: TorrentFile) => video.path?.match(/extras?\//i); - const isAnimeExtra = (video: TorrentFile) => video.path?.match(/(?:\b|_)(?:NC)?(?:ED|OP|PV)(?:v?\d\d?)?(?:\b|_)/i) + const isSample = (video: ITorrentFile) => video.path?.match(/sample|bonus|promo/i) && maxSize / parseInt(video.path.toString()) > minSampleRatio; + const isRedundant = (video: ITorrentFile) => maxSize / parseInt(video.path.toString()) > minRedundantRatio; + const isExtra = (video: ITorrentFile) => video.path?.match(/extras?\//i); + const isAnimeExtra = (video: ITorrentFile) => video.path?.match(/(?:\b|_)(?:NC)?(?:ED|OP|PV)(?:v?\d\d?)?(?:\b|_)/i) && maxSize / parseInt(video.length.toString()) > minAnimeExtraRatio; - const isWatermark = (video: TorrentFile) => video.path?.match(/^[A-Z-]+(?:\.[A-Z]+)?\.\w{3,4}$/) + const isWatermark = (video: ITorrentFile) => video.path?.match(/^[A-Z-]+(?:\.[A-Z]+)?\.\w{3,4}$/) && maxSize / parseInt(video.length.toString()) > minAnimeExtraRatio return videos @@ -100,17 +101,17 @@ class TorrentDownloadService { .map(video => this.mapTorrentFileToFileAttributes(torrent, video)); } - private filterSubtitles(torrent: ParsedTorrent, torrentFiles: TorrentFile[]): SubtitleAttributes[] { - return torrentFiles.filter(file => extensionService.isSubtitle(file.name || '')) + private filterSubtitles(torrent: IParsedTorrent, torrentFiles: ITorrentFile[]): ISubtitleAttributes[] { + return torrentFiles.filter(file => ExtensionHelpers.isSubtitle(file.name || '')) .map(file => this.mapTorrentFileToSubtitleAttributes(torrent, file)); } - private createContent(torrent: ParsedTorrent, torrentFiles: TorrentFile[]): ContentAttributes[] { + private createContent(torrent: IParsedTorrent, torrentFiles: ITorrentFile[]): IContentAttributes[] { return torrentFiles.map(file => this.mapTorrentFileToContentAttributes(torrent, file)); } - private mapTorrentFileToFileAttributes(torrent: ParsedTorrent, file: TorrentFile): FileAttributes { - const videoFile: FileAttributes = { + private mapTorrentFileToFileAttributes(torrent: IParsedTorrent, file: ITorrentFile): IFileAttributes { + const videoFile: IFileAttributes = { title: file.name, size: file.length, fileIndex: file.fileIndex || 0, @@ -125,7 +126,7 @@ class TorrentDownloadService { return {...videoFile, ...parse(file.name)}; } - private mapTorrentFileToSubtitleAttributes(torrent: ParsedTorrent, file: TorrentFile): SubtitleAttributes { + private mapTorrentFileToSubtitleAttributes(torrent: IParsedTorrent, file: ITorrentFile): ISubtitleAttributes { return { title: file.name, infoHash: torrent.infoHash, @@ -135,7 +136,7 @@ class TorrentDownloadService { }; } - private mapTorrentFileToContentAttributes(torrent: ParsedTorrent, file: TorrentFile): ContentAttributes { + private mapTorrentFileToContentAttributes(torrent: IParsedTorrent, file: ITorrentFile): IContentAttributes { return { infoHash: torrent.infoHash, fileIndex: file.fileIndex, diff --git a/src/node/consumer/src/lib/services/torrent_entries_service.ts b/src/node/consumer/src/lib/services/torrent_entries_service.ts index c4b9fcc..32d5360 100644 --- a/src/node/consumer/src/lib/services/torrent_entries_service.ts +++ b/src/node/consumer/src/lib/services/torrent_entries_service.ts @@ -1,20 +1,21 @@ import {parse} from 'parse-torrent-title'; -import {ParsedTorrent} from "../interfaces/parsed_torrent"; +import {IParsedTorrent} from "../interfaces/parsed_torrent"; import {repository} from '../../repository/database_repository'; import {TorrentType} from '../enums/torrent_types'; -import {TorrentFileCollection} from "../interfaces/torrent_file_collection"; +import {ITorrentFileCollection} from "../interfaces/torrent_file_collection"; import {Torrent} from "../../repository/models/torrent"; import {PromiseHelpers} from '../helpers/promises_helpers'; import {logger} from './logging_service'; import {metadataService} from './metadata_service'; import {torrentFileService} from './torrent_file_service'; import {torrentSubtitleService} from './torrent_subtitle_service'; -import {TorrentAttributes} from "../../repository/interfaces/torrent_attributes"; +import {ITorrentAttributes} from "../../repository/interfaces/torrent_attributes"; import {File} from "../../repository/models/file"; import {Subtitle} from "../../repository/models/subtitle"; +import {ITorrentEntriesService} from "../interfaces/torrent_entries_service"; -class TorrentEntriesService { - public async createTorrentEntry(torrent: ParsedTorrent, overwrite = false): Promise { +class TorrentEntriesService implements ITorrentEntriesService { + public async createTorrentEntry(torrent: IParsedTorrent, overwrite = false): Promise { const titleInfo = parse(torrent.title); if (!torrent.imdbId && torrent.type !== TorrentType.Anime) { @@ -49,9 +50,9 @@ class TorrentEntriesService { return; } - const fileCollection: TorrentFileCollection = await torrentFileService.parseTorrentFiles(torrent) - .then((torrentContents: TorrentFileCollection) => overwrite ? this.overwriteExistingFiles(torrent, torrentContents) : torrentContents) - .then((torrentContents: TorrentFileCollection) => torrentSubtitleService.assignSubtitles(torrentContents)) + const fileCollection: ITorrentFileCollection = await torrentFileService.parseTorrentFiles(torrent) + .then((torrentContents: ITorrentFileCollection) => overwrite ? this.overwriteExistingFiles(torrent, torrentContents) : torrentContents) + .then((torrentContents: ITorrentFileCollection) => torrentSubtitleService.assignSubtitles(torrentContents)) .catch(error => { logger.warn(`Failed getting files for ${torrent.title}`, error.message); return {}; @@ -86,8 +87,8 @@ class TorrentEntriesService { .catch(() => undefined); } - public async checkAndUpdateTorrent(torrent: ParsedTorrent): Promise { - const query: TorrentAttributes = { + public async checkAndUpdateTorrent(torrent: IParsedTorrent): Promise { + const query: ITorrentAttributes = { infoHash: torrent.infoHash, provider: torrent.provider, } @@ -128,7 +129,7 @@ class TorrentEntriesService { const imdbId: string | undefined = PromiseHelpers.mostCommonValue(storedVideos.map(stored => stored.imdbId)); const kitsuId: number | undefined = PromiseHelpers.mostCommonValue(storedVideos.map(stored => stored.kitsuId)); - const fileCollection: TorrentFileCollection = await torrentFileService.parseTorrentFiles(torrent) + const fileCollection: ITorrentFileCollection = await torrentFileService.parseTorrentFiles(torrent) .then(torrentContents => notOpenedVideo ? torrentContents : {...torrentContents, videos: storedVideos}) .then(torrentContents => torrentSubtitleService.assignSubtitles(torrentContents)) .then(torrentContents => this.assignMetaIds(torrentContents, imdbId, kitsuId)) @@ -176,7 +177,7 @@ class TorrentEntriesService { .catch(error => logger.error(`Failed saving contents for [${torrent.infoHash}] ${torrent.title}`, error)); } - public async updateTorrentSeeders(torrent: TorrentAttributes) { + public async updateTorrentSeeders(torrent: ITorrentAttributes) { if (!(torrent.infoHash || (torrent.provider && torrent.torrentId)) || !Number.isInteger(torrent.seeders)) { return torrent; } @@ -188,7 +189,7 @@ class TorrentEntriesService { }); } - private assignMetaIds(fileCollection: TorrentFileCollection, imdbId: string, kitsuId: number): TorrentFileCollection { + private assignMetaIds(fileCollection: ITorrentFileCollection, imdbId: string, kitsuId: number): ITorrentFileCollection { if (fileCollection.videos && fileCollection.videos.length) { fileCollection.videos.forEach(video => { video.imdbId = imdbId; @@ -199,7 +200,7 @@ class TorrentEntriesService { return fileCollection; } - private async overwriteExistingFiles(torrent: ParsedTorrent, torrentContents: TorrentFileCollection) { + private async overwriteExistingFiles(torrent: IParsedTorrent, torrentContents: ITorrentFileCollection) { const videos = torrentContents && torrentContents.videos; if (videos && videos.length) { const existingFiles = await repository.getFiles(torrent.infoHash) diff --git a/src/node/consumer/src/lib/services/torrent_file_service.ts b/src/node/consumer/src/lib/services/torrent_file_service.ts index e079bc5..1ad6bcd 100644 --- a/src/node/consumer/src/lib/services/torrent_file_service.ts +++ b/src/node/consumer/src/lib/services/torrent_file_service.ts @@ -4,30 +4,31 @@ import {parse} from 'parse-torrent-title'; import {PromiseHelpers} from '../helpers/promises_helpers'; import {TorrentType} from '../enums/torrent_types'; import {configurationService} from './configuration_service'; -import {extensionService} from './extension_service'; +import {ExtensionHelpers} from '../helpers/extension_helpers'; import {metadataService} from './metadata_service'; import {torrentDownloadService} from "./torrent_download_service"; import {logger} from "./logging_service"; -import {MetadataResponse} from "../interfaces/metadata_response"; -import {MetaDataQuery} from "../interfaces/metadata_query"; -import {CommonVideoMetadata} from "../interfaces/common_video_metadata"; -import {TorrentFileCollection} from "../interfaces/torrent_file_collection"; -import {ParsedTorrent} from "../interfaces/parsed_torrent"; -import {FileAttributes} from "../../repository/interfaces/file_attributes"; -import {ContentAttributes} from "../../repository/interfaces/content_attributes"; +import {IMetadataResponse} from "../interfaces/metadata_response"; +import {IMetaDataQuery} from "../interfaces/metadata_query"; +import {ICommonVideoMetadata} from "../interfaces/common_video_metadata"; +import {ITorrentFileCollection} from "../interfaces/torrent_file_collection"; +import {IParsedTorrent} from "../interfaces/parsed_torrent"; +import {IFileAttributes} from "../../repository/interfaces/file_attributes"; +import {IContentAttributes} from "../../repository/interfaces/content_attributes"; +import {ITorrentFileService} from "../interfaces/torrent_file_service"; const MIN_SIZE: number = 5 * 1024 * 1024; // 5 MB const MULTIPLE_FILES_SIZE = 4 * 1024 * 1024 * 1024; // 4 GB -class TorrentFileService { +class TorrentFileService implements ITorrentFileService { private readonly imdb_limiter: Bottleneck = new Bottleneck({ maxConcurrent: configurationService.metadataConfig.IMDB_CONCURRENT, minTime: configurationService.metadataConfig.IMDB_INTERVAL_MS }); - public async parseTorrentFiles(torrent: ParsedTorrent): Promise { + public async parseTorrentFiles(torrent: IParsedTorrent): Promise { const parsedTorrentName = parse(torrent.title); - const query: MetaDataQuery = { + const query: IMetaDataQuery = { id: torrent.kitsuId || torrent.imdbId, type: torrent.type || TorrentType.Movie, }; @@ -48,7 +49,7 @@ class TorrentFileService { return this.parseSeriesFiles(torrent, metadata) } - public isPackTorrent(torrent: ParsedTorrent): boolean { + public isPackTorrent(torrent: IParsedTorrent): boolean { if (torrent.isPack) { return true; } @@ -65,7 +66,7 @@ class TorrentFileService { return hasMultipleEpisodes && !hasSingleEpisode; } - private parseSeriesVideos(torrent: ParsedTorrent, videos: FileAttributes[]): FileAttributes[] { + private parseSeriesVideos(torrent: IParsedTorrent, videos: IFileAttributes[]): IFileAttributes[] { const parsedTorrentName = parse(torrent.title); const hasMovies = parsedTorrentName.complete || !!torrent.title.match(/movies?(?:\W|$)/i); const parsedVideos = videos.map(video => this.parseSeriesVideo(video)); @@ -73,8 +74,8 @@ class TorrentFileService { return parsedVideos.map(video => ({ ...video, isMovie: this.isMovieVideo(torrent, video, parsedVideos, hasMovies) })); } - private async parseMovieFiles(torrent: ParsedTorrent, metadata: MetadataResponse): Promise { - const fileCollection: TorrentFileCollection = await this.getMoviesTorrentContent(torrent); + private async parseMovieFiles(torrent: IParsedTorrent, metadata: IMetadataResponse): Promise { + const fileCollection: ITorrentFileCollection = await this.getMoviesTorrentContent(torrent); const filteredVideos = fileCollection.videos .filter(video => video.size > MIN_SIZE) .filter(video => !this.isFeaturette(video)); @@ -103,9 +104,9 @@ class TorrentFileService { return {...fileCollection, videos: parsedVideos}; } - private async parseSeriesFiles(torrent: ParsedTorrent, metadata: MetadataResponse): Promise { - const fileCollection: TorrentFileCollection = await this.getSeriesTorrentContent(torrent); - const parsedVideos: FileAttributes[] = await Promise.resolve(fileCollection.videos) + private async parseSeriesFiles(torrent: IParsedTorrent, metadata: IMetadataResponse): Promise { + const fileCollection: ITorrentFileCollection = await this.getSeriesTorrentContent(torrent); + const parsedVideos: IFileAttributes[] = await Promise.resolve(fileCollection.videos) .then(videos => videos.filter(video => videos.length === 1 || video.size > MIN_SIZE)) .then(videos => this.parseSeriesVideos(torrent, videos)) .then(videos => this.decomposeEpisodes(torrent, videos, metadata)) @@ -119,7 +120,7 @@ class TorrentFileService { return {...torrent.fileCollection, videos: parsedVideos}; } - private async getMoviesTorrentContent(torrent: ParsedTorrent): Promise { + private async getMoviesTorrentContent(torrent: IParsedTorrent): Promise { const files = await torrentDownloadService.getTorrentFiles(torrent) .catch(error => { if (!this.isPackTorrent(torrent)) { @@ -135,11 +136,11 @@ class TorrentFileService { return files; } - private getDefaultFileEntries(torrent: ParsedTorrent): FileAttributes[] { + private getDefaultFileEntries(torrent: IParsedTorrent): IFileAttributes[] { return [{title: torrent.title, path: torrent.title, size: torrent.size, fileIndex: null}]; } - private async getSeriesTorrentContent(torrent: ParsedTorrent): Promise { + private async getSeriesTorrentContent(torrent: IParsedTorrent): Promise { return torrentDownloadService.getTorrentFiles(torrent) .catch(error => { if (!this.isPackTorrent(torrent)) { @@ -149,7 +150,7 @@ class TorrentFileService { }); } - private async mapSeriesEpisode(torrent: ParsedTorrent, file: FileAttributes, files: FileAttributes[]) : Promise { + private async mapSeriesEpisode(torrent: IParsedTorrent, file: IFileAttributes, files: IFileAttributes[]) : Promise { if (!file.episodes && !file.episodes) { if (files.length === 1 || files.some(f => f.episodes || f.episodes) || parse(torrent.title).seasons) { return Promise.resolve([{ @@ -179,7 +180,7 @@ class TorrentFileService { }))) } - private async mapSeriesMovie(torrent: ParsedTorrent, file: FileAttributes): Promise { + private async mapSeriesMovie(torrent: IParsedTorrent, file: IFileAttributes): Promise { const kitsuId= torrent.type === TorrentType.Anime ? await this.findMovieKitsuId(file) .then(result => { if (result instanceof Error) { @@ -191,7 +192,7 @@ class TorrentFileService { const imdbId = !kitsuId ? await this.findMovieImdbId(file) : undefined; - const query: MetaDataQuery = { + const query: IMetaDataQuery = { id: kitsuId || imdbId, type: TorrentType.Movie }; @@ -230,7 +231,7 @@ class TorrentFileService { }]; } - private async decomposeEpisodes(torrent: ParsedTorrent, files: FileAttributes[], metadata: MetadataResponse = { episodeCount: [] }) { + private async decomposeEpisodes(torrent: IParsedTorrent, files: IFileAttributes[], metadata: IMetadataResponse = { episodeCount: [] }) { if (files.every(file => !file.episodes && !file.date)) { return files; } @@ -275,7 +276,7 @@ class TorrentFileService { return files; } - private preprocessEpisodes(files: FileAttributes[]) { + private preprocessEpisodes(files: IFileAttributes[]) { // reverse special episode naming when they named with 0 episode, ie. S02E00 files .filter(file => Number.isInteger(file.season) && file.episode === 0) @@ -286,7 +287,7 @@ class TorrentFileService { }) } - private isConcatSeasonAndEpisodeFiles(files: FileAttributes[], sortedEpisodes: number[], metadata: MetadataResponse) { + private isConcatSeasonAndEpisodeFiles(files: IFileAttributes[], sortedEpisodes: number[], metadata: IMetadataResponse) { if (metadata.kitsuId !== undefined) { // anime does not use this naming scheme in 99% of cases; return false; @@ -313,11 +314,11 @@ class TorrentFileService { || concatAboveTotalEpisodeCount.length >= thresholdAbove; } - private isDateEpisodeFiles(files: FileAttributes[], metadata: MetadataResponse) { + private isDateEpisodeFiles(files: IFileAttributes[], metadata: IMetadataResponse) { return files.every(file => (!file.season || !metadata.episodeCount[file.season - 1]) && file.date); } - private isAbsoluteEpisodeFiles(torrent: ParsedTorrent, files: FileAttributes[], metadata: MetadataResponse) { + private isAbsoluteEpisodeFiles(torrent: IParsedTorrent, files: IFileAttributes[], metadata: IMetadataResponse) { const threshold = Math.ceil(files.length / 5); const isAnime = torrent.type === TorrentType.Anime && torrent.kitsuId; const nonMovieEpisodes = files @@ -330,7 +331,7 @@ class TorrentFileService { || absoluteEpisodes.length >= threshold; } - private isNewEpisodeNotInMetadata(torrent: ParsedTorrent, video: FileAttributes, metadata: MetadataResponse) { + private isNewEpisodeNotInMetadata(torrent: IParsedTorrent, video: IFileAttributes, metadata: IMetadataResponse) { // new episode might not yet been indexed by cinemeta. // detect this if episode number is larger than the last episode or season is larger than the last one // only for non anime metas @@ -341,7 +342,7 @@ class TorrentFileService { && video.episodes.every(ep => ep > (metadata.episodeCount[video.season - 1] || 0)); } - private decomposeConcatSeasonAndEpisodeFiles(files: FileAttributes[], metadata: MetadataResponse) { + private decomposeConcatSeasonAndEpisodeFiles(files: IFileAttributes[], metadata: IMetadataResponse) { files .filter(file => file.episodes && file.season !== 0 && file.episodes.every(ep => ep > 100)) .filter(file => metadata.episodeCount[(file.season || this.div100(file.episodes[0])) - 1] < 100) @@ -353,7 +354,7 @@ class TorrentFileService { } - private decomposeAbsoluteEpisodeFiles(torrent: ParsedTorrent, videos: FileAttributes[], metadata: MetadataResponse) { + private decomposeAbsoluteEpisodeFiles(torrent: IParsedTorrent, videos: IFileAttributes[], metadata: IMetadataResponse) { if (metadata.episodeCount.length === 0) { videos .filter(file => !Number.isInteger(file.season) && file.episodes && !file.isMovie) @@ -377,7 +378,7 @@ class TorrentFileService { }); } - private decomposeDateEpisodeFiles(files: FileAttributes[], metadata: MetadataResponse) { + private decomposeDateEpisodeFiles(files: IFileAttributes[], metadata: IMetadataResponse) { if (!metadata || !metadata.videos || !metadata.videos.length) { return; } @@ -411,7 +412,7 @@ class TorrentFileService { } } - private assignKitsuOrImdbEpisodes(torrent: ParsedTorrent, files: FileAttributes[], metadata: MetadataResponse) { + private assignKitsuOrImdbEpisodes(torrent: IParsedTorrent, files: IFileAttributes[], metadata: IMetadataResponse) { if (!metadata || !metadata.videos || !metadata.videos.length) { if (torrent.type === TorrentType.Anime) { // assign episodes as kitsu episodes for anime when no metadata available for imdb mapping @@ -429,7 +430,7 @@ class TorrentFileService { return files; } - const seriesMapping: CommonVideoMetadata = metadata.videos + const seriesMapping: ICommonVideoMetadata = metadata.videos .reduce((map, video) => { const episodeMap = map[video.season] || {}; episodeMap[video.episode] = video; @@ -464,13 +465,13 @@ class TorrentFileService { file.episodes = file.episodes.map(ep => seasonMapping[ep] && seasonMapping[ep].kitsuEpisode); } else if (seriesMapping[file.season - 1]) { // sometimes a second season might be a continuation of the previous season - const seasonMapping = seriesMapping[file.season - 1] as CommonVideoMetadata; + const seasonMapping = seriesMapping[file.season - 1] as ICommonVideoMetadata; const episodes = Object.values(seasonMapping); const firstKitsuId = episodes.length && episodes[0]; const differentTitlesCount = new Set(episodes.map(ep => ep.id)).size const skippedCount = episodes.filter(ep => ep.id === firstKitsuId).length; const seasonEpisodes = files - .filter((otherFile: FileAttributes) => otherFile.season === file.season) + .filter((otherFile: IFileAttributes) => otherFile.season === file.season) .reduce((a, b) => a.concat(b.episodes), []); const isAbsoluteOrder = seasonEpisodes.every(ep => ep > skippedCount && ep <= episodes.length) const isNormalOrder = seasonEpisodes.every(ep => ep + skippedCount <= episodes.length) @@ -494,7 +495,7 @@ class TorrentFileService { return files; } - private needsCinemetaMetadataForAnime(files: FileAttributes[], metadata: MetadataResponse) { + private needsCinemetaMetadataForAnime(files: IFileAttributes[], metadata: IMetadataResponse) { if (!metadata || !metadata.imdbId || !metadata.videos || !metadata.videos.length) { return false; } @@ -510,8 +511,8 @@ class TorrentFileService { .some(file => file.season < minSeason || file.season > maxSeason || file.episodes.every(ep => ep > total)); } - private async updateToCinemetaMetadata(metadata: MetadataResponse) { - const query: MetaDataQuery = { + private async updateToCinemetaMetadata(metadata: IMetadataResponse) { + const query: IMetaDataQuery = { id: metadata.imdbId, type: metadata.type }; @@ -536,7 +537,7 @@ class TorrentFileService { }) } - private findMovieImdbId(title: FileAttributes | string) { + private findMovieImdbId(title: IFileAttributes | string) { const parsedTitle = typeof title === 'string' ? parse(title) : title; logger.debug(`Finding movie imdbId for ${title}`); return this.imdb_limiter.schedule(async () => { @@ -553,7 +554,7 @@ class TorrentFileService { }); } - private async findMovieKitsuId(title: FileAttributes | string) { + private async findMovieKitsuId(title: IFileAttributes | string) { const parsedTitle = typeof title === 'string' ? parse(title) : title; const kitsuQuery = { title: parsedTitle.title, @@ -568,22 +569,22 @@ class TorrentFileService { } } - private isDiskTorrent(contents: ContentAttributes[]) { - return contents.some(content => extensionService.isDisk(content.path)); + private isDiskTorrent(contents: IContentAttributes[]) { + return contents.some(content => ExtensionHelpers.isDisk(content.path)); } - private isSingleMovie(videos: FileAttributes[]) { + private isSingleMovie(videos: IFileAttributes[]) { return videos.length === 1 || (videos.length === 2 && videos.find(v => /\b(?:part|disc|cd)[ ._-]?0?1\b|^0?1\.\w{2,4}$/i.test(v.path)) && videos.find(v => /\b(?:part|disc|cd)[ ._-]?0?2\b|^0?2\.\w{2,4}$/i.test(v.path))); } - private isFeaturette(video: FileAttributes) { + private isFeaturette(video: IFileAttributes) { return /featurettes?\/|extras-grym/i.test(video.path); } - private parseSeriesVideo(video: FileAttributes): FileAttributes { + private parseSeriesVideo(video: IFileAttributes): IFileAttributes { const videoInfo = parse(video.title); // the episode may be in a folder containing season number if (!Number.isInteger(videoInfo.season) && video.path.includes('/')) { @@ -629,7 +630,7 @@ class TorrentFileService { return { ...video, ...videoInfo }; } - private isMovieVideo(torrent: ParsedTorrent, video: FileAttributes, otherVideos: FileAttributes[], hasMovies: boolean): boolean { + private isMovieVideo(torrent: IParsedTorrent, video: IFileAttributes, otherVideos: IFileAttributes[], hasMovies: boolean): boolean { if (Number.isInteger(torrent.season) && Array.isArray(torrent.episodes)) { // not movie if video has season return false; @@ -653,7 +654,7 @@ class TorrentFileService { && otherVideos.filter(other => other.title === video.title && other.year === video.year).length < 3; } - private clearInfoFields(video: FileAttributes) { + private clearInfoFields(video: IFileAttributes) { video.imdbId = undefined; video.imdbSeason = undefined; video.imdbEpisode = undefined; diff --git a/src/node/consumer/src/lib/services/torrent_processing_service.ts b/src/node/consumer/src/lib/services/torrent_processing_service.ts index 9c59c39..f25fffd 100644 --- a/src/node/consumer/src/lib/services/torrent_processing_service.ts +++ b/src/node/consumer/src/lib/services/torrent_processing_service.ts @@ -2,14 +2,15 @@ import {TorrentType} from "../enums/torrent_types"; import {logger} from "./logging_service"; import {trackerService} from "./tracker_service"; import {torrentEntriesService} from "./torrent_entries_service"; -import {IngestedTorrentAttributes} from "../../repository/interfaces/ingested_torrent_attributes"; -import {ParsedTorrent} from "../interfaces/parsed_torrent"; +import {IIngestedTorrentAttributes} from "../../repository/interfaces/ingested_torrent_attributes"; +import {IParsedTorrent} from "../interfaces/parsed_torrent"; +import {ITorrentProcessingService} from "../interfaces/torrent_processing_service"; -class TorrentProcessingService { - public async processTorrentRecord(torrent: IngestedTorrentAttributes): Promise { - const { category } = torrent; +class TorrentProcessingService implements ITorrentProcessingService { + public async processTorrentRecord(torrent: IIngestedTorrentAttributes): Promise { + const {category} = torrent; const type = category === 'tv' ? TorrentType.Series : TorrentType.Movie; - const torrentInfo: ParsedTorrent = await this.parseTorrent(torrent, type); + const torrentInfo: IParsedTorrent = await this.parseTorrent(torrent, type); logger.info(`Processing torrent ${torrentInfo.title} with infoHash ${torrentInfo.infoHash}`); @@ -25,7 +26,7 @@ class TorrentProcessingService { return trackers.join(','); } - private async parseTorrent(torrent: IngestedTorrentAttributes, category: string): Promise { + private async parseTorrent(torrent: IIngestedTorrentAttributes, category: string): Promise { const infoHash = torrent.info_hash?.trim().toLowerCase() return { title: torrent.name, @@ -41,7 +42,7 @@ class TorrentProcessingService { } } - private parseImdbId(torrent: IngestedTorrentAttributes): string | undefined { + private parseImdbId(torrent: IIngestedTorrentAttributes): string | undefined { if (torrent.imdb === undefined || torrent.imdb === null) { return undefined; } diff --git a/src/node/consumer/src/lib/services/torrent_subtitle_service.ts b/src/node/consumer/src/lib/services/torrent_subtitle_service.ts index e0c52a1..4ddabb3 100644 --- a/src/node/consumer/src/lib/services/torrent_subtitle_service.ts +++ b/src/node/consumer/src/lib/services/torrent_subtitle_service.ts @@ -1,28 +1,32 @@ -import { parse } from 'parse-torrent-title'; -import {TorrentFileCollection} from "../interfaces/torrent_file_collection"; -import {FileAttributes} from "../../repository/interfaces/file_attributes"; +import {parse} from 'parse-torrent-title'; +import {ITorrentFileCollection} from "../interfaces/torrent_file_collection"; +import {IFileAttributes} from "../../repository/interfaces/file_attributes"; +import {ITorrentSubtitleService} from "../interfaces/torrent_subtitle_service"; -class TorrentSubtitleService { - public assignSubtitles(fileCollection: TorrentFileCollection) : TorrentFileCollection { +class TorrentSubtitleService implements ITorrentSubtitleService { + public assignSubtitles(fileCollection: ITorrentFileCollection): ITorrentFileCollection { if (fileCollection.videos && fileCollection.videos.length && fileCollection.subtitles && fileCollection.subtitles.length) { if (fileCollection.videos.length === 1) { fileCollection.videos[0].subtitles = fileCollection.subtitles; - return { ...fileCollection, subtitles: [] }; + return {...fileCollection, subtitles: []}; } const parsedVideos = fileCollection.videos.map(video => this.parseVideo(video)); - const assignedSubs = fileCollection.subtitles.map(subtitle => ({ subtitle, videos: this.mostProbableSubtitleVideos(subtitle, parsedVideos) })); + const assignedSubs = fileCollection.subtitles.map(subtitle => ({ + subtitle, + videos: this.mostProbableSubtitleVideos(subtitle, parsedVideos) + })); const unassignedSubs = assignedSubs.filter(assignedSub => !assignedSub.videos).map(assignedSub => assignedSub.subtitle); assignedSubs .filter(assignedSub => assignedSub.videos) .forEach(assignedSub => assignedSub.videos.forEach(video => video.subtitles = (video.subtitles || []).concat(assignedSub.subtitle))); - return { ...fileCollection, subtitles: unassignedSubs }; + return {...fileCollection, subtitles: unassignedSubs}; } return fileCollection; } - private parseVideo(video: FileAttributes) { + private parseVideo(video: IFileAttributes) { const fileName = video.title.split('/').pop().replace(/\.(\w{2,4})$/, ''); const folderName = video.title.replace(/\/?[^/]+$/, ''); return { diff --git a/src/node/consumer/src/lib/services/tracker_service.ts b/src/node/consumer/src/lib/services/tracker_service.ts index d17c7bb..a85e5de 100644 --- a/src/node/consumer/src/lib/services/tracker_service.ts +++ b/src/node/consumer/src/lib/services/tracker_service.ts @@ -1,13 +1,14 @@ -import axios, { AxiosResponse } from 'axios'; -import { cacheService } from "./cache_service"; -import { configurationService } from './configuration_service'; -import { logger } from "./logging_service"; +import axios, {AxiosResponse} from 'axios'; +import {cacheService} from "./cache_service"; +import {configurationService} from './configuration_service'; +import {logger} from "./logging_service"; +import {ITrackerService} from "../interfaces/tracker_service"; -class TrackerService { - public async getTrackers() : Promise { +class TrackerService implements ITrackerService { + public async getTrackers(): Promise { return cacheService.cacheTrackers(this.downloadTrackers); }; - + private async downloadTrackers(): Promise { const response: AxiosResponse = await axios.get(configurationService.trackerConfig.TRACKERS_URL); const trackersListText: string = response.data; diff --git a/src/node/consumer/src/repository/database_repository.ts b/src/node/consumer/src/repository/database_repository.ts index 51edac1..c3c4544 100644 --- a/src/node/consumer/src/repository/database_repository.ts +++ b/src/node/consumer/src/repository/database_repository.ts @@ -10,8 +10,8 @@ import {IngestedTorrent} from "./models/ingestedTorrent"; import {Subtitle} from "./models/subtitle"; import {Content} from "./models/content"; import {SkipTorrent} from "./models/skipTorrent"; -import {FileAttributes} from "./interfaces/file_attributes"; -import {TorrentAttributes} from "./interfaces/torrent_attributes"; +import {IFileAttributes} from "./interfaces/file_attributes"; +import {ITorrentAttributes} from "./interfaces/torrent_attributes"; import {IngestedPage} from "./models/ingestedPage"; import {logger} from "../lib/services/logging_service"; @@ -50,7 +50,7 @@ class DatabaseRepository { } } - public async getTorrent(torrent: TorrentAttributes): Promise { + public async getTorrent(torrent: ITorrentAttributes): Promise { const where = torrent.infoHash ? { infoHash: torrent.infoHash } : { provider: torrent.provider, torrentId: torrent.torrentId }; @@ -61,11 +61,11 @@ class DatabaseRepository { return this.getTorrentsBasedOnQuery({ title: { [Op.regexp]: `${titleQuery}` }, type }); } - public async getTorrentsBasedOnQuery(where: WhereOptions): Promise { + public async getTorrentsBasedOnQuery(where: WhereOptions): Promise { return await Torrent.findAll({ where }); } - public async getFilesBasedOnQuery(where: WhereOptions): Promise { + public async getFilesBasedOnQuery(where: WhereOptions): Promise { return await File.findAll({ where }); } @@ -118,7 +118,7 @@ class DatabaseRepository { await this.createSubtitles(torrent.infoHash, torrent.subtitles); } - public async setTorrentSeeders(torrent: TorrentAttributes, seeders: number): Promise<[number]> { + public async setTorrentSeeders(torrent: ITorrentAttributes, seeders: number): Promise<[number]> { const where = torrent.infoHash ? { infoHash: torrent.infoHash } : { provider: torrent.provider, torrentId: torrent.torrentId }; diff --git a/src/node/consumer/src/repository/interfaces/content_attributes.ts b/src/node/consumer/src/repository/interfaces/content_attributes.ts index c7d8e85..03d7944 100644 --- a/src/node/consumer/src/repository/interfaces/content_attributes.ts +++ b/src/node/consumer/src/repository/interfaces/content_attributes.ts @@ -1,11 +1,11 @@ import {Optional} from "sequelize"; -export interface ContentAttributes { +export interface IContentAttributes { infoHash: string; fileIndex: number; path: string; size: number; } -export interface ContentCreationAttributes extends Optional { +export interface IContentCreationAttributes extends Optional { } \ No newline at end of file diff --git a/src/node/consumer/src/repository/interfaces/file_attributes.ts b/src/node/consumer/src/repository/interfaces/file_attributes.ts index 7ae8469..da75405 100644 --- a/src/node/consumer/src/repository/interfaces/file_attributes.ts +++ b/src/node/consumer/src/repository/interfaces/file_attributes.ts @@ -1,8 +1,8 @@ import {Optional} from "sequelize"; -import {SubtitleAttributes} from "./subtitle_attributes"; -import {ParseTorrentTitleResult} from "../../lib/interfaces/parse_torrent_title_result"; +import {ISubtitleAttributes} from "./subtitle_attributes"; +import {IParseTorrentTitleResult} from "../../lib/interfaces/parse_torrent_title_result"; -export interface FileAttributes extends ParseTorrentTitleResult { +export interface IFileAttributes extends IParseTorrentTitleResult { id?: number; infoHash?: string; fileIndex?: number; @@ -13,10 +13,10 @@ export interface FileAttributes extends ParseTorrentTitleResult { imdbEpisode?: number; kitsuId?: number; kitsuEpisode?: number; - subtitles?: SubtitleAttributes[]; + subtitles?: ISubtitleAttributes[]; path?: string; isMovie?: boolean; } -export interface FileCreationAttributes extends Optional { +export interface IFileCreationAttributes extends Optional { } \ No newline at end of file diff --git a/src/node/consumer/src/repository/interfaces/ingested_page_attributes.ts b/src/node/consumer/src/repository/interfaces/ingested_page_attributes.ts index 21bf9a6..b8814f5 100644 --- a/src/node/consumer/src/repository/interfaces/ingested_page_attributes.ts +++ b/src/node/consumer/src/repository/interfaces/ingested_page_attributes.ts @@ -1,6 +1,6 @@ -export interface IngestedPageAttributes { +export interface IIngestedPageAttributes { url: string; } -export interface IngestedPageCreationAttributes extends IngestedPageAttributes { +export interface IIngestedPageCreationAttributes extends IIngestedPageAttributes { } \ No newline at end of file diff --git a/src/node/consumer/src/repository/interfaces/ingested_torrent_attributes.ts b/src/node/consumer/src/repository/interfaces/ingested_torrent_attributes.ts index c29ba9e..53ab7ab 100644 --- a/src/node/consumer/src/repository/interfaces/ingested_torrent_attributes.ts +++ b/src/node/consumer/src/repository/interfaces/ingested_torrent_attributes.ts @@ -1,6 +1,6 @@ import {Optional} from "sequelize"; -export interface IngestedTorrentAttributes { +export interface IIngestedTorrentAttributes { name: string; source: string; category: string; @@ -13,5 +13,5 @@ export interface IngestedTorrentAttributes { createdAt?: Date; } -export interface IngestedTorrentCreationAttributes extends Optional { +export interface IIngestedTorrentCreationAttributes extends Optional { } \ No newline at end of file diff --git a/src/node/consumer/src/repository/interfaces/provider_attributes.ts b/src/node/consumer/src/repository/interfaces/provider_attributes.ts index c175caa..984ae2e 100644 --- a/src/node/consumer/src/repository/interfaces/provider_attributes.ts +++ b/src/node/consumer/src/repository/interfaces/provider_attributes.ts @@ -1,10 +1,10 @@ import {Optional} from "sequelize"; -export interface ProviderAttributes { +export interface IProviderAttributes { name: string; lastScraped: Date; lastScrapedId: string; } -export interface ProviderCreationAttributes extends Optional { +export interface IProviderCreationAttributes extends Optional { } \ No newline at end of file diff --git a/src/node/consumer/src/repository/interfaces/skip_torrent_attributes.ts b/src/node/consumer/src/repository/interfaces/skip_torrent_attributes.ts index 9b843da..4d8c1da 100644 --- a/src/node/consumer/src/repository/interfaces/skip_torrent_attributes.ts +++ b/src/node/consumer/src/repository/interfaces/skip_torrent_attributes.ts @@ -1,8 +1,8 @@ import {Optional} from "sequelize"; -export interface SkipTorrentAttributes { +export interface ISkipTorrentAttributes { infoHash: string; } -export interface SkipTorrentCreationAttributes extends Optional { +export interface ISkipTorrentCreationAttributes extends Optional { } \ No newline at end of file diff --git a/src/node/consumer/src/repository/interfaces/subtitle_attributes.ts b/src/node/consumer/src/repository/interfaces/subtitle_attributes.ts index 6e4a28b..ce0ecb9 100644 --- a/src/node/consumer/src/repository/interfaces/subtitle_attributes.ts +++ b/src/node/consumer/src/repository/interfaces/subtitle_attributes.ts @@ -1,6 +1,6 @@ import {Optional} from "sequelize"; -export interface SubtitleAttributes { +export interface ISubtitleAttributes { infoHash: string; fileIndex: number; fileId?: number; @@ -8,5 +8,5 @@ export interface SubtitleAttributes { path: string; } -export interface SubtitleCreationAttributes extends Optional { +export interface ISubtitleCreationAttributes extends Optional { } \ No newline at end of file diff --git a/src/node/consumer/src/repository/interfaces/torrent_attributes.ts b/src/node/consumer/src/repository/interfaces/torrent_attributes.ts index 60e4bb7..3dfba91 100644 --- a/src/node/consumer/src/repository/interfaces/torrent_attributes.ts +++ b/src/node/consumer/src/repository/interfaces/torrent_attributes.ts @@ -1,9 +1,9 @@ import {Optional} from "sequelize"; -import {ContentAttributes} from "./content_attributes"; -import {SubtitleAttributes} from "./subtitle_attributes"; -import {FileAttributes} from "./file_attributes"; +import {IContentAttributes} from "./content_attributes"; +import {ISubtitleAttributes} from "./subtitle_attributes"; +import {IFileAttributes} from "./file_attributes"; -export interface TorrentAttributes { +export interface ITorrentAttributes { infoHash: string; provider?: string; torrentId?: string; @@ -17,10 +17,10 @@ export interface TorrentAttributes { resolution?: string; reviewed?: boolean; opened?: boolean; - contents?: ContentAttributes[]; - files?: FileAttributes[]; - subtitles?: SubtitleAttributes[]; + contents?: IContentAttributes[]; + files?: IFileAttributes[]; + subtitles?: ISubtitleAttributes[]; } -export interface TorrentCreationAttributes extends Optional { +export interface ITorrentCreationAttributes extends Optional { } \ No newline at end of file diff --git a/src/node/consumer/src/repository/models/content.ts b/src/node/consumer/src/repository/models/content.ts index 7e70d7e..c17bca1 100644 --- a/src/node/consumer/src/repository/models/content.ts +++ b/src/node/consumer/src/repository/models/content.ts @@ -1,9 +1,9 @@ import {Table, Column, Model, HasMany, DataType, BelongsTo, ForeignKey} from 'sequelize-typescript'; -import {ContentAttributes, ContentCreationAttributes} from "../interfaces/content_attributes"; +import {IContentAttributes, IContentCreationAttributes} from "../interfaces/content_attributes"; import {Torrent} from "./torrent"; @Table({modelName: 'content', timestamps: false}) -export class Content extends Model { +export class Content extends Model { @Column({ type: DataType.STRING(64), primaryKey: true, allowNull: false, onDelete: 'CASCADE' }) @ForeignKey(() => Torrent) declare infoHash: string; diff --git a/src/node/consumer/src/repository/models/file.ts b/src/node/consumer/src/repository/models/file.ts index 2383a78..62a4ea2 100644 --- a/src/node/consumer/src/repository/models/file.ts +++ b/src/node/consumer/src/repository/models/file.ts @@ -1,8 +1,8 @@ import {Table, Column, Model, HasMany, DataType, BelongsTo, ForeignKey} from 'sequelize-typescript'; -import {FileAttributes, FileCreationAttributes} from "../interfaces/file_attributes"; +import {IFileAttributes, IFileCreationAttributes} from "../interfaces/file_attributes"; import {Torrent} from "./torrent"; import {Subtitle} from "./subtitle"; -import {SubtitleAttributes} from "../interfaces/subtitle_attributes"; +import {ISubtitleAttributes} from "../interfaces/subtitle_attributes"; const indexes = [ { @@ -23,7 +23,7 @@ const indexes = [ ]; @Table({modelName: 'file', timestamps: true, indexes: indexes }) -export class File extends Model { +export class File extends Model { @Column({ type: DataType.STRING(64), allowNull: false, onDelete: 'CASCADE' }) @ForeignKey(() => Torrent) declare infoHash: string; diff --git a/src/node/consumer/src/repository/models/ingestedPage.ts b/src/node/consumer/src/repository/models/ingestedPage.ts index 6504344..2dc64a3 100644 --- a/src/node/consumer/src/repository/models/ingestedPage.ts +++ b/src/node/consumer/src/repository/models/ingestedPage.ts @@ -1,5 +1,5 @@ import { Table, Column, Model, HasMany, DataType } from 'sequelize-typescript'; -import {IngestedPageAttributes, IngestedPageCreationAttributes} from "../interfaces/ingested_page_attributes"; +import {IIngestedPageAttributes, IIngestedPageCreationAttributes} from "../interfaces/ingested_page_attributes"; const indexes = [ { @@ -10,7 +10,7 @@ const indexes = [ ]; @Table({modelName: 'ingested_page', timestamps: true, indexes: indexes}) -export class IngestedPage extends Model { +export class IngestedPage extends Model { @Column({ type: DataType.STRING(512), allowNull: false }) declare url: string; } \ No newline at end of file diff --git a/src/node/consumer/src/repository/models/ingestedTorrent.ts b/src/node/consumer/src/repository/models/ingestedTorrent.ts index 5a21a4a..a40571b 100644 --- a/src/node/consumer/src/repository/models/ingestedTorrent.ts +++ b/src/node/consumer/src/repository/models/ingestedTorrent.ts @@ -1,5 +1,5 @@ import { Table, Column, Model, HasMany, DataType } from 'sequelize-typescript'; -import {IngestedTorrentAttributes, IngestedTorrentCreationAttributes} from "../interfaces/ingested_torrent_attributes"; +import {IIngestedTorrentAttributes, IIngestedTorrentCreationAttributes} from "../interfaces/ingested_torrent_attributes"; const indexes = [ { @@ -10,7 +10,7 @@ const indexes = [ ]; @Table({modelName: 'ingested_torrent', timestamps: true, indexes: indexes}) -export class IngestedTorrent extends Model { +export class IngestedTorrent extends Model { @Column({ type: DataType.STRING(512) }) declare name: string; diff --git a/src/node/consumer/src/repository/models/provider.ts b/src/node/consumer/src/repository/models/provider.ts index 26a8bfc..97b5036 100644 --- a/src/node/consumer/src/repository/models/provider.ts +++ b/src/node/consumer/src/repository/models/provider.ts @@ -1,8 +1,8 @@ import { Table, Column, Model, HasMany, DataType } from 'sequelize-typescript'; -import {ProviderAttributes, ProviderCreationAttributes} from "../interfaces/provider_attributes"; +import {IProviderAttributes, IProviderCreationAttributes} from "../interfaces/provider_attributes"; @Table({modelName: 'provider', timestamps: false}) -export class Provider extends Model { +export class Provider extends Model { @Column({ type: DataType.STRING(32), primaryKey: true }) declare name: string; diff --git a/src/node/consumer/src/repository/models/skipTorrent.ts b/src/node/consumer/src/repository/models/skipTorrent.ts index 101ce68..7e1eb4a 100644 --- a/src/node/consumer/src/repository/models/skipTorrent.ts +++ b/src/node/consumer/src/repository/models/skipTorrent.ts @@ -1,9 +1,9 @@ import { Table, Column, Model, HasMany, DataType } from 'sequelize-typescript'; -import {SkipTorrentAttributes, SkipTorrentCreationAttributes} from "../interfaces/skip_torrent_attributes"; +import {ISkipTorrentAttributes, ISkipTorrentCreationAttributes} from "../interfaces/skip_torrent_attributes"; @Table({modelName: 'skip_torrent', timestamps: false}) -export class SkipTorrent extends Model { +export class SkipTorrent extends Model { @Column({ type: DataType.STRING(64), primaryKey: true }) declare infoHash: string; diff --git a/src/node/consumer/src/repository/models/subtitle.ts b/src/node/consumer/src/repository/models/subtitle.ts index 0a4157c..7a66baf 100644 --- a/src/node/consumer/src/repository/models/subtitle.ts +++ b/src/node/consumer/src/repository/models/subtitle.ts @@ -1,5 +1,5 @@ import {Table, Column, Model, HasMany, DataType, BelongsTo, ForeignKey} from 'sequelize-typescript'; -import {SubtitleAttributes, SubtitleCreationAttributes} from "../interfaces/subtitle_attributes"; +import {ISubtitleAttributes, ISubtitleCreationAttributes} from "../interfaces/subtitle_attributes"; import {File} from "./file"; import {Torrent} from "./torrent"; @@ -17,7 +17,7 @@ const indexes = [ ]; @Table({modelName: 'subtitle', timestamps: false, indexes: indexes}) -export class Subtitle extends Model { +export class Subtitle extends Model { @Column({ type: DataType.STRING(64), allowNull: false, onDelete: 'CASCADE' }) declare infoHash: string; diff --git a/src/node/consumer/src/repository/models/torrent.ts b/src/node/consumer/src/repository/models/torrent.ts index b6b7a80..f345b55 100644 --- a/src/node/consumer/src/repository/models/torrent.ts +++ b/src/node/consumer/src/repository/models/torrent.ts @@ -1,12 +1,12 @@ import { Table, Column, Model, HasMany, DataType } from 'sequelize-typescript'; -import {TorrentAttributes, TorrentCreationAttributes} from "../interfaces/torrent_attributes"; +import {ITorrentAttributes, ITorrentCreationAttributes} from "../interfaces/torrent_attributes"; import {Content} from "./content"; import {File} from "./file"; import {Subtitle} from "./subtitle"; @Table({modelName: 'torrent', timestamps: true}) -export class Torrent extends Model { +export class Torrent extends Model { @Column({type: DataType.STRING(64), primaryKey: true}) declare infoHash: string;