- ${T}${this._buildIntegrationPlaceholders(e)}
`,n},injectPanel(e){const n=E.getModerationPanel();if(n)n.parentNode.insertBefore(e,n);else{const t=document.querySelector(_resolvedSelectors.torrentTags);t&&t.parentNode.insertBefore(e,t.nextSibling)}this.attachEvents(e)},attachEvents(e){const n=e.querySelector("#mh-toggle-all");if(n){let D=!1;n.addEventListener("click",()=>{D=!D,e.querySelectorAll(".mh-accordion, .mh-section").forEach(x=>x.open=D),n.querySelector("i").className=D?"fas fa-angles-up":"fas fa-angles-down",n.title=D?"Collapse all sections":"Expand all sections"})}const t=e.querySelector("#mh-corrective-text"),s=e.querySelector("#mh-corrective-editor");t&&s&&(t.addEventListener("click",()=>{t.style.display="none",s.style.display="block",s.focus()}),s.addEventListener("blur",()=>{t.textContent=s.value,t.style.display="",s.style.display=""}));const a=()=>s?s.value:"",o=e.querySelector("#mh-copy-message");o&&o.addEventListener("click",()=>{const D=a();navigator.clipboard.writeText(D).then(()=>{const x=o.querySelector("i");x.className="fas fa-check",o.classList.add("mh-btn--success"),setTimeout(()=>{x.className="fas fa-copy",o.classList.remove("mh-btn--success")},2e3)})});const c=D=>{const x=document.querySelectorAll(_resolvedSelectors.moderationForms);for(const p of x){const y=p.querySelector(_resolvedSelectors.moderationStatus);if(y&&y.value===D)return p.querySelector(_resolvedSelectors.moderationMessage)}return null},r=(D,x)=>{const p=e.querySelector(D);p&&p.addEventListener("click",()=>{const y=a(),C=c(x);if(C){C.value=y,C.dispatchEvent(new Event("input",{bubbles:!0}));const h=p.querySelector("i");h.className="fas fa-check",p.classList.add("mh-btn--success"),setTimeout(()=>{h.className="fas fa-paste",p.classList.remove("mh-btn--success")},2e3)}else{const h=p.querySelector("i");h.className="fas fa-xmark",p.classList.add("mh-btn--fail"),setTimeout(()=>{h.className="fas fa-paste",p.classList.remove("mh-btn--fail")},2e3)}})};r("#mh-fill-postpone",_resolvedModStatuses.postpone),r("#mh-fill-reject",_resolvedModStatuses.reject);const d=e.querySelector("#mh-filename-compare-btn"),f=e.querySelector("#mh-filename-block"),A=e.querySelector("#mh-filename-uploaded"),T=e.querySelector("#mh-filename-input"),b=e.querySelector("#mh-filename-run"),u=e.querySelector("#mh-filename-result");if(d&&f&&d.addEventListener("click",()=>{const D=f.style.display!=="none";if(f.style.display=D?"none":"",d.classList.toggle("mh-btn--active",!D),!D&&A){const x=E.getMediaInfoFilename()||"(not found)";A.textContent=x}}),b&&T&&u){const D=()=>{const x=T.value.trim();if(!x){u.innerHTML='Please enter a reference filename.';return}const p=E.getMediaInfoFilename();if(!p||p==="(not found)"){u.innerHTML='Could not read MediaInfo filename from page.';return}const y=z.diff(x,p);u.innerHTML=z.renderDiff(y)};b.addEventListener("click",D),T.addEventListener("keydown",x=>{x.key==="Enter"&&D()})}},_buildIntegrationPlaceholders(results){if(!results.nogroup&&!results.dpTitle)return"";return`SRRDBChecking scene database...
ProwlarrSearching indexers...
`},renderIntegrationResult(name,result){const esc=(s)=>String(s||"").replace(/&/g,"&").replace(//g,">");if(result.error){return`${esc(name)}${esc(result.error)}
`}if(result.notConfigured){return`${esc(name)}Not configured — open ModQ Helper Settings
`}if(name==="SRRDB"){if(!result.found){return`SRRDBNot found — may not be a scene release
`}const relName=esc(result.release?.release||"Unknown");let fileHtml="";if(result.fileCheck){if(result.fileCheck.match){fileHtml=` File/folder names match SRRDB record
`}else if(result.fileCheck.error){fileHtml=` Could not verify files: ${esc(result.fileCheck.error)}
`}else{const diffs=(result.fileCheck.discrepancies||[]).map(d=>`${esc(d)}`).join("");fileHtml=` File names differ from SRRDB record — possible rename (REJECT per A1.1)${diffs?`
`:""}
`}}return`SRRDBScene release found: ${relName}${fileHtml}
`}if(name==="Prowlarr"){if(!result.found){return`ProwlarrRelease not indexed — may be new or not tracked
`}const count=result.results?.length||0;const bestMatch=result.bestMatch;let matchHtml="";if(bestMatch){const conf=bestMatch.confidence||{};const confLevel=conf.level||"uncertain";matchHtml+=`Best match: ${esc(bestMatch.title)} [${esc(bestMatch.indexer)}]
`;if(confLevel==="match"||confLevel==="likely_match"){matchHtml+=` Title consistent with indexed release
`}else if(confLevel==="uncertain"){matchHtml+=` Cannot confidently assess rename status — review manually
`}else if(confLevel==="likely_renamed"||confLevel==="renamed"){const issueItems=(conf.issues||[]).map(i=>{if(i.type==="folder")return`Folder differs: expected ${esc(i.expected)}, found ${esc(i.found)}`;if(i.type==="filename")return`Filename differs: expected ${esc(i.expected)}, found ${esc(i.found)}`;return`Mismatch: expected ${esc(i.expected)}, found ${esc(i.found)}`}).join("");const renameMsg=confLevel==="renamed"?"Strong evidence of renaming — review filenames":"Possible rename detected — review filenames";matchHtml+=` ${renameMsg}${issueItems?`
`:""}
`}if(conf.note){matchHtml+=`${esc(conf.note)}
`}}const prowlStatusMap={"match":"pass","likely_match":"pass","uncertain":"advisory","likely_renamed":"warn","renamed":"warn"};const prowlIconMap={"match":"check-circle mh-icon--pass","likely_match":"check-circle mh-icon--pass","uncertain":"info-circle mh-icon--advisory","likely_renamed":"exclamation-triangle mh-icon--warn","renamed":"exclamation-triangle mh-icon--warn"};const confLevel=bestMatch?.confidence?.level||"uncertain";const prowlStatus=prowlStatusMap[confLevel]||"advisory";const prowlIcon=prowlIconMap[confLevel]||"info-circle mh-icon--advisory";return`ProwlarrFound on ${count} indexer(s)${matchHtml}
`}return`${esc(name)}${result.found?"Found":"Not found"}
`}};
+ ${T}${this._buildIntegrationPlaceholders(e)}
`,n},injectPanel(e){const n=E.getModerationPanel();if(n)n.parentNode.insertBefore(e,n);else{const t=document.querySelector(_resolvedSelectors.torrentTags);t&&t.parentNode.insertBefore(e,t.nextSibling)}this.attachEvents(e)},attachEvents(e){const n=e.querySelector("#mh-toggle-all");if(n){let D=!1;n.addEventListener("click",()=>{D=!D,e.querySelectorAll(".mh-accordion, .mh-section").forEach(x=>x.open=D),n.querySelector("i").className=D?"fas fa-angles-up":"fas fa-angles-down",n.title=D?"Collapse all sections":"Expand all sections"})}const t=e.querySelector("#mh-corrective-text"),s=e.querySelector("#mh-corrective-editor");t&&s&&(t.addEventListener("click",()=>{t.style.display="none",s.style.display="block",s.focus()}),s.addEventListener("blur",()=>{t.textContent=s.value,t.style.display="",s.style.display=""}));const a=()=>s?s.value:"",o=e.querySelector("#mh-copy-message");o&&o.addEventListener("click",()=>{const D=a();navigator.clipboard.writeText(D).then(()=>{const x=o.querySelector("i");x.className="fas fa-check",o.classList.add("mh-btn--success"),setTimeout(()=>{x.className="fas fa-copy",o.classList.remove("mh-btn--success")},2e3)})});const c=D=>{const x=document.querySelectorAll(_resolvedSelectors.moderationForms);for(const p of x){const y=p.querySelector(_resolvedSelectors.moderationStatus);if(y&&y.value===D)return p.querySelector(_resolvedSelectors.moderationMessage)}return null},r=(D,x)=>{const p=e.querySelector(D);p&&p.addEventListener("click",()=>{const y=a(),C=c(x);if(C){C.value=y,C.dispatchEvent(new Event("input",{bubbles:!0}));const h=p.querySelector("i");h.className="fas fa-check",p.classList.add("mh-btn--success"),setTimeout(()=>{h.className="fas fa-paste",p.classList.remove("mh-btn--success")},2e3)}else{const h=p.querySelector("i");h.className="fas fa-xmark",p.classList.add("mh-btn--fail"),setTimeout(()=>{h.className="fas fa-paste",p.classList.remove("mh-btn--fail")},2e3)}})};r("#mh-fill-postpone",_resolvedModStatuses.postpone),r("#mh-fill-reject",_resolvedModStatuses.reject);const d=e.querySelector("#mh-filename-compare-btn"),f=e.querySelector("#mh-filename-block"),A=e.querySelector("#mh-filename-uploaded"),T=e.querySelector("#mh-filename-input"),b=e.querySelector("#mh-filename-run"),u=e.querySelector("#mh-filename-result");if(d&&f&&d.addEventListener("click",()=>{const D=f.style.display!=="none";if(f.style.display=D?"none":"",d.classList.toggle("mh-btn--active",!D),!D&&A){const x=E.getMediaInfoFilename()||"(not found)";A.textContent=x}}),b&&T&&u){const D=()=>{const x=T.value.trim();if(!x){u.innerHTML='Please enter a reference filename.';return}const p=E.getMediaInfoFilename();if(!p||p==="(not found)"){u.innerHTML='Could not read MediaInfo filename from page.';return}const y=z.diff(x,p);u.innerHTML=z.renderDiff(y)};b.addEventListener("click",D),T.addEventListener("keydown",x=>{x.key==="Enter"&&D()})}},_buildIntegrationPlaceholders(results){if(!results.nogroup&&!results.dpTitle)return"";return`SRRDBChecking scene database...
ProwlarrSearching indexers...
`},renderIntegrationResult(name,result){const esc=(s)=>String(s||"").replace(/&/g,"&").replace(//g,">");if(result.error){return`${esc(name)}${esc(result.error)}
`}if(result.notConfigured){return`${esc(name)}Not configured — open ModQ Helper Settings
`}if(name==="SRRDB"){if(!result.found){return`SRRDBNot found — may not be a scene release
`}const relName=esc(result.release?.release||"Unknown");let fileHtml="";if(result.fileCheck){if(result.fileCheck.match){fileHtml=` File/folder names match SRRDB record
`}else if(result.fileCheck.error){fileHtml=` Could not verify files: ${esc(result.fileCheck.error)}
`}else{const diffs=(result.fileCheck.discrepancies||[]).map(d=>`${esc(d)}`).join("");fileHtml=` File names differ from SRRDB record — possible rename (REJECT per A1.1)${diffs?`
`:""}
`}}return`SRRDBScene release found: ${relName}${fileHtml}
`}if(name==="Prowlarr"){if(!result.found){return`ProwlarrRelease not indexed — may be new or not tracked
`}const count=result.results?.length||0;const bestMatch=result.bestMatch;let matchHtml="";if(bestMatch){const conf=bestMatch.confidence||{};const confLevel=conf.level||"uncertain";const fs=conf.fieldScores||{};const issues=conf.issues||[];const alts=bestMatch.alternatives||[];
+// Best match title with optional link
+const titleText=esc(bestMatch.title);const indexerText=esc(bestMatch.indexer);const linkHtml=bestMatch.infoUrl?` `:"";
+matchHtml+=`Best match: ${titleText} [${indexerText}]${linkHtml}
`;
+// Summary line — specific to confidence level
+const summaryMessages={"match":`Name matches release on ${indexerText} — title, year, resolution, codecs all consistent`,"likely_match":`Name likely matches release on ${indexerText} — minor field differences`,"uncertain":"No strong match found — cannot verify release name automatically","likely_renamed":issues.length>0?`Possible rename — ${issues[0].type==="folder"?"folder name differs from torrent name":"filename differs from torrent name"}`:"Possible rename detected — review filenames","renamed":"Likely renamed — folder and filename both differ from torrent name"};
+const summaryIcons={"match":"check-circle mh-icon--pass","likely_match":"check-circle mh-icon--pass","uncertain":"info-circle mh-icon--advisory","likely_renamed":"exclamation-triangle mh-icon--warn","renamed":"exclamation-triangle mh-icon--warn"};
+const summaryClasses={"match":"pass","likely_match":"pass","uncertain":"advisory","likely_renamed":"warn","renamed":"warn"};
+matchHtml+=` ${summaryMessages[confLevel]||"Review manually"}
`;
+// Rename issues — reframed labels
+if(confLevel==="likely_renamed"||confLevel==="renamed"){const issueItems=issues.map(i=>{if(i.type==="folder")return`Torrent name: ${esc(i.expected)}
Folder name: ${esc(i.found)}`;if(i.type==="filename")return`Torrent name: ${esc(i.expected)}
Filename: ${esc(i.found)}`;return`${esc(i.expected)} vs ${esc(i.found)}`}).join("");if(issueItems)matchHtml+=``}
+// Group note
+if(conf.note&&confLevel!=="likely_renamed"&&confLevel!=="renamed"){matchHtml+=`${esc(conf.note)}
`}
+// Collapsible field comparison detail
+const fieldLabels={titleName:"Title",year:"Year",resolution:"Resolution",source:"Source",vcodec:"Video codec",acodec:"Audio codec"};const fieldEntries=Object.entries(fs).filter(([k])=>fieldLabels[k]);if(fieldEntries.length>0){let detailRows=fieldEntries.map(([k,v])=>{const label=fieldLabels[k];const icon=v>=1.0?'':v>0?'':'';return`${icon} ${label}
`}).join("");
+// Self-consistency summary
+const selfIssues=issues.filter(i=>i.severity==="high");const folderOk=!selfIssues.some(i=>i.type==="folder");const fileOk=!selfIssues.some(i=>i.type==="filename");detailRows+=`Self-check: folder ${folderOk?"✓":"✗"} · filename ${fileOk?"✓":"✗"}
`;
+// Alternatives
+if(alts.length>0){const altNames=alts.map(a=>esc(a.indexer)).join(", ");detailRows+=`Also found on: ${altNames}
`}
+matchHtml+=`Comparison details
${detailRows}
`}}
+const prowlStatusMap={"match":"pass","likely_match":"pass","uncertain":"advisory","likely_renamed":"warn","renamed":"warn"};const prowlIconMap={"match":"check-circle mh-icon--pass","likely_match":"check-circle mh-icon--pass","uncertain":"info-circle mh-icon--advisory","likely_renamed":"exclamation-triangle mh-icon--warn","renamed":"exclamation-triangle mh-icon--warn"};const confLevel=bestMatch?.confidence?.level||"uncertain";const prowlStatus=prowlStatusMap[confLevel]||"advisory";const prowlIcon=prowlIconMap[confLevel]||"info-circle mh-icon--advisory";
+// Header: precise indexer wording
+const headerMsg=count===1?`Found on ${esc(bestMatch?.indexer||"1 indexer")}`:`Best match from ${esc(bestMatch?.indexer||"unknown")} (${count} indexers total)`;
+return`Prowlarr${headerMsg}${matchHtml}
`}return`${esc(name)}${result.found?"Found":"Not found"}
`}};
/* ========================================================================
* CSS — Panel styles (injected via GM_addStyle)
@@ -4740,6 +4824,22 @@ const Z=`
.mh-integration__diffs { margin: 4px 0 0 16px; padding: 0; list-style: disc; font-size: 11px; }
.mh-integration__compare { margin-top: 4px; font-size: 11px; opacity: 0.85; }
.mh-integration__compare div { padding: 2px 0; }
+ .mh-integration__detail--advisory { color: rgba(96, 165, 250, 0.80); }
+ .mh-integration__detail--note { color: var(--text-color, #8c8c8c); }
+ .mh-integration__link { color: rgba(96, 165, 250, 0.70); text-decoration: none; margin-left: 4px; font-size: 11px; }
+ .mh-integration__link:hover { color: rgba(96, 165, 250, 1); }
+ .mh-integration__expand { margin-top: 6px; }
+ .mh-integration__expand-trigger { cursor: pointer; list-style: none; color: rgba(96, 165, 250, 0.70); user-select: none; }
+ .mh-integration__expand-trigger::-webkit-details-marker,
+ .mh-integration__expand-trigger::marker { display: none; }
+ .mh-integration__expand-trigger::before { content: "▸ "; }
+ .mh-integration__expand[open] > .mh-integration__expand-trigger::before { content: "▾ "; }
+ .mh-integration__fields { padding: 4px 0 0 8px; }
+ .mh-field-row { font-size: 11px; padding: 1px 0; color: var(--text-color, #cbd5e1); display: flex; align-items: center; gap: 4px; }
+ .mh-field-row i { font-size: 10px; width: 12px; text-align: center; }
+ .mh-field-label { min-width: 80px; }
+ .mh-field-row--self { margin-top: 4px; padding-top: 4px; border-top: 1px solid rgba(59, 61, 62, 0.40); color: var(--text-color, #8c8c8c); }
+ .mh-field-row--alts { color: var(--text-color, #8c8c8c); }
`;;
/* ========================================================================
@@ -4911,7 +5011,8 @@ function main() {
const searchResult = await Integrations.prowlarr.search(settings.prowlarr, tvScope.searchQuery);
if (searchResult.found && searchResult.results.length > 0) {
- const bestMatch = RenameDetector.findBestMatch(uploadTokens, searchResult.results);
+ const preferredIndexers = settings.prowlarr?.preferredIndexers || [];
+ const bestMatch = RenameDetector.findBestMatch(uploadTokens, searchResult.results, preferredIndexers);
if (bestMatch) {
const assessment = RenameDetector.assessRename(data, bestMatch, tvScope);
@@ -4919,9 +5020,11 @@ function main() {
title: bestMatch.title,
indexer: bestMatch.indexer,
size: bestMatch.size,
+ infoUrl: bestMatch.infoUrl,
uploadedTitle: data.torrentName,
relevanceScore: bestMatch.relevanceScore,
confidence: assessment,
+ alternatives: bestMatch.alternatives || [],
};
}
}