MiM / BLU Mod Help

// ==UserScript==
// @name         BLU Mod Help
// @version      1.0
// @description  Moderation Assistant for BLU.
// @author       MiM
// @license      MIT
// @match        https://blutopia.cc/torrents/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=blutopia.cc
// @grant        none
// @updateURL    https://openuserjs.org/meta/MiM/BLU_Mod_Help.meta.js
// @downloadURL  https://openuserjs.org/install/MiM/BLU_Mod_Help.user.js
// ==/UserScript==

// Video settings
let video_format = null;
let video_bits = NaN;
let video_settings = {};

let video_resolution = null;
let video_height = NaN;
let video_width = NaN;

let container = null;
let video_hdr_format = "";

let stream_op_failed = [];
let dxva_op_failed = [];

let audio_format = null;

let sub_format = null;
let mediainfo_section = null;
let general_section = null;
let encode_settings = null;
let audio_section = null;
let video_section = null;
let bdinfo_section = null;

let flagged_as_stream_op = null;
let torrent_type = null;
let torrent_category = null;
let torrent_title = null;

let mod_results = [];

function get_torrent_title() {
  return document.getElementsByClassName("torrent__name")[0].innerHTML.trim();
}

function set_resolution() {
  try {
    video_section = get_mediainfo_video();
    let video_nodes = video_section.childNodes;
    let i = 1;
    while (i < video_nodes.length) {
      if (video_nodes[i].innerHTML.trim() == "Resolution") {
        video_width = video_nodes[i + 2].innerHTML.split("×")[0].replace(' ', '').trim();
        video_height = video_nodes[i + 2].innerHTML.split("×")[1].replace(' ', '').trim();
        break;
      }
      i += 2;
    }
  }
  catch {
    video_width = NaN;
    video_height = NaN;
  }
  try {
    video_resolution = document.getElementsByClassName("torrent__resolution-link")[0].innerHTML.trim();
  }
  catch {
    video_resolution = "Unknown";
  }
}

function is_stream_op() {
  if (!mediainfo_section) {
    stream_op_failed.push("Missing mediainfo, impossible to check.");
    return false;
  }
  if (Object.keys(video_settings).length == 0) {
    stream_op_failed.push("Missing encode settings, impossible to check.");
    return false;
  }
  set_hdr_type();
  if (stream_op_failed.length > 0) {
    return false;
  }
  if (container != "MPEG-4") {
    stream_op_failed.push("Must use mp4 container");
  }
  let aq_mode = 0;
  try {
    aq_mode = video_settings.aq.substring(0, video_settings.aq.indexOf(':'));
  }
  catch {
    aq_mode = video_settings["aq-mode"];
  }
  switch (video_resolution) {
    case "1080p":
    case "720p":
      if (video_format != "AVC") {
        stream_op_failed.push(`Must use x264`);
      }
      if (!(video_settings.me == "umh" || video_settings.me == "esa" || video_settings.me == "tesa")) {
        stream_op_failed.push(`me = ${video_settings.me} < "umh"`);
      }
      if (video_settings.ref > 3) {
        stream_op_failed.push(`ref (${video_settings.ref}) > 3 max`);
      }
      if (video_settings.bframes > 6) {
        stream_op_failed.push(`bframes (${video_settings.bframes}) > 6 max`);
      }
      if (video_settings.rc_lookahead < 80) {
        stream_op_failed.push(`rc-lookahead (${video_settings.rc_lookahead}) < 80 min`);
      }
      if (video_settings.trellis != 2) {
        stream_op_failed.push(`trellis (${video_settings.trellis}) must be 2`);
      }
      if (aq_mode != 2) {
        stream_op_failed.push(`aq-mode (${aq_mode}) must be 2`);
      }
      //TODO: add bitrate check.
      break;
    case "2160p":
      if (video_format != "HEVC") {
        stream_op_failed.push(`Must use x265`);
      }
      // TODO: Add HLG check.
      if (!video_hdr_format.includes("HDR10")) {
        stream_op_failed.push(`Must be HDR10 or HLG"`);
      }
      if (video_settings.me < 2) {
        stream_op_failed.push(`me = ${video_settings.me} < "umh (2)"`);
      }
      if (video_settings.ref > 4) {
        stream_op_failed.push(`ref (${video_settings.ref}) > 4 max`);
      }
      if (video_settings.bframes > 8) {
        stream_op_failed.push(`bframes (${video_settings.bframes}) > 8 max`);
      }
      if (video_settings["rc-lookahead"] < 80) {
        stream_op_failed.push(`rc-lookahead (${video_settings["rc-lookahead"]}) < 80 min`);
      }
      if ("b-intra" in video_settings) {
        stream_op_failed.push(`Must use no-b-intra`);
      }
      if (aq_mode < 2) {
        stream_op_failed.push(`aq-mode (${aq_mode}) < 2 min`);
      }
      //TODO: add bitrate check.
      break;
    default:
      break;
  }
  return stream_op_failed.length == 0;
}

