start adding metadata tests

This commit is contained in:
iPromKnight
2024-02-08 18:01:28 +00:00
committed by iPromKnight
parent 901186c109
commit 82cf976273
25 changed files with 25567 additions and 119 deletions

View File

@@ -1,10 +1,10 @@
const { pathsToModuleNameMapper } = require('ts-jest');
const { compilerOptions } = require('./tsconfig.json');
const {pathsToModuleNameMapper} = require('ts-jest');
const {compilerOptions} = require('./tsconfig.json');
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths, { prefix: '<rootDir>/src/' }),
moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths, {prefix: '<rootDir>/src/'}),
modulePaths: [
'<rootDir>'
],

View File

@@ -7,4 +7,5 @@ export interface ICacheService {
cacheWrapMetadata: (id: string, method: CacheMethod) => Promise<any>;
cacheTrackers: (method: CacheMethod) => Promise<any>;
}
/* eslint-enable @typescript-eslint/no-explicit-any */

View File

@@ -1,8 +1,12 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
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;
}
/* eslint-enable @typescript-eslint/no-explicit-any */

View File

@@ -29,4 +29,5 @@ export class LoggingService implements ILoggingService {
this.logger.warn(message, args);
};
}
/* eslint-enable @typescript-eslint/no-explicit-any */

View File

