diff --git a/src/node/consumer/src/lib/interfaces/parsable_torrent.ts b/src/node/consumer/src/lib/interfaces/parsable_torrent.ts new file mode 100644 index 0000000..20683c2 --- /dev/null +++ b/src/node/consumer/src/lib/interfaces/parsable_torrent.ts @@ -0,0 +1,9 @@ +import {TorrentType} from "../enums/torrent_types"; + +export interface ParsableTorrent { + title: string; + type: TorrentType; + size: number; + pack?: boolean; +} + diff --git a/src/node/consumer/src/lib/interfaces/parsable_torrent_video.ts b/src/node/consumer/src/lib/interfaces/parsable_torrent_video.ts new file mode 100644 index 0000000..d57b7e7 --- /dev/null +++ b/src/node/consumer/src/lib/interfaces/parsable_torrent_video.ts @@ -0,0 +1,6 @@ +import {ParseTorrentTitleResult} from "./parse_torrent_title_result"; + +export interface ParsableTorrentVideo extends ParseTorrentTitleResult { + name: string; + path: 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 new file mode 100644 index 0000000..ea85e8e --- /dev/null +++ b/src/node/consumer/src/lib/interfaces/parse_torrent_title_result.ts @@ -0,0 +1,31 @@ +export interface ParseTorrentTitleResult { + title: string; + date?: string; + year?: number | string; + resolution?: string; + extended?: boolean; + unrated?: boolean; + proper?: boolean; + repack?: boolean; + convert?: boolean; + hardcoded?: boolean; + retail?: boolean; + remastered?: boolean; + complete?: boolean; + region?: string; + container?: string; + extension?: string; + source?: string; + codec?: string; + bitDepth?: string; + hdr?: Array; + audio?: string; + group?: string; + volumes?: Array; + seasons?: Array; + season?: number; + episodes?: Array; + episode?: number; + languages?: string; + dubbed?: boolean; +} \ No newline at end of file diff --git a/src/node/consumer/src/lib/parseHelper.js b/src/node/consumer/src/lib/parseHelper.js deleted file mode 100644 index 131e4f0..0000000 --- a/src/node/consumer/src/lib/parseHelper.js +++ /dev/null @@ -1,98 +0,0 @@ -import { parse } from 'parse-torrent-title'; -import { TorrentType } from './enums/torrent_types'; - -const MULTIPLE_FILES_SIZE = 4 * 1024 * 1024 * 1024; // 4 GB - -export function parseSeriesVideos(torrent, videos) { - const parsedTorrentName = parse(torrent.title); - const hasMovies = parsedTorrentName.complete || !!torrent.title.match(/movies?(?:\W|$)/i); - const parsedVideos = videos.map(video => parseSeriesVideo(video, parsedTorrentName)); - return parsedVideos.map(video => ({ ...video, isMovie: isMovieVideo(video, parsedVideos, torrent.type, hasMovies) })); -} - -function parseSeriesVideo(video, parsedTorrentName) { - const videoInfo = parse(video.name); - // the episode may be in a folder containing season number - if (!Number.isInteger(videoInfo.season) && video.path.includes('/')) { - const folders = video.path.split('/'); - const pathInfo = parse(folders[folders.length - 2]); - videoInfo.season = pathInfo.season; - } - if (!Number.isInteger(videoInfo.season) && parsedTorrentName.season) { - videoInfo.season = parsedTorrentName.season; - } - if (!Number.isInteger(videoInfo.season) && videoInfo.seasons && videoInfo.seasons.length > 1) { - // in case single file was interpreted as having multiple seasons - videoInfo.season = videoInfo.seasons[0]; - } - if (!Number.isInteger(videoInfo.season) && video.path.includes('/') && parsedTorrentName.seasons - && parsedTorrentName.seasons.length > 1) { - // russian season are usually named with 'series name-2` i.e. Улицы разбитых фонарей-6/22. Одиночный выстрел.mkv - const folderPathSeasonMatch = video.path.match(/[\u0400-\u04ff]-(\d{1,2})(?=.*\/)/); - videoInfo.season = folderPathSeasonMatch && parseInt(folderPathSeasonMatch[1], 10) || undefined; - } - // sometimes video file does not have correct date format as in torrent title - if (!videoInfo.episodes && !videoInfo.date && parsedTorrentName.date) { - videoInfo.date = parsedTorrentName.date; - } - // limit number of episodes in case of incorrect parsing - if (videoInfo.episodes && videoInfo.episodes.length > 20) { - videoInfo.episodes = [videoInfo.episodes[0]]; - videoInfo.episode = videoInfo.episodes[0]; - } - // force episode to any found number if it was not parsed - if (!videoInfo.episodes && !videoInfo.date) { - const epMatcher = videoInfo.title.match( - /(? 3 - && otherVideos.filter(other => other.title === video.title && other.year === video.year) < 3; -} - -export function isPackTorrent(torrent) { - if (torrent.pack) { - return true; - } - const parsedInfo = parse(torrent.title); - if (torrent.type === TorrentType.MOVIE) { - return parsedInfo.complete || typeof parsedInfo.year === 'string' || /movies/i.test(torrent.title); - } - const hasMultipleEpisodes = parsedInfo.complete || - torrent.size > MULTIPLE_FILES_SIZE || - (parsedInfo.seasons && parsedInfo.seasons.length > 1) || - (parsedInfo.episodes && parsedInfo.episodes.length > 1) || - (parsedInfo.seasons && !parsedInfo.episodes); - const hasSingleEpisode = Number.isInteger(parsedInfo.episode) || (!parsedInfo.episodes && parsedInfo.date); - return hasMultipleEpisodes && !hasSingleEpisode; -} \ No newline at end of file diff --git a/src/node/consumer/src/lib/parseHelper.ts b/src/node/consumer/src/lib/parseHelper.ts new file mode 100644 index 0000000..d393186 --- /dev/null +++ b/src/node/consumer/src/lib/parseHelper.ts @@ -0,0 +1,101 @@ +import { parse } from 'parse-torrent-title'; +import { TorrentType } from './enums/torrent_types'; +import {ParseTorrentTitleResult} from "./interfaces/parse_torrent_title_result"; +import {ParsableTorrentVideo} from "./interfaces/parsable_torrent_video"; +import {ParsableTorrent} from "./interfaces/parsable_torrent"; + +const MULTIPLE_FILES_SIZE = 4 * 1024 * 1024 * 1024; // 4 GB + +export function parseSeriesVideos(torrent: ParsableTorrent, videos: ParsableTorrentVideo[]): ParseTorrentTitleResult[] { + const parsedTorrentName = parse(torrent.title); + const hasMovies = parsedTorrentName.complete || !!torrent.title.match(/movies?(?:\W|$)/i); + const parsedVideos = videos.map(video => parseSeriesVideo(video, parsedTorrentName)); + return parsedVideos.map(video => ({ ...video, isMovie: isMovieVideo(video, parsedVideos, torrent.type, hasMovies) })); +} + +function parseSeriesVideo(video: ParsableTorrentVideo, parsedTorrentName: ParseTorrentTitleResult): ParseTorrentTitleResult { + const videoInfo = parse(video.name); + // the episode may be in a folder containing season number + if (!Number.isInteger(videoInfo.season) && video.path.includes('/')) { + const folders = video.path.split('/'); + const pathInfo = parse(folders[folders.length - 2]); + videoInfo.season = pathInfo.season; + } + if (!Number.isInteger(videoInfo.season) && parsedTorrentName.season) { + videoInfo.season = parsedTorrentName.season; + } + if (!Number.isInteger(videoInfo.season) && videoInfo.seasons && videoInfo.seasons.length > 1) { + // in case single file was interpreted as having multiple seasons + videoInfo.season = videoInfo.seasons[0]; + } + if (!Number.isInteger(videoInfo.season) && video.path.includes('/') && parsedTorrentName.seasons + && parsedTorrentName.seasons.length > 1) { + // russian season are usually named with 'series name-2` i.e. Улицы разбитых фонарей-6/22. Одиночный выстрел.mkv + const folderPathSeasonMatch = video.path.match(/[\u0400-\u04ff]-(\d{1,2})(?=.*\/)/); + videoInfo.season = folderPathSeasonMatch && parseInt(folderPathSeasonMatch[1], 10) || undefined; + } + // sometimes video file does not have correct date format as in torrent title + if (!videoInfo.episodes && !videoInfo.date && parsedTorrentName.date) { + videoInfo.date = parsedTorrentName.date; + } + // limit number of episodes in case of incorrect parsing + if (videoInfo.episodes && videoInfo.episodes.length > 20) { + videoInfo.episodes = [videoInfo.episodes[0]]; + videoInfo.episode = videoInfo.episodes[0]; + } + // force episode to any found number if it was not parsed + if (!videoInfo.episodes && !videoInfo.date) { + const epMatcher = videoInfo.title.match( + /(? 3 + && otherVideos.filter(other => other.title === video.title && other.year === video.year).length < 3; +} + +export function isPackTorrent(torrent: ParsableTorrent): boolean { + if (torrent.pack) { + return true; + } + const parsedInfo = parse(torrent.title); + if (torrent.type === TorrentType.MOVIE) { + return parsedInfo.complete || typeof parsedInfo.year === 'string' || /movies/i.test(torrent.title); + } + const hasMultipleEpisodes = parsedInfo.complete || + torrent.size > MULTIPLE_FILES_SIZE || + (parsedInfo.seasons && parsedInfo.seasons.length > 1) || + (parsedInfo.episodes && parsedInfo.episodes.length > 1) || + (parsedInfo.seasons && !parsedInfo.episodes); + const hasSingleEpisode = Number.isInteger(parsedInfo.episode) || (!parsedInfo.episodes && parsedInfo.date); + return hasMultipleEpisodes && !hasSingleEpisode; +} \ No newline at end of file diff --git a/src/node/consumer/src/lib/torrentEntries.js b/src/node/consumer/src/lib/torrentEntries.js index 3288126..f341192 100644 --- a/src/node/consumer/src/lib/torrentEntries.js +++ b/src/node/consumer/src/lib/torrentEntries.js @@ -1,6 +1,6 @@ import { parse } from 'parse-torrent-title'; import { getImdbId, getKitsuId } from './metadata'; -import { isPackTorrent } from './parseHelper.js'; +import { isPackTorrent } from './parseHelper'; import * as Promises from './promises'; import { repository } from '../repository/database_repository'; import { parseTorrentFiles } from './torrentFiles.js'; diff --git a/src/node/consumer/src/lib/torrentFiles.js b/src/node/consumer/src/lib/torrentFiles.js index abef29c..27b8551 100644 --- a/src/node/consumer/src/lib/torrentFiles.js +++ b/src/node/consumer/src/lib/torrentFiles.js @@ -5,7 +5,7 @@ import { parse } from 'parse-torrent-title'; import { metadataConfig } from './config.js'; import { isDisk } from './extension.js'; import { getMetadata, getImdbId, getKitsuId } from './metadata'; -import { parseSeriesVideos, isPackTorrent } from './parseHelper.js'; +import { parseSeriesVideos, isPackTorrent } from './parseHelper'; import * as Promises from './promises'; import {torrentFiles} from "./torrent.js"; import { TorrentType } from './enums/torrent_types';