function set_hdr_type() {
  try {
    let mediainfo_video_section = get_mediainfo_video();
    let video_nodes = mediainfo_video_section.childNodes;
    let i = 1;
    while (i < video_nodes.length) {
      if (video_nodes[i].innerHTML.trim() == "HDR format") {
        video_hdr_format = video_nodes[i + 2].innerHTML.trim();
      }
      i += 2;
    }
  }
  catch {
    video_hdr_format = "SDR";
  }
}

function get_mediainfo_section() {
  try {
    return document.getElementsByClassName("mediainfo")[0];
  }
  catch {
    return null;
  }
}

function get_bdinfo_section() {
  try {
    return document.getElementsByClassName("panelV2 torrent-bdinfo")[0].getElementsByTagName("code")[0];;
  }
  catch {
    return null;
  }
}

function get_mediainfo_encode_settings() {
  try {
    return mediainfo_section.getElementsByClassName("mediainfo__encode-settings")[0].getElementsByTagName("code")[0];
  }
  catch {
    return null;
  }
}

function get_mediainfo_audio() {
  try {
    return mediainfo_section.getElementsByClassName("mediainfo__audio")[0].getElementsByTagName("dl")[0];
  }
  catch {
    return null;
  }
}

function get_mediainfo_video() {
  try {
    return mediainfo_section.getElementsByClassName("mediainfo__video")[0].getElementsByTagName("dl")[0];
  }
  catch {
    return null;
  }
}

function get_mediainfo_general() {
  try {
    return mediainfo_section.getElementsByClassName("mediainfo__general")[0].getElementsByTagName("dl")[0];
  }
  catch {
    return null;
  }
}

function set_video_enc_settings(encode_settings) {
  let setting_name = null;
  let setting_value = null;
  let mediainfo_split = encode_settings.split(" / ");
  for (const setting of mediainfo_split) {
    setting_name = setting.substring(0, setting.indexOf('='))
    setting_value = setting.substring(setting.indexOf('=') + 1)
    video_settings[setting_name] = setting_value;
  }
}

function get_enc_type() {
  switch (video_settings.rc) {
    case "crf":
      return `Constant Rate Factor (${video_settings.crf})`;
    case "2pass":
      return `Multi-Pass (${video_settings.bitrate} kbps)`;
  }
  // abr is rest
  try {
    if (video_settings["stats-read"] > 0) {
      return `Multi-Pass (${video_settings.bitrate} kbps)`;
    }
    else {
      return `Single-Pass (${video_settings.bitrate} kbps)`;
    }
  }
  catch {
    try {
      return `Single-Pass (${video_settings.bitrate} kbps)`;
    }
    catch {
      return "Unknown";
    }
  }
}

function set_container() {
  try {
    general_section = get_mediainfo_general();
    let general_nodes = general_section.childNodes;
    let i = 1;
    while (i < general_nodes.length) {
      if (general_nodes[i].innerHTML.trim() == "Format") {
        container = general_nodes[i + 2].innerHTML.trim();
        break;
      }
      i += 2;
    }
  }
  catch {
    container = "Unknown";
  }
}

function is_stream_op_flagged() {
  if (flagged_as_stream_op != null) {
    return flagged_as_stream_op
  }
  flagged_as_stream_op = document.getElementsByClassName("fas fa-play torrent-icons__stream-optimized").length == 1;
  return flagged_as_stream_op;
}

function get_torrent_type() {
  if (torrent_type != null) {
    return torrent_type
  }
  torrent_type = document.getElementsByClassName("torrent__type-link")[0].innerHTML.trim();
  return torrent_type;
}

function get_torrent_category() {
  if (torrent_category != null) {
    return torrent_category
  }
  torrent_category = document.getElementsByClassName("torrent__category-link")[0].innerHTML.trim();
  return torrent_category;
}

function stream_op_mod_check() {
  if (torrent_type != "Encode" && !flagged_as_stream_op) {
    return "CORRECT"
  }
  if (torrent_type != "Encode" && flagged_as_stream_op) {
    return `POSTPONE: Flagged as stream optimized and is of type '${torrent_type}'`
  }
  if (stream_op_failed.length > 0 && flagged_as_stream_op) {
    return "REJECT: Does not meet stream optimized encode settings requirement."
  }
  if (container == "MPEG-4" && stream_op_failed.length == 0 && !flagged_as_stream_op) {
    return "POSTPONE: mp4 with correct settings, missing stream optimized flag."
  }
  if (container == "MPEG-4" && stream_op_failed.length > 0 && !flagged_as_stream_op) {
    return "REJECT: mp4 with incorrect settings"
  }
  return "CORRECT";
}

function hybrid_dovi_check_remux() {
  let description = document.getElementsByClassName("panel__body bbcode-rendered")[0];
  let derived_p8_base_note_check = description.innerHTML.includes("This release contains a derived Dolby Vision profile 8 layer");
  let derived_p8_comparison_check = description.innerHTML.includes("Comparisons between HDR masters:");
  derived_p8_comparison_check = derived_p8_comparison_check && !description.innerHTML.includes("omparisons: missing");
  let torrent_title = document.getElementsByClassName("torrent__name")[0].innerHTML.trim();
  if (torrent_type == "Remux" && video_hdr_format.includes("dvhe.08") && !(derived_p8_base_note_check && derived_p8_comparison_check) && torrent_title.includes("Hybrid")) {
    return "POSTPONE: Missing required note/comps."
  }
  if (torrent_type == "Remux" && video_hdr_format.includes("dvhe.08") && !(derived_p8_base_note_check && derived_p8_comparison_check) && !torrent_title.includes("Hybrid")) {
    return "POSTPONE: Missing required note/comps and hybrid in title."
  }
  if (torrent_type == "Remux" && video_hdr_format.includes("dvhe.08") && (derived_p8_base_note_check && derived_p8_comparison_check) && !torrent_title.includes("Hybrid")) {
    return "POSTPONE: Missing hybrid in title."
  }
  if (torrent_type == "Remux" && video_hdr_format.includes("dvhe.05")) {
    return "REJECT: Profile 5 Dolby Vision not possible in a remux."
  }
  return "CORRECT";
}

function hybrid_dovi_web() {
  let filename = document.getElementsByClassName("mediainfo__filename")[0].innerHTML.replace("<h3>Filename</h3>", '').trim();
  let description = document.getElementsByClassName("panel__body bbcode-rendered")[0];
  let derived_p8_base_note_check = description.innerHTML.includes("This release contains a derived Dolby Vision profile 8 layer");
  let derived_p8_comparison_check = description.innerHTML.includes("DV and HDR are from same provider");
  derived_p8_comparison_check = derived_p8_comparison_check && !description.innerHTML.includes("omparisons: missing");
  let torrent_title = document.getElementsByClassName("torrent__name")[0].innerHTML.trim();
  let needs_hybrid = true;
  needs_hybrid = needs_hybrid && (!torrent_title.includes("HULU WEB") && !torrent_title.includes(" MAX WEB"))
  needs_hybrid = needs_hybrid && (!(filename.includes("HULU WEB") || filename.includes("HULU.WEB")))
  needs_hybrid = needs_hybrid && (!(filename.includes(" MAX WEB") || filename.includes(".MAX.WEB")))
  if (torrent_type.includes("WEB") && video_hdr_format.includes("dvhe.08") && !(derived_p8_base_note_check && derived_p8_comparison_check) && torrent_title.includes("Hybrid") && needs_hybrid) {
    return "POSTPONE: Missing required note/comps."
  }
  if (torrent_type.includes("WEB") && video_hdr_format.includes("dvhe.08") && !(derived_p8_base_note_check && derived_p8_comparison_check) && !torrent_title.includes("Hybrid") && needs_hybrid) {
    return "POSTPONE: Missing required note/comps and hybrid in title."
  }
  if (torrent_type.includes("WEB") && video_hdr_format.includes("dvhe.08") && (derived_p8_base_note_check && derived_p8_comparison_check) && !torrent_title.includes("Hybrid") && needs_hybrid) {
    return "POSTPONE: Missing hybrid in title."
  }
  if (!needs_hybrid && derived_p8_base_note_check && !torrent_title.includes("Hybrid")) {
    return "POSTPONE: Has note when not a hybrid."
  }
  if (!needs_hybrid && derived_p8_base_note_check && torrent_title.includes("Hybrid")) {
    return "POSTPONE: Has note when not a hybrid and title has hybrid when it likely is not one."
  }
  if (torrent_type.includes("WEB") && video_hdr_format.includes("dvhe.05") && derived_p8_base_note_check && torrent_title.includes("Hybrid")) {
    return "POSTPONE: Contains profile 8 hybrid note and hybrid in title when this is profile 5, likely not a hybrid."
  }
  if (torrent_type.includes("WEB") && video_hdr_format.includes("dvhe.05") && derived_p8_base_note_check) {
    return "POSTPONE: Contains profile 8 hybrid note when this is profile 5, likely not a hybrid."
  }
  if (torrent_type.includes("WEB") && video_hdr_format.includes("dvhe.05") && !derived_p8_base_note_check && torrent_title.includes("Hybrid")) {
    return "CHECK: Contains hybrid in title on profile 5 Dolby Vision, verify the audio is hybrid."
  }
  return "CORRECT";
}

function hybrid_dovi_check() {
  if (torrent_type == "Remux" && (video_hdr_format.includes("dvhe.08") || video_hdr_format.includes("dvhe.05"))) {
    mod_results.push(`Hybrid remux check: ${hybrid_dovi_check_remux()}`)
  }
  if (torrent_type.includes("WEB") && video_hdr_format.includes("dvhe.08")) {
    mod_results.push(`Hybrid WEB-DL/WEBRip moderation: ${hybrid_dovi_web()}`)
  }
  if (torrent_type.includes("Encode") && video_hdr_format.includes("dvhe.08")) {
    mod_results.push(`Hybrid Dolby Vision moderation: CHECK: Unable to check reliably, see sources used and if they are hybrids.`)
  }
}

function truehd_compat_check() {
  if (torrent_type.includes("Full Disc")) {
    return "CORRECT";
  }
  let prev_atmos = false;
  let num_atmos = 0;
  let num_compat = 0;
  let truehd_language = null;
  if (mediainfo_section) {
    let audios = document.getElementsByClassName("mediainfo__audio")[0].getElementsByTagName("dd");
    for (var i = 0; i < audios.length; i++) {
      if (torrent_type.includes("WEB")) {
        if (prev_atmos &&
          (!audios[i].innerHTML.includes('/ AC-3 /') &&
            !audios[i].innerHTML.includes('/ E-AC-3 /') &&
            !audios[i].innerHTML.includes('/ E-AC-3 JOC /')
          ) && !audios[i].innerHTML.toLowerCase().includes("commentary")) {
          return "CHECK: Possible reject, might not have required (E-)AC-3 compatibility track."
        }
        if (prev_atmos && truehd_language == audios[i].getElementsByTagName("img")[0].title &&
          (audios[i].innerHTML.includes('/ AC-3 /') ||
            audios[i].innerHTML.includes('/ E-AC-3 /') ||
            audios[i].innerHTML.includes('/ E-AC-3 JOC /')
          ) && !audios[i].innerHTML.toLowerCase().includes("commentary")) {
          num_compat++;
        }
      }
      else {
        if (prev_atmos && !audios[i].innerHTML.includes("/ AC-3 /") && !audios[i].innerHTML.toLowerCase().includes("commentary")) {
          return "CHECK: Possible reject, might not have required AC-3 compatibility track."
        }
        if (prev_atmos && truehd_language == audios[i].getElementsByTagName("img")[0].title && audios[i].innerHTML.includes("/ AC-3 /") && !audios[i].innerHTML.toLowerCase().includes("commentary")) {
          num_compat++
        }
      }
      prev_atmos = false;
      if (audios[i].innerHTML.includes("/ MLP FBA ")) {
        prev_atmos = true;
        num_atmos++;
        truehd_language = audios[i].getElementsByTagName("img")[0].title;
      }
    }
  }
  if (num_atmos != num_compat) {
    return "CHECK: Might not have required compatibility track, E-AC-3 only allowed for WEB sourced releases.";
  }
  return "CORRECT";
}

function missing_any_language_mod() {
  if (mediainfo_section) {
    let audios = document.getElementsByClassName("mediainfo__audio")[0].getElementsByTagName("dd");
    for (var i = 0; i < audios.length; i++) {
      // remove newlines and extra spaces.  remove two unknowns in a row or it may fail on DVDs.
      if (audios[i].innerHTML.replace(/[\r\n]+/gm, "").replace(/ +/gm, ' ').replaceAll('/ Unknown /', '/').replaceAll('/ Unknown /', '/').replaceAll('/ Unknown /', '/').includes("Unknown /")) {
        if (torrent_type.includes("Full Disc")) {
          return "CHECK: One or more audio/subtitle tracks does not show the language, they likely need to downgrade mediainfo or scan the .BUP, if issue remains it must be noted in the description.";
        }
        else {
          return "CHECK: One or more audio/subtitle tracks does not show the language, they must indicate it in the description.";
        }
      }
    }
    let subtitles = document.getElementsByClassName("mediainfo__subtitles")
    if (subtitles.length > 0) {
      subtitles = subtitles[0].getElementsByTagName("li");
      for (i = 0; i < subtitles.length; i++) {
        if (subtitles[i].innerHTML.replaceAll('| Unknown', '|').replaceAll('| Unknown', '|').replaceAll('| Unknown', '|').includes("Unknown |")) {
          return "CHECK: One or more audio/subtitle tracks does not show the language, they must indicate it in the description.";
        }
      }
    }
  }
  return "CORRECT";
}

function has_required_english_subs() {
  if (mediainfo_section) {
    let original_language = document.getElementsByClassName("meta__language")[0].innerHTML;
    if (!original_language) {
      return "CHECK: TMDb missing original language, unable to check."
    }
    if (original_language.replace(/[\r\n]+/gm, "").includes(" en ")) {
      return "CORRECT"
    }
    let subtitles = document.getElementsByClassName("mediainfo__subtitles");
    if (subtitles.length > 0) {
      subtitles = subtitles[0].getElementsByTagName("li");
      for (var i = 0; i < subtitles.length; i++) {
        if (subtitles[i].innerHTML.includes("English")) {
          return "CORRECT";
        }
      }
    }
    return "CHECK: There are no internal English soft subs, check subtitle manager and if it has hardsubs noted.  Postpone/reject accordingly."
  }
  return "CORRECT";
}

/**
 * Returns image dimensions for specified URL.
 */
function getImageDimensions(url) {
  return new Promise((resolve, reject) => {
    const img = new Image();
    img.onload = () => resolve({
      width: img.width,
      height: img.height,
      full_object: img,
    });
    img.onerror = (error) => reject(error);
    img.src = url;
  });
};

async function getImageBlob(imageUrl) {
  const response = await fetch(imageUrl)
  return response.blob()
}

async function screen_ar_size_format() {
  //TODO
  //Check the AR matches (within 2% or the mediainfo AR)
  //Check that they either the width or height matches the resolution
  //Check that they are png.
  //https://imgbox.c om/vjZ1VDbS
  //https://thumbs2.imgbox.com/76/fb/vjZ1VDbS_t.png
  //https://images2.imgbox.com/76/fb/vjZ1VDbS_o.png

  //https://wsrv.nl/?n=-1&url=https%3A%2F%2Fi.ibb.co%2FY31GgBR%2FOne-for-the-Road-2023-1080p-Blu-Ray-REMUX-AVC-True-HD7-1-Atmos-MANi-AC-mkv-snapshot-01-29-02-253.png
  //https://wsrv.nl/?n=-1&url=https%3A%2F%2Fi.ibb.co%2F80fVkgK%2FOne-for-the-Road-2023-1080p-Blu-Ray-REMUX-AVC-True-HD7-1-Atmos-MANi-AC-mkv-snapshot-01-29-02-253.png
  //80fVkgK
  //Y31GgBR
  try {
    let dimensions = {};
    dimensions = await getImageDimensions("https://images2.imgbox.com/76/fb/vjZ1VDbS_o.png");
    console.log(`Image dimensions: ${dimensions.width}px x ${dimensions.height}px`);
    console.log(`${dimensions.full_object}`)
  }
  catch (e) {
    // Could not load image from specified URL
    console.error(e);
  }
}

function only_one_mediainfo_bdinfo() {
  if (mediainfo_section && bdinfo_section) {
    return "POSTPONE: Contains mediainfo and bdinfo, should only have one"
  }
  return "CORRECT"
}

function resolution_check() {
  //TODO
  //verify resolution selected against mediainfo
  //verify SD flag
  let found_resolution = null;
  let total_pixels = null;
  if (!mediainfo_section && !bdinfo_section) {
    return "POSTPONE: Missing mediainfo/bdinfo."
  }
  if (mediainfo_section) {
    const regex = /[\d,\.]+ ?× ?[\d,\.]+\d+/g;
    let video_attributes = video_section.getElementsByTagName("dd");
    for (var i = 0; i < video_attributes.length; i++) {
      found_resolution = video_attributes[i].innerHTML.replace(/[\s\r\n]+/gm, "").match(regex);
      if (found_resolution) {
        found_resolution = found_resolution[0].split('×')
        total_pixels = found_resolution[0] * found_resolution[1];
        break;
      }
    }
    if (total_pixels < 600000) {
      //SD
      if (found_resolution[1] == 480) {
        found_resolution = '480'
      }
      else if (found_resolution[1] == 576) {
        found_resolution = '576'
      }
      else if (total_pixels > 430000) {
        found_resolution = '576'
      }
      else {
        found_resolution = '480'
      }
    }
    else {
      if (total_pixels > 70000000) {
        found_resolution = '8640'
      }
      else if (total_pixels > 13500000) {
        found_resolution = '4320'
      }
      else if (total_pixels > 3000000) {
        found_resolution = '2160'
      }
      else if (total_pixels > 960000) {
        found_resolution = '1080'
      }
      else if (total_pixels > 960000) {
        found_resolution = '720'
      }
      else {
        return "CHECK: Seemingly invalid resolution, between HD and SD."
      }
    }
  }
  else if (bdinfo_section) {
    const regex_bdinfo = /(\s+kbps\s+[\s/]+)(\d+[ip])([\s/]+)([\d\.]+ fps)/
    found_resolution = bdinfo_section.innerHTML.replace(/[\r\n]+/gm, "").match(regex_bdinfo);
    if (found_resolution && found_resolution.length >= 5) {
      found_resolution = found_resolution[2].replace(/[ip]/gm, "")
    }
    else {
      return "POSTPONE: Unable to find resolution in bdinfo."
    }
  }
  if (!video_resolution.includes(found_resolution)) {
    return `POSTPONE: Detected resolution of '${found_resolution}' does not match selected '${video_resolution}'`
  }
  return "CORRECT"
}