@@ -70,8 +70,15 @@ export class MetadataService implements IMetadataService {
const key = Number.isInteger(query.id) || query.id.toString().match(/^\d+$/) ? `kitsu:${query.id}` : query.id;
const metaType = query.type === TorrentType.Movie ? TorrentType.Movie : TorrentType.Series;
return this.cacheService.cacheWrapMetadata(key.toString(), () => this.requestKitsuMetadata(`${KITSU_URL}/meta/${metaType}/${key}.json`)
.catch(() => this.requestCinemetaMetadata(`${CINEMETA_URL}/meta/${metaType}/${key}.json`))
const isImdbId = Boolean(key.toString().match(/^tt\d+$/));
return this.cacheService.cacheWrapMetadata(key.toString(), () => {
switch (isImdbId) {
case true:
return this.requestCinemetaMetadata(`${CINEMETA_URL}/meta/imdb/${key}.json`);
default:
return this.requestKitsuMetadata(`${KITSU_URL}/meta/${metaType}/${key}.json`)
}})
.catch(() => {
// try different type in case there was a mismatch
const otherType = metaType === TorrentType.Movie ? TorrentType.Series : TorrentType.Movie;
@@ -79,7 +86,7 @@ export class MetadataService implements IMetadataService {
})
.catch((error) => {
throw new Error(`failed metadata query ${key} due: ${error.message}`);
}));
});
};
public isEpisodeImdbId = async (imdbId: string | undefined): Promise<boolean> => {
@@ -174,7 +181,7 @@ export class MetadataService implements IMetadataService {
.filter(entry => entry.season !== null && entry.season !== 0 && entry.episode !== 0)
.sort((a, b) => (a.season || 0) - (b.season || 0))
.reduce((map: Record<number, number>, next) => {
if(next.season || next.season === 0) {
if (next.season || next.season === 0) {
map[next.season] = (map[next.season] || 0) + 1;
}
return map;

View File

@@ -62,7 +62,8 @@ export class TorrentDownloadService implements ITorrentDownloadService {
this.logger.debug(`Adding torrent with infoHash ${torrent.infoHash} to torrent engine...`);
const timeoutId = setTimeout(() => {
engine.destroy(() => {});
engine.destroy(() => {
});
reject(new Error('No available connections for torrent!'));
}, timeout);
@@ -78,7 +79,8 @@ export class TorrentDownloadService implements ITorrentDownloadService {
resolve(files);
clearTimeout(timeoutId);
engine.destroy(() => {});
engine.destroy(() => {
});
});
});
};
@@ -94,13 +96,13 @@ export class TorrentDownloadService implements ITorrentDownloadService {
const minRedundantRatio = videos.length <= 3 ? 30 : Number.MAX_VALUE;
const isSample = (video: ITorrentFile): boolean => video.path?.toString()?.match(/sample|bonus|promo/i) && maxSize / video.length > minSampleRatio || false;
const isRedundant = (video: ITorrentFile):boolean => maxSize / video.length > minRedundantRatio;
const isRedundant = (video: ITorrentFile): boolean => maxSize / video.length > minRedundantRatio;
const isExtra = (video: ITorrentFile): boolean => /extras?\//i.test(video.path?.toString() || "");
const isAnimeExtra = (video: ITorrentFile): boolean => {
if (!video.path || !video.length) {
return false;
}
return video.path.toString()?.match(/(?:\b|_)(?:NC)?(?:ED|OP|PV)(?:v?\d\d?)?(?:\b|_)/i)
&& maxSize / parseInt(video.length.toString()) > minAnimeExtraRatio || false;
};
@@ -108,7 +110,7 @@ export class TorrentDownloadService implements ITorrentDownloadService {
if (!video.path || !video.length) {
return false;
}
return video.path.toString()?.match(/^[A-Z-]+(?:\.[A-Z]+)?\.\w{3,4}$/)
&& maxSize / parseInt(video.length.toString()) > minAnimeExtraRatio || false;
}

View File

@@ -45,7 +45,7 @@ export class TorrentEntriesService implements ITorrentEntriesService {
this.logger.warn(`No title found for ${torrent.provider} [${torrent.infoHash}]`);
return;
}
const titleInfo = parse(torrent.title);
if (!torrent.imdbId && torrent.type !== TorrentType.Anime) {
@@ -98,7 +98,7 @@ export class TorrentEntriesService implements ITorrentEntriesService {
contents: fileCollection.contents,
subtitles: fileCollection.subtitles
});
return this.repository.createTorrent(newTorrent)
.then(() => PromiseHelpers.sequence(fileCollection.videos!.map(video => () => {
const newVideo: IFileCreationAttributes = {...video, infoHash: video.infoHash, title: video.title};
@@ -110,22 +110,7 @@ export class TorrentEntriesService implements ITorrentEntriesService {
.then(() => this.logger.info(`Created ${torrent.provider} entry for [${torrent.infoHash}] ${torrent.title}`));
};
private assignKitsuId = async (kitsuQuery: IMetaDataQuery, torrent: IParsedTorrent): Promise<void> => {
await this.metadataService.getKitsuId(kitsuQuery)
.then((result: number | Error) => {
if (typeof result === 'number') {
torrent.kitsuId = result;
} else {
torrent.kitsuId = 0;
}
})
.catch((error: Error) => {
this.logger.debug(`Failed getting kitsuId for ${torrent.title}`, error.message);
torrent.kitsuId = 0;
});
};
public createSkipTorrentEntry: (torrent: Torrent) => Promise<[SkipTorrent, boolean | null]> = async (torrent: Torrent)=> this.repository.createSkipTorrent(torrent.dataValues);
public createSkipTorrentEntry: (torrent: Torrent) => Promise<[SkipTorrent, boolean | null]> = async (torrent: Torrent) => this.repository.createSkipTorrent(torrent.dataValues);
public getStoredTorrentEntry = async (torrent: Torrent): Promise<Torrent | SkipTorrent | null | undefined> => this.repository.getSkipTorrent(torrent.infoHash)
.catch(() => this.repository.getTorrent(torrent.dataValues))
@@ -156,7 +141,7 @@ export class TorrentEntriesService implements ITorrentEntriesService {
await existingTorrent.save();
this.logger.debug(`Updated [${existingTorrent.infoHash}] ${existingTorrent.title} language to ${torrent.languages}`);
}
return this.createTorrentContents(existingTorrent)
.then(() => this.updateTorrentSeeders(existingTorrent.dataValues))
.then(() => Promise.resolve(true))
@@ -177,7 +162,10 @@ export class TorrentEntriesService implements ITorrentEntriesService {
const kitsuId: number = PromiseHelpers.mostCommonValue(storedVideos.map(stored => stored.kitsuId || 0));
const fileCollection: ITorrentFileCollection = await this.fileService.parseTorrentFiles(torrent)
.then(torrentContents => notOpenedVideo ? torrentContents : {...torrentContents, videos: storedVideos.map(video => video.dataValues)})
.then(torrentContents => notOpenedVideo ? torrentContents : {
...torrentContents,
videos: storedVideos.map(video => video.dataValues)
})
.then(torrentContents => this.subtitleService.assignSubtitles(torrentContents))
.then(torrentContents => this.assignMetaIds(torrentContents, imdbId, kitsuId))
.catch(error => {
@@ -230,13 +218,13 @@ export class TorrentEntriesService implements ITorrentEntriesService {
if (!(torrent.infoHash || (torrent.provider && torrent.torrentId)) || !Number.isInteger(torrent.seeders)) {
return [0];
}
if (torrent.seeders === undefined) {
this.logger.warn(`Seeders not found for ${torrent.provider} [${torrent.infoHash}] ${torrent.title}`);
return [0];
}
return this.repository.setTorrentSeeders(torrent, torrent.seeders)
.catch(error => {
this.logger.warn('Failed updating seeders:', error);
@@ -244,6 +232,21 @@ export class TorrentEntriesService implements ITorrentEntriesService {
});
};
private assignKitsuId = async (kitsuQuery: IMetaDataQuery, torrent: IParsedTorrent): Promise<void> => {
await this.metadataService.getKitsuId(kitsuQuery)
.then((result: number | Error) => {
if (typeof result === 'number') {
torrent.kitsuId = result;
} else {
torrent.kitsuId = 0;
}
})
.catch((error: Error) => {
this.logger.debug(`Failed getting kitsuId for ${torrent.title}`, error.message);
torrent.kitsuId = 0;
});
};
private assignMetaIds = (fileCollection: ITorrentFileCollection, imdbId: string | undefined, kitsuId: number): ITorrentFileCollection => {
if (fileCollection.videos && fileCollection.videos.length) {
fileCollection.videos.forEach(video => {

View File

@@ -45,7 +45,7 @@ export class TorrentFileService implements ITorrentFileService {
if (!torrent.title) {
return Promise.reject(new Error('Torrent title is missing'));
}
const parsedTorrentName = parse(torrent.title);
const query: IMetaDataQuery = {
id: torrent.kitsuId || torrent.imdbId,
@@ -54,7 +54,7 @@ export class TorrentFileService implements ITorrentFileService {
const metadata = await this.metadataService.getMetadata(query)
.then(meta => Object.assign({}, meta))
.catch(() => undefined);
if (metadata === undefined || metadata instanceof Error) {
return Promise.reject(new Error('Failed to retrieve metadata'));
}
@@ -83,14 +83,14 @@ export class TorrentFileService implements ITorrentFileService {
if (torrent.type === TorrentType.Movie) {
return parsedInfo.complete || typeof parsedInfo.year === 'string' || /movies/i.test(torrent.title);
}
const hasMultipleEpisodes = Boolean(parsedInfo.complete || torrent.size || 0 > MULTIPLE_FILES_SIZE ||
(parsedInfo.seasons && parsedInfo.seasons.length > 1) ||
(parsedInfo.episodes && parsedInfo.episodes.length > 1) ||
(parsedInfo.seasons && !parsedInfo.episodes));
const hasSingleEpisode: boolean = Boolean(Number.isInteger(parsedInfo.episode) || (!parsedInfo.episodes && parsedInfo.date));
return hasMultipleEpisodes && !hasSingleEpisode;
};
@@ -107,7 +107,7 @@ export class TorrentFileService implements ITorrentFileService {
if (fileCollection.videos === undefined || fileCollection.videos.length === 0) {
return {...fileCollection, videos: this.getDefaultFileEntries(torrent)};
}
const filteredVideos = fileCollection.videos
.filter(video => video.size! > MIN_SIZE)
.filter(video => !this.isFeaturette(video));
@@ -118,7 +118,7 @@ export class TorrentFileService implements ITorrentFileService {
title: video.path || video.title || video.fileName || '',
size: video.size || torrent.size,
imdbId: torrent.imdbId?.toString() || metadata && metadata.imdbId?.toString(),
kitsuId: parseInt(torrent.kitsuId?.toString() || metadata && metadata.kitsuId?.toString() || '0')
kitsuId: parseInt(torrent.kitsuId?.toString() || metadata && metadata.kitsuId?.toString() || '0')
}));
return {...fileCollection, videos: parsedVideos};
}
@@ -141,7 +141,7 @@ export class TorrentFileService implements ITorrentFileService {
if (fileCollection.videos === undefined || fileCollection.videos.length === 0) {
return {...fileCollection, videos: this.getDefaultFileEntries(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))
@@ -169,7 +169,7 @@ export class TorrentFileService implements ITorrentFileService {
if (files.contents && files.contents.length && !files.videos?.length && this.isDiskTorrent(files.contents)) {
files.videos = this.getDefaultFileEntries(torrent);
}
return files;
};
@@ -269,7 +269,7 @@ export class TorrentFileService implements ITorrentFileService {
}];
};
private decomposeEpisodes = async (torrent: IParsedTorrent, files: IFileAttributes[], metadata: IMetadataResponse = {episodeCount: []}):Promise<IFileAttributes[]> => {
private decomposeEpisodes = async (torrent: IParsedTorrent, files: IFileAttributes[], metadata: IMetadataResponse = {episodeCount: []}): Promise<IFileAttributes[]> => {
if (files.every(file => !file.episodes && !file.date)) {
return files;
}
@@ -370,7 +370,7 @@ export class TorrentFileService implements ITorrentFileService {
private isNewEpisodeNotInMetadata = (torrent: IParsedTorrent, video: IFileAttributes, metadata: IMetadataResponse): boolean => {
const isAnime = torrent.type === TorrentType.Anime && torrent.kitsuId;
return !!( !isAnime && !video.isMovie && video.episodes && video.season !== 1
return !!(!isAnime && !video.isMovie && video.episodes && video.season !== 1
&& metadata.status && /continuing|current/i.test(metadata.status)
&& metadata.episodeCount && video.season && video.season >= metadata.episodeCount.length
&& video.episodes.every(ep => metadata.episodeCount && video.season && ep > (metadata.episodeCount[video.season - 1] || 0)));
@@ -410,9 +410,9 @@ export class TorrentFileService implements ITorrentFileService {
return !file.season || (metadata.episodeCount[file.season - 1] || 0) < file.episodes[0];
})
.forEach(file => {
if(!file.episodes || !metadata.episodeCount) return;
if (!file.episodes || !metadata.episodeCount) return;
let seasonIdx = metadata.episodeCount
let seasonIdx = metadata.episodeCount
.map((_, i) => i)
.find(i => metadata.episodeCount && file.episodes && metadata.episodeCount.slice(0, i + 1).reduce((a, b) => a + b) >= file.episodes[0]);
@@ -609,7 +609,7 @@ export class TorrentFileService implements ITorrentFileService {
})
};
private findMovieImdbId = (title: IFileAttributes | string):Promise<string | undefined> => {
private findMovieImdbId = (title: IFileAttributes | string): Promise<string | undefined> => {
const parsedTitle = typeof title === 'string' ? parse(title) : title;
this.logger.debug(`Finding movie imdbId for ${title}`);
return this.imdb_limiter.schedule(async () => {
@@ -626,7 +626,7 @@ export class TorrentFileService implements ITorrentFileService {
});
};
private findMovieKitsuId = async (title: IFileAttributes | string):Promise<number | Error | undefined> => {
private findMovieKitsuId = async (title: IFileAttributes | string): Promise<number | Error | undefined> => {
const parsedTitle = typeof title === 'string' ? parse(title) : title;
const kitsuQuery = {
title: parsedTitle.title,
@@ -645,10 +645,10 @@ export class TorrentFileService implements ITorrentFileService {
private isSingleMovie = (videos: IFileAttributes[]): boolean => 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?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!))) !== undefined;
private isFeaturette = (video: IFileAttributes):boolean => /featurettes?\/|extras-grym/i.test(video.path!);
private isFeaturette = (video: IFileAttributes): boolean => /featurettes?\/|extras-grym/i.test(video.path!);
private parseSeriesVideo = (video: IFileAttributes): IFileAttributes => {
const videoInfo = parse(video.title);

View File

@@ -40,7 +40,7 @@ export class TorrentSubtitleService implements ITorrentSubtitleService {
};
}
private mostProbableSubtitleVideos = (subtitle: ISubtitleAttributes, parsedVideos: IFileAttributes[]) : IFileAttributes[] => {
private mostProbableSubtitleVideos = (subtitle: ISubtitleAttributes, parsedVideos: IFileAttributes[]): IFileAttributes[] => {
const subTitle = (subtitle.title || subtitle.path)?.split('/')?.pop()?.replace(/\.(\w{2,4})$/, '') || '';
const parsedSub = this.parsePath(subtitle.title || subtitle.path);
const byFileName = parsedVideos.filter(video => subTitle.includes(video.title!));
@@ -79,7 +79,7 @@ export class TorrentSubtitleService implements ITorrentSubtitleService {
return parsedWithEpisode || pathParts[pathParts.length - 1];
}
private parseFilename = (filename: string) : IFileAttributes => {
private parseFilename = (filename: string): IFileAttributes => {
const parsedInfo = parse(filename)
const titleEpisode = parsedInfo.title.match(/(\d+)$/);
if (!parsedInfo.episodes && titleEpisode) {

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,232 @@
{
"meta": {
"status": "Ended",
"videos": [
{
"name": "Pilot",
"season": 1,
"number": 0,
"firstAired": "1990-09-20T00:00:00.000Z",
"rating": "6.6",
"id": "tt0098798:1:0",
"overview": "",
"imdb_id": "tt0099580"
},
{
"name": "Out of Control",
"season": 1,
"number": 1,
"firstAired": "1990-09-26T00:00:00.000Z",
"rating": "6.8",
"id": "tt0098798:1:1",
"overview": "",
"imdb_id": "tt0579962"
},
{
"name": "Watching the Detectives",
"season": 1,
"number": 2,
"firstAired": "1990-10-17T00:00:00.000Z",
"rating": "6.9",
"id": "tt0098798:1:2",
"overview": "",
"imdb_id": "tt0579971"
},
{
"name": "Honor Among Thieves",
"season": 1,
"number": 3,
"firstAired": "1990-10-25T00:00:00.000Z",
"rating": "6.8",
"id": "tt0098798:1:3",
"overview": "",
"imdb_id": "tt0579961"
},
{
"name": "Double Vision",
"season": 1,
"number": 4,
"firstAired": "1990-11-01T00:00:00.000Z",
"rating": "6.6",
"id": "tt0098798:1:4",
"overview": "",
"imdb_id": "tt0579957"
},
{
"name": "Sins of the Father",
"season": 1,
"number": 5,
"firstAired": "1990-11-08T00:00:00.000Z",
"rating": "6.9",
"id": "tt0098798:1:5",
"overview": "",
"imdb_id": "tt0579965"
},
{
"name": "Child's Play",
"season": 1,
"number": 6,
"firstAired": "1990-11-15T00:00:00.000Z",
"rating": "6.8",
"id": "tt0098798:1:6",
"overview": "",
"imdb_id": "tt0579955"
},
{
"name": "Shroud of Death",
"season": 1,
"number": 7,
"firstAired": "1990-11-29T00:00:00.000Z",
"rating": "7.1",
"id": "tt0098798:1:7",
"overview": "",
"imdb_id": "tt0579963"
},
{
"name": "Ghost in the Machine",
"season": 1,
"number": 8,
"firstAired": "1990-12-13T00:00:00.000Z",
"rating": "7.7",
"id": "tt0098798:1:8",
"overview": "",
"imdb_id": "tt0579959"
},
{
"name": "Sight Unseen",
"season": 1,
"number": 9,
"firstAired": "1991-01-10T00:00:00.000Z",
"rating": "7",
"id": "tt0098798:1:9",
"overview": "",
"imdb_id": "tt0579964"
},
{
"name": "Beat the Clock",
"season": 1,
"number": 10,
"firstAired": "1991-01-31T00:00:00.000Z",
"rating": "7",
"id": "tt0098798:1:10",
"overview": "",
"imdb_id": "tt0579953"
},
{
"name": "The Trickster",
"season": 1,
"number": 11,
"firstAired": "1991-02-07T00:00:00.000Z",
"rating": "7.7",
"id": "tt0098798:1:11",
"overview": "",
"imdb_id": "tt0579969"
},
{
"name": "Tina, Is That You?",
"season": 1,
"number": 12,
"firstAired": "1991-02-14T00:00:00.000Z",
"rating": "6.8",
"id": "tt0098798:1:12",
"overview": "",
"imdb_id": "tt0579967"
},
{
"name": "Be My Baby",
"season": 1,
"number": 13,
"firstAired": "1991-02-20T00:00:00.000Z",
"rating": "6.5",
"id": "tt0098798:1:13",
"overview": "",
"imdb_id": "tt0579952"
},
{
"name": "Fast Forward",
"season": 1,
"number": 14,
"firstAired": "1991-02-27T00:00:00.000Z",
"rating": "7.9",
"id": "tt0098798:1:14",
"overview": "",
"imdb_id": "tt0579958"
},
{
"name": "Deadly Nightshade",
"season": 1,
"number": 15,
"firstAired": "1991-03-28T00:00:00.000Z",
"rating": "7.7",
"id": "tt0098798:1:15",
"overview": "",
"imdb_id": "tt0579966"
},
{
"name": "Captain Cold",
"season": 1,
"number": 16,
"firstAired": "1991-04-05T00:00:00.000Z",
"rating": "7.7",
"id": "tt0098798:1:16",
"overview": "",
"imdb_id": "tt0579954"
},
{
"name": "Twin Streaks",
"season": 1,
"number": 17,
"firstAired": "1991-04-12T00:00:00.000Z",
"rating": "7.1",
"id": "tt0098798:1:17",
"overview": "",
"imdb_id": "tt0579970"
},
{
"name": "Done with Mirrors",
"season": 1,
"number": 18,
"firstAired": "1991-04-27T00:00:00.000Z",
"rating": "7.3",
"id": "tt0098798:1:18",
"overview": "",
"imdb_id": "tt0579956"
},
{
"name": "Good Night, Central City",
"season": 1,
"number": 19,
"firstAired": "1991-05-04T00:00:00.000Z",
"rating": "7.1",
"id": "tt0098798:1:19",
"overview": "",
"imdb_id": "tt0579960"
},
{
"name": "Alpha",
"season": 1,
"number": 20,
"firstAired": "1991-05-11T00:00:00.000Z",
"rating": "7.5",
"id": "tt0098798:1:20",
"overview": "",
"imdb_id": "tt0579951"
},
{
"name": "Trial of the Trickster",
"season": 1,
"number": 21,
"firstAired": "1991-05-18T00:00:00.000Z",
"rating": "7.9",
"id": "tt0098798:1:21",
"overview": "",
"imdb_id": "tt0579968"
}
],
"id": "tt0098798",
"behaviorHints": {
"defaultVideoId": null,
"hasScheduledVideos": false
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,111 @@
{
"d": [
{
"i": [
"https://m.media-amazon.com/images/M/MV5BMTQ2MTc0MTAtN2VlYi00N2ZkLTlhNmUtMjcyZDg0YzNiYjEyXkEyXkFqcGdeQXVyMzU3MTc5OTE@._V1_.jpg",
680,
1000
],
"id": "tt0098798",
"l": "The Flash",
"q": "TV series",
"qid": "tvSeries",
"s": "John Wesley Shipp, Amanda Pays",
"y": 1990,
"yr": "1990-1991"
},
{
"i": [
"https://m.media-amazon.com/images/M/MV5BMGU2OGMyNzUtM2Q5NS00ZTdhLThmNjItODRiZDQ5MGMzZGNmXkEyXkFqcGdeQXVyMjQzMzQzODY@._V1_.jpg",
766,
1023
],
"id": "tt0100678",
"l": "Stan the Flasher",
"q": "feature",
"qid": "movie",
"s": "Claude Berri, Aurore Clément",
"y": 1990
},
{
"i": [
"https://m.media-amazon.com/images/M/MV5BMDlmNTQ3NzctMGUzYi00MjEyLWIzNmUtNjhjYzZlMDQ5OTE3XkEyXkFqcGdeQXVyMzY4ODk0Nw@@._V1_.jpg",
800,
600
],
"id": "tt27750546",
"l": "The Flash: Video News Release",
"q": "TV special",
"qid": "tvSpecial",
"s": "John Wesley Shipp, Amanda Pays",
"y": 1990
},
{
"i": [
"https://m.media-amazon.com/images/M/MV5BMjViZmVlMmUtNzljNS00NjhjLTk5ODMtNTZiZWRlOWRkMWU5XkEyXkFqcGdeQXVyMDA1NzM3OA@@._V1_.jpg",
1500,
2000
],
"id": "tt8408320",
"l": "Shenzhen Flash",
"q": "short",
"qid": "short",
"s": "",
"y": 1990
},
{
"i": [
"https://m.media-amazon.com/images/M/MV5BODU4YmIxYzItYTdjYy00NmQ3LWJkYmQtM2UwZGZjNjZhMjYwXkEyXkFqcGdeQXVyMTQxNzMzNDI@._V1_.jpg",
1991,
2931
],
"id": "tt0097365",
"l": "Flesh Gordon Meets the Cosmic Cheerleaders",
"q": "feature",
"qid": "movie",
"s": "Vince Murdocco, Robyn Kelly",
"y": 1990
},
{
"i": [
"https://m.media-amazon.com/images/M/MV5BYjMwMjgwNjAtZjllYi00Y2QyLWI2MzUtOGIwMDJjNWQ3YjQxL2ltYWdlL2ltYWdlXkEyXkFqcGdeQXVyNjg3MTAzODM@._V1_.jpg",
3183,
2015
],
"id": "tt2356508",
"l": "Arrow Flash",
"q": "video game",
"qid": "videoGame",
"s": "Action, Sci-Fi",
"y": 1990
},
{
"i": [
"https://m.media-amazon.com/images/M/MV5BNTZkNzU2MzEtNzUwMS00NTFmLTgxY2EtZTk1ZTI3NDUzN2M1XkEyXkFqcGdeQXVyMTgwMDI4MTc@._V1_.jpg",
299,
230
],
"id": "tt0444569",
"l": "Clash!",
"q": "TV series",
"qid": "tvSeries",
"s": "Billy Kimball, Dave Levin",
"y": 1990
},
{
"i": [
"https://m.media-amazon.com/images/M/MV5BNGNkZTYwNTYtNTc2Mi00ODFjLTg3MjctNTRiMGQwMDc4MjBkXkEyXkFqcGdeQXVyMzU0NzkwMDg@._V1_.jpg",
1152,
1548
],
"id": "tt0099584",
"l": "Of Flesh and Blood",
"q": "feature",
"qid": "movie",
"s": "Breon, Dick Bangham",
"y": 1990
}
],
"q": "the_flash%201990",
"v": 1
}

View File

@@ -0,0 +1,373 @@
{
"meta": {
"awards": "Nominated for 2 Primetime Emmys. 4 nominations total",
"cast": [
"John Wesley Shipp",
"Amanda Pays",
"Alex Désert"
],
"country": "United States",
"description": "A police forensic scientist, Barry Allen, battles crimes as the super-fast superhero \"The Flash.\"",
"director": null,
"dvdRelease": "2011-01-25T00:00:00.000Z",
"genre": [
"Action",
"Crime",
"Fantasy"
],
"imdbRating": "7.1",
"imdb_id": "tt0098798",
"name": "The Flash",
"popularity": 0.987,
"poster": "https://images.metahub.space/poster/small/tt0098798/img",
"released": "1990-09-20T00:00:00.000Z",
"runtime": "2 min",
"status": "Ended",
"tvdb_id": "78650",
"type": "series",
"writer": [
"Danny Bilson",
"Paul De Meo"
],
"year": "19901991",
"popularities": {
"moviedb": 57.842,
"trakt": 2,
"stremio": 0.987,
"stremio_lib": 0
},
"background": "https://images.metahub.space/background/medium/tt0098798/img",
"logo": "https://images.metahub.space/logo/medium/tt0098798/img",
"moviedb_id": 236,
"slug": "series/the-flash-0098798",
"id": "tt0098798",
"genres": [
"Action",
"Crime",
"Fantasy"
],
"releaseInfo": "19901991",
"videos": [
{
"name": "Pilot",
"season": 1,
"number": 1,
"firstAired": "1990-09-20T00:00:00.000Z",
"tvdb_id": 335168,
"rating": "7.0",
"thumbnail": "https://episodes.metahub.space/tt0098798/1/1/w780.jpg",
"id": "tt0098798:1:1",
"released": "1990-09-20T00:00:00.000Z",
"episode": 1
},
{
"name": "Out of Control",
"season": 1,
"number": 2,
"firstAired": "1990-09-27T00:00:00.000Z",
"tvdb_id": 291041,
"rating": "7.5",
"thumbnail": "https://episodes.metahub.space/tt0098798/1/2/w780.jpg",
"id": "tt0098798:1:2",
"released": "1990-09-27T00:00:00.000Z",
"episode": 2
},
{
"name": "Watching the Detectives",
"season": 1,
"number": 3,
"firstAired": "1990-10-18T00:00:00.000Z",
"tvdb_id": 291042,
"rating": "7.0",
"thumbnail": "https://episodes.metahub.space/tt0098798/1/3/w780.jpg",
"id": "tt0098798:1:3",
"released": "1990-10-18T00:00:00.000Z",
"episode": 3
},
{
"name": "Honor Among Thieves",
"season": 1,
"number": 4,
"firstAired": "1990-10-25T00:00:00.000Z",
"tvdb_id": 291043,
"rating": "7.0",
"thumbnail": "https://episodes.metahub.space/tt0098798/1/4/w780.jpg",
"id": "tt0098798:1:4",
"released": "1990-10-25T00:00:00.000Z",
"episode": 4
},
{
"name": "Double Vision",
"season": 1,
"number": 5,
"firstAired": "1990-11-01T00:00:00.000Z",
"tvdb_id": 291044,
"rating": "7.0",
"thumbnail": "https://episodes.metahub.space/tt0098798/1/5/w780.jpg",
"id": "tt0098798:1:5",
"released": "1990-11-01T00:00:00.000Z",
"episode": 5
},
{
"name": "Sins of the Father",
"season": 1,
"number": 6,
"firstAired": "1990-11-08T00:00:00.000Z",
"tvdb_id": 291045,
"rating": "7.0",
"thumbnail": "https://episodes.metahub.space/tt0098798/1/6/w780.jpg",
"id": "tt0098798:1:6",
"released": "1990-11-08T00:00:00.000Z",
"episode": 6
},
{
"name": "Child's Play",
"season": 1,
"number": 7,
"firstAired": "1990-11-15T00:00:00.000Z",
"tvdb_id": 291046,
"rating": "7.0",
"thumbnail": "https://episodes.metahub.space/tt0098798/1/7/w780.jpg",
"id": "tt0098798:1:7",
"released": "1990-11-15T00:00:00.000Z",
"episode": 7
},
{
"name": "Shroud of Death",
"season": 1,
"number": 8,
"firstAired": "1990-11-29T00:00:00.000Z",
"tvdb_id": 291047,
"rating": "7.0",
"thumbnail": "https://episodes.metahub.space/tt0098798/1/8/w780.jpg",
"id": "tt0098798:1:8",
"released": "1990-11-29T00:00:00.000Z",
"episode": 8
},
{
"name": "Ghost in the Machine",
"season": 1,
"number": 9,
"firstAired": "1990-12-13T00:00:00.000Z",
"tvdb_id": 291048,
"rating": "7.5",
"thumbnail": "https://episodes.metahub.space/tt0098798/1/9/w780.jpg",
"id": "tt0098798:1:9",
"released": "1990-12-13T00:00:00.000Z",
"episode": 9
},
{
"name": "Sight Unseen",
"season": 1,
"number": 10,
"firstAired": "1991-01-10T00:00:00.000Z",
"tvdb_id": 291049,
"rating": "7.0",
"thumbnail": "https://episodes.metahub.space/tt0098798/1/10/w780.jpg",
"id": "tt0098798:1:10",
"released": "1991-01-10T00:00:00.000Z",
"episode": 10
},
{
"name": "Beat the Clock",
"season": 1,
"number": 11,
"firstAired": "1991-01-31T00:00:00.000Z",
"tvdb_id": 291050,
"rating": "6.0",
"thumbnail": "https://episodes.metahub.space/tt0098798/1/11/w780.jpg",
"id": "tt0098798:1:11",
"released": "1991-01-31T00:00:00.000Z",
"episode": 11
},
{
"name": "The Trickster",
"season": 1,
"number": 12,
"firstAired": "1991-02-07T00:00:00.000Z",
"tvdb_id": 291051,
"rating": "7.5",
"thumbnail": "https://episodes.metahub.space/tt0098798/1/12/w780.jpg",
"id": "tt0098798:1:12",
"released": "1991-02-07T00:00:00.000Z",
"episode": 12
},
{
"name": "Tina, Is That You?",
"season": 1,
"number": 13,
"firstAired": "1991-02-14T00:00:00.000Z",
"tvdb_id": 291052,
"rating": "7.0",
"thumbnail": "https://episodes.metahub.space/tt0098798/1/13/w780.jpg",
"id": "tt0098798:1:13",
"released": "1991-02-14T00:00:00.000Z",
"episode": 13
},
{
"name": "Be My Baby",
"season": 1,
"number": 14,
"firstAired": "1991-02-21T00:00:00.000Z",
"tvdb_id": 291053,
"rating": "7.0",
"thumbnail": "https://episodes.metahub.space/tt0098798/1/14/w780.jpg",
"id": "tt0098798:1:14",
"released": "1991-02-21T00:00:00.000Z",
"episode": 14
},
{
"name": "Fast Forward",
"season": 1,
"number": 15,
"firstAired": "1991-02-27T00:00:00.000Z",
"tvdb_id": 291054,
"rating": "6.5",
"thumbnail": "https://episodes.metahub.space/tt0098798/1/15/w780.jpg",
"id": "tt0098798:1:15",
"released": "1991-02-27T00:00:00.000Z",
"episode": 15
},
{
"name": "Deadly Nightshade",
"season": 1,
"number": 16,
"firstAired": "1991-03-30T00:00:00.000Z",
"tvdb_id": 291055,
"rating": "7.0",
"thumbnail": "https://episodes.metahub.space/tt0098798/1/16/w780.jpg",
"id": "tt0098798:1:16",
"released": "1991-03-30T00:00:00.000Z",
"episode": 16
},
{
"name": "Captain Cold",
"season": 1,
"number": 17,
"firstAired": "1991-04-06T00:00:00.000Z",
"tvdb_id": 291056,
"rating": "7.0",
"thumbnail": "https://episodes.metahub.space/tt0098798/1/17/w780.jpg",
"id": "tt0098798:1:17",
"released": "1991-04-06T00:00:00.000Z",
"episode": 17
},
{
"name": "Twin Streaks",
"season": 1,
"number": 18,
"firstAired": "1991-04-13T00:00:00.000Z",
"tvdb_id": 291057,
"rating": "6.0",
"thumbnail": "https://episodes.metahub.space/tt0098798/1/18/w780.jpg",
"id": "tt0098798:1:18",
"released": "1991-04-13T00:00:00.000Z",
"episode": 18
},
{
"name": "Done with Mirrors",
"season": 1,
"number": 19,
"firstAired": "1991-04-27T00:00:00.000Z",
"tvdb_id": 291058,
"rating": "7.0",
"thumbnail": "https://episodes.metahub.space/tt0098798/1/19/w780.jpg",
"id": "tt0098798:1:19",
"released": "1991-04-27T00:00:00.000Z",
"episode": 19
},
{
"name": "Good Night, Central City",
"season": 1,
"number": 20,
"firstAired": "1991-05-04T00:00:00.000Z",
"tvdb_id": 291059,
"rating": "7.0",
"thumbnail": "https://episodes.metahub.space/tt0098798/1/20/w780.jpg",
"id": "tt0098798:1:20",
"released": "1991-05-04T00:00:00.000Z",
"episode": 20
},
{
"name": "Alpha",
"season": 1,
"number": 21,
"firstAired": "1991-05-11T00:00:00.000Z",
"tvdb_id": 291060,
"rating": "7.0",
"thumbnail": "https://episodes.metahub.space/tt0098798/1/21/w780.jpg",
"id": "tt0098798:1:21",
"released": "1991-05-11T00:00:00.000Z",
"episode": 21
},
{
"name": "The Trial of the Trickster",
"season": 1,
"number": 22,
"firstAired": "1991-05-18T00:00:00.000Z",
"tvdb_id": 291061,
"rating": "7.5",
"thumbnail": "https://episodes.metahub.space/tt0098798/1/22/w780.jpg",
"id": "tt0098798:1:22",
"released": "1991-05-18T00:00:00.000Z",
"episode": 22
}
],
"links": [
{
"name": "7.1",
"category": "imdb",
"url": "https://imdb.com/title/tt0098798"
},
{
"name": "The Flash",
"category": "share",
"url": "https://www.strem.io/s/series/the-flash-0098798"
},
{
"name": "Action",
"category": "Genres",
"url": "stremio:///discover/https%3A%2F%2Fv3-cinemeta.strem.io%2Fmanifest.json/series/top?genre=Action"
},
{
"name": "Crime",
"category": "Genres",
"url": "stremio:///discover/https%3A%2F%2Fv3-cinemeta.strem.io%2Fmanifest.json/series/top?genre=Crime"
},
{
"name": "Fantasy",
"category": "Genres",
"url": "stremio:///discover/https%3A%2F%2Fv3-cinemeta.strem.io%2Fmanifest.json/series/top?genre=Fantasy"
},
{
"name": "John Wesley Shipp",
"category": "Cast",
"url": "stremio:///search?search=John%20Wesley%20Shipp"
},
{
"name": "Amanda Pays",
"category": "Cast",
"url": "stremio:///search?search=Amanda%20Pays"
},
{
"name": "Alex Désert",
"category": "Cast",
"url": "stremio:///search?search=Alex%20D%C3%A9sert"
},
{
"name": "Danny Bilson",
"category": "Writers",
"url": "stremio:///search?search=Danny%20Bilson"
},
{
"name": "Paul De Meo",
"category": "Writers",
"url": "stremio:///search?search=Paul%20De%20Meo"
}
],
"behaviorHints": {
"defaultVideoId": null,
"hasScheduledVideos": true
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
import "reflect-metadata"; // required
import { ILoggingService } from '@interfaces/logging_service';
import { CacheService, CacheMethod } from '@services/cache_service';
import {ILoggingService} from '@interfaces/logging_service';
import {CacheMethod, CacheService} from '@services/cache_service';
jest.mock('@services/configuration_service', () => {
return {
@@ -12,7 +12,7 @@ jest.mock('@services/configuration_service', () => {
MONGO_INITDB_ROOT_USERNAME: 'mongo',
MONGO_INITDB_ROOT_PASSWORD: 'mongo',
NO_CACHE: false,
COLLECTION_NAME: 'knightcrawler_consumer_collection',
COLLECTION_NAME: 'knightcrawler_consumer_collection',
},
}
}
@@ -43,8 +43,8 @@ jest.mock('@tirke/node-cache-manager-mongodb', () => {
describe('CacheService Tests', () => {
let cacheService: CacheService,
loggingService: ILoggingService,
cacheMethod: CacheMethod;
loggingService: ILoggingService,
cacheMethod: CacheMethod;
beforeEach(() => {
process.env.LOG_LEVEL = 'debug';
@@ -100,7 +100,7 @@ describe('CacheService Tests', () => {
cacheMethod = jest.fn().mockRejectedValue(new Error('Test error'));
await expect(cacheService.cacheTrackers(cacheMethod)).rejects.toThrow('Test error');
});
it('should handle when cache is disabled', async () => {
jest.mock('@services/configuration_service', () => {
return {

View File

@@ -26,13 +26,13 @@ describe('Configuration Tests', () => {
process.env.IMDB_INTERVAL_MS = '1000';
process.env.JOB_CONCURRENCY = '1';
process.env.JOBS_ENABLED = 'true';
// shitty hack cause jest caches modules and resetModules isnt working
({ configurationService } = await import("@services/configuration_service"));
({configurationService} = await import("@services/configuration_service"));
});
it('should populate cacheConfig correctly', () => {
const { cacheConfig } = configurationService;
const {cacheConfig} = configurationService;
expect(cacheConfig.MONGODB_HOST).toBe('test_mongodb');
expect(cacheConfig.MONGODB_PORT).toBe('27017');
expect(cacheConfig.MONGODB_DB).toBe('knightcrawler');
@@ -44,7 +44,7 @@ describe('Configuration Tests', () => {
});
it('should populate databaseConfig correctly', () => {
const { databaseConfig } = configurationService;
const {databaseConfig} = configurationService;
expect(databaseConfig.POSTGRES_HOST).toBe('postgres');
expect(databaseConfig.POSTGRES_PORT).toBe(5432);
expect(databaseConfig.POSTGRES_DB).toBe('knightcrawler');
@@ -55,33 +55,33 @@ describe('Configuration Tests', () => {
});
it('should populate jobConfig correctly', () => {
const { jobConfig } = configurationService;
const {jobConfig} = configurationService;
expect(jobConfig.JOB_CONCURRENCY).toBe(1);
expect(jobConfig.JOBS_ENABLED).toBe(true);
});
it('should populate metadataConfig correctly', () => {
const { metadataConfig } = configurationService;
const {metadataConfig} = configurationService;
expect(metadataConfig.IMDB_CONCURRENT).toBe(1);
expect(metadataConfig.IMDB_INTERVAL_MS).toBe(1000);
});
it('should populate rabbitConfig correctly', () => {
const { rabbitConfig } = configurationService;
const {rabbitConfig} = configurationService;
expect(rabbitConfig.RABBIT_URI).toBe('amqp://localhost');
expect(rabbitConfig.QUEUE_NAME).toBe('test-queue');
});
it('should populate torrentConfig correctly', () => {
const { torrentConfig } = configurationService;
const {torrentConfig} = configurationService;
expect(torrentConfig.MAX_CONNECTIONS_PER_TORRENT).toBe(20);
expect(torrentConfig.TIMEOUT).toBe(30000);
});
it('should populate trackerConfig correctly', () => {
const { trackerConfig } = configurationService;
const {trackerConfig} = configurationService;
expect(trackerConfig.TRACKERS_URL).toBe('https://ngosang.github.io/trackerslist/trackers_all.txt');
expect(trackerConfig.UDP_ENABLED).toBe(false);
});
});

View File

@@ -1,5 +1,5 @@
import "reflect-metadata"; // required
import { LoggingService } from '@services/logging_service';
import {LoggingService} from '@services/logging_service';
jest.mock('pino', () => {
const actualPino = jest.requireActual('pino');
@@ -14,7 +14,7 @@ jest.mock('pino', () => {
};
});
describe('LoggingService', () => {
describe('LoggingService Tests', () => {
let service: LoggingService,
mockLogger: any;
@@ -24,22 +24,22 @@ describe('LoggingService', () => {
});
it('should log info', () => {
service.info('test message', { key: 'value' });
expect(mockLogger.info).toHaveBeenCalledWith('test message', [{ key: 'value' }]);
service.info('test message', {key: 'value'});
expect(mockLogger.info).toHaveBeenCalledWith('test message', [{key: 'value'}]);
});
it('should log error', () => {
service.error('test message', { key: 'value' });
expect(mockLogger.error).toHaveBeenCalledWith('test message', [{ key: 'value' }]);
service.error('test message', {key: 'value'});
expect(mockLogger.error).toHaveBeenCalledWith('test message', [{key: 'value'}]);
});
it('should log debug', () => {
service.debug('test message', { key: 'value' });
expect(mockLogger.debug).toHaveBeenCalledWith('test message', [{ key: 'value' }]);
service.debug('test message', {key: 'value'});
expect(mockLogger.debug).toHaveBeenCalledWith('test message', [{key: 'value'}]);
});
it('should log warn', () => {
service.warn('test message', { key: 'value' });
expect(mockLogger.warn).toHaveBeenCalledWith('test message', [{ key: 'value' }]);
service.warn('test message', {key: 'value'});
expect(mockLogger.warn).toHaveBeenCalledWith('test message', [{key: 'value'}]);
})
});

View File

@@ -0,0 +1,91 @@
import "reflect-metadata"; // required
import {ICacheService} from "@interfaces/cache_service";
import {IMetadataResponse} from "@interfaces/metadata_response";
import {MetadataService} from "@services/metadata_service";
import {setupServer} from "msw/node";
import * as responses from "./mock-responses/metadata_mock_responses";
jest.mock('@services/cache_service', () => {
return {
cacheWrapImdbId: jest.fn().mockImplementation(async (key, fn) => await fn()),
cacheWrapKitsuId: jest.fn().mockImplementation(async (key, fn) => await fn()),
cacheWrapMetadata: jest.fn().mockImplementation(async (key, fn) => await fn()),
}
})
const server = setupServer(
responses.cinemetaQueryResponse,
responses.cinemetaFlashMetadataSearchTestResponse,
responses.kitsuNarutoIdSearchTestResponse,
responses.kitsuNarutoMetaDataSearchTestResponse,
responses.nameToImdbTheFlash,
responses.checkIfImdbEpisode);
beforeAll(() => server.listen())
beforeEach(() => {
jest.spyOn(Date, 'now').mockImplementation(() => 1234567890);
})
afterEach(() => () => {
server.resetHandlers()
jest.spyOn(Date, 'now').mockRestore();
})
afterAll(() => server.close())
describe('MetadataService Tests', () => {
let metadataService: MetadataService,
mockCacheService: ICacheService;
beforeEach(() => {
mockCacheService = jest.requireMock<ICacheService>('@services/cache_service');
metadataService = new MetadataService(mockCacheService);
});
it("should get kitsu id", async () => {
const result = await metadataService.getKitsuId({
title: 'Naruto',
year: 2002,
season: 1
});
expect(mockCacheService.cacheWrapKitsuId).toHaveBeenCalledWith('naruto 2002 S1', expect.any(Function));
expect(result).not.toBeNull();
expect(result).toEqual('11');
});
it("should get kitsu metadata", async () => {
const result = await metadataService.getMetadata({
id: 'kitsu:11',
type: 'series'
});
expect(mockCacheService.cacheWrapMetadata).toHaveBeenCalledWith('kitsu:11', expect.any(Function));
expect(result).not.toBeNull();
const body = result as IMetadataResponse;
expect(body.videos).not.toBeNull();
expect(body.videos.length).toBe(220);
});
it("should get imdb metadata", async () => {
const result = await metadataService.getMetadata({
id: 'tt0098798',
type: 'series'
});
expect(mockCacheService.cacheWrapMetadata).toHaveBeenCalledWith('tt0098798', expect.any(Function));
expect(result).not.toBeNull();
const body = result as IMetadataResponse;
expect(body.videos).not.toBeNull();
expect(body.videos.length).toBe(22);
});
it("should check if imdb id is an episode", async () => {
const result = await metadataService.isEpisodeImdbId('tt0579968');
expect(result).toBe(true);
});
it("should escape title", () => {
const result = metadataService.escapeTitle('Naruto: Shippuden');
expect(result).toEqual('naruto shippuden');
});
});

View File

@@ -0,0 +1,40 @@
import {http, HttpResponse} from "msw";
import cinemetaQuery from "../assets/cinemeta-query-response.json";
import cinemetaFlashFull from "../assets/flash-episode-list.json";
import kitsuNarutoFull from "../assets/kitsu-naruto-full.json";
import imdbTheFlash from "../assets/name-to-imdb-flash.json";
import kitsuNarutoSearchId from "../assets/test-kitsu-search-id-naruto.json";
const kitsuNarutoIdSearchTestResponse = http.get('https://anime-kitsu.strem.fun/catalog/series/kitsu-anime-list/search=naruto%202002%20S1.json', () => {
return HttpResponse.json(kitsuNarutoSearchId);
});
const kitsuNarutoMetaDataSearchTestResponse = http.get('https://anime-kitsu.strem.fun/meta/Series/kitsu:11.json', () => {
return HttpResponse.json(kitsuNarutoFull);
});
const nameToImdbTheFlash = http.get('https://sg.media-imdb.com/suggests/t/the%20flash%201990.json', () => {
const jsonpResponse = `/**/imdb$the_flash%201990(${JSON.stringify(imdbTheFlash)});`;
return HttpResponse.json(jsonpResponse);
});
const cinemetaQueryResponse = http.get('https://cinemeta.strem.io/stremioget/stremio/v1/q.json', () => {
return HttpResponse.json(cinemetaQuery);
});
const cinemetaFlashMetadataSearchTestResponse = http.get('https://v3-cinemeta.strem.io/meta/imdb/tt0098798.json', () => {
return HttpResponse.json(cinemetaFlashFull);
});
const checkIfImdbEpisode = http.get('https://www.imdb.com/title/tt0579968/', () => {
return HttpResponse.text('<meta property="og:type" content="video.episode">');
});
export {
kitsuNarutoIdSearchTestResponse,
kitsuNarutoMetaDataSearchTestResponse,
nameToImdbTheFlash,
cinemetaQueryResponse,
cinemetaFlashMetadataSearchTestResponse,
checkIfImdbEpisode
}

View File

@@ -0,0 +1,5 @@
import {http, HttpResponse} from "msw";
export const trackerTestResponse = http.get('https://ngosang.github.io/trackerslist/trackers_all.txt', () => {
return HttpResponse.text('http://tracker1.com\nhttp://tracker2.com')
});

View File

@@ -1,8 +1,8 @@
import "reflect-metadata"; // required
import { ILoggingService } from '@interfaces/logging_service';
import { ITorrentProcessingService } from '@interfaces/torrent_processing_service';
import { ProcessTorrentsJob } from '@jobs/process_torrents_job';
import { configurationService } from '@services/configuration_service';
import {ILoggingService} from '@interfaces/logging_service';
import {ITorrentProcessingService} from '@interfaces/torrent_processing_service';
import {ProcessTorrentsJob} from '@jobs/process_torrents_job';
import {configurationService} from '@services/configuration_service';
import client, {ConsumeMessage} from 'amqplib';
jest.mock('@services/configuration_service', () => {
@@ -48,7 +48,7 @@ jest.mock('@services/torrent_processing_service', () => {
})
describe('ProcessTorrentsJob Tests', () => {
let processTorrentsJob: ProcessTorrentsJob,
let processTorrentsJob: ProcessTorrentsJob,
loggingService: ILoggingService,
torrentProcessingService: ITorrentProcessingService;
@@ -60,7 +60,7 @@ describe('ProcessTorrentsJob Tests', () => {
afterEach(() => {
jest.clearAllMocks()
})
})
describe('listenToQueue', () => {
test('should connect to the rabbitmq server and create a channel', async () => {
@@ -79,7 +79,7 @@ describe('ProcessTorrentsJob Tests', () => {
test('should process messages from the queue', async () => {
const mockMessage = {
content: Buffer.from(JSON.stringify({
content: Buffer.from(JSON.stringify({
message: {
name: 'test_name',
source: 'test_source',
@@ -90,7 +90,7 @@ describe('ProcessTorrentsJob Tests', () => {
leechers: 0,
imdb: 'test_imdb',
processed: false,
}
}
})),
} as ConsumeMessage;

View File

@@ -1,7 +1,7 @@
import "reflect-metadata"; // required
import { ILoggingService } from '@interfaces/logging_service';
import {ILoggingService} from '@interfaces/logging_service';
import {IParsedTorrent} from "@interfaces/parsed_torrent";
import { TorrentDownloadService } from '@services/torrent_download_service';
import {TorrentDownloadService} from '@services/torrent_download_service';
import torrentStream from 'torrent-stream';
jest.mock('@services/logging_service', () => {
@@ -22,7 +22,7 @@ jest.mock('torrent-stream', () => {
describe('TorrentDownloadService', () => {
let torrentDownloadService: TorrentDownloadService,
mockLoggingService: ILoggingService;
mockLoggingService: ILoggingService;
beforeEach(() => {
mockLoggingService = jest.requireMock<ILoggingService>('@services/logging_service');
@@ -42,7 +42,7 @@ describe('TorrentDownloadService', () => {
uploadDate: new Date(),
seeders: 100,
torrentId: 'torrent1',
fileCollection: { },
fileCollection: {},
title: 'Test Movie',
year: 2020,
season: 1,
@@ -58,7 +58,7 @@ describe('TorrentDownloadService', () => {
container: 'mp4',
unrated: false,
};
const mockFiles = [
{
name: 'file1.mp4',
@@ -134,7 +134,7 @@ describe('TorrentDownloadService', () => {
fileId: file.fileIndex,
})),
});
expect(torrentStream).toHaveBeenCalledWith(expect.any(String), expect.any(Object));
expect(mockLoggingService.debug).toHaveBeenCalledWith(`Adding torrent with infoHash ${mockTorrent.infoHash} to torrent engine...`);
});

View File

@@ -1,15 +1,11 @@
import "reflect-metadata"; // required
import { ICacheService } from '@interfaces/cache_service';
import { ILoggingService } from '@interfaces/logging_service';
import { TrackerService } from '@services/tracker_service';
import { http, HttpResponse } from 'msw'
import { setupServer } from 'msw/node';
import {ICacheService} from '@interfaces/cache_service';
import {ILoggingService} from '@interfaces/logging_service';
import {TrackerService} from '@services/tracker_service';
import {setupServer} from 'msw/node';
import * as responses from "./mock-responses/trackers_mock_responses";
const server = setupServer(
http.get('https://ngosang.github.io/trackerslist/trackers_all.txt', ({ request, params, cookies }) => {
return HttpResponse.text('http://tracker1.com\nhttp://tracker2.com')
}),
)
const server = setupServer(responses.trackerTestResponse);
jest.mock('@services/logging_service', () => {
return {
@@ -38,8 +34,8 @@ afterAll(() => server.close())
describe('TrackerService', () => {
let trackerService: TrackerService,
mockCacheService: ICacheService,
mockLoggingService: ILoggingService;
mockCacheService: ICacheService,
mockLoggingService: ILoggingService;
beforeEach(() => {
mockCacheService = jest.requireMock<ICacheService>('@services/cache_service');
@@ -54,6 +50,6 @@ describe('TrackerService', () => {
expect(result).toEqual(mockTrackers);
expect(mockLoggingService.info).toHaveBeenCalledWith(`Trackers updated at 1234567890: ${mockTrackers.length} trackers`);
});
});

View File

@@ -2,8 +2,12 @@
"compileOnSave": false,
"compilerOptions": {
"target": "ESNext",
"lib": ["ESNext"],
"typeRoots": ["node_modules/@types"],
"lib": [
"ESNext"
],
"typeRoots": [
"node_modules/@types"
],
"allowSyntheticDefaultImports": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
@@ -21,16 +25,37 @@
"importHelpers": true,
"baseUrl": "src",
"paths": {
"@/*": ["*"],
"@enums/*": ["lib/enums/*"],
"@repository/*": ["lib/repository/*"],
"@interfaces/*": ["lib/interfaces/*"],
"@models/*": ["lib/models/*"],
"@services/*": ["lib/services/*"],
"@helpers/*": ["lib/helpers/*"],
"@jobs/*": ["lib/jobs/*"]
"@/*": [
"*"
],
"@enums/*": [
"lib/enums/*"
],
"@repository/*": [
"lib/repository/*"
],
"@interfaces/*": [
"lib/interfaces/*"
],
"@models/*": [
"lib/models/*"
],
"@services/*": [
"lib/services/*"
],
"@helpers/*": [
"lib/helpers/*"
],
"@jobs/*": [
"lib/jobs/*"
]
}
},
"include": ["src", "test"],
"exclude": ["node_modules"]
"include": [
"src",
"test"
],
"exclude": [
"node_modules"
]
}