/* eslint-disable object-property-newline */
// ==UserScript==
// @name Post Formatter
// @description Format upload info
// @version 1.3.2.5
// @author Anonymous inspired by Secant(TYT@NexusHD)
// @match *.nexushd.org/*
// @match pterclub.com/*
// @match pt.sjtu.edu.cn/*
// @match kp.m-team.cc/*
// @match totheglory.im/*
// @match greatposterwall.com/*
// @match uhdbits.org/*
// @grant GM_xmlhttpRequest
// @require https://cdn.staticfile.org/jquery/2.1.4/jquery.js
// @require https://code.jquery.com/jquery-migrate-1.0.0.js
// @icon http://www.nexushd.org/favicon.ico
// @namespace d8e7078b-abee-407d-bcb6-096b59eeac17
// @license MIT
// ==/UserScript==
//= ========================================================================================================
// constants
const $ = window.jQuery
const NHD = 'nexushd'; const PUTAO = 'pt.sjtu'; const MTEAM = 'm-team'; const TTG = 'totheglory'; const GPW = 'greatposterwall'; const UHD = 'uhdbits'
const PTERCLUB = 'pterclub'; const IMGPILE = 'imgpile'; const PTPIMG = 'ptpimg'; const KSHARE = 'kshare.club'; const PIXHOST = 'pixhost'; const IMGBOX = 'imgbox'; const IMG4K = 'img4k'; const ILIKESHOTS = 'yes.ilikeshots.club'
const allImageHosts = [ PIXHOST, IMGBOX, IMG4K, ILIKESHOTS, PTERCLUB, IMGPILE, PTPIMG, KSHARE ]
// 特殊组名备注
const weirdTeams = ['de[42]', 'D-Z0N3']
const NEXUSPHP = 'nexusphp'; const GAZELLE = 'gazelle'
const allTagBoxes = ['box', 'hide', 'spoiler', 'expand']
// 匿名发布开关
const ANONYMOUS = true
// medianinfo 键长(方便格式化)
const mediainfoKeyLength = 31
const subtitleLanguages = {chinese_simplified: 'chs|zh', chinese_traditional: 'cht', japanese: 'jp|jpn|jap|ja', korean: 'kor|ko', english: 'en|eng',
french: 'fre|fra|fr', german: 'ger|deu|de', italian: 'ita|it', polish: 'pol|pl', romanian: 'rum|ron|ro', russian: 'ru|rus', spanish: 'spa|es', thai: 'tai',
turkish: 'tur|tr', vietnamese: 'vi|vie', hindi: 'hin|hi', greek: 'gre|ell|el', swedish: 'swe|sv', azerbaijani: 'aze|az', bulgarian: 'bul|bg', danish: 'dan|da',
estonian: 'est|et', finnish: 'fin|fi', hebrew: 'heb|he', croatian: 'hrv|hr', hungarian: 'hun|hu', icelandic: 'ice|isl|is', latvian: 'lav|lv', lithuanian: 'lit|lt',
dutch: 'dut|nld|nl', norwegian: 'nor|no', portuguese: 'por|pt', slovenian: 'slv|sl', slovak: 'slo|slk|sk', latin: 'lat|la',
ukrainian: 'ukr|uk', persian: 'per|fas|fa', arabic: 'ara|ar', brazilian_port: 'bra', czech: 'cze|ces|cs', idonesian: 'ido', serbian: 'srp|sr'
}
const weirdTeamsStr = weirdTeams.map(team => `(?:${escapeRegExp(team)})`).join('|')
const regexTeam = RegExp('\\b(?:(?:\\w[\\w-. ]+)|' + weirdTeamsStr + ') ?(?:\\([\\w. ]+\\)|<[\\w. ]+>|\\[[\\w. ]+\\])?', 'i')
const regexTeamsSplitter = /\||,|\/|(?<!D)-(?=Z0N3)|(?<=D)-(?!Z0N3)|(?<!D)-(?!Z0N3)| v\.?s\.? |>\s*v\.?s\.?\s*</i
const regexNormalUrl = /[A-Za-z0-9\-._~!$&'()*+;=:@/?]+/i
const regexImageUrl = RegExp(
'https?:' + regexNormalUrl.source + '?\\.(?:png|jpg)',
'i')
// compare with comparison (GPW style)
const regexScreenshotsComparison = RegExp(
'\\[comparison=(' +
regexTeam.source + '\\s*(?:,\\s*' + regexTeam.source +
'?)+)\\](\\s*(?:' +
regexImageUrl.source + '(?:\\s+|\\s*,)\\s*)+' + regexImageUrl.source +
')\\s*\\[\\/comparison\\]',
'mig')
// compare with thumbs
const regexScreenshotsThumbsCombined = RegExp(
'((?:\\s*(\\[url=' +
regexNormalUrl.source + '?\\])?\\s*\\[img\\]' +
regexImageUrl.source + '?\\[\\/img\\]\\s*(?:\\[\\/url\\])?\\s*)+)',
'mi')
const regexScreenshotsThumbsSeparated = RegExp(
'(\\[url=' +
regexNormalUrl.source + '?\\])?\\s*\\[img\\]' +
regexImageUrl.source + '?\\[\\/img\\]\\s*(?:\\[\\/url\\])?',
'mig')
const regexImageUrlsSeparated = RegExp(
'(' + regexImageUrl.source + ')',
'mig')
// 两种截图模式,第一种是包含[box|hide|expand|spoiler|quote=]标签的
// possible splitters for teams: '|',',','/','-','vs'
const regexScreenshotsThumbsBoxed = RegExp(
'\\[(box|hide|expand|spoiler|quote)\\s*=\\s*\\w*?\\s*(' +
regexTeam.source + '(?:\\s*(?:' + regexTeamsSplitter.source + ')\\s*' + regexTeam.source +
')+)\\s*\\]' +
regexScreenshotsThumbsCombined.source +
'\\s*\\[\\/\\1\\]',
'mig')
// 第二种不包含[box|hide|expand|spoiler|quote=]标签,要求Source, Encode与截图之间至少有一个换行符
const regexScreenshotsThumbsTitled = RegExp(
'\\b(' +
regexTeam.source + '(?:\\s*(?:' + regexTeamsSplitter.source + ')\\s*' + regexTeam.source +
')+)[\\W]*\\r?\\n+\\s*' +
regexScreenshotsThumbsCombined.source,
'mig')
const regexScreenshotsSimple = RegExp(
'(?:\\[b\\])?Screenshots(?:\\[\\/b\\])?\\s*(\\[img\\]' + regexImageUrl + '\\s*\\[\\/img\\]+)',
'mig')
// 对比图相关正则表达式信息
const regexInfo = {
// [box=team1, team2, team3]url1 url2 url3[/box]
boxed: { regex: regexScreenshotsThumbsBoxed, groupForTeams: 2, groupForUrls: 3, groupForThumbs: 4 },
// [center]team1 | team2 | team3\nurl1 url2 url3[/center]
titled: { regex: regexScreenshotsThumbsTitled, groupForTeams: 1, groupForUrls: 2, groupForThumbs: 3 },
// [comparison=team1, team2, team3]url1 url2 url3[/comparison]
comparison: { regex: regexScreenshotsComparison, groupForTeams: 1, groupForUrls: 2, groupForThumbs: -1 },
// [img]https://1.png[/img][img]https://2.png[/img][img]https://3.png[/img]
simple: { regex: regexScreenshotsSimple, groupForTeams: -1, groupForUrls: 1, groupForThumbs: -1 }
}
const siteInfoMap = {
// bracket makes the value of the string 'nexushd' the true key or instead the string 'NHD' will be used as key
[NHD]: {
// 主页
hostName: 'nexushd.org',
// 匹配页面
pages: {
upload: 'upload.php',
edit: 'edit.php',
subtitles: 'subtitles.php'
},
// 架构
construct: NEXUSPHP,
// box 类标签,具备隐藏功能
targetBoxTag: 'box',
// 是否支持 [box=...]的形式
boxSupportDescr: true,
// 是否需要在 box 标签右括号末端加上换行
boxNeedBreakLine: false,
// 不支持的标签
unsupportedTags: ['align', 'pre'],
inputFile: $('input[type="file"][name="file"]'),
nameBoxUpload: $('#name'), nameBoxEdit: $("input[type='text'][name='name']"), anonymousControl: $("input[name='uplver'][type='checkbox']")[0],
descrBox: $('#descr'), smallDescBox: $("input[name='small_descr']"),
imdbLinkBox: $("input[name='url'][type='text']"), doubanLinkBox: $("input[name='douban_url']"),
categorySel: $('#browsecat'), sourceSel: $("select[name='source_sel']"), standardSel: $("select[name='standard_sel']"), processingSel: $("select[name='processing_sel']"), codecSel: $("select[name='codec_sel']"),
pullMovieScore: false, translatedChineseNameInTitle: false, doubanIdInsteadofLink: false,
screenshotsStyle: 'conventional',
categoryInfo: { default: 0, movie: 101, tvSeries: 102, tvShow: 103, documentary: 104, animation: 105 },
sourceInfo: { default: 0, bluray: 1, hddvd: 2, dvd: 3, hdtv: 4, webdl: 7, webrip: 9 },
standardInfo: { default: 0, res1080p: 1, res1080i: 2, res720p: 3, res2160p: 6, sd: 4 },
processingInfo: { default: 0, raw: 1, encode: 2 },
codecInfo: { default: 0, h264: 1, h265: 2, vc1: 3, xvid: 4, mpeg2: 5, flac: 10, ape: 11 },
inputFileSubtitle: $('input[type="file"][name="file"]'),
titleBoxSubtitle: $('input[type="text"][name="title"]'),
languageSelSubtitle: $('select[name="sel_lang"]'),
anonymousCheckSubtitle: $("input[name='uplver'][type='checkbox']")[0],
subtitleInfo: {
default: 0, english: 6, chinese_simplified: 25, chinese_traditional: 28, japanese: 15, french: 9,
german: 10, italian: 14, korean: 16, spanish: 26, other: 18
}
},
[PTERCLUB]: {
hostName: 'pterclub.com',
pages: {
upload: 'upload.php',
edit: 'edit.php',
subtitles: 'subtitles.php'
},
construct: NEXUSPHP,
targetBoxTag: 'hide',
boxSupportDescr: true,
boxNeedBreakLine: false,
unsupportedTags: ['align', 'pre'],
inputFile: $('input[type="file"][name="file"]'), nameBoxUpload: $('#name'), nameBoxEdit: $("input[type='text'][name='name']"),
anonymousControl: $("input[name='uplver'][type='checkbox']")[0],
descrBox: $('#descr'), smallDescBox: $("input[name='small_descr']"),
imdbLinkBox: $("input[name='url'][type='text']"), doubanLinkBox: $("input[name='douban']"),
categorySel: $('#browsecat'), sourceSel: $("select[name='source_sel']"), areaSel: $("select[name='team_sel']"),
chsubCheck: $('#zhongzi')[0], englishSubCheck: $('#ensub')[0], chdubCheck: $('#guoyu')[0], cantodubCheck: $('#yueyu')[0],
pullMovieScore: true, translatedChineseNameInTitle: false, doubanIdInsteadofLink: false,
// 对比图风格,conventional 是指缩略图超链接方式
screenshotsStyle: 'conventional',
categoryInfo: { default: 0, movie: 401, tvSeries: 404, tvShow: 405, documentary: 402, animation: 403 },
sourceInfo: { default: 0, bluray: 2, remux: 3, encode: 6, hdtv: 4, webdl: 5, dvd: 7 },
areaInfo: { default: 0, cnMl: 1, hk: 2, tw: 3, euAme: 4, kor: 5, jap: 6, ind: 7, other: 8 },
inputFileSubtitle: $('input[type="file"][name="file"]'),
titleBoxSubtitle: $('input[type="text"][name="title"]'),
languageSelSubtitle: $('select[name="sel_lang"]'),
anonymousCheckSubtitle: $("input[name='uplver'][type='checkbox']")[0],
subtitleInfo: {
default: 0, english: 6, chinese_simplified: 25, chinese_traditional: 28, japanese: 15, french: 9,
german: 10, italian: 14, korean: 16, spanish: 26, other: 18
}
},
[PUTAO]: {
hostName: 'pt.sjtu.edu.cn',
pages: {
upload: 'upload.php',
edit: 'edit.php',
subtitles: 'subtitles.php'
},
construct: NEXUSPHP,
targetBoxTag: '',
boxSupportDescr: true,
boxNeedBreakLine: false,
unsupportedTags: ['align', 'center', 'pre'],
inputFile: $('input[type="file"][name="file"]'), nameBoxUpload: $('#name'), nameBoxEdit: $("input[type='text'][name='name']"),
anonymousControl: $("input[name='uplver'][type='checkbox']")[0],
descrBox: $('#descr'), smallDescBox: $("input[name='small_descr']"),
imdbLinkBox: $("input[name='url'][type='text']"), doubanLinkBox: $("input[name='douban_url']"),
categorySel: $('#browsecat'), standardSel: $("select[name='standard_sel']"), codecSel: $("select[name='codec_sel']"),
pullMovieScore: false, translatedChineseNameInTitle: true, doubanIdInsteadofLink: false,
screenshotsStyle: 'conventional',
categoryInfo: {
default: 0, documentary: 406, animation: 431, movieCn: 401, movieEuAme: 402, movieAsia: 403,
tvSeriesHkTw: 407, tvSeriesAsia: 408, tvSeriesCnMl: 409, tvSeriesEuAme: 410,
catTvShowCnMl: 411, tvShowHkTw: 412, tvShowEuAme: 413, tvshowJapKor: 414
},
standardInfo: { default: 0, res1080p: 1, res1080i: 2, res720p: 3, res2160p: 6, sd: 4 },
codecInfo: { default: 0, h264: 1, vc1: 2, xvid: 3, mpeg2: 4, flac: 5, ape: 6, h265: 10 },
inputFileSubtitle: $('input[type="file"][name="file"]'),
titleBoxSubtitle: $('input[type="text"][name="title"]'),
languageSelSubtitle: $('select[name="sel_lang"]'),
anonymousCheckSubtitle: $("input[name='uplver'][type='checkbox']")[0],
subtitleInfo: {
default: 0, english: 6, chinese_simplified: 25, chinese_traditional: 28, japanese: 15, french: 9,
german: 10, italian: 14, korean: 16, spanish: 26, other: 18
}
},
[MTEAM]: {
hostName: 'm-team.cc',
pages: {
upload: 'upload.php',
edit: 'edit.php',
subtitles: 'subtitles.php'
},
construct: NEXUSPHP,
targetBoxTag: 'expand',
boxSupportDescr: false,
boxNeedBreakLine: false,
unsupportedTags: ['align', 'pre'],
inputFile: $('input[type="file"][name="file"]'), nameBoxUpload: $('#name'), nameBoxEdit: $("input[type='text'][name='name']"),
anonymousControl: $("input[name='uplver'][type='checkbox']")[0],
descrBox: $('#descr'), smallDescBox: $("input[name='small_descr']"),
imdbLinkBox: $("input[name='url'][type='text']"),
categorySel: $('#browsecat'), teamSel: $("select[name='team_sel']"), standardSel: $("select[name='standard_sel']"), areaSel: $("select[name='processing_sel']"), codecSel: $("select[name='codec_sel']"),
chsubCheck: $("input[type='checkbox'][name='l_sub']")[0], chdubCheck: $("input[type='checkbox'][name='l_dub']")[0],
pullMovieScore: true, translatedChineseNameInTitle: false, doubanIdInsteadofLink: false,
screenshotsStyle: 'conventional',
categoryInfo: { default: 0, movieHd: 419, movieRemux: 439, tvSeriesHd: 402, documentary: 404, animation: 405 },
areaInfo: { default: 0, cnMl: 1, euAme: 2, hkTw: 3, jap: 4, kor: 5, other: 6 },
standardInfo: { default: 0, res1080p: 1, res1080i: 2, res720p: 3, res2160p: 6, sd: 5 },
codecInfo: { default: 0, h264: 1, vc1: 2, h265: 16, xvid: 3, mpeg2: 4, flac: 5, ape: 10 },
inputFileSubtitle: $('input[type="file"][name="file[]"]'),
titleBoxSubtitle: $('input[type="text"][name="title[]"]'),
languageSelSubtitle: $('select[name="sel_lang[]"]'),
anonymousCheckSubtitle: $("input[name='uplver'][type='checkbox']")[0],
subtitleInfo: {
default: 0, english: 6, chinese_simplified: 25, chinese_traditional: 28, japanese: 15, korean: 16, other: 18
}
},
[TTG]: {
hostName: 'totheglory.im',
pages: {
upload: 'upload.php',
edit: 'edit.php',
subtitles: 'dox.php'
},
construct: NEXUSPHP,
targetBoxTag: '',
boxSupportDescr: false,
boxNeedBreakLine: false,
unsupportedTags: ['align'],
inputFile: $('input[type="file"][name="file"]'), nameBoxUpload: $("input[type='text'][name='name']"), nameBoxEdit: $("input[type='text'][name='name']"),
descrBox: $('textarea[name="descr"]'), smallDescBox: $("input[type='text'][name='subtitle']"), subtitleBox: $("input[type='text'][name='highlight']"),
imdbLinkBox: $("input[name='imdb_c'][type='text']"), doubanLinkBox: $("input[name='douban_id'][type='text']"),
categorySel: $('select[name="type"]'), anonymousControl: $('select[name="anonymity"]'),
pullMovieScore: true, translatedChineseNameInTitle: false, doubanIdInsteadofLink: true,
screenshotsStyle: 'conventional',
categoryInfo: {
default: 0, movie720P: 52, movie1080ip: 53, movie2160p: 108, documentary720p: 62, documentary1080ip: 63,
tvSeriesEuAme: 87, tvSeriesJap: 88, tvSeriesKor: 99, tvSeriesCn: 90, tvShowJap: 101, tvShowKor: 103, tvShow: 60
}
},
[GPW]: {
hostName: 'greatposterwall.com',
pages: {
upload: 'upload.php',
edit: 'torrents.php?action=edit',
subtitles: 'subtitles.php'
},
construct: GAZELLE,
targetBoxTag: 'hide',
boxSupportDescr: true,
boxNeedBreakLine: true,
unsupportedTags: ['align', 'pre'],
inputFile: $('#file'),
mediainfoBox: $('textarea[name="mediainfo[]"]'), descrBox: $('#release_desc'),
sourceSel: $('select[id="source"]'), codecSel: $('select[id="codec"]'), standardSel: $('select[id="resolution"]'), processingSel: $('select[id="processing"]'), containerSel: $('select[id="container"]'),
videoInfo: {
bit10: $('input[type="checkbox"][id="10_bit"]')[0],
hdr10: $('input[type="checkbox"][id="hdr10"]')[0],
hdr10plus: $('input[type="checkbox"][id="hdr10plus"]')[0],
dovi: $('input[type="checkbox"][id="dolby_vision"]')[0]
},
audioInfo: {
dtsX: $('input[type="checkbox"][id="dts_x"]')[0],
atmos: $('input[type="checkbox"][id="dolby_atmos"]')[0],
chineseDub: $('input[type="checkbox"][id="chinese_dubbed"]')[0]
},
movieEditionCheck: $('input[type="checkbox"][id="movie_edition_information"]')[0],
movieEditionInfo: {
criterionCollection: $('a[onclick*="the_criterion_collection"]')[0],
mastersOfCinema: $('a[onclick*="masters_of_cinema"]')[0],
withCommentary: $('a[onclick*="with_commentary"]')[0],
directorCut: $('a[onclick*="director_cut"]')[0],
theatrical: $('a[onclick*="theatrical_cut"]')[0],
uncut: $('a[onclick*="uncut"]')[0],
unrated: $('a[onclick*="unrated"]')[0],
extended: $('a[onclick*="extended_edition"]')[0],
remaster4k: $('a[onclick*="4k_remaster"]')[0],
remaster: $('a[onclick*="remaster"]')[0],
restoration4k: $('a[onclick*="4k_restoration"]')[0],
twoInOne: $('a[onclick*="2_in_1"]')[0]
},
mixedSubCheck: $('input[type="radio"][id="mixed_subtitles"]')[0],
noSubCheck: $('input[type="radio"][id="no_subtitles"]')[0],
otherSubtitlesDiv: $('div[id="other_subtitles"]'),
subtitleInfo: {
chinese_simplified: $('input[type="checkbox"][id="chinese_simplified"]')[0],
chinese_traditional: $('input[type="checkbox"][id="chinese_traditional"]')[0],
english: $('input[type="checkbox"][id="english"]')[0],
japanese: $('input[type="checkbox"][id="japanese"]')[0],
korean: $('input[type="checkbox"][id="korean"]')[0],
french: $('input[type="checkbox"][id="french"]')[0],
german: $('input[type="checkbox"][id="german"]')[0],
italian: $('input[type="checkbox"][id="italian"]')[0],
polish: $('input[type="checkbox"][id="polish"]')[0],
romanian: $('input[type="checkbox"][id="romanian"]')[0],
russian: $('input[type="checkbox"][id="russian"]')[0],
spanish: $('input[type="checkbox"][id="spanish"]')[0],
thai: $('input[type="checkbox"][id="thai"]')[0],
turkish: $('input[type="checkbox"][id="turkish"]')[0],
vietnamese: $('input[type="checkbox"][id="vietnamese"]')[0],
hindi: $('input[type="checkbox"][id="hindi"]')[0],
greek: $('input[type="checkbox"][id="greek"]')[0],
swedish: $('input[type="checkbox"][id="swedish"]')[0],
azerbaijani: $('input[type="checkbox"][id="azerbaijani"]')[0],
bulgarian: $('input[type="checkbox"][id="bulgarian"]')[0],
danish: $('input[type="checkbox"][id="danish"]')[0],
estonian: $('input[type="checkbox"][id="estonian"]')[0],
finnish: $('input[type="checkbox"][id="finnish"]')[0],
hebrew: $('input[type="checkbox"][id="hebrew"]')[0],
croatian: $('input[type="checkbox"][id="croatian"]')[0],
hungarian: $('input[type="checkbox"][id="hungarian"]')[0],
icelandic: $('input[type="checkbox"][id="icelandic"]')[0],
latvian: $('input[type="checkbox"][id="latvian"]')[0],
lithuanian: $('input[type="checkbox"][id="lithuanian"]')[0],
dutch: $('input[type="checkbox"][id="dutch"]')[0],
norwegian: $('input[type="checkbox"][id="norwegian"]')[0],
portuguese: $('input[type="checkbox"][id="portuguese"]')[0],
slovenian: $('input[type="checkbox"][id="slovenian"]')[0],
slovak: $('input[type="checkbox"][id="slovak"]')[0],
latin: $('input[type="checkbox"][id="latin"]')[0],
ukrainian: $('input[type="checkbox"][id="ukrainian"]')[0],
persian: $('input[type="checkbox"][id="persian"]')[0],
arabic: $('input[type="checkbox"][id="arabic"]')[0],
brazilian_port: $('input[type="checkbox"][id="brazilian_port"]')[0],
czech: $('input[type="checkbox"][id="czech"]')[0],
idonesian: $('input[type="checkbox"][id="idonesian"]')[0],
serbian: $('input[type="checkbox"][id="serbian"]')[0]
},
pullMovieScore: true, translatedChineseNameInTitle: false,
minScreenshots: 3, maxScreenshots: 10, supportedImageHosts: [KSHARE, PIXHOST, PTPIMG, PTERCLUB, ILIKESHOTS, IMGBOX],
screenshotsStyle: 'comparison',
sourceInfo: { default: '---', bluray: 'Blu-ray', web: 'WEB', hdtv: 'HDTV', dvd: 'DVD' },
codecInfo: { default: '---', h264: 'H.264', h265: 'H.265', xvid: 'XviD', divx: 'DivX', x264: 'x264', x265: 'x265' },
standardInfo: { default: '---', res1080i: '1080i', res1080p: '1080p', res2160p: '2160p', res720p: '720p', sd: '480p' },
processingInfo: { default: '---', encode: 'Encode', remux: 'Remux' },
containerInfo: { default: '---', mkv: 'MKV', mp4: 'MP4', avi: 'AVI' },
inputFileSubtitle: $('#file')
},
[UHD]: {
hostName: 'uhdbits.org',
pages: {
upload: 'upload.php',
edit: 'torrents.php?action=edit',
subtitles: 'subtitle.php'
},
construct: GAZELLE,
targetBoxTag: 'hide',
boxSupportDescr: true,
boxNeedBreakLine: true,
unsupportedTags: ['align', 'pre'],
inputFile: $('#file'),
mediainfoBox: $('textarea[name="mediainfo"]'), descrBox: $('#release_desc'),
sourceSel: $('select[id="media"]'), codecSel: $('select[id="codec"]'), standardSel: $('select[id="format"]'), teamBox: $('input[type="text"][id="team"]'),
categorySel: $('select[id="categories"]'), anonymousControl: $('input[type="checkbox"][id="anonymous"]')[0],
hdrSel: $('select[id="hdr"]'), seasonSel: $('select[id="season"]'),
movieEditionInfo: {
criterionCollection: $('a:contains("Criterion")')[0],
twoInOne: $('a:contains("2in1")')[0],
threeInOne: $('a:contains("3in1")')[0],
bit10: $('a:contains("10-bit")')[0],
remaster4k: $('a:contains("4K Remaster")')[0],
restoration4k: $('a:contains("4K Restoration")')[0],
bAndWVersion: $('a:contains("B & W Version")')[0],
directorCut: $('a:contains("Director\'s Cut")')[0],
extras: $('a:contains("Extras")')[0],
theatrical: $('a:contains("Theatrical")')[0],
extended: $('a:contains("Extended")')[0],
hybrid: $('a:contains("Hybrid")')[0],
imax: $('a:contains("IMAX")')[0],
remaster: $('a:contains("Remastered")')[0],
uncut: $('a:contains("Uncut")')[0],
tvCut: $('a:contains("TV Cut")')[0],
unrated: $('a:contains("Unrated")')[0],
},
pullMovieScore: true, translatedChineseNameInTitle: false,
screenshotsStyle: 'conventional',
sourceInfo: { default: '---', bluray: 'Blu-ray', remux: 'Remux', encode: 'Encode', webdl: 'WEB-DL', webrip: 'WEBRip', hdrip: 'HDRip', hdtv: 'HDTV', others: 'Others', hdAudio: 'HD Audio' },
codecInfo: { default: '---', h264: 'H.264', h265: 'HEVC', vc1: 'VC-1', mpeg2: 'MPEG-2', av1: 'AV1', x264: 'x264', x265: 'x265', x266: 'x266' },
standardInfo: { default: '---', mhd: 'mHD', res1080i: '1080i', res1080p: '1080p', res2160p: '2160p', res720p: '720p', others: 'Others' },
hdrInfo: { default: 'No', hdr10: 'HDR10', hdr10plus: 'HDR10+', dovi: 'DoVi' },
categoryInfo: { movie: '0', music: '1', tvSeries: '2' },
seansonInfo: { default: '---', s01: '1' },
inputFileSubtitle: $('input[type="file"][name="sub"]'),
titleBoxSubtitle: $('input[type="text"][name="releasename"]'),
languageSelSubtitle: $('select[name="language"]'),
subtitleInfo: {
default: '', english: 'English', vietnamese: 'Vietnamese', danish: 'Danish', norwegian: 'Norwegian', finnish: 'Finnish', spanish: 'Spanish', french: 'French'
}
}
}
//= ========================================================================================================
// functions
function escapeRegExp (string) {
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
}
// requires numbers of left and right tags match
// keepNonQuoted 选择是否保留两个0级别 quote 之间的内容,如'是这些文字[quote]不是这些文字[/quote]是这些文字[quote]不是这些文字[/quote]是这些文字'
function processTags (inputText, tag, processLeft, processRight, keepNonQuoted=true) {
let regexTagsLeft = new RegExp('\\[((' + tag + ')((?:=([^\\]]+))?))\\]', 'g')
let regexTagsRight = new RegExp('\\[\\/(' + tag + ')\\]', 'g')
let outputText = ''
let remainedText = ''
let indexOutput = 0
let indexRemained = 0
let currentLevel = 0
// eslint-disable-next-line no-constant-condition
while(true) {
regexTagsLeft.lastIndex = indexOutput
regexTagsRight.lastIndex = indexOutput
let matchLeft = regexTagsLeft.exec(inputText)
let matchRight = regexTagsRight.exec(inputText)
let match = null
let left = true
if (matchLeft && matchRight) {
if (matchLeft.index < matchRight.index) {
match = matchLeft
left = true
} else {
match = matchRight
left = false
}
} else {
left = matchLeft
match = matchLeft
? matchLeft
: matchRight
? matchRight
: null
}
if (match) {
if (currentLevel === 0) {
if (left) {
// 左括号,0级,根据 keepNonQuoted 确定是否保留上一次匹配末尾到本次匹配之间的内容
indexOutput = keepNonQuoted ? indexOutput : match.index
} else {
// 右括号,0级,无法匹配,扔掉前面的内容,直接从本次匹配末尾开始
indexOutput = match.index + match[0].length
}
}
if (indexOutput < match.index) {
outputText += inputText.substring(indexOutput, match.index)
indexOutput = match.index
} else {
// indexOutput === match.index || indexOutput === match.index + match[0].length
remainedText += inputText.substring(indexRemained, match.index)
indexRemained = match.index
}
if (indexOutput < match.index + match[0].length) {
outputText += left
? processLeft(match[0])
: processRight(match[0])
} else {
remainedText += left
? processLeft(match[0])
: processRight(match[0])
}
indexOutput = match.index + match[0].length
indexRemained = indexOutput
left ? currentLevel++
: currentLevel >=1
? currentLevel--
: 0
} else {
break
}
}
return [outputText, remainedText]
}
function nestExplode (inputText, targetBoxTag) {
let outputText, c
const pat1 = '\\[' +
targetBoxTag + '((?:=[^\\]]+)?\\](?:(?!\\[\\/' +
targetBoxTag + '\\])[\\s\\S])*\\[' +
targetBoxTag + '(?:=[^\\]]+)?\\])'
const pat2 = '(\\[\\/' +
targetBoxTag + '\\](?:(?!\\[' +
targetBoxTag + '(?:=[^\\]]+)?\\])[\\s\\S])*)\\[\\/' +
targetBoxTag + '\\]'
const regex1 = RegExp(pat1, 'g')
const regex2 = RegExp(pat2, 'g')
do {
outputText = inputText.replace(regex1, '[quote$1').replace(regex2, '$1[/quote]')
c = (inputText !== outputText)
inputText = outputText
} while (c)
return outputText
}
function compactContent (inputText, targetBoxTag) {
let outputText, c
const pat1 = '(\\[\\/?(?:' + targetBoxTag + ')(?:=[^\\]]+)?\\])\\s+(\\S)'
const pat2 = '(\\S)\\s+(\\[\\/?(?:' + targetBoxTag + ')(?:=[^\\]]+)?\\])'
const pat3 = '(\\[' + targetBoxTag + '(?:=[^\\]]+)?\\](?:(?!\\[\\/)[\\s\\S])*\\[(?:font|b|i|u|color|size)(?:=[^\\]]+)?\\])\\r?\\n+([^\\r\\n])'
const regex1 = RegExp(pat1, 'g')
const regex2 = RegExp(pat2, 'g')
const regex3 = RegExp(pat3, 'g')
do {
outputText = inputText.replace(regex1, '$1$2').replace(regex2, '$1$2').replace(regex3, '$1$2')
c = (inputText !== outputText)
inputText = outputText
} while (c)
return outputText
}
function formatTorrentName (torrentName) {
if (!torrentName) {
return ''
} else {
return (
torrentName
.replace(/(\.torrent)+$/, '')
.replace(/^\s?(\[.*?\]\s?)+/gi, '')
.replace(/\s?(\(\d+\)\s?)+$/gi, '')
.replace(/(\.(mkv|mp4|avi|ts|wmv|mpg|torrent))+$/, '')
.replace(/\bh\.(26[45])\b/gi, 'H/$1')
.replace(/(\b[a-zA-Z]*\d{1,2})\.(\d{1,2}\b)/g, function (_, p1, p2) {
return p1 + '/' + p2
})
.replace(/\b\((\d{4})\)\b/g, '$1')
.replace(/\bWEB(?!-DL)\b/gi, 'WEB-DL')
.replace(/\bweb-?rip\b/gi, 'WEBRip')
.replace(/\bblu-?ray\b/gi, 'BluRay')
.replace(/\bdvd(rip)?\b/gi, function (_, p1) {
return 'DVD' + (p1 ? 'Rip' : '')
})
.replace(/\b(480|720|1080|2160)([PI])\b/g, function (_, p1, p2) {
return p1 + p2.toLowerCase()
})
.replace(/\bx\.?(26[45])\b/gi, 'x$1')
.replace(/((?<!\d{1,2})\.)|(\.(?!\d\b))/g, ' ')//点号前面是数字(一至两位),后面是单个数字的情况不替换(DDP5.1)
.replace(/\//g, '.')
.trim()
)
}
}
// eslint-disable-next-line no-unused-vars
function getThumbSize(numTeams, siteName) {
return numTeams === 2
? 300
: numTeams === 3
? 250
: numTeams === 4
? 190
: numTeams === 5
? 150
: 150
}
// decode [url=...][img]...[/img][/url] -> [comparison=...]...[/comparison]
async function thumbs2ImageUrls (thumbUrls, numTeams, siteName) {
thumbUrls = thumbUrls.trim()
const imageHost = allImageHosts.find(ih => thumbUrls.match(RegExp(escapeRegExp(ih), 'i')))
if (!imageHost) {
return []
}
let regex = ''
let replacement = ''
if (imageHost === PIXHOST) {
regex = /\[url=https:\/\/pixhost\.to\/show\/([A-Za-z0-9\-._~!$&'()*+,;=:@/?]+.png)\]\s*\[img\]https:\/\/t([A-Za-z0-9\-._~!$&'()*+,;=:@/?]+)\.pixhost[A-Za-z0-9\-._~!$&'()*+,;=:@/?]+?\[\/img\]\s*\[\/url\]/gi
replacement = 'https://img$2.pixhost.to/images/$1'
} else if (imageHost === IMGBOX) {
regex = /\[url=[A-Za-z0-9\-._~!$&'()*+,;=:@/?]+\]\s*\[img\]https:\/\/thumbs([A-Za-z0-9\-._~!$&'()*+,;=:@/?]+)_t\.png\[\/img\]\s*\[\/url\]/gi
replacement = 'https://images$1_o.png'
} else if (imageHost === IMG4K) {
regex = /\[url=[A-Za-z0-9\-._~!$&'()*+,;=:@/?]+\]\s*\[img\]([A-Za-z0-9\-._~!$&'()*+,;=:@/?]+)\.md\.png\[\/img\]\s*\[\/url\]/gi
replacement = '$1.png'
} else if (imageHost === PTERCLUB) {
regex = /\[url=[A-Za-z0-9\-._~!$&'()*+,;=:@/?]+\]\s*\[img\]([A-Za-z0-9\-._~!$&'()*+,;=:@/?]+)\.th\.png\[\/img\]\s*\[\/url\]/gi
replacement = '$1.png'
} else if (imageHost === IMGPILE) {
regex = /\[url=https:\/\/imgpile\.com\/i\/([A-Za-z0-9\-._~!$&'()*+,;=:@/?]+)\]\s*\[img\][A-Za-z0-9\-._~!$&'()*+,;=:@/?]+\.png\[\/img\]\s*\[\/url\]/gi
replacement = 'https://imgpile.com/images/$1.png'
}
let imageUrls = []
if (regex) {
const matches = thumbUrls.match(regex)
const site = siteInfoMap[siteName]
const supportCurrentImageHost = site.supportedImageHosts ? site.supportedImageHosts.includes(imageHost) : true
const supportPixhost = site.supportedImageHosts ? site.supportedImageHosts.includes(PIXHOST) : true
let imageUrlsTest = matches
? matches.map(matched => {
return matched.replace(regex, replacement)
})
: []
if (supportCurrentImageHost) {
imageUrls = imageUrlsTest
} else if (supportPixhost) {
const size = getThumbSize(numTeams, siteName)
thumbUrls = await sendImagesToPixhost(imageUrlsTest, size)
imageUrls = await thumbs2ImageUrls(thumbUrls.join(' '), numTeams, siteName)
}
}
return imageUrls
}
// [comparison=...]...[/comparison] -> decode [url=...][img]...[/img][/url]
async function images2ThumbUrls (imageUrls, numTeams, siteName) {
imageUrls = imageUrls.trim()
const imageHost = allImageHosts.find(ih => imageUrls.match(RegExp(escapeRegExp(ih), 'i')))
if (!imageHost) {
return []
}
let regex = ''
let replacement = ''
if (imageHost === PIXHOST) {
regex = /https:\/\/img(\d+)\.pixhost\.to\/images\/([\w/]+)\.png/gi
replacement = '[url=https://pixhost.to/show/$2.png][img]https://t$1.pixhost.to/thumbs/$2.png[/img][/url]'
} else if (imageHost === IMGBOX) {
regex = /https:\/\/images(\d+)\.imgbox\.com\/(\w+\/\w+)\/(\w+)_o\.png/gi
replacement = '[url=https://imgbox.com/$3][img]https://thumbs$1.imgbox.com/$2/$3_t.png[/img][/url]'
}
const site = siteInfoMap[siteName]
const supportCurrentImageHost = site.supportedImageHosts ? site.supportedImageHosts.includes(imageHost) : true
const supportPixhost = site.supportedImageHosts ? site.supportedImageHosts.includes(PIXHOST) : true
const size = getThumbSize(numTeams, siteName)
let thumbUrls = []
if (regex) {
const matches = imageUrls.match(regex)
if (supportCurrentImageHost) {
thumbUrls = matches
? matches.map(matched => {
return matched.replace(regex, replacement)
})
: []
} else {
thumbUrls = matches && supportPixhost
? await sendImagesToPixhost(matches, size)
: []
}
} else {
// 不可从图片链接解析缩略图的图床(如PTPIMG),发送至Pixhost
regex = /(https?:[A-Za-z0-9\-._~!$&'()*+,;=:@/?]+?\.(png|jpg))/gi
const matches = imageUrls.match(regex)
thumbUrls = matches && supportPixhost
? await sendImagesToPixhost(matches, size)
: []
}
return thumbUrls
}
function mediainfo2String(mediainfo) {
let mediainfoStr = ''
if (!mediainfo) {
return mediainfoStr
}
Object.entries(mediainfo).forEach(([sectorKey, sector]) => {
mediainfoStr += `${sectorKey}\n`
Object.entries(sector).forEach(([fieldKey, fieldValue]) => {
// at least keep 1 empty space
let emptyLength = Math.max(mediainfoKeyLength - fieldKey.length, 1)
mediainfoStr += `${fieldKey}${' '.repeat(emptyLength)}: ${fieldValue}\n`
})
mediainfoStr += '\n'
})
return mediainfoStr
}
function string2Mediainfo (mediainfoStr) {
let mi = {}
if (!mediainfoStr) {
return mi
}
let currentSectorKey = ''
// \r is for clipboard content operation
mediainfoStr.split(/\r?\n/g).forEach(sector => {
if (sector && sector.trim()) {
let [fieldKey, fieldValue] = sector.split(/ +: +/)
if (fieldKey) {
fieldKey = fieldKey.trim()
if (fieldValue) {
fieldValue = fieldValue.trim()
if (currentSectorKey) {
mi[currentSectorKey][fieldKey] = fieldValue
} else {
// invalid mediainfo format
mi = {}
return
}
} else {
currentSectorKey = fieldKey
mi[currentSectorKey] = {}
}
}
}
})
return mi
}
async function sendImagesToPixhost (urls, size) {
const hostname = 'https://pixhost.to/remote/'
const data = encodeURI(`imgs=${urls.join('\r\n')}&content_type=0&max_th_size=${size}`)
const headers = {
'Content-Type': 'application/x-www-form-urlencoded',
Accept: 'application/json',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36'
}
return new Promise((resolve, reject) => {
// eslint-disable-next-line no-undef
GM_xmlhttpRequest({
method: 'POST',
url: hostname,
headers,
data,
onload: response => {
if (response.status !== 200) {
reject(response.status)
} else {
const data = response.responseText.match(/(upload_results = )({.*})(;)/)
if (data && data.length) {
const imgResultList = JSON.parse(data[2]).images
resolve(imgResultList.map(item => {
return `[url=${item.show_url}][img]${item.th_url}[/img][/url]`
}))
} else {
console.log(response)
reject(new Error('Failed to upload'))
}
}
}
})
})
}
// 提取全部对比图信息
function collectComparisons (text) {
const replacements = []
let lastIndex = 0
// eslint-disable-next-line no-constant-condition
while (true) {
const currentIndex = lastIndex
for (const key in regexInfo) {
const regex = regexInfo[key].regex
regex.lastIndex = lastIndex
const match = regex.exec(text)
if (match) {
const result = { starts: 0, ends: 0, teams: [], urls: [], regexType: '', thumbs: false, text: '' }
result.regexType = key
if (regexInfo[key].groupForTeams >= 0) {
result.teams = match[regexInfo[key].groupForTeams]
.split(regexTeamsSplitter)
.map(ele => { return ele.trim() })
}
if (regexInfo[key].groupForUrls >= 0) {
const urls = match[regexInfo[key].groupForUrls]
if (key === 'comparison') {
result.urls = urls.match(regexImageUrlsSeparated)
} else {
result.urls = urls.match(regexScreenshotsThumbsSeparated)
}
}
if (regexInfo[key].groupForThumbs >= 0) {
result.thumbs = !!match[regexInfo[key].groupForThumbs]
} else if (key === 'comparison') {
result.thumbs = false
} else if (key === 'simple') {
result.thumbs = false
}
result.starts = match.index
result.ends = match.index + match[0].length
result.text = match[0]
replacements.push(result)
lastIndex = result.ends
break
}
}
if (lastIndex === currentIndex) {
return replacements
}
}
}
// 从简介中提取信息并格式化截图
async function decomposeDescription (siteName, textToConsume, torrentTitle) {
let mediainfo = {}
let description = ''
const site = siteInfoMap[siteName]
// 优先从简介中获取mediainfo
const tagForMediainfo = site.targetBoxTag || 'quote'
const regexMIStr = site.boxSupportDescr
? '\\[(' + tagForMediainfo + '|quote)\\s*=\\s*mediainfo\\]\\s*(General\\s+Unique ID[^\\0]+?)\\[\\/\\1\\]'
: '\\[(' + tagForMediainfo + '|quote)\\]\\s*(General\\s+Unique ID[^\\0]+?)\\[\\/\\1\\]'
const regexMI = RegExp(regexMIStr, 'im')
const mediainfoArray = textToConsume.match(regexMI)
if (mediainfoArray) {
let mediainfoStr = mediainfoArray[2]
.replace(/^\s*\[\w+(\s*=[^\]]+)?\]/g, '')
.replace(/\s*\[\/\w+\]\s*$/g, '')
mediainfo = string2Mediainfo(mediainfoStr)
// if the site has a place to fill out the mediainfo, remove it in the description box
if (site.mediainfoBox) {
textToConsume = textToConsume.substring(0, mediainfoArray.index) +
textToConsume.substring(mediainfoArray.index + mediainfoArray[0].length)
}
}
if (!torrentTitle && mediainfo && mediainfo.General) {
torrentTitle = mediainfo.General['Complete name'] || mediainfo.General['Movie name']
if (torrentTitle) {
torrentTitle = torrentTitle.replace(/.*?([^\\]+)$/, '$1')
torrentTitle = formatTorrentName(torrentTitle)
}
}
if (site.screenshotsStyle === 'conventional') {
let removePlainScreenshots = false
const comparisons = collectComparisons(textToConsume)
.sort((a, b) => b.starts - a.starts)
for (let { starts, ends, teams, urls, regexType, thumbs } of comparisons) {
let screenshotsStr = ''
if (regexType === 'boxed' || regexType === 'titled' || regexType === 'comparison') {
screenshotsStr = `[b]${teams.join(' | ')}[/b]`
if (!thumbs) {
urls = await images2ThumbUrls(urls.join(' '), teams.length, siteName)
}
if (urls.length > 0) {
urls.forEach((url, i) => {
screenshotsStr += (i % teams.length === 0
? '\n' + url
: ' ' + url)
})
screenshotsStr = `[center]${screenshotsStr}[/center]\n`
removePlainScreenshots = true
}
} else if (regexType === 'simple') {
if (removePlainScreenshots) {
screenshotsStr = ''
} else {
screenshotsStr = textToConsume.substring(starts, ends)
}
} else {
screenshotsStr = ''
}
textToConsume = textToConsume.substring(0, starts) +
screenshotsStr +
textToConsume.substring(ends)
}
description = textToConsume
} else if (site.screenshotsStyle === 'comparison') {
let teamEncode = ''
let screenshots = ''
let currentScreenshots = 0
const teamArray = torrentTitle.match(/\b(D-Z0N3)|(([^\s-@]*)(@[^\s-]+)?)$/)
if (teamArray) {
teamEncode = teamArray[0]
}
const comparisons = collectComparisons(textToConsume)
.sort((a, b) => b.starts - a.starts)
for (let { starts, ends, teams, urls, regexType, thumbs } of comparisons) {
let screenshotsStr = ''
if (regexType === 'comparison') {
screenshotsStr = textToConsume.substring(starts, ends)
} else if (regexType === 'boxed' || regexType === 'titled') {
if (thumbs) {
urls = await thumbs2ImageUrls(urls.join(' '), teams.length, siteName)
}
if (urls.length > 0) {
screenshotsStr = `[comparison=${teams.join(', ')}]${urls.join(' ')}[/comparison]`
}
} else if (regexType === 'simple') {
screenshotsStr = ''
} else {
screenshotsStr = ''
}
description += screenshotsStr
// 如果之前没有获取到teamEncode,直接用Encode赋值,避免后续'includes'判断错误(string.includes('') === true)
teamEncode = teamEncode || 'Encode'
if (urls.length > 0 && urls.length % teams.length === 0) {
if (!teams.find(team => team.toLowerCase() === teamEncode.toLowerCase() || team.toLowerCase() === 'encode')) {
// 截图对比描述中可能会多一些内容,如 Source vs TayTO<Shout Factory> vs CRiSC<MGM>
teamEncode = teams.find(team => team.toLowerCase().includes(teamEncode.toLowerCase()) || team.toLowerCase().includes('encode'))
}
if (teamEncode && !screenshots && urls.length / teams.length >= site.minScreenshots) {
for (let i = 0; i < urls.length; i++) {
let image = urls[i]
const teamCurrent = teams[i % teams.length]
if (currentScreenshots < site.maxScreenshots && (teamCurrent.toLowerCase() === 'encode' || teamCurrent.toLowerCase() === teamEncode.toLowerCase())) {
if (image.match(/\[img\].*?\[\/img\]/)) {
screenshots += image
} else {
screenshots += `[img]${image}[/img]`
}
currentScreenshots += 1
}
}
}
}
textToConsume = textToConsume.substring(0, starts) +
screenshotsStr +
textToConsume.substring(ends)
}
if (screenshots) {
description += `[b]Screenshots[/b]\n${screenshots}`
}
let [quotes, remained] = processTags(
textToConsume, 'quote',
matchLeft => { return matchLeft.replace(/\[quote(?:=([^\]]+))\]/g, '[b]$1[/b]\n[quote]') },
matchRight => { return matchRight },
false)
// 只是为了提取出 hides,内容不做改变
let [hides] = processTags(remained, 'hide',
matchLeft => { return matchLeft },
matchRight => { return matchRight },
false)
description = quotes + hides + description
}
return [description, mediainfo, torrentTitle]
}
// 处理简介文本
function processDescription (siteName, description) {
const site = siteInfoMap[siteName]
const targetBoxTag = site.targetBoxTag
const boxSupportDescr = site.boxSupportDescr
const boxNeedBreakLine = site.boxNeedBreakLine
const allTagBoxesStr = allTagBoxes.join('|')
const otherTagBoxesStr = allTagBoxes.filter(tag => tag !== site.targetBoxTag).join('|')
const unsupportedTagsStr = site.unsupportedTags.join('|')
// 对于不支持box标签的站,统一替换为'quote'标签
const replaceTag = targetBoxTag || 'quote'
if (targetBoxTag) {
description = nestExplode(description, targetBoxTag)
description = compactContent(description, targetBoxTag)
}
description = description
// 处理 mediainfo 容器标签,切换为 [box=mediainfo] 的形式,以便于后续统一匹配 mediainfo
.replace(RegExp('\\[(' + allTagBoxesStr + '|quote|code)(?:\\s*=\\s*mediainfo)?\\]\\s*(General\\s+Unique ID[^\\0]+?)\\[\\/\\1\\]', 'gim'),
boxSupportDescr
? '[' + replaceTag + '=mediainfo]$2[/' + replaceTag + ']'
: '[' + replaceTag + ']$2[/' + replaceTag + ']')
// NHD mediainfo style
.replace(/\[mediainfo\](\s*General\s+Unique ID[^\0]+?)\[\/mediainfo\]/gim,
boxSupportDescr
? '[' + replaceTag + '=mediainfo]$1[/' + replaceTag + ']'
: '[' + replaceTag + ']$1[/' + replaceTag + ']')
// 处理除了 mediainfo 以外的容器类标签
// 注意 allTagBoxesStr(由多个'|'组成)不需要 escape
// 注意 GPW虽然 boxSupportDescr===true,但显示效果有区别,所以最后也会处理为`[b]$1[/b]\n[${replaceTag}]`形式,
// 但这一操作会留到后续才执行,因为现在需要保留这个格式方便识别
.replace(RegExp('\\[(?:' + otherTagBoxesStr + ')(=([^\\]]+))\\]', 'g'),
boxSupportDescr
? `[${replaceTag}$1]`
: `[b]$2[/b]\n[${replaceTag}]`)
.replace(RegExp('\\[(?:' + otherTagBoxesStr + ')\\]', 'g'), `[${replaceTag}]`)
.replace(RegExp('\\[\\/(?:' + otherTagBoxesStr + ')\\]', 'g'), `[/${replaceTag}]`)
.replace(RegExp('\\[\\/(?:' + replaceTag + ')\\](?!\\r?\\n)', 'g'),
boxNeedBreakLine
? `[/${replaceTag}]\n`
: `[/${replaceTag}]`)
// 不支持的标签
.replace(RegExp('\\[\\/?(' + unsupportedTagsStr + ')(=[^\\]]+)?\\]', 'g'), '\n')
.replace(/(\[\/?)(\w+)((?:=(?:[^\r\n\t\f\v [\]])+)?\])/g, (_, p1, p2, p3) => {
return p1 + p2.toLowerCase() + p3
})
.replace(/(?:(?:\[\/(url|flash|flv))|^)(?:(?!\[(url|flash|flv))[\s\S])*(?:(?:\[(url|flash|flv))|$)/g, matches => {
return (matches.replace(/\[align(=\w*)?\]/g, '\n'))
})
.replace(/^\s*([\s\S]*\S)\s*$/g, '$1')
// for pterclub
.replace(/\[(\/?img)\d+\]/g, '[$1]')
if (siteName === GPW) {
description = description
.replace(/\[\/?(size|color|font|b|i|pre)(=[^\]]+)?\]/g, '')
.replace(/\[\/?center\]/g, '\n')
}
return description
}
(() => {
'use strict'
//= ========================================================================================================
// Main
const siteName = Object.keys(siteInfoMap).find(sn => {
let st = siteInfoMap[sn]
return window.location.href.match(escapeRegExp(st.hostName))
})
let page = ''
let site = {}
if (siteName) {
site = siteInfoMap[siteName]
page = Object.keys(site.pages).find(pg => {
let url = `${site.hostName}/${site.pages[pg]}`
return window.location.href.match(escapeRegExp(url))
})
}
if (!siteName || !page) {
return
}
console.log(`running in site ${siteName} and page ${page}`)
if (page === 'upload' || page === 'edit') {
//= ========================================================================================================
// 上传和编辑种子页面
const nameBox = page === 'upload'
? site.nameBoxUpload
: site.nameBoxEdit
const btnBingo = $('<input>')
if (site.construct === NEXUSPHP) {
btnBingo.attr({
type: 'button',
name: 'bingo',
value: 'BINGO',
style: 'font-size: 11px; font-weight: bold; color: blue; margin-right: 3px'
})
const tableBingo = $('<table>').attr({
cellspacing: '1',
cellpadding: '2',
border: '0',
style: 'margin-top:3px'
}).append(
$('<tbody>').append(
$('<tr>').attr({ id: 'multi_function' }).append(
$('<td>').attr({ class: 'embedded' }).append(btnBingo)
)
)
)
if (siteName === MTEAM || siteName === NHD || siteName === PTERCLUB || siteName === PUTAO) {
$('#compose input[name="quote"]').closest('table').after(tableBingo)
} else if (siteName === TTG) {
$('#upload input[name="quote"]').closest('table').after(tableBingo)
}
} else if (site.construct === GAZELLE) {
if (siteName === GPW) {
btnBingo.attr({
type: 'button',
name: 'bingo',
value: 'BINGO',
style: 'font-weight: bold; color: white;',
class: 'BBCodeToolbar-button'
})
const bbcodeToolbar = $('div.BBCodeToolbar').closest('#description-container').find('div.BBCodeToolbar')
bbcodeToolbar.append(btnBingo)
} else if (siteName === UHD) {
btnBingo.attr({
type: 'button',
name: 'bingo',
value: 'BINGO',
style: 'font-weight: bold; color: white;',
class: 'wysibb-toolbar-btn'
})
const divBingo = $('<div>').attr({
class: 'wysibb-toolbar-container'
}).append(btnBingo)
const bbcodeToolbar = $('div.wysibb-toolbar').closest('#textarea_wrap_0').find('div.wysibb-toolbar')
bbcodeToolbar.append(divBingo)
}
}
// function definition
btnBingo.on('click', async () => {
const oriTextBingo = btnBingo.val()
const torrentInfo = {}
try {
btnBingo.val('Handling')
//= ========================================================================================================
// processing description
let textToConsume = ''
if (site.construct === NEXUSPHP) {
const oldText = site.descrBox.val()
let readClipboard = false
if (siteName === NHD || siteName === PTERCLUB || siteName === PUTAO || siteName === MTEAM) {
readClipboard = !oldText
} else if (siteName === TTG) {
readClipboard = !oldText ? true : oldText.length < 125
}
textToConsume = readClipboard ? await navigator.clipboard.readText() : oldText
} else if (site.construct === GAZELLE) {
const oldText = site.descrBox.val()
let readClipboard = !oldText
if (readClipboard) {
btnBingo.focus()
}
textToConsume = readClipboard ? await navigator.clipboard.readText() : oldText
}
textToConsume = processDescription(siteName, textToConsume)
// 为了在未选择种子文件的情况下也能获取torrentTitle,将torrentTitle中信息的识别放到mediainfo之后
// 优先读取nameBox
torrentInfo.torrentTitle = nameBox ? nameBox.val() : ''
// 再读取inpuFile
if (!torrentInfo.torrentTitle) {
let inputFile = (site.inputFile.val() || '').replace(/.*?([^\\]+)$/, '$1')
torrentInfo.torrentTitle = formatTorrentName(inputFile)
}
//= ========================================================================================================
// decompose description (and generate comparison screenshots)
[textToConsume, torrentInfo.mediainfo, torrentInfo.torrentTitle] = await decomposeDescription(siteName, textToConsume, torrentInfo.torrentTitle)
torrentInfo.audioInfo = {
dtsX: false, atmos: false, chineseDub: false, cantoneseDub: false, commentary: false
}
torrentInfo.videoInfo = {
bit10: false, hdr10: false, hdr10plus: false, dovi: false, container: ''
}
torrentInfo.subtitleInfo = {}
Object.keys(subtitleLanguages).forEach(lang => {
torrentInfo.subtitleInfo[lang] = false
})
if (Object.keys(torrentInfo.mediainfo).length === 0 && site.mediainfoBox) {
// 如果简介中没有有效的mediainfo,读取mediainfobox
let mediainfoStr = site.mediainfoBox.val()
torrentInfo.mediainfo = string2Mediainfo(mediainfoStr)
}
// info from mediainfo
Object.entries(torrentInfo.mediainfo).forEach(([infoKey, infoValue]) => {
if (infoKey.match(/text( #\d+)?/i)) {
// subtitle
let matchLang = false
const language = infoValue.Language || infoValue.Title
if (language.match(/chinese|chs|cht/i)) {
if (language.match(/cht|(chinese( |_)traditional)/i)) {
torrentInfo.subtitleInfo.chinese_traditional = true
} else {
torrentInfo.subtitleInfo.chinese_simplified = true
}
matchLang = true
} else {
Object.keys(torrentInfo.subtitleInfo).forEach(lang => {
if (language.match(RegExp(escapeRegExp(lang), 'i')) || language.match(RegExp(escapeRegExp(lang.replace(/_/ig, ' ')), 'i'))) {
torrentInfo.subtitleInfo[lang] = true
matchLang = true
}
})
}
if (matchLang) {
console.log(`Match sub ${language}`)
} else {
console.log(`Other sub ${language}`)
}
} else if (infoKey.match(/audio( #\d+)?/i)) {
// audio
const title = infoValue.Title || ''
const language = infoValue.Language || ''
if (title.match(/commentary/i)) {
torrentInfo.audioInfo.commentary = true
}
if (title.match(/cantonese/i) || language.match(/cantonese/i)) {
torrentInfo.audioInfo.cantoneseDub = true
console.log('Cantonese dub')
} else if (title.match(/chinese|mandarin/i) || language.match(/chinese|mandarin/i)) {
torrentInfo.audioInfo.chineseDub = true
console.log('Chinese Mandarin dub')
} else {
console.log('Other dub')
}
const commecialName = infoValue['Commercial name']
if (commecialName) {
if (commecialName.match(/Dolby Atmos/i)) {
torrentInfo.audioInfo.atmos = true
console.log('Dolby Atmos')
} else if (commecialName.match(/DTS-HD Master Audio/i)) {
torrentInfo.audioInfo.dtsX = true
console.log('DTS:X')
}
}
} else if (infoKey.match(/video/i)) {
// video
const hdrFormat = infoValue['HDR format']
const bitDepth = infoValue['Bit depth']
if (hdrFormat) {
if (hdrFormat.match(/HDR10\+/i)) {
torrentInfo.videoInfo.hdr10plus = true
console.log('HDR10+')
} else if (hdrFormat.match(/HDR10/i)) {
torrentInfo.videoInfo.hdr10 = true
console.log('HDR10')
}
if (hdrFormat.match(/Dolby Vision/i)) {
torrentInfo.videoInfo.dovi = true
console.log('Dolby Vision')
}
} else if (bitDepth.match(/10 bits/i)) {
torrentInfo.videoInfo.bit10 = true
console.log('10 bits')
}
} else if (infoKey.match(/general/i)) {
// general
if (infoValue.Format === 'Matroska') {
torrentInfo.videoInfo.container = 'MKV'
} else if (infoValue.Format === 'MPEG-4') {
torrentInfo.videoInfo.container = 'MP4'
} else if (infoValue.Format === 'AVI') {
torrentInfo.videoInfo.container = 'AVI'
} else {
torrentInfo.videoInfo.container = infoValue.Format.trim()
}
console.log(torrentInfo.videoInfo.container)
// 如果 torrentInfo.torrentTitle 尚未被赋值,直接使用mediainfo 中的值
torrentInfo.torrentTitle = torrentInfo.torrentTitle ||
formatTorrentName(infoValue['Complete name']) ||
formatTorrentName(infoValue['Movie name'])
}
})
//= ========================================================================================================
// info from title
torrentInfo.editionInfo = {}
torrentInfo.sourceInfo = {}
torrentInfo.standardInfo = {}
torrentInfo.processingInfo = {}
torrentInfo.codecInfo = {}
if (torrentInfo.torrentTitle) {
// edition
torrentInfo.editionInfo.criterionCollection = torrentInfo.torrentTitle.match(/\bcc|criterion\b/i)
torrentInfo.editionInfo.mastersOfCinema = torrentInfo.torrentTitle.match(/\bmoc\b/i)
torrentInfo.editionInfo.directorCut = torrentInfo.torrentTitle.match(/\bdc\b/i)
torrentInfo.editionInfo.unrated = torrentInfo.torrentTitle.match(/\bunrated\b/i)
torrentInfo.editionInfo.uncut = torrentInfo.torrentTitle.match(/\buncut\b/i)
torrentInfo.editionInfo.theatrical = torrentInfo.torrentTitle.match(/\btheatrical\b/i)
torrentInfo.editionInfo.extended = torrentInfo.torrentTitle.match(/\bextended\b/i)
torrentInfo.editionInfo.remaster4k = torrentInfo.torrentTitle.match(/\b4k remaster\b/i)
torrentInfo.editionInfo.remaster = !torrentInfo.editionInfo.remaster4k && torrentInfo.torrentTitle.match(/\bremaster\b/i)
torrentInfo.editionInfo.restoration4k = torrentInfo.torrentTitle.match(/\b4k restoration\b/i)
torrentInfo.editionInfo.twoInOne = torrentInfo.torrentTitle.match(/\b2in1\b/i)
torrentInfo.editionInfo.threeInOne = torrentInfo.torrentTitle.match(/\b3in1\b/i)
torrentInfo.editionInfo.hybrid = torrentInfo.torrentTitle.match(/\bhybrid\b/i)
torrentInfo.editionInfo.imax = torrentInfo.torrentTitle.match(/\bimax\b/i)
torrentInfo.editionInfo.tvCut = torrentInfo.torrentTitle.match(/\btv ?cut\b/i)
// source
torrentInfo.sourceInfo.remux = torrentInfo.torrentTitle.match(/\b(remux)\b/i)
torrentInfo.sourceInfo.encode = torrentInfo.torrentTitle.match(/\b(blu-?ray|bdrip|dvdrip|webrip)\b/i)
torrentInfo.sourceInfo.bluray = torrentInfo.torrentTitle.match(/\b(blu-?ray|bdrip)\b/i)
torrentInfo.sourceInfo.hdtv = torrentInfo.torrentTitle.match(/\bhdtv(rip)?\b/i)
torrentInfo.sourceInfo.hdrip = torrentInfo.torrentTitle.match(/\bhdrip\b/i)
torrentInfo.sourceInfo.webdl = torrentInfo.torrentTitle.match(/\bweb-?dl\b/i)
torrentInfo.sourceInfo.webrip = torrentInfo.torrentTitle.match(/\bwebrip\b/i)
torrentInfo.sourceInfo.web = torrentInfo.sourceInfo.webdl || torrentInfo.sourceInfo.webrip
torrentInfo.sourceInfo.dvd = torrentInfo.torrentTitle.match(/\bdvd(rip)?/i)
torrentInfo.sourceInfo.hddvd = torrentInfo.torrentTitle.match(/\bhddvd\b/i)
// resolution
torrentInfo.standardInfo.res1080p = torrentInfo.torrentTitle.match(/\b1080p\b/i)
torrentInfo.standardInfo.res1080i = torrentInfo.torrentTitle.match(/\b1080i\b/i)
torrentInfo.standardInfo.res720p = torrentInfo.torrentTitle.match(/\b720p\b/i)
torrentInfo.standardInfo.res2160p = torrentInfo.torrentTitle.match(/\b2160p|4k\b/i)
torrentInfo.standardInfo.sd = torrentInfo.torrentTitle.match(/\b480p\b/i) || torrentInfo.sourceInfo.dvd
torrentInfo.standardInfo.mhd = torrentInfo.torrentTitle.match(/\bmhd\b/i)
// processing
torrentInfo.processingInfo.raw = torrentInfo.torrentTitle.match(/\b(remux|web-?dl|(bd|dvd)?iso)\b/i)
torrentInfo.processingInfo.encode = !torrentInfo.processingInfo.raw
torrentInfo.processingInfo.remux = torrentInfo.torrentTitle.match(/\bremux\b/i)
// codec
torrentInfo.codecInfo.h264 = torrentInfo.torrentTitle.match(/\bh\.?264\b/i)
torrentInfo.codecInfo.x264 = torrentInfo.torrentTitle.match(/\bavc|x264\b/i)
torrentInfo.codecInfo.h265 = torrentInfo.torrentTitle.match(/\bh\.?265\b/i)
torrentInfo.codecInfo.x265 = torrentInfo.torrentTitle.match(/\bhevc|x265\b/i)
torrentInfo.codecInfo.x266 = torrentInfo.torrentTitle.match(/\bx266\b/i)
torrentInfo.codecInfo.vc1 = torrentInfo.torrentTitle.match(/\bvc-1\b/i)
torrentInfo.codecInfo.av1 = torrentInfo.torrentTitle.match(/\bav1\b/i)
torrentInfo.codecInfo.mpeg2 = torrentInfo.torrentTitle.match(/\bmpeg-2\b/i)
torrentInfo.codecInfo.xvid = torrentInfo.torrentTitle.match(/\bxvid\b/i)
torrentInfo.codecInfo.divx = torrentInfo.torrentTitle.match(/\bdivx\b/i)
torrentInfo.codecInfo.flac = torrentInfo.torrentTitle.match(/\bflac\b/i)
torrentInfo.codecInfo.ape = torrentInfo.torrentTitle.match(/\bape\b/i)
// team
const teamArray = torrentInfo.torrentTitle.match(/\b(D-Z0N3)|(([^\s-@]*)(@[^\s-]+)?)$/)
torrentInfo.team = teamArray ? teamArray[0] : ''
}
//= ========================================================================================================
// info from douban / imdb
const categoryMovie = 'Movie'; const categoryTvSeries = 'TV Series'; const categoryAnimation = 'Animation'
const categoryDocumentary = 'Documentary'; const categoryTvShow = 'TV Show'
if (site.construct === NEXUSPHP) {
torrentInfo.movieInfo = { areaInfo: {} }
// area
const areaArray = textToConsume.match(/产\s*地\s*(.*)\s*/)
const area = areaArray ? areaArray[1] : ''
if (area.match(/中国大陆/)) {
torrentInfo.movieInfo.areaInfo.cnMl = true
} else if (area.match(/香港/)) {
torrentInfo.movieInfo.areaInfo.hk = true
} else if (area.match(/台湾/)) {
torrentInfo.movieInfo.areaInfo.tw = true
} else if (area.match(/美国|加拿大|英国|法国|德国|希腊|匈牙利|爱尔兰|意大利|阿尔巴尼亚|安道尔|奥地利|白俄罗斯|比利时|波斯尼亚|黑塞哥维那|保加利亚|克罗地亚|塞浦路斯|捷克|丹麦|爱沙尼亚|法罗群岛|冰岛|芬兰|拉脱维亚|列支敦士登|立陶宛|卢森堡|马其顿|马耳他|摩尔多瓦|摩纳哥|荷兰|挪威|波兰|葡萄牙|罗马尼亚|俄罗斯|圣马力诺|塞黑|斯洛伐克|斯洛文尼亚|西班牙|瑞典|瑞士|乌克兰|梵蒂冈/)) {
torrentInfo.movieInfo.areaInfo.euAme = true
} else if (area.match(/印度|韩国|日本|新加坡|泰国|印度尼西亚|菲律宾|越南|土耳其|老挝|柬埔寨|缅甸|马来西亚|文莱|东帝汶|尼泊尔|不丹|孟加拉国|巴基斯坦|斯里兰卡|马尔代夫|阿富汗|伊拉克|伊朗|叙利亚|约旦|黎巴嫩|以色列|巴勒斯坦|沙特阿拉伯|阿曼|也门|格鲁吉亚|亚美尼亚|塞浦路斯|哈萨克斯坦|吉尔吉斯斯坦|塔吉克斯坦|乌兹别克斯坦|土库曼斯坦|蒙古|朝鲜/)) {
torrentInfo.movieInfo.areaInfo.asia = true
if (area.match(area.match(/韩国/))) {
torrentInfo.movieInfo.areaInfo.kor = true
} else if (area.match(/日本/)) {
torrentInfo.movieInfo.areaInfo.jap = true
} else if (area.match(/印度/)) {
torrentInfo.movieInfo.areaInfo.ind = true
}
}
// title
const translatedTitleArray = textToConsume.match(/译\s*名\s*([^/\n]+)(?:\/|\n)/)
const originalTitleArray = textToConsume.match(/片\s*名\s*([^/\n]+)(?:\/|\n)/)
if (translatedTitleArray && originalTitleArray) {
torrentInfo.movieInfo.translatedTitle = translatedTitleArray[1].trim()
torrentInfo.movieInfo.originalTitle = originalTitleArray[1].trim()
}
// festival
const festivalArray = textToConsume.match(/(\d{4})-\d{2}-\d{2}\((\S+电影节)\)/)
torrentInfo.movieInfo.festival = festivalArray ? (festivalArray[1] + festivalArray[2]).trim() : ''
// category
const genresArray = textToConsume.match(/类\s*别\s+([^\n]*)\s*\n/)
torrentInfo.movieInfo.genres = genresArray
? genresArray[1].replace(/([^ ])\/([^ ])/g, '$1 / $2')
: ''
torrentInfo.movieInfo.category = torrentInfo.movieInfo.genres.match('纪录')
? categoryDocumentary
: torrentInfo.movieInfo.genres.match('动画')
? categoryAnimation
: textToConsume.match(/集\s*数\s+/g)
? categoryTvSeries
: torrentInfo.movieInfo.genres.match('秀')
? categoryTvShow
: categoryMovie
// douban and imdb score in small_desc
const doubanScoreArray = textToConsume.match(/豆\s*瓣\s*评\s*分\s+(\d\.\d)\/10\sfrom\s((?:\d+,)*\d+)\susers/)
if (doubanScoreArray) {
torrentInfo.movieInfo.doubanScore = doubanScoreArray[1]
torrentInfo.movieInfo.doubanScoreRatingNumber = doubanScoreArray[2]
}
const imdbScoreArray = textToConsume.match(/IMDb\s*评\s*分\s+(\d\.\d)\/10\sfrom\s((?:\d+,)*\d+)\susers/i)
if (imdbScoreArray) {
torrentInfo.movieInfo.imdbScore = imdbScoreArray[1]
torrentInfo.movieInfo.imdbRatingNumber = imdbScoreArray[2]
}
// director
const directorArray = textToConsume.match(/导\s*演\s+([^\w\n\s]*)\s*/)
torrentInfo.movieInfo.director = directorArray ? directorArray[1] : ''
// douban link
const doubanLinkArray = textToConsume.match(/豆瓣\s*链\s*接.+(https?:\/\/movie\.douban\.com\/subject\/(\d+)\/?)/)
torrentInfo.movieInfo.doubanLink = doubanLinkArray ? doubanLinkArray[1] : ''
torrentInfo.movieInfo.doubanId = doubanLinkArray ? doubanLinkArray[2] : ''
// imdb link
const imdbLinkArray = textToConsume.match(/IMDb\s*链\s*接.+(https?:\/\/www\.imdb\.com\/title\/(tt\d+)\/?)/i)
torrentInfo.movieInfo.imdbLink = imdbLinkArray ? imdbLinkArray[1] : ''
torrentInfo.movieInfo.imdbId = imdbLinkArray ? imdbLinkArray[2] : ''
}
//= ========================================================================================================
// fill the page
// common controls
// 用于记录种子在站点的匹配信息
torrentInfo.infoInSite = { 'site': siteName }
// namebox
if (nameBox && torrentInfo.torrentTitle) {
torrentInfo.infoInSite.torrentTitle = torrentInfo.torrentTitle
if (site.translatedChineseNameInTitle) {
if (torrentInfo.movieInfo.areaInfo.cnMl) {
torrentInfo.infoInSite.torrentTitle = torrentInfo.torrentTitle.match(torrentInfo.movieInfo.originalTitle)
? torrentInfo.torrentTitle
: `[${torrentInfo.movieInfo.originalTitle}] ${torrentInfo.torrentTitle}`
} else {
torrentInfo.infoInSite.torrentTitle = torrentInfo.torrentTitle.match(torrentInfo.movieInfo.translatedTitle)
? torrentInfo.torrentTitle
: `[${torrentInfo.movieInfo.translatedTitle}] ${torrentInfo.torrentTitle}`
}
} else {
torrentInfo.infoInSite.torrentTitle = torrentInfo.torrentTitle
}
nameBox.val(torrentInfo.infoInSite.torrentTitle)
}
// small description
if (site.smallDescBox && torrentInfo.movieInfo && (torrentInfo.movieInfo.doubanLink || torrentInfo.movieInfo.imdbLink)) {
// container for small_desc(副标题) fields
const smallDescrArray = []
if (torrentInfo.movieInfo.originalTitle && torrentInfo.movieInfo.translatedTitle) {
if (!site.translatedChineseNameInTitle) {
if (torrentInfo.movieInfo.areaInfo.cnMl) {
smallDescrArray.push(torrentInfo.torrentTitle.match(torrentInfo.movieInfo.originalTitle)
? torrentInfo.movieInfo.translatedTitle
: torrentInfo.movieInfo.originalTitle)
} else {
smallDescrArray.push(torrentInfo.movieInfo.translatedTitle)
}
}
}
if (torrentInfo.movieInfo.festival) {
smallDescrArray.push(torrentInfo.movieInfo.festival)
}
if (torrentInfo.movieInfo.genres) {
smallDescrArray.push(torrentInfo.movieInfo.genres)
}
if (!site.pullMovieScore) {
if (torrentInfo.movieInfo.doubanScore) {
smallDescrArray.push('豆瓣 ' + torrentInfo.movieInfo.doubanScore + '(' + torrentInfo.movieInfo.doubanScoreRatingNumber + ')')
}
if (torrentInfo.movieInfo.imdbScore) {
smallDescrArray.push('IMDb ' + torrentInfo.movieInfo.imdbScore + '(' + torrentInfo.movieInfo.imdbRatingNumber + ')')
}
}
if (torrentInfo.movieInfo.director) {
smallDescrArray.push(torrentInfo.movieInfo.director)
}
// complete small_descr
torrentInfo.infoInSite.smallDescr = smallDescrArray.join(' | ')
site.smallDescBox.val(torrentInfo.infoInSite.smallDescr)
}
// douban link
if (site.doubanLinkBox && torrentInfo.movieInfo && torrentInfo.movieInfo.doubanLink) {
if (!site.doubanIdInsteadofLink) {
site.doubanLinkBox.val(torrentInfo.movieInfo.doubanLink)
} else {
site.doubanLinkBox.val(torrentInfo.movieInfo.doubanId)
}
}
// imdb link
if (site.imdbLinkBox && torrentInfo.movieInfo && torrentInfo.movieInfo.imdbLink) {
if (!site.doubanIdInsteadofLink) {
site.imdbLinkBox.val(torrentInfo.movieInfo.imdbLink)
} else {
site.imdbLinkBox.val(torrentInfo.movieInfo.imdbId)
}
}
// source
if (site.sourceSel && torrentInfo.sourceInfo && Object.values(torrentInfo.sourceInfo).some(option => option)) {
torrentInfo.infoInSite.source = site.sourceInfo.default || 0
if (siteName === PTERCLUB) {
torrentInfo.infoInSite.source = torrentInfo.sourceInfo.remux
? site.sourceInfo.remux// remux
: torrentInfo.sourceInfo.encode
? site.sourceInfo.encode// encode
: torrentInfo.sourceInfo.hdtv
? site.sourceInfo.hdtv// hdtv
: torrentInfo.sourceInfo.webdl
? site.sourceInfo.webdl// web-dl
: torrentInfo.sourceInfo.dvd || torrentInfo.sourceInfo.hddvd
? site.sourceInfo.dvd
: torrentInfo.infoInSite.source// other
} else if (siteName === NHD) {
torrentInfo.infoInSite.source = torrentInfo.sourceInfo.bluray
? site.sourceInfo.bluray
: torrentInfo.sourceInfo.hddvd
? site.sourceInfo.hddvd
: torrentInfo.sourceInfo.dvd
? site.sourceInfo.dvd
: torrentInfo.sourceInfo.webdl
? site.sourceInfo.webdl
: torrentInfo.sourceInfo.webrip
? site.sourceInfo.webrip
: torrentInfo.infoInSite.source
} else if (siteName === GPW) {
torrentInfo.infoInSite.source = torrentInfo.sourceInfo.bluray
? site.sourceInfo.bluray
: torrentInfo.sourceInfo.hddvd
? site.sourceInfo.hddvd
: torrentInfo.sourceInfo.dvd
? site.sourceInfo.dvd
: torrentInfo.sourceInfo.web
? site.sourceInfo.web
: torrentInfo.infoInSite.source
} else if (siteName === UHD) {
torrentInfo.infoInSite.source = torrentInfo.sourceInfo.encode
? site.sourceInfo.encode
: torrentInfo.sourceInfo.remux
? site.sourceInfo.remux
: torrentInfo.sourceInfo.webdl
? site.sourceInfo.webdl
: torrentInfo.sourceInfo.webrip
? site.sourceInfo.webrip
: torrentInfo.sourceInfo.hdrip
? site.sourceInfo.hdrip
: torrentInfo.sourceInfo.hdtv
? site.sourceInfo.hdtv
: torrentInfo.sourceInfo.bluray
? site.sourceInfo.bluray
: torrentInfo.infoInSite.source
}
site.sourceSel.val(torrentInfo.infoInSite.source)
}
// standard
if (site.standardSel && torrentInfo.standardInfo && Object.values(torrentInfo.standardInfo).some(option => option)) {
torrentInfo.infoInSite.standard = torrentInfo.standardInfo.res1080p
? site.standardInfo.res1080p
: torrentInfo.standardInfo.res1080i
? site.standardInfo.res1080i
: torrentInfo.standardInfo.res720p
? site.standardInfo.res720p
: torrentInfo.standardInfo.res2160p
? site.standardInfo.res2160p
: site.standardInfo.default
if (torrentInfo.infoInSite.standard === site.standardInfo.default) {
if (Object.keys(site.standardInfo).includes('sd') && torrentInfo.standardInfo.sd) {
torrentInfo.infoInSite.standard = site.standardInfo.sd
} else if (Object.keys(site.standardInfo).includes('mhd') && torrentInfo.standardInfo.mhd) {
torrentInfo.infoInSite.standard = site.standardInfo.mhd
}
}
site.standardSel.val(torrentInfo.infoInSite.standard)
}
// processing
if (site.processingSel && torrentInfo.processingInfo && Object.values(torrentInfo.processingInfo).some(option => option)) {
torrentInfo.infoInSite.processing = site.processingInfo.default || 0
if (siteName === NHD) {
torrentInfo.infoInSite.processing = torrentInfo.processingInfo.raw
? site.processingInfo.raw
: torrentInfo.processingInfo.encode
? site.processingInfo.encode
: torrentInfo.infoInSite.processing
} else if (siteName === GPW) {
site.processingSel.closest('tr.hidden').removeClass('hidden')
torrentInfo.infoInSite.processing = torrentInfo.processingInfo.remux
? site.processingInfo.remux
: torrentInfo.processingInfo.encode
? site.processingInfo.encode
: torrentInfo.infoInSite.processing
}
site.processingSel.val(torrentInfo.infoInSite.processing)
}
// codec
if (site.codecSel && torrentInfo.codecInfo && Object.values(torrentInfo.codecInfo).some(option => option)) {
torrentInfo.infoInSite.codec = site.codecInfo.default || 0
if (siteName === NHD || siteName === PUTAO || siteName === MTEAM) {
torrentInfo.infoInSite.codec = torrentInfo.codecInfo.x264 || torrentInfo.codecInfo.h264
? site.codecInfo.h264
: torrentInfo.codecInfo.x265 || torrentInfo.codecInfo.h265
? site.codecInfo.h265
: torrentInfo.codecInfo.vc1
? site.codecInfo.vc1
: torrentInfo.codecInfo.mpeg2
? site.codecInfo.mpeg2
: torrentInfo.codecInfo.xvid
? site.codecInfo.xvid
: torrentInfo.codecInfo.flac
? site.codecInfo.flac
: torrentInfo.codecInfo.ape
? site.codecInfo.ape
: torrentInfo.infoInSite.codec
} else if (siteName === GPW) {
torrentInfo.infoInSite.codec = torrentInfo.codecInfo.h264
? site.codecInfo.h264
: torrentInfo.codecInfo.h265
? site.codecInfo.h265
: torrentInfo.codecInfo.x264
? site.codecInfo.x264
: torrentInfo.codecInfo.x265
? site.codecInfo.x265
: torrentInfo.codecInfo.xvid
? site.codecInfo.xvid
: torrentInfo.codecInfo.divx
? site.codecInfo.divx
: torrentInfo.infoInSite.codec
} else if (siteName === UHD) {
torrentInfo.infoInSite.codec = torrentInfo.codecInfo.h264
? site.codecInfo.h264
: torrentInfo.codecInfo.h265
? site.codecInfo.h265
: torrentInfo.codecInfo.x264
? site.codecInfo.x264
: torrentInfo.codecInfo.x265
? site.codecInfo.x265
: torrentInfo.codecInfo.x266
? site.codecInfo.x266
: torrentInfo.codecInfo.vc1
? site.codecInfo.vc1
: torrentInfo.codecInfo.mpeg2
? site.codecInfo.mpeg2
: torrentInfo.codecInfo.av1
? site.codecInfo.av1
: torrentInfo.infoInSite.codec
}
site.codecSel.val(torrentInfo.infoInSite.codec)
}
// team
if (torrentInfo.team) {
if (site.teamSel) {
torrentInfo.infoInSite.team = torrentInfo.team
site.teamSel.find('option').each((_, element) => {
if (element.text.toLowerCase() === torrentInfo.team.toLowerCase()) {
site.teamSel.val(element.value)
}
})
} else if (site.teamBox) {
torrentInfo.infoInSite.team = torrentInfo.team
site.teamBox.val(torrentInfo.team)
}
}
// area selection
if (site.areaSel && torrentInfo.movieInfo && torrentInfo.movieInfo.areaInfo) {
torrentInfo.infoInSite.area = site.areaInfo.default || 0
if (siteName === PTERCLUB) {
torrentInfo.infoInSite.area = torrentInfo.movieInfo.areaInfo.cnMl
? site.areaInfo.cnMl
: torrentInfo.movieInfo.areaInfo.hk
? site.areaInfo.hk
: torrentInfo.movieInfo.areaInfo.tw
? site.areaInfo.tw
: torrentInfo.movieInfo.areaInfo.euAme
? site.areaInfo.euAme
: torrentInfo.movieInfo.areaInfo.kor
? site.areaInfo.kor
: torrentInfo.movieInfo.areaInfo.jap
? site.areaInfo.jap
: torrentInfo.movieInfo.areaInfo.ind
? site.areaInfo.ind
: site.areaInfo.other
} else if (siteName === MTEAM) {
torrentInfo.infoInSite.area = torrentInfo.movieInfo.areaInfo.cnMl
? site.areaInfo.cnMl
: torrentInfo.movieInfo.areaInfo.euAme
? site.areaInfo.euAme
: torrentInfo.movieInfo.areaInfo.hk || torrentInfo.movieInfo.areaInfo.tw
? site.areaInfo.hkTw
: torrentInfo.movieInfo.areaInfo.jap
? site.areaInfo.jap
: torrentInfo.movieInfo.areaInfo.kor
? site.areaInfo.kor
: site.areaInfo.other
}
site.areaSel.val(torrentInfo.infoInSite.area)
}
// category selection
if (site.categorySel) {
torrentInfo.infoInSite.category = site.categoryInfo.default || 0
if ((siteName === NHD || siteName === PTERCLUB) && torrentInfo.movieInfo) {
torrentInfo.infoInSite.category = torrentInfo.movieInfo.category === categoryMovie
? site.categoryInfo.movie
: torrentInfo.movieInfo.category === categoryTvSeries
? site.categoryInfo.tvSeries
: torrentInfo.movieInfo.category === categoryAnimation
? site.categoryInfo.animation
: torrentInfo.movieInfo.category === categoryDocumentary
? site.categoryInfo.documentary
: torrentInfo.movieInfo.category === categoryTvShow
? site.categoryInfo.tvShow
: torrentInfo.infoInSite.category
} else if (siteName === PUTAO && torrentInfo.movieInfo && torrentInfo.movieInfo.areaInfo) {
if (torrentInfo.movieInfo.category === categoryMovie) {
torrentInfo.infoInSite.category = torrentInfo.movieInfo.areaInfo.cnMl ||
torrentInfo.movieInfo.areaInfo.hk || torrentInfo.movieInfo.areaInfo.tw
? site.categoryInfo.movieCn
: torrentInfo.movieInfo.areaInfo.euAme
? site.categoryInfo.movieEuAme
: torrentInfo.movieInfo.areaInfo.asia
? site.categoryInfo.movieAsia
: torrentInfo.infoInSite.category
} else if (torrentInfo.movieInfo.category === categoryDocumentary) {
// for clarification
torrentInfo.infoInSite.category = site.categoryInfo.documentary
} else if (torrentInfo.movieInfo.category === categoryAnimation) {
// for clarification
torrentInfo.infoInSite.category = site.categoryInfo.animation
} else if (torrentInfo.movieInfo.category === categoryTvSeries) {
torrentInfo.infoInSite.category = torrentInfo.movieInfo.areaInfo.hk || torrentInfo.movieInfo.areaInfo.tw
? site.categoryInfo.tvSeriesHkTw
: torrentInfo.movieInfo.areaInfo.cnMl
? site.categoryInfo.tvSeriesCnMl
: torrentInfo.movieInfo.areaInfo.asia
? site.categoryInfo.tvSeriesAsia
: torrentInfo.movieInfo.areaInfo.euAme
? site.categoryInfo.tvSeriesEuAme
: torrentInfo.infoInSite.category
} else if (torrentInfo.movieInfo.category === categoryTvShow) {
torrentInfo.infoInSite.category = torrentInfo.movieInfo.areaInfo.cnMl
? site.categoryInfo.tvShowCnMl
: torrentInfo.movieInfo.areaInfo.hk || torrentInfo.movieInfo.areaInfo.tw
? site.categoryInfo.tvShowHkTw
: torrentInfo.movieInfo.areaInfo.euAme
? site.categoryInfo.tvShowEuAme
: torrentInfo.movieInfo.areaInfo.jap || torrentInfo.movieInfo.areaInfo.kor
? site.categoryInfo.tvShowJapKor
: torrentInfo.infoInSite.category
}
} else if (siteName === MTEAM && torrentInfo.sourceInfo) {
if (torrentInfo.movieInfo.category === categoryMovie) {
torrentInfo.infoInSite.category = torrentInfo.sourceRemux
? site.categoryInfo.movieRemux
: torrentInfo.sourceInfo.encode || torrentInfo.sourceInfo.hdtv || torrentInfo.sourceInfo.hddvd || torrentInfo.sourceInfo.web
? site.categoryInfo.movieHd
: torrentInfo.infoInSite.category
} else if (torrentInfo.movieInfo.category === categoryTvSeries || torrentInfo.movieInfo.category === categoryTvShow) {
torrentInfo.infoInSite.category = torrentInfo.sourceInfo.encode || torrentInfo.sourceInfo.hdtv || torrentInfo.sourceInfo.hddvd || torrentInfo.sourceInfo.web
? site.categoryInfo.tvSeriesHd
: torrentInfo.infoInSite.category
} else if (torrentInfo.movieInfo.category === categoryDocumentary) {
torrentInfo.infoInSite.category = site.categoryInfo.documentary
} else if (torrentInfo.movieInfo.category === categoryAnimation) {
torrentInfo.infoInSite.category = site.categoryInfo.animation
}
} else if (siteName === TTG && torrentInfo.standardInfo && torrentInfo.movieInfo && torrentInfo.movieInfo.areaInfo) {
if (torrentInfo.movieInfo.category === categoryMovie) {
torrentInfo.infoInSite.category = torrentInfo.standardInfo.res720p
? site.categoryInfo.movie720p
: torrentInfo.standardInfo.res1080i || torrentInfo.standardInfo.res1080p
? site.categoryInfo.movie1080ip
: torrentInfo.standardInfo.res2160p
? site.categoryInfo.movie2160p
: torrentInfo.infoInSite.category
} else if (torrentInfo.movieInfo.category === categoryDocumentary) {
torrentInfo.infoInSite.category = torrentInfo.standardInfo.res720p
? site.categoryInfo.documentary720p
: torrentInfo.standardInfo.res1080i || torrentInfo.standardInfo.res1080p
? site.categoryInfo.documentary1080ip
: torrentInfo.infoInSite.category
} else if (torrentInfo.movieInfo.category === categoryAnimation) {
torrentInfo.infoInSite.category = site.categoryInfo.animation
} else if (torrentInfo.movieInfo.category === categoryTvSeries) {
torrentInfo.infoInSite.category = torrentInfo.movieInfo.areaInfo.jap
? site.categoryInfo.tvSeriesJap
: torrentInfo.movieInfo.areaInfo.kor
? site.categoryInfo.tvSeriesKor
: torrentInfo.euAme
? site.categoryInfo.tvSeriesEuAme
: torrentInfo.movieInfo.areaInfo.cnMl || torrentInfo.movieInfo.areaInfo.hk || torrentInfo.movieInfo.areaInfo.tw
? site.categoryInfo.tvSeriesCn
: torrentInfo.infoInSite.category
} else if (torrentInfo.movieInfo.category === categoryTvShow) {
torrentInfo.infoInSite.category = torrentInfo.movieInfo.areaInfo.kor
? site.categoryInfo.tvShowKor
: torrentInfo.movieInfo.areaInfo.jap
? site.categoryInfo.tvShowJap
: site.categoryInfo.tvShow
}
}
site.categorySel.val(torrentInfo.infoInSite.category)
}
// site-specific
if (site.construct === NEXUSPHP) {
if (siteName === PTERCLUB && torrentInfo.subtitleInfo && torrentInfo.audioInfo) {
if (site.chsubCheck && site.englishSubCheck && site.chdubCheck && site.cantodubCheck) {
site.chsubCheck.checked = torrentInfo.subtitleInfo.chinese_simplified || torrentInfo.subtitleInfo.chinese_traditional
site.englishSubCheck.checked = torrentInfo.subtitleInfo.english
site.chdubCheck.checked = torrentInfo.audioInfo.chineseDub
site.cantodubCheck.checked = torrentInfo.audioInfo.cantoneseDub
}
} else if (siteName === MTEAM && torrentInfo.subtitleInfo && torrentInfo.audioInfo) {
if (site.chsubCheck && site.chdubCheck) {
site.chsubCheck.checked = torrentInfo.subtitleInfo.chinese_simplified || torrentInfo.subtitleInfo.chinese_traditional
site.chdubCheck.checked = torrentInfo.audioInfo.chineseDub
}
} else if (siteName === TTG && torrentInfo.subtitleInfo) {
if (torrentInfo.subtitleInfo.chinese_simplified && torrentInfo.subtitleInfo.chinese_traditional) {
site.subtitleBox.val('* 内封简繁字幕')
} else if (torrentInfo.subtitleInfo.chinese_simplified) {
site.subtitleBox.val('* 内封简体字幕')
} else if (torrentInfo.subtitleInfo.chinese_traditional) {
site.subtitleBox.val('* 内封繁体字幕')
}
}
} else if (site.construct === GAZELLE) {
if (siteName === GPW) {
// movie edition
if (torrentInfo.editionInfo) {
site.movieEditionCheck.click()
if (torrentInfo.editionInfo.criterionCollection) { site.movieEditionInfo.criterionCollection.click() }
if (torrentInfo.editionInfo.mastersOfCinema) { site.movieEditionInfo.mastersOfCinema.click() }
if (torrentInfo.editionInfo.directorCut) { site.movieEditionInfo.directorCut.click() }
if (torrentInfo.editionInfo.unrated) { site.movieEditionInfo.unrated.click() }
if (torrentInfo.editionInfo.uncut) { site.movieEditionInfo.uncut.click() }
if (torrentInfo.editionInfo.theatrical) { site.movieEditionInfo.theatrical.click() }
if (torrentInfo.editionInfo.extended) { site.movieEditionInfo.extended.click() }
if (torrentInfo.editionInfo.remaster4k) { site.movieEditionInfo.remaster4k.click() }
if (torrentInfo.editionInfo.remaster) { site.movieEditionInfo.remaster.click() }
if (torrentInfo.editionInfo.restoration4k) { site.movieEditionInfo.restoration4k.click() }
if (torrentInfo.editionInfo.twoInOne) { site.movieEditionInfo.twoInOne.click() }
if (torrentInfo.audioInfo && torrentInfo.audioInfo.commentary) { site.movieEditionInfo.withCommentary.click() }
}
// subtitles
const subbed = Object.values(torrentInfo.subtitleInfo).some(x => x)
site.noSubCheck.checked = !subbed
site.mixedSubCheck.checked = subbed
if (subbed) {
site.otherSubtitlesDiv.removeClass('hidden')
Object.keys(torrentInfo.subtitleInfo).forEach(lang => {
if (site.subtitleInfo[lang]) {
site.subtitleInfo[lang].checked = torrentInfo.subtitleInfo[lang]
}
})
}
// video info
if (torrentInfo.videoInfo) {
site.videoInfo.bit10.checked = torrentInfo.videoInfo.bit10
site.videoInfo.hdr10.checked = torrentInfo.videoInfo.hdr10
site.videoInfo.hdr10plus.checked = torrentInfo.videoInfo.hdr10plus
site.videoInfo.dovi.checked = torrentInfo.videoInfo.dovi
}
// audio info
if (torrentInfo.audioInfo) {
site.audioInfo.dtsX.checked = torrentInfo.audioInfo.dtsX
site.audioInfo.atmos.checked = torrentInfo.audioInfo.atmos
site.audioInfo.chineseDub.checked = torrentInfo.audioInfo.chineseDub
}
// container info
if (Object.values(site.containerInfo).includes(torrentInfo.videoInfo.container)) {
site.containerSel.val(torrentInfo.videoInfo.container)
}
} else if (siteName === UHD) {
// movie edition
if (torrentInfo.editionInfo) {
if (torrentInfo.editionInfo.criterionCollection) { site.movieEditionInfo.criterionCollection.click() }
if (torrentInfo.editionInfo.directorCut) { site.movieEditionInfo.directorCut.click() }
if (torrentInfo.editionInfo.unrated) { site.movieEditionInfo.unrated.click() }
if (torrentInfo.editionInfo.uncut) { site.movieEditionInfo.uncut.click() }
if (torrentInfo.editionInfo.theatrical) { site.movieEditionInfo.theatrical.click() }
if (torrentInfo.editionInfo.extended) { site.movieEditionInfo.extended.click() }
if (torrentInfo.editionInfo.remaster4k) { site.movieEditionInfo.remaster4k.click() }
if (torrentInfo.editionInfo.remaster) { site.movieEditionInfo.remaster.click() }
if (torrentInfo.editionInfo.restoration4k) { site.movieEditionInfo.restoration4k.click() }
if (torrentInfo.editionInfo.twoInOne) { site.movieEditionInfo.twoInOne.click() }
if (torrentInfo.editionInfo.threeInOne) { site.movieEditionInfo.threeInOne.click() }
if (torrentInfo.editionInfo.hybrid) { site.movieEditionInfo.hybrid.click() }
if (torrentInfo.editionInfo.imax) { site.movieEditionInfo.imax.click() }
if (torrentInfo.editionInfo.tvCut) { site.movieEditionInfo.tvCut.click() }
if (torrentInfo.videoInfo && (torrentInfo.videoInfo.bit10 || torrentInfo.videoInfo.hdr10 || torrentInfo.videoInfo.hdr10plus || torrentInfo.videoInfo.dovi)) {
site.movieEditionInfo.bit10.click()
}
}
// hdr info
if (torrentInfo.videoInfo) {
if (torrentInfo.videoInfo.dovi) { site.hdrSel.val(site.hdrInfo.dovi) }
else if (torrentInfo.videoInfo.hdr10plus) { site.hdrSel.val(site.hdrInfo.hdr10plus) }
else if (torrentInfo.videoInfo.hdr10) { site.hdrSel.val(site.hdrInfo.hdr10) }
}
// season info
if (site.seasonSel) {
// totally unnecessary, only to pass the uploading procedure
site.seasonSel.val(site.seansonInfo.s01)
}
}
// repair the mediainfo in case 'Complete name' is missing
if (torrentInfo.mediainfo && torrentInfo.mediainfo.General) {
if (!torrentInfo.mediainfo.General['Complete name'] &&
torrentInfo.mediainfo.General['Movie name'] &&
torrentInfo.videoInfo &&
torrentInfo.videoInfo.container) {
torrentInfo.mediainfo.General['Complete name'] = `${torrentInfo.mediainfo.General['Movie name']}.${torrentInfo.videoInfo.container.toLowerCase()}`
}
site.mediainfoBox.val(mediainfo2String(torrentInfo.mediainfo))
}
}
// anonymously uploading
if (site.anonymousControl) {
if (siteName === NHD || siteName === PTERCLUB || siteName === PUTAO || siteName === MTEAM || siteName === UHD) {
site.anonymousControl.checked = ANONYMOUS
} else if (siteName === TTG) {
site.anonymousControl.val(ANONYMOUS ? 'yes' : 'no')
}
}
site.descrBox.val(textToConsume)
} catch (error) {
console.error('Error:', error)
} finally {
btnBingo.val(oriTextBingo)
}
})
} else if (page === 'subtitles') {
//= ========================================================================================================
// 字幕页面
if (!site.inputFileSubtitle) {
return
}
site.inputFileSubtitle.change(() => {
if (site.anonymousCheckSubtitle) {
site.anonymousCheckSubtitle.checked = ANONYMOUS
}
const pathSub = site.inputFileSubtitle.val()
const fileName = pathSub.replace(/.*?([^\\]+)$/, '$1')
if (fileName) {
if (site.titleBoxSubtitle) {
site.titleBoxSubtitle.val(fileName)
}
const abbrLangInSub = pathSub.replace(/.*\.([^.]+)\.[^.]+$/i, '$1') || ''
if (site.languageSelSubtitle) {
let langSelected = site.subtitleInfo.default
if (site.subtitleInfo.other && abbrLangInSub.match(/(chs|cht|cn|zh)\s*( |&)?.+/) || abbrLangInSub.match(/.+( |&)?(chs|cht|cn|zh)/)) {
langSelected = site.subtitleInfo.other
}
else {
Object.entries(subtitleLanguages).forEach(([languageInAll, abbrLang]) => {
if (abbrLangInSub.match(RegExp(abbrLang, 'i'))) {
langSelected = site.subtitleInfo[languageInAll] || site.subtitleInfo.default
return
}
})
}
site.languageSelSubtitle.val(langSelected)
} else if (siteName === GPW) {
Object.entries(subtitleLanguages).forEach(([languageInAll, abbrLang]) => {
if (abbrLangInSub.match(RegExp(abbrLang, 'i'))) {
if (site.subtitleInfo[languageInAll]) {
Object.keys(site.subtitleInfo).forEach(lang => {
if (site.subtitleInfo[lang]) {
site.subtitleInfo[lang].checked = languageInAll === lang
}
})
}
return
}
})
}
}
})
}
})()
// ////////////////////////////////////////////////////////////////////////////////////////////////
// for unit test
// Conditionally export for unit testing
if (typeof module !== 'undefined' && module.exports) {
module.exports = {
collectComparisons, decomposeDescription, processDescription, mediainfo2String, string2Mediainfo, processTags,
NHD, PTERCLUB, GPW, MTEAM, TTG, PUTAO, siteInfoMap
}
}