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 dccede4..be2641c 100644 --- a/src/node/consumer/src/lib/services/torrent_subtitle_service.ts +++ b/src/node/consumer/src/lib/services/torrent_subtitle_service.ts @@ -10,8 +10,14 @@ export 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: []}; + const matchingSubtitles = fileCollection.subtitles.filter(subtitle => + this.mostProbableSubtitleVideos(subtitle, [fileCollection.videos[0]]).length > 0 + ); + fileCollection.videos[0].subtitles = matchingSubtitles; + const nonMatchingSubtitles = fileCollection.subtitles.filter(subtitle => + !matchingSubtitles.includes(subtitle) + ); + return {...fileCollection, subtitles: nonMatchingSubtitles}; } const parsedVideos = fileCollection.videos.map(video => this.parseVideo(video)); @@ -32,12 +38,11 @@ export class TorrentSubtitleService implements ITorrentSubtitleService { private parseVideo = (video: IFileAttributes): IFileAttributes => { const fileName = video.title?.split('/')?.pop()?.replace(/\.(\w{2,4})$/, '') || ''; const folderName = video.title?.replace(/\/?[^/]+$/, '') || ''; - return { - videoFile: video, + return Object.assign(video, { fileName: fileName, folderName: folderName, ...this.parseFilename(video.title.toString() || '') - }; + }); } private mostProbableSubtitleVideos = (subtitle: ISubtitleAttributes, parsedVideos: IFileAttributes[]): IFileAttributes[] => { @@ -48,13 +53,15 @@ export class TorrentSubtitleService implements ITorrentSubtitleService { return byFileName.map(v => v); } const byTitleSeasonEpisode = parsedVideos.filter(video => video.title === parsedSub.title - && this.arrayEquals(video.seasons || [], parsedSub.seasons || []) - && this.arrayEquals(video.episodes || [], parsedSub.episodes || [])); + && parsedSub.seasons && parsedSub.episodes + && this.arrayEquals(video.seasons || [], parsedSub.seasons) + && this.arrayEquals(video.episodes || [], parsedSub.episodes)); if (this.singleVideoFile(byTitleSeasonEpisode)) { return byTitleSeasonEpisode.map(v => v); } - const bySeasonEpisode = parsedVideos.filter(video => this.arrayEquals(video.seasons || [], parsedSub.seasons || []) - && this.arrayEquals(video.episodes || [], parsedSub.episodes || [])); + const bySeasonEpisode = parsedVideos.filter(video => parsedSub.seasons && parsedSub.episodes + && this.arrayEquals(video.seasons || [], parsedSub.seasons) + && this.arrayEquals(video.episodes || [], parsedSub.episodes)); if (this.singleVideoFile(bySeasonEpisode)) { return bySeasonEpisode.map(v => v); } @@ -62,10 +69,15 @@ export class TorrentSubtitleService implements ITorrentSubtitleService { if (this.singleVideoFile(byTitle)) { return byTitle.map(v => v); } - const byEpisode = parsedVideos.filter(video => this.arrayEquals(video.episodes || [], parsedSub.episodes || [])); + const byEpisode = parsedVideos.filter(video => parsedSub.episodes + && this.arrayEquals(video.episodes || [], parsedSub.episodes || [])); if (this.singleVideoFile(byEpisode)) { return byEpisode.map(v => v); } + const byInfoHash = parsedVideos.filter(video => video.infoHash === subtitle.infoHash); + if (this.singleVideoFile(byInfoHash)) { + return byInfoHash.map(v => v); + } return []; } diff --git a/src/node/consumer/test/torrent_subtitle_service.test.ts b/src/node/consumer/test/torrent_subtitle_service.test.ts new file mode 100644 index 0000000..d4c097d --- /dev/null +++ b/src/node/consumer/test/torrent_subtitle_service.test.ts @@ -0,0 +1,102 @@ +import "reflect-metadata"; // required +import {ITorrentFileCollection} from "@interfaces/torrent_file_collection"; +import {TorrentSubtitleService} from "@services/torrent_subtitle_service"; + +describe('TrrentSubtitleService tests', () => { + let torrentSubtitleService: TorrentSubtitleService; + + beforeEach(() => { + jest.clearAllMocks(); + torrentSubtitleService = new TorrentSubtitleService(); + }); + + it('should assign subtitles to a single video', () => { + const fileCollection: ITorrentFileCollection = { + videos: [{ title: 'Test video', size: 123456, imdbId: 'tt1234567', infoHash: 'Test infoHash' }], + contents: [], + subtitles: [{ title: 'Test subtitle', fileIndex: 0, path: 'Test path', infoHash: 'Test infoHash' }], + }; + + const result = torrentSubtitleService.assignSubtitles(fileCollection); + + expect(result.videos[0].subtitles).toEqual(fileCollection.subtitles); + expect(result.subtitles).toEqual([]); + }); + + it('should not assign subtitles if there are no videos', () => { + const fileCollection: ITorrentFileCollection = { + videos: [], + contents: [], + subtitles: [{ title: 'Test subtitle', fileIndex: 0, path: 'Test path', infoHash: 'Test infoHash' }], + }; + + const result = torrentSubtitleService.assignSubtitles(fileCollection); + + expect(result).toEqual(fileCollection); + }); + + it('should not assign subtitles if there are no subtitles', () => { + const fileCollection: ITorrentFileCollection = { + videos: [{ title: 'Test video', size: 123456, imdbId: 'tt1234567', infoHash: 'Test infoHash' }], + contents: [], + subtitles: [], + }; + + const result = torrentSubtitleService.assignSubtitles(fileCollection); + + expect(result).toEqual(fileCollection); + }); + + it('should assign subtitles to multiple videos', () => { + const fileCollection: ITorrentFileCollection = { + videos: [ + { title: 'Test video S01E01', size: 123456, imdbId: 'tt1234567', infoHash: 'Test infoHash' }, + { title: 'Test video S01E02', size: 123456, imdbId: 'tt1234567', infoHash: 'Test infoHash' } + ], + contents: [], + subtitles: [ + { title: 'Test subtitle S01E01', fileIndex: 0, path: 'Test path', infoHash: 'Test infoHash' }, + { title: 'Test subtitle S01E02', fileIndex: 1, path: 'Test path', infoHash: 'Test infoHash' } + ], + }; + + const result = torrentSubtitleService.assignSubtitles(fileCollection); + + expect(result.videos[0].subtitles).toEqual([fileCollection.subtitles[0]]); + expect(result.videos[1].subtitles).toEqual([fileCollection.subtitles[1]]); + expect(result.subtitles).toEqual([]); + }); + + it('should not assign subtitles if there are no matching videos', () => { + const fileCollection: ITorrentFileCollection = { + videos: [{ title: 'Test video', size: 123456, imdbId: 'tt1234567', infoHash: 'Test infoHash' }], + contents: [], + subtitles: [{ title: 'Non-matching subtitle', fileIndex: 0, path: 'Test path', infoHash: 'Non-matching infoHash' }], + }; + + const result = torrentSubtitleService.assignSubtitles(fileCollection); + + expect(result.videos[0].subtitles).toEqual([]); + expect(result.subtitles).toEqual([fileCollection.subtitles[0]]); + }); + + it('should assign subtitles to the most probable videos based on filename, title, season, and episode', () => { + const fileCollection: ITorrentFileCollection = { + videos: [ + { title: 'Test video S01E01', size: 123456, imdbId: 'tt1234567', infoHash: 'Test infoHash' }, + { title: 'Test video S01E02', size: 123456, imdbId: 'tt1234567', infoHash: 'Test infoHash' } + ], + contents: [], + subtitles: [ + { title: 'Test subtitle S01E01', fileIndex: 0, path: 'Test path', infoHash: 'Test infoHash' }, + { title: 'Test subtitle S01E02', fileIndex: 1, path: 'Test path', infoHash: 'Test infoHash' } + ], + }; + + const result = torrentSubtitleService.assignSubtitles(fileCollection); + + expect(result.videos[0].subtitles).toEqual([fileCollection.subtitles[0]]); + expect(result.videos[1].subtitles).toEqual([fileCollection.subtitles[1]]); + expect(result.subtitles).toEqual([]); + }); +}); \ No newline at end of file