function verify_interlaced() {
  //TODO
  //DVDs need to parse VOB in description.
  //25fps allow both
  let found_resolution = null;
  let found_interlaced = false;
  let is_25fps = false;
  if (torrent_type.includes("Full Disc")) {
    // Check bdinfo, else check for the VOB in the description.
    if (bdinfo_section) {
      const regex_bdinfo = /(\s+kbps\s+[\s/]+)(\d+[ip])([\s/]+)([\d\.]+ fps)/
      found_resolution = bdinfo_section.innerHTML.replace(/[\r\n]+/gm, "").match(regex_bdinfo);
      if (found_resolution && found_resolution.length >= 5) {
        is_25fps = found_resolution[4] == "25 fps";
        found_interlaced = found_resolution[2].includes("i");
        if (is_25fps && found_interlaced && found_resolution.replace(/[ip]/gm, "") == "1080") {
          return "CORRECT"
        }
        if (found_interlaced == video_resolution.includes("i")) {
          return "CORRECT"
        }
        else if (found_interlaced) {
          return "POSTPONE: Flagged as interlaced, user selected progressive."
        }
        else {
          return "POSTPONE: Flagged as progressive, user selected interlaced."
        }
      }
      else {
        return "POSTPONE: Unable to find resolution and FPS in bdinfo."
      }
      // DVD logic here
      //TODO
    }
  }
  else {
    let mediainfo_text = document.getElementsByClassName("torrent-mediainfo-dump bbcode-rendered")[0].innerHTML.replace(/[\r\n]+/gm, "");
    const interlaced_regex = /Scan type +: Interlaced/g;
    const mbaff_regex = /Scan type +: MBAFF/g;
    const fps_regex = /Frame rate +: \d+(\.\d+)?( \(\d+\/\d+\))? FPS/g;
    const is_25fps = mediainfo_text.match(fps_regex)[0].replace(' ', '').split(':')[1].includes('25.000')
    const mbaff_check = mediainfo_text.match(mbaff_regex);
    if (mediainfo_text.match(interlaced_regex) || mbaff_check) {
      if (!video_resolution.includes("i") && !is_25fps) {
        return "POSTPONE: Marked as progressive, but mediainfo shows interlaced and is not 25FPS."
      }
      if (!video_resolution.includes("i") && is_25fps && !mbaff_check) {
        return "POSTPONE: Not MBAFF flagged 24FPS content, likely interlaced."
      }
      if (video_resolution.includes("i") && is_25fps) {
        return "CORRECT: Most 24FPS is progressive, but mediainfo shows interlaced and is fine to title it as such."
      }
    }
  }
  return "CORRECT"
}

function encode_check() {
  //some basic quality requirements
  if (torrent_type.includes("Encode")) {
    if (!encode_settings) {
      return "REJECT: Hardware encodes not allowed."
    }
  }
  if (encode_settings && (torrent_type.includes("HDTV") || torrent_type.includes("Encode"))) {
    let encode_type = get_enc_type();
    if (encode_type.includes("Single-Pass")) {
      return "REJECT: Single-Pass encodes are low quality."
    }
    if (!["umh", "esa", "tesa", "2", "3", "4", "5"].includes(video_settings.me)) {
      return `REJECT: me='${video_settings.me}' < umh (x264=umh | x265=3)`
    }
    if (video_settings.trellis && video_settings.trellis != 2) {
      return `REJECT: trellis='${video_settings.trellis}', likely low quality.`
    }
    let deblock = video_settings.deblock.split(":");
    if (video_settings.merange < 24) {
      return "REJECT: low merange, should be at least 24 to allow decent motion vectors."
    }
    if (!is_stream_op() && video_settings.bframes < 5) {
      return `REJECT: low bframe count, ${video_settings.bframes}, likely low quality.`
    }
    if (!is_stream_op() && video_settings.bframes < 7) {
      return `CHECK: low bframe count, ${video_settings.bframes}, check quality looks good enough.`
    }
    if (deblock[0] != 1 && deblock[1] >= 0 && deblock[2] >= 0) {
      return `CHECK: deblock='${video_settings.deblock}' > 1:-1:-1.  Non-annimation this is normally LQ.`
    }
    return "CORRECT"
  }
  return "CORRECT: Not an encode"
}

function title_check() {
  let title = null;
  let title_aka = null;
  let year = null;
  let cut = null;
  let hybrid = null;
  let repack = null;
  let resolution = null;
  let web_source = null;
  let source = null;
  let dual_audio = null;
  let audio_codec = null;
  let audio_channels = null;
  let audio_object = null;
  let dv = null;
  let hdr = null;
  let video_codec = null;
  let group = null;
  let title_aka_sans = null;
  let title_sans = null;
  let audio_space = null;
  //TODO
  let returns = [];
  if (torrent_category.includes("TV Show")) {}
  else {
    if (torrent_type.includes("Full Disc")) {
      const full_disc_regex = /Movie.../g;
    }
    else {
      // does not work on Blu-ray remux and alike, DVD will.
      const encode_web_regex = /^(.*? )(AKA .*?)*?(\d{4} )(.*? )?(Hybrid )?(REPACK )?(\d{3,4}[ip] |NTSC |PAL )?([A-Z]{2,10} )?(ProRes|UHD BluRay|BluRay|HDDVD|DVD|WEB-DL|WEBDL|WEB|WEBRip|HDTV)?( REMUX)?( )(Dual-Audio )?(DDP|DD\+|DDPA|DD|TrueHD|LPCM|DTS|DTS-?.?HD.?MA|DTS-HD HRA|DTS\:X|DTS-X|MPA|MP3|MP2|AAC|Opus|FLAC|L?PCM)( ?)(\d\.\d ?)+?(Atmos |Auro3D )?(DV )?(HDR |HDR10\+ )?(x26[45]|H\.26[45]|VC-?1|MEEG-?2|AV1|AVC|HEVC)?( ?-.*)?$/ig;
      let base_encode_web_check = torrent_title.matchAll(encode_web_regex);
      base_encode_web_check = base_encode_web_check.toArray();
      if (base_encode_web_check.length == 0) {
        returns.push("POSTPONE: Base title check failed, double check everything is in the correct order.");
        return format_title_mod(returns);
      }
      base_encode_web_check = base_encode_web_check[0];
      title = base_encode_web_check[1];
      title_aka = base_encode_web_check[2];
      year = base_encode_web_check[3];
      if (!year) {
        returns.push("POSTPONE: Year is missing.")
      }
      cut = base_encode_web_check[4];
      if (cut) {
        if (cut.toLowerCase().includes("repack") || cut.toLowerCase().includes("hybrid")) {
          returns.push("POSTPONE: Hybrid and/or repack in the wrong spot, 'CUT Hybrid REPACK' is expected")
        }
      }
      hybrid = base_encode_web_check[5];
      if (hybrid) {
        if (!hybrid.startsWith("REPACK")) {
          returns.push("POSTPONE: Hybrid casing is wrong.")
        }
      }
      repack = base_encode_web_check[6];
      if (repack) {
        if (!repack.startsWith("REPACK")) {
          returns.push("POSTPONE: REPACK casing is wrong.")
        }
      }
      resolution = base_encode_web_check[7];
      web_source = base_encode_web_check[8];
      source = base_encode_web_check[9];
      if (!source) {
        returns.push("POSTPONE: Source is missing or in wrong spot, check and postpone.")
      }
      dual_audio = base_encode_web_check[12];
      audio_codec = base_encode_web_check[13];
      if (audio_codec == "DDP") {
        returns.push("POSTPONE: DDP is incorrect, should be DD+.")
      }
      if (audio_codec == "MPA") {
        returns.push("POSTPONE: MPA is incorrect, check mediainfo for if this is MP3 or MP2.")
      }
      if (audio_codec == "MP3") {
        returns.push("REJECT: MP3 is not allowed without good reason.")
      }
      if (audio_codec == "DDPA") {
        returns.push("POSTPONE: DDPA is incorrect, should be DD+.")
      }
      if (audio_codec == "DTS-X") {
        returns.push("POSTPONE: DTS-X is incorrect, should be DTS:X.")
      }
      if (audio_codec == "DTSMA") {
        returns.push("POSTPONE: DTSMA is incorrect, should be DTS-HD MA.")
      }
      if (audio_codec == "DTS MA") {
        returns.push("POSTPONE: DTS MA is incorrect, should be DTS-HD MA.")
      }
      if (audio_codec == "DTS.MA") {
        returns.push("POSTPONE: DTS.MA is incorrect, should be DTS-HD MA.")
      }
      if (audio_codec == "DTSHD") {
        returns.push("POSTPONE: DTSHD is incorrect, likely should be DTS-HD MA, check mediainfo.")
      }
      if (audio_codec == "DTSHDMA") {
        returns.push("POSTPONE: DTSHDMA is incorrect, should be DTS-HD MA.")
      }
      if (audio_codec == "DTSHD.MA") {
        returns.push("POSTPONE: DTSHD.MA is incorrect, should be DTS-HD MA.")
      }
      if (audio_codec == "DTSHD MA") {
        returns.push("POSTPONE: DTSHD MA is incorrect, should be DTS-HD MA.")
      }
      audio_space = base_encode_web_check[14];
      if (!audio_space) {
        returns.push("POSTPONE: Missing space between audio codec and channels.")
      }
      audio_channels = base_encode_web_check[15];
      if (!audio_channels) {
        returns.push("POSTPONE: Missing audio channels.")
      }
      audio_object = base_encode_web_check[16];
      dv = base_encode_web_check[17];
      hdr = base_encode_web_check[18];
      video_codec = base_encode_web_check[19];
      if (!video_codec) {
        returns.push("POSTPONE: Music not allowed, no video codec specified.")
      }
      if (video_codec == "MPEG2") {
        returns.push("POSTPONE: MPEG2 is incorrect, should be MPEG-2.")
      }
      if (video_codec == "VC1") {
        returns.push("POSTPONE: VC1 is incorrect, should be VC-1.")
      }
      group = base_encode_web_check[20];
      if (title && title_aka) {
        if (title_aka.startsWith("aka")) {
          "POSTPONE: 'aka' needs to be in proper caps, 'AKA'."
        }
        title_sans = title.replace(/\s/g, '').replace(/[^A-z0-9]/g, '').toLowerCase();
        title_aka_sans = title_aka.replace(/\s/g, '').replace(/[^A-z0-9]/g, '').toLowerCase().substring(3);
        if (title_sans.includes(title_aka_sans) || title_aka_sans.includes(title_sans)) {
          returns.push("POSTPONE: Redundant AKA and English/International title, only keep one.")
        }
      }
      if (source == "WEBDL") {
        returns.push("POSTPONE: Source should be WEB-DL, not WEBDL.")
      }
      if (source == "WEB") {
        returns.push("POSTPONE: Source is only specified as WEB, likely a WEB-DL, needs checked.")
      }
      if (!["ProRes", "UHD BluRay", "BluRay", "HDDVD", "DVD", "WEB-DL", "WEBRip", "HDTV"].includes(source)) {
        "POSTPONE: The source is the wrong casing."
      }
      if (torrent_type.includes("Encode")) {
        if (!["ProRes", "UHD BluRay", "BluRay", "HDDVD"].includes(source)) {
          "POSTPONE: Encode type selected, source matches another, check which is wrong."
        }
        if (!["x265", "x264"].includes(video_codec)) {
          returns.push("REJECT: Only x265 and x264 are allowed for encodes.")
        }
      }
      if (torrent_type.includes("WEB-DL")) {
        if (!["WEB-DL"].includes(source)) {
          "POSTPONE: WEB-DL type selected, source matches another, check which is wrong."
        }
        if (["x265", "x264"].includes(video_codec)) {
          returns.push("POSTPONE: x264/x265 specified and claims WEB-DL, check which it is.")
        }
      }
      if (torrent_type.includes("WEBRip")) {
        if (!["WEB-DL"].includes(source)) {
          returns.push("POSTPONE: WEBRip type selected, source matches another, check which is wrong.")
        }
      }
      if (torrent_type.includes("Remux")) {
        if (!["UHD BluRay", "BluRay", "HDDVD", "DVD"].includes(source)) {
          returns.push("POSTPONE: Remux type selected, source matches another, check which is wrong.")
        }
      }
    }
  }
  return format_title_mod(returns);
}

function format_title_mod(returns) {
  if (returns.length == 0) {
    returns.push("CHECK: Title checking is not guaranteed double check.  No major issues detected.")
  }
  for (var i = 0; i < returns.length; i++) {
    returns[i] = `Title moderation: ${returns[i]}`
  }
  return returns
}

function AddModerationHelp() {
  let panelV2s = document.getElementsByClassName("panelV2")
  let moderationPanel = null;
  for (var i = 0; i < panelV2s.length; i++) {
    if (panelV2s[i].innerHTML.includes(`class="fal fa-hammer-war"`)) {
      moderationPanel = panelV2s[i];
    }
  }
  if (!moderationPanel) {
    console.log(mod_results);
    return;
  }
  moderationPanel = moderationPanel.getElementsByTagName("div")[0];
  moderationPanel.innerHTML += "<br><ul>";
  let color = null;
  for (i = 0; i < mod_results.length; i++) {
    if (mod_results[i].includes("POSTPONE:")) {
      color = 'yellow';
    }
    if (mod_results[i].includes("REJECT:")) {
      color = 'red'
    }
    if (mod_results[i].includes("CHECK:")) {
      color = 'orange'
    }
    if (mod_results[i].includes("CORRECT")) {
      color = 'green'
    }
    moderationPanel.innerHTML += `<li style='margin: 0px 0;'><span style="color:${color};'">${mod_results[i]}</span></li>`
  }
  moderationPanel.innerHTML += "</ul><br>";
}

(async function () {
  'use strict';

  set_resolution();
  mediainfo_section = get_mediainfo_section();
  bdinfo_section = get_bdinfo_section();
  if (mediainfo_section) {
    encode_settings = get_mediainfo_encode_settings();
    audio_section = get_mediainfo_audio();
    video_section = get_mediainfo_video();
    general_section = get_mediainfo_general();
    set_hdr_type()
  }
  if (encode_settings) {
    set_video_enc_settings(encode_settings.textContent);
  }
  set_container();
  is_stream_op()
  set_resolution();
  is_stream_op_flagged();
  get_torrent_type();
  get_torrent_category();
  torrent_title = get_torrent_title();

  mod_results.push(`Stream optimized moderation: ${stream_op_mod_check()}`)
  hybrid_dovi_check();
  mod_results.push(`TrueHD compatibility moderation: ${truehd_compat_check()}`)
  mod_results.push(`Required English sub moderation: ${has_required_english_subs()}`)
  mod_results.push(`All tracks have languages moderation: ${missing_any_language_mod()}`)
  mod_results.push(`Encode quality check moderation: ${encode_check()}`)
  mod_results.push(`Only has mediainfo or bdinfo moderation: ${only_one_mediainfo_bdinfo()}`)
  mod_results.push(`Resolution matches calculated resolution moderation: ${resolution_check()}`)
  mod_results.push(`Interlaced/Progressive moderation: ${verify_interlaced()}`)

  let title_mod = title_check();
  Array.prototype.push.apply(mod_results, title_mod)

  //await screen_ar_size_format();

  AddModerationHelp();
})();