diff --git a/Find Unique Titles/config/userscript.config.ts b/Find Unique Titles/config/userscript.config.ts index 722fbab..53c8b47 100644 --- a/Find Unique Titles/config/userscript.config.ts +++ b/Find Unique Titles/config/userscript.config.ts @@ -60,6 +60,7 @@ export const UserScriptConfig: IWebpackUserScript = { "https://torrentseeds.org/categories/*", "https://www.morethantv.me/torrents/browse*", "https://jpopsuki.eu/torrents.php*", + "https://tntracker.org/*", ], require: [ `https://cdn.jsdelivr.net/npm/jquery@${pkg.dependencies.jquery}/dist/jquery.min.js`, diff --git a/Find Unique Titles/dist/find.unique.titles.user.js b/Find Unique Titles/dist/find.unique.titles.user.js index 5cdb899..88a785d 100644 --- a/Find Unique Titles/dist/find.unique.titles.user.js +++ b/Find Unique Titles/dist/find.unique.titles.user.js @@ -35,6 +35,7 @@ // @match https://torrentseeds.org/categories/* // @match https://www.morethantv.me/torrents/browse* // @match https://jpopsuki.eu/torrents.php* +// @match https://tntracker.org/*/?perPage=* // @grant GM.xmlHttpRequest // @grant GM.setValue // @grant GM.getValue @@ -1162,11 +1163,6 @@ if (categoryLogo.includes("TV-")) return _tracker__WEBPACK_IMPORTED_MODULE_0__.Category.TV; if (categoryLogo.includes("Music-")) return _tracker__WEBPACK_IMPORTED_MODULE_0__.Category.MUSIC; }; - const parseYear = title => { - const regex = /-(\d{4})-/; - const match = title.match(regex); - if (match) return match[1]; - }; class CG { canBeUsedAsSource() { return true; @@ -1211,7 +1207,7 @@ const splittedTitle = torrentTitle.split("-"); const artists = [ splittedTitle[0] ]; const titles = [ splittedTitle[1] ]; - const year = parseYear(torrentTitle); + const year = (0, _utils_utils__WEBPACK_IMPORTED_MODULE_1__.parseYear)(torrentTitle); const {container, format} = (0, _utils_utils__WEBPACK_IMPORTED_MODULE_1__.parseContainerAndFormat)(torrentTitle); request = { ...request, @@ -2036,7 +2032,25 @@ default: () => RED }); var _tracker__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("./src/trackers/tracker.ts"); - var common_http__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__("../common/dist/http/index.mjs"); + var common_http__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__("../common/dist/http/index.mjs"); + var common_logger__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__("../common/dist/logger/index.mjs"); + const parseTorrents = html => { + const groups = Array.from(html.querySelectorAll(".group_info strong")); + const torrents = []; + for (let group of groups) { + const yearAndTitle = group?.textContent?.split(" - "); + if (yearAndTitle) torrents.push({ + year: parseInt(yearAndTitle[0], 10), + title: yearAndTitle[1] + }); + } + return torrents; + }; + function titlesAreEqual(first, second) { + first = first.normalize("NFD").replace(/[\u0300-\u036f]/g, ""); + second = second.normalize("NFD").replace(/[\u0300-\u036f]/g, ""); + return first.toLowerCase() == second.toLowerCase(); + } class RED { canBeUsedAsSource() { return false; @@ -2059,12 +2073,20 @@ if (request.category != _tracker__WEBPACK_IMPORTED_MODULE_0__.Category.MUSIC) return _tracker__WEBPACK_IMPORTED_MODULE_0__.SearchResult.NOT_ALLOWED; const musicRequest = request; if (musicRequest.type != _tracker__WEBPACK_IMPORTED_MODULE_0__.MusicReleaseType.ALBUM && musicRequest.type != _tracker__WEBPACK_IMPORTED_MODULE_0__.MusicReleaseType.SINGLE) return _tracker__WEBPACK_IMPORTED_MODULE_0__.SearchResult.NOT_ALLOWED; - if (!musicRequest.artists || !musicRequest.titles || !musicRequest.year) return _tracker__WEBPACK_IMPORTED_MODULE_0__.SearchResult.NOT_CHECKED; + if (!musicRequest.artists || !musicRequest.titles || !musicRequest.year) { + common_logger__WEBPACK_IMPORTED_MODULE_1__.logger.debug("[{0}] Not enough data to check request: {1}", this.name(), request); + return _tracker__WEBPACK_IMPORTED_MODULE_0__.SearchResult.NOT_CHECKED; + } for (let artist of musicRequest.artists) for (let title of musicRequest.titles) { if ("VA" === artist) artist = ""; - const queryUrl = `https://redacted.ch/torrents.php?artistname=${encodeURIComponent(artist)}&groupname=${encodeURIComponent(title)}&year=${musicRequest.year}&order_by=time&order_way=desc&group_results=1&filter_cat%5B1%5D=1&action=advanced&searchsubmit=1`; - const result = await (0, common_http__WEBPACK_IMPORTED_MODULE_1__.fetchAndParseHtml)(queryUrl); - if (!result.textContent?.includes("Your search did not match anything.")) return _tracker__WEBPACK_IMPORTED_MODULE_0__.SearchResult.EXIST; + if (artist) { + const queryUrl = `https://redacted.ch/artist.php?artistname=${encodeURIComponent(artist)}`; + const result = await (0, common_http__WEBPACK_IMPORTED_MODULE_2__.fetchAndParseHtml)(queryUrl); + if (result.textContent?.includes("Your search did not match anything.") || result.querySelector("#search_terms")) return _tracker__WEBPACK_IMPORTED_MODULE_0__.SearchResult.NOT_EXIST; + const torrents = parseTorrents(result); + common_logger__WEBPACK_IMPORTED_MODULE_1__.logger.debug("[{0}] Parsed following torrents for artist: {1}: {2}", this.name(), artist, torrents); + for (let torrent of torrents) if (torrent.year == request.year && titlesAreEqual(title, torrent.title)) return _tracker__WEBPACK_IMPORTED_MODULE_0__.SearchResult.EXIST; + } } return _tracker__WEBPACK_IMPORTED_MODULE_0__.SearchResult.NOT_EXIST; } @@ -2232,6 +2254,121 @@ } } }, + "./src/trackers/TNT.ts": (__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + __webpack_require__.d(__webpack_exports__, { + default: () => TNT + }); + var _utils_utils__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__("./src/utils/utils.ts"); + var _tracker__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("./src/trackers/tracker.ts"); + var common_dom__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__("../common/dist/dom/index.mjs"); + var common_http__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__("../common/dist/http/index.mjs"); + var common_logger__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__("../common/dist/logger/index.mjs"); + const parseCategory = category => { + if ([ 24, 18, 17, 20, 19, 34, 36, 45, 22, 37, 35, 43, 38, 39, 46 ].includes(category)) return _tracker__WEBPACK_IMPORTED_MODULE_0__.Category.MOVIE; + if ([ 27, 28, 16, 2, 29, 40, 41, 42 ].includes(category)) return _tracker__WEBPACK_IMPORTED_MODULE_0__.Category.TV; + if ([ 10, 12, 13, 14 ].includes(category)) return _tracker__WEBPACK_IMPORTED_MODULE_0__.Category.GAME; + if ([ 1 ].includes(category)) return _tracker__WEBPACK_IMPORTED_MODULE_0__.Category.AUDIOBOOK; + if ([ 8 ].includes(category)) return _tracker__WEBPACK_IMPORTED_MODULE_0__.Category.BOOK; + if ([ 2, 41 ].includes(category)) return _tracker__WEBPACK_IMPORTED_MODULE_0__.Category.ANIME; + if ([ 44, 25, 26 ].includes(category)) return _tracker__WEBPACK_IMPORTED_MODULE_0__.Category.MUSIC; + }; + const getTorrentTitle = element => { + const h5 = element.children[1].querySelector("h5"); + const newTag = h5.querySelector(".newTag"); + if (newTag) h5.removeChild(newTag); + return h5.textContent; + }; + class TNT { + canBeUsedAsSource() { + return true; + } + canBeUsedAsTarget() { + return false; + } + canRun(url) { + return url.includes("tntracker.org") && url.includes("/?perPage="); + } + async* getSearchRequest() { + common_logger__WEBPACK_IMPORTED_MODULE_1__.logger.debug("[{0}] Parsing titles to check", this.name()); + const elements = Array.from(document.querySelectorAll("#table_gen_wrapper .sTable tbody tr")); + yield { + total: elements.length + }; + for (let element of elements) { + const torrentTitle = getTorrentTitle(element); + common_logger__WEBPACK_IMPORTED_MODULE_1__.logger.debug("[TNT] Checking torrent: {0}", torrentTitle); + const torrentId = element.children[1].querySelector("a").href.split("/").pop(); + const token = JSON.parse(localStorage.getItem("ngStorage-token")); + const data = await (0, common_http__WEBPACK_IMPORTED_MODULE_2__.fetchAndParseJson)(`https://tntracker.org/api/torrent?id=${torrentId}`, { + headers: { + Authorization: token + } + }); + const imdbId = "tt" + data.imdb; + const size = data.size / 1024 / 1024; + const category = parseCategory(data.category); + let title; + let year; + let request = { + dom: [ element ], + category + }; + if (category == _tracker__WEBPACK_IMPORTED_MODULE_0__.Category.MOVIE) { + ({title, year} = (0, _utils_utils__WEBPACK_IMPORTED_MODULE_3__.parseYearAndTitle)(torrentTitle)); + request = { + ...request, + torrents: [ { + size, + tags: (0, _utils_utils__WEBPACK_IMPORTED_MODULE_3__.parseTags)(torrentTitle), + dom: element, + resolution: (0, _utils_utils__WEBPACK_IMPORTED_MODULE_3__.parseResolution)(torrentTitle), + container: (0, _utils_utils__WEBPACK_IMPORTED_MODULE_3__.parseCodec)(torrentTitle) + } ], + year, + title: title?.replace(".", " "), + imdbId + }; + } else if (category == _tracker__WEBPACK_IMPORTED_MODULE_0__.Category.MUSIC) { + const splittedTitle = torrentTitle.split("-"); + const artists = [ splittedTitle[0].replaceAll("_", " ") ]; + const titles = [ splittedTitle[1].replaceAll("_", " ") ]; + const year = (0, _utils_utils__WEBPACK_IMPORTED_MODULE_3__.parseYear)(torrentTitle); + const {container, format} = (0, _utils_utils__WEBPACK_IMPORTED_MODULE_3__.parseContainerAndFormat)(torrentTitle); + request = { + ...request, + torrents: [ { + dom: element, + size, + format, + container + } ], + artists, + titles, + year, + type: _tracker__WEBPACK_IMPORTED_MODULE_0__.MusicReleaseType.ALBUM + }; + } + yield request; + } + } + name() { + return "TNT"; + } + async search(request) { + return _tracker__WEBPACK_IMPORTED_MODULE_0__.SearchResult.NOT_CHECKED; + } + async insertTrackersSelect(select) { + await (0, common_http__WEBPACK_IMPORTED_MODULE_2__.sleep)(3e3); + if (document.getElementById("find-unique-titles")) return; + const form = document.getElementById("search").parentElement; + const wrapper = document.createElement("div"); + wrapper.classList.add("selector"); + wrapper.id = "find-unique-titles"; + (0, common_dom__WEBPACK_IMPORTED_MODULE_4__.addChild)(wrapper, select); + (0, common_dom__WEBPACK_IMPORTED_MODULE_4__.addChild)(form, wrapper); + } + } + }, "./src/trackers/TSeeds.ts": (__unused_webpack_module, __webpack_exports__, __webpack_require__) => { __webpack_require__.d(__webpack_exports__, { default: () => TSeeds @@ -2441,6 +2578,7 @@ RED: () => _RED__WEBPACK_IMPORTED_MODULE_28__.default, SC: () => _SC__WEBPACK_IMPORTED_MODULE_1__.default, TL: () => _TL__WEBPACK_IMPORTED_MODULE_17__.default, + TNT: () => _TNT__WEBPACK_IMPORTED_MODULE_29__.default, TSeeds: () => _TSeeds__WEBPACK_IMPORTED_MODULE_25__.default, TiK: () => _TiK__WEBPACK_IMPORTED_MODULE_23__.default, nCore: () => _nCore__WEBPACK_IMPORTED_MODULE_20__.default @@ -2451,29 +2589,30 @@ var _BLU__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__("./src/trackers/BLU.ts"); var _BTarg__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__("./src/trackers/BTarg.ts"); var _CG__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__("./src/trackers/CG.ts"); + var _CHD__WEBPACK_IMPORTED_MODULE_21__ = __webpack_require__("./src/trackers/CHD.ts"); var _CLAN_SUD__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__("./src/trackers/CLAN-SUD.ts"); var _CinemaZ__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__("./src/trackers/CinemaZ.ts"); var _FL__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__("./src/trackers/FL.ts"); var _GPW__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__("./src/trackers/GPW.ts"); var _HDB__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__("./src/trackers/HDB.ts"); + var _HDSky__WEBPACK_IMPORTED_MODULE_22__ = __webpack_require__("./src/trackers/HDSky.ts"); var _HDT__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__("./src/trackers/HDT.ts"); var _IPT__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__("./src/trackers/IPT.ts"); var _JPTV__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__("./src/trackers/JPTV.ts"); + var _JPop__WEBPACK_IMPORTED_MODULE_27__ = __webpack_require__("./src/trackers/JPop.ts"); var _KG__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__("./src/trackers/KG.ts"); + var _MTV__WEBPACK_IMPORTED_MODULE_26__ = __webpack_require__("./src/trackers/MTV.ts"); + var _MTeam__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__("./src/trackers/MTeam.ts"); var _NewInsane__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__("./src/trackers/NewInsane.ts"); var _PTP__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("./src/trackers/PTP.ts"); + var _Pter__WEBPACK_IMPORTED_MODULE_24__ = __webpack_require__("./src/trackers/Pter.ts"); + var _RED__WEBPACK_IMPORTED_MODULE_28__ = __webpack_require__("./src/trackers/RED.ts"); var _SC__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__("./src/trackers/SC.ts"); - var _TiK__WEBPACK_IMPORTED_MODULE_23__ = __webpack_require__("./src/trackers/TiK.ts"); var _TL__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__("./src/trackers/TL.ts"); - var _MTeam__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__("./src/trackers/MTeam.ts"); - var _nCore__WEBPACK_IMPORTED_MODULE_20__ = __webpack_require__("./src/trackers/nCore.ts"); - var _CHD__WEBPACK_IMPORTED_MODULE_21__ = __webpack_require__("./src/trackers/CHD.ts"); - var _HDSky__WEBPACK_IMPORTED_MODULE_22__ = __webpack_require__("./src/trackers/HDSky.ts"); - var _Pter__WEBPACK_IMPORTED_MODULE_24__ = __webpack_require__("./src/trackers/Pter.ts"); + var _TNT__WEBPACK_IMPORTED_MODULE_29__ = __webpack_require__("./src/trackers/TNT.ts"); var _TSeeds__WEBPACK_IMPORTED_MODULE_25__ = __webpack_require__("./src/trackers/TSeeds.ts"); - var _MTV__WEBPACK_IMPORTED_MODULE_26__ = __webpack_require__("./src/trackers/MTV.ts"); - var _JPop__WEBPACK_IMPORTED_MODULE_27__ = __webpack_require__("./src/trackers/JPop.ts"); - var _RED__WEBPACK_IMPORTED_MODULE_28__ = __webpack_require__("./src/trackers/RED.ts"); + var _TiK__WEBPACK_IMPORTED_MODULE_23__ = __webpack_require__("./src/trackers/TiK.ts"); + var _nCore__WEBPACK_IMPORTED_MODULE_20__ = __webpack_require__("./src/trackers/nCore.ts"); var __webpack_async_dependencies__ = __webpack_handle_async_dependencies__([ _PTP__WEBPACK_IMPORTED_MODULE_0__ ]); _PTP__WEBPACK_IMPORTED_MODULE_0__ = (__webpack_async_dependencies__.then ? (await __webpack_async_dependencies__)() : __webpack_async_dependencies__)[0]; __webpack_async_result__(); @@ -2695,6 +2834,7 @@ parseResolution: () => parseResolution, parseSize: () => parseSize, parseTags: () => parseTags, + parseYear: () => parseYear, parseYearAndTitle: () => parseYearAndTitle, parseYearAndTitleFromReleaseName: () => parseYearAndTitleFromReleaseName }); @@ -2780,15 +2920,17 @@ title: void 0, year: void 0 }; - const regex = /^(.*?)\s+\(?(\d{4})\)?\s+(.*)/; - const match = title.match(regex); - if (match) { - const title = match[1].trim(); - const year = parseInt(match[2], 10); - return { - title, - year - }; + const regexes = [ /^(.*?)\s+\(?(\d{4})\)?\s+(.*)/, /(.+?)\.(\d\d\d\d)/ ]; + for (let regex of regexes) { + const match = title.match(regex); + if (match) { + const title = match[1].trim(); + const year = parseInt(match[2], 10); + return { + title, + year + }; + } } return { title: void 0, @@ -2808,6 +2950,11 @@ }; return result; }; + const parseYear = title => { + const regex = /-(\d{4})-/; + const match = title.match(regex); + if (match) return parseInt(match[1], 10); + }; }, "../common/dist/dom/index.mjs": (__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { __webpack_require__.d(__webpack_exports__, { @@ -2857,11 +3004,12 @@ "../common/dist/http/index.mjs": (__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { __webpack_require__.d(__webpack_exports__, { fetchAndParseHtml: () => fetchAndParseHtml, + fetchAndParseJson: () => fetchAndParseJson, fetchUrl: () => fetchUrl, sleep: () => sleep }); - var _trim21_gm_fetch__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__("../node_modules/@trim21/gm-fetch/dist/index.mjs"); var _logger_index_mjs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("../common/dist/logger/index.mjs"); + var _trim21_gm_fetch__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__("../node_modules/@trim21/gm-fetch/dist/index.mjs"); const parser = new DOMParser; const fetchUrl = async (input, options = {}, wait = 1e3) => { await sleep(wait); @@ -2873,6 +3021,10 @@ const response = await fetchUrl(query_url); return parser.parseFromString(response, "text/html").body; }; + const fetchAndParseJson = async (query_url, options = {}) => { + const response = await fetchUrl(query_url, options); + return JSON.parse(response); + }; const sleep = ms => new Promise((resolve => setTimeout(resolve, ms))); }, "../common/dist/logger/index.mjs": (__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { @@ -2907,9 +3059,21 @@ return `${logger.prefix} ${levelPrefix} "` + message.replace(/{(\d+)}/g, ((match, index) => { const argIndex = parseInt(index, 10); const argValue = args[argIndex]; - return "object" == typeof argValue ? JSON.stringify(argValue) : argValue; + return "object" == typeof argValue ? stringifyWithoutDOM(argValue) : argValue; })).trim(); }; + const stringifyWithoutDOM = obj => { + const seen = new WeakSet; + function replacer(key, value) { + if (value instanceof Node) return; + if ("object" == typeof value && null !== value) { + if (seen.has(value)) return "[Circular Reference]"; + seen.add(value); + } + return value; + } + return JSON.stringify(obj, replacer); + }; }, "../common/dist/searcher/index.mjs": (__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { __webpack_require__.d(__webpack_exports__, { diff --git a/Find Unique Titles/src/trackers/IPT.ts b/Find Unique Titles/src/trackers/IPT.ts index e7c9617..2a6e9a0 100644 --- a/Find Unique Titles/src/trackers/IPT.ts +++ b/Find Unique Titles/src/trackers/IPT.ts @@ -4,6 +4,7 @@ import { parseResolution, parseSize, parseTags, + parseYear, parseYearAndTitle, } from "../utils/utils"; import { @@ -20,16 +21,12 @@ import { fetchAndParseHtml } from "common/http"; const parseCategory = (element: HTMLElement): Category => { let categoryImg = element.children[0].querySelector("img")!!; const categoryLogo = categoryImg.src!!; - const alt = categoryImg.alt - if (categoryLogo.includes("Movies-") || alt.includes("Movie")) return Category.MOVIE; + const alt = categoryImg.alt; + if (categoryLogo.includes("Movies-") || alt.includes("Movie")) + return Category.MOVIE; if (categoryLogo.includes("TV-")) return Category.TV; if (categoryLogo.includes("Music-")) return Category.MUSIC; }; -const parseYear = (title: string) => { - const regex = /-(\d{4})-/; - const match = title.match(regex); - if (match) return match[1]; -}; export default class CG implements tracker { canBeUsedAsSource(): boolean { return true; diff --git a/Find Unique Titles/src/trackers/RED.ts b/Find Unique Titles/src/trackers/RED.ts index b66bf94..68e933e 100644 --- a/Find Unique Titles/src/trackers/RED.ts +++ b/Find Unique Titles/src/trackers/RED.ts @@ -8,6 +8,28 @@ import { tracker, } from "./tracker"; import { fetchAndParseHtml } from "common/http"; +import { logger } from "common/logger"; + +const parseTorrents = (html: HTMLElement) => { + const groups = Array.from(html.querySelectorAll(".group_info strong")); + const torrents = []; + for (let group of groups) { + const yearAndTitle = group?.textContent?.split(" - "); + if (yearAndTitle) { + torrents.push({ + year: parseInt(yearAndTitle[0], 10), + title: yearAndTitle[1], + }); + } + } + return torrents; +}; + +function titlesAreEqual(first: string, second: string) { + first = first.normalize("NFD").replace(/[\u0300-\u036f]/g, ""); + second = second.normalize("NFD").replace(/[\u0300-\u036f]/g, ""); + return first.toLowerCase() == second.toLowerCase(); +} export default class RED implements tracker { canBeUsedAsSource(): boolean { @@ -40,28 +62,51 @@ export default class RED implements tracker { musicRequest.type != MusicReleaseType.SINGLE ) return SearchResult.NOT_ALLOWED; - if (!musicRequest.artists || !musicRequest.titles || !musicRequest.year) + if (!musicRequest.artists || !musicRequest.titles || !musicRequest.year) { + logger.debug( + "[{0}] Not enough data to check request: {1}", + this.name(), + request + ); return SearchResult.NOT_CHECKED; + } for (let artist of musicRequest.artists) { for (let title of musicRequest.titles) { if (artist === "VA") { - artist = "" + artist = ""; + } + if (artist) { + const queryUrl = `https://redacted.ch/artist.php?artistname=${encodeURIComponent( + artist + )}`; + const result = await fetchAndParseHtml(queryUrl); + if ( + result.textContent?.includes( + "Your search did not match anything." + ) || + result.querySelector("#search_terms") + ) { + return SearchResult.NOT_EXIST; + } + const torrents = parseTorrents(result); + logger.debug( + "[{0}] Parsed following torrents for artist: {1}: {2}", + this.name(), + artist, + torrents + ); + for (let torrent of torrents) { + if ( + torrent.year == request.year && + titlesAreEqual(title, torrent.title) + ) { + return SearchResult.EXIST; + } + } } - const queryUrl = `https://redacted.ch/torrents.php?artistname=${encodeURIComponent( - artist - )}&groupname=${encodeURIComponent(title)}&year=${ - musicRequest.year - }&order_by=time&order_way=desc&group_results=1&filter_cat%5B1%5D=1&action=advanced&searchsubmit=1`; - const result = await fetchAndParseHtml(queryUrl); - if ( - !result.textContent?.includes("Your search did not match anything.") - ) - return SearchResult.EXIST; } } return SearchResult.NOT_EXIST; - - return SearchResult.EXIST; } insertTrackersSelect(select: HTMLElement): void {} diff --git a/Find Unique Titles/src/trackers/TNT.ts b/Find Unique Titles/src/trackers/TNT.ts new file mode 100644 index 0000000..ea13b86 --- /dev/null +++ b/Find Unique Titles/src/trackers/TNT.ts @@ -0,0 +1,154 @@ +import { + parseCodec, + parseContainerAndFormat, + parseResolution, + parseTags, + parseYear, + parseYearAndTitle, +} from "../utils/utils"; +import { + Category, + MetaData, + MusicReleaseType, + Request, + SearchResult, + tracker, +} from "./tracker"; +import { addChild } from "common/dom"; +import { fetchAndParseJson, sleep } from "common/http"; +import { logger } from "common/logger"; + +const parseCategory = (category: number) => { + if ( + [24, 18, 17, 20, 19, 34, 36, 45, 22, 37, 35, 43, 38, 39, 46].includes( + category + ) + ) + return Category.MOVIE; + if ([27, 28, 16, 2, 29, 40, 41, 42].includes(category)) return Category.TV; + if ([10, 12, 13, 14].includes(category)) return Category.GAME; + if ([1].includes(category)) return Category.AUDIOBOOK; + if ([8].includes(category)) return Category.BOOK; + if ([2, 41].includes(category)) return Category.ANIME; + if ([44, 25, 26].includes(category)) return Category.MUSIC; +}; + +const getTorrentTitle = (element: HTMLElement) => { + const h5 = element.children[1].querySelector("h5")!!; + const newTag = h5.querySelector(".newTag"); + if (newTag) { + h5.removeChild(newTag); + } + return h5!!.textContent!!; +}; + +export default class TNT implements tracker { + canBeUsedAsSource(): boolean { + return true; + } + + canBeUsedAsTarget(): boolean { + return false; + } + + canRun(url: string): boolean { + return url.includes("tntracker.org") && url.includes('/?perPage='); + } + + async *getSearchRequest(): AsyncGenerator { + logger.debug(`[{0}] Parsing titles to check`, this.name()); + const elements = Array.from( + document.querySelectorAll("#table_gen_wrapper .sTable tbody tr") + ); + yield { + total: elements.length, + }; + for (let element of elements) { + const torrentTitle = getTorrentTitle(element); + logger.debug("[TNT] Checking torrent: {0}", torrentTitle); + const torrentId = element.children[1] + .querySelector("a")!! + .href.split("/") + .pop(); + const token = JSON.parse( + localStorage.getItem("ngStorage-token")!! + ) as string; + const data = await fetchAndParseJson( + `https://tntracker.org/api/torrent?id=${torrentId}`, + { + headers: { + Authorization: token, + }, + } + ); + const imdbId = "tt" + data["imdb"]; + const size = data.size / 1024 / 1024; + const category = parseCategory(data.category); + let title; + let year = undefined; + let request: Request = { + dom: [element as HTMLElement], + category, + }; + if (category == Category.MOVIE) { + ({ title, year } = parseYearAndTitle(torrentTitle)); + request = { + ...request, + torrents: [ + { + size, + tags: parseTags(torrentTitle), + dom: element as HTMLElement, + resolution: parseResolution(torrentTitle), + container: parseCodec(torrentTitle), + }, + ], + year, + title: title?.replace(".", " "), + imdbId, + }; + } else if (category == Category.MUSIC) { + const splittedTitle = torrentTitle.split("-")!!; + const artists = [splittedTitle[0].replaceAll("_", " ")]; + const titles = [splittedTitle[1].replaceAll("_", " ")]; + const year = parseYear(torrentTitle); + const { container, format } = parseContainerAndFormat(torrentTitle); + request = { + ...request, + torrents: [ + { + dom: element, + size, + format, + container, + }, + ], + artists, + titles, + year, + type: MusicReleaseType.ALBUM, + }; + } + yield request; + } + } + + name(): string { + return "TNT"; + } + + async search(request: Request): Promise { + return SearchResult.NOT_CHECKED; + } + + async insertTrackersSelect(select: HTMLElement): Promise { + await sleep(3000); + if (document.getElementById("find-unique-titles")) return; + const form = document.getElementById("search")!!.parentElement; + const wrapper = document.createElement("div"); + wrapper.classList.add("selector"); + wrapper.id = "find-unique-titles"; + addChild(wrapper, select); + addChild(form as HTMLElement, wrapper); + } +} diff --git a/Find Unique Titles/src/trackers/index.ts b/Find Unique Titles/src/trackers/index.ts index ca8aa04..5a91522 100644 --- a/Find Unique Titles/src/trackers/index.ts +++ b/Find Unique Titles/src/trackers/index.ts @@ -4,29 +4,30 @@ import BHD from "./BHD"; import BLU from "./BLU"; import BTarg from "./BTarg"; import CG from "./CG"; +import CHD from "./CHD"; import CLANSUD from "./CLAN-SUD"; import CinemaZ from "./CinemaZ"; import FL from "./FL"; import GPW from "./GPW"; import HDB from "./HDB"; +import HDSky from "./HDSky"; import HDT from "./HDT"; import IPT from "./IPT"; import JPTV from "./JPTV"; +import JPop from "./JPop"; import KG from "./KG"; +import MTV from "./MTV"; +import MTeam from "./MTeam"; import NewInsane from "./NewInsane"; import PTP from "./PTP"; +import Pter from "./Pter"; +import RED from "./RED"; import SC from "./SC"; -import TiK from "./TiK"; import TL from "./TL"; -import MTeam from "./MTeam"; -import nCore from "./nCore"; -import CHD from "./CHD"; -import HDSky from "./HDSky"; -import Pter from "./Pter"; +import TNT from "./TNT"; import TSeeds from "./TSeeds"; -import MTV from "./MTV"; -import JPop from "./JPop"; -import RED from "./RED"; +import TiK from "./TiK"; +import nCore from "./nCore"; export { PTP, @@ -58,4 +59,5 @@ export { MTV, JPop, RED, + TNT, }; diff --git a/Find Unique Titles/src/utils/utils.ts b/Find Unique Titles/src/utils/utils.ts index 87c6be3..8065cf6 100644 --- a/Find Unique Titles/src/utils/utils.ts +++ b/Find Unique Titles/src/utils/utils.ts @@ -107,14 +107,16 @@ export const parseTags = (title: string) => { export const parseYearAndTitle = (title: string | undefined) => { if (!title) return { title: undefined, year: undefined }; - const regex = /^(.*?)\s+\(?(\d{4})\)?\s+(.*)/; - const match = title.match(regex); + const regexes = [/^(.*?)\s+\(?(\d{4})\)?\s+(.*)/, /(.+?)\.(\d\d\d\d)/]; + for (let regex of regexes) { + const match = title.match(regex); - if (match) { - const title = match[1].trim(); - const year = parseInt(match[2], 10); + if (match) { + const title = match[1].trim(); + const year = parseInt(match[2], 10); - return { title, year }; + return { title, year }; + } } return { title: undefined, year: undefined }; @@ -144,3 +146,9 @@ export const parseContainerAndFormat = ( return result; }; + +export const parseYear = (title: string) => { + const regex = /-(\d{4})-/; + const match = title.match(regex); + if (match) return parseInt(match[1], 10); +}; diff --git a/common/src/http/index.ts b/common/src/http/index.ts index 264dcdf..bdd5732 100644 --- a/common/src/http/index.ts +++ b/common/src/http/index.ts @@ -1,5 +1,5 @@ -import GM_fetch from "@trim21/gm-fetch"; import { logger } from "../logger/index.js"; +import GM_fetch from "@trim21/gm-fetch"; const parser = new DOMParser(); @@ -9,7 +9,7 @@ export const fetchUrl = async ( wait = 1000 ) => { await sleep(wait); - logger.debug("Will fetch {0}", input) + logger.debug("Will fetch {0}", input); const res = await GM_fetch(input, options); return await res.text(); }; @@ -19,6 +19,11 @@ export const fetchAndParseHtml = async (query_url: string) => { return parser.parseFromString(response, "text/html").body; }; +export const fetchAndParseJson = async (query_url: string, options={}) => { + const response = await fetchUrl(query_url, options); + return JSON.parse(response); +}; + export const sleep = (ms: number) => { return new Promise((resolve) => setTimeout(resolve, ms)); }; diff --git a/common/src/logger/index.ts b/common/src/logger/index.ts index 6c50e27..5adefc3 100644 --- a/common/src/logger/index.ts +++ b/common/src/logger/index.ts @@ -43,9 +43,33 @@ const formatMessage = (level: LEVEL, message: string, args: any[]): string => { const argValue = args[argIndex]; return typeof argValue === "object" - ? JSON.stringify(argValue) + ? stringifyWithoutDOM(argValue) : argValue; }) .trim() ); }; + +const stringifyWithoutDOM = (obj) => { + const seen = new WeakSet(); // Use a WeakSet to track visited objects + + function replacer(key, value) { + if (value instanceof Node) { + // Ignore DOM elements + return undefined; + } + + if (typeof value === "object" && value !== null) { + // Check for circular references + if (seen.has(value)) { + return "[Circular Reference]"; + } + + seen.add(value); + } + + return value; + } + + return JSON.stringify(obj, replacer); +};