MiM / BLU Add Wikidata Metadata

// ==UserScript==
// @name         BLU Add Wikidata Metadata
// @description  Search Wikidata and add more metadata
// @version      1.3.0
// @author       MiM
// @copyright    2024
// @license      MIT
// @match        https://blutopia.cc/torrents/similar/*
// @match        https://blutopia.cc/torrents/*
// @match        https://blutopia.cc/requests/*
// @match        https://blutopia.cc/wikidata
// @grant        GM_xmlhttpRequest
// @icon         https://blutopia.cc/favicon.ico
// @updateURL    https://openuserjs.org/meta/MiM/BLU_Add_Wikidata_Metadata.meta.js
// @downloadURL  https://openuserjs.org/install/MiM/BLU_Add_Wikidata_Metadata.user.js
// @connect      query.wikidata.org
// ==/UserScript==

// How long to cache.  24 hours is the default.
const CACHE_TIME = 1000 * 60 * 60 * 24;

var streamingRow;
var socialRow;
var customSites;
var customRow;
var imdb_id;

const break_html = `<div style="flex-basis: 100%; height: 0;"></div>`;

// Base URL to be used, potentially create this dynamically later updates
const sparql = "https://query.wikidata.org/sparql?query="
const sparql_log = "https://query.wikidata.org/index.html#"
const base_query = `SELECT DISTINCT *
   WHERE {
      ?subject wdt:P345 "{IMDB}".
`
// Add metadata_links
const metadata_links_sparql = `
   OPTIONAL {?Wikipedia schema:about ?subject}
   OPTIONAL {?subject wdt:P2671 ?Google}
   OPTIONAL {?subject wdt:P1258 ?RottenTomatoes}
   OPTIONAL {?subject wdt:P345 ?IMDb}
   OPTIONAL {?subject wdt:P8013 ?Trakt}
   OPTIONAL {?subject wdt:P4983 ?TMDbTV}
   OPTIONAL {?subject wdt:P4947 ?TMDbMovie}
   OPTIONAL {?subject wdt:P4835 ?TVDbTV}
   OPTIONAL {?subject wdt:P12196 ?TVDbMovie}
   OPTIONAL {?subject wdt:P8600 ?TVMaze}
   OPTIONAL {?subject wdt:P3808 ?TheNumbers}
   OPTIONAL {?subject wdt:P6127 ?Letterboexd}
   OPTIONAL {?subject wdt:P5865 ?IGN}
   OPTIONAL {?subject wdt:P4839 ?WolframSearch BIND(ENCODE_FOR_URI(?WolframSearch) AS ?Wolfram)}
   OPTIONAL {?subject wdt:P7334 ?Vudu}
   OPTIONAL {?subject wdt:P1874 ?Netflix}
   OPTIONAL {?subject wdt:P7596 ?DisneyPlusSeries}
   OPTIONAL {?subject wdt:P7595 ?DisneyPlusMovie}
   OPTIONAL {?subject wdt:P6466 ?HuluMovie}
   OPTIONAL {?subject wdt:P6467 ?HuluSeries}
   OPTIONAL {?subject wdt:P8055 ?AmazonPrimeUS}
   OPTIONAL {?subject wdt:P9751 ?AppleTV}
   OPTIONAL {?subject wdt:P9586 ?AppleMovie}
   OPTIONAL {?subject wdt:P11460 ?Plex}
   OPTIONAL {?subject wdt:P1651 ?Youtube}
   OPTIONAL {?subject wdt:P8823 ?Xfinity}
   OPTIONAL {?subject wdt:P2013 ?Facebook}
   OPTIONAL {?subject wdt:P2002 ?Twitter}
   OPTIONAL {?subject wdt:P2003 ?Instagram}
   OPTIONAL {?subject wdt:P3943 ?Tumblr}
 } `

const base_order_by = " ORDER BY ASC(IF(contains(str(?Wikipedia),'en.wikipedia'), 0, 1)) "
const original_language_sparql = " ASC(IF(contains(str(?Wikipedia),'{LANGUAGE_CODE}.wikipedia'), 0, 1)) "
const final_order_by = " ASC(?Wikipedia) "

const metadataType = Object.freeze({
  METADATA: Symbol("metadata"),
  STREAMING: Symbol("streaming"),
  SOCIAL: Symbol("metadata"),
  CUSTOM: Symbol("custom")
});
const tvdb_logo = 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjxzdmcgaGVpZ2h0PSIxMzgzIiB2aWV3Qm94PSIwIC0uNDQxIDEwMCA1NC44ODMiIHdpZHRoPSIyNTAwIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Im0wIDBoMTAwdjU0aC0xMDB6IiBvcGFjaXR5PSIuNjkiLz48cGF0aCBkPSJtMCA1LjA5NmMwLTMuMjg2IDIuOTY0LTUuNTM3IDYuNDYyLTUuMDIzbDQ1LjY0IDUuOTYyYzIuMDUuMzAyIDMuNjg1IDIuNTEyIDMuNjg1IDQuOTE5djYuMjNjLTMuODQ2IDIuNTc3LTYuMzY3IDYuODktNi4zNjcgMTEuNzc2IDAgNC45MzIgMi40NiA5LjIwOCA2LjM2NyAxMS43NzZ2Mi43NGMwIDIuNDA3LTEuNjM0IDQuNjE3LTMuNjg2IDQuOTE5bC00MC40OTggNS41MzJjLTMuNDk5LjUxNS02LjQ2Mi0xLjczNy02LjQ2Mi01LjAyM3ptMTkuNjggNS43MjZoLTYuMDE5djcuODg0aC0zLjgxOXY0LjU1NmgzLjgydjguNzZjLS4xNzcgNS41MDUgMS45ODUgNy45NyA2Ljk1MyA4LjAxN2g0LjYzNnYtNC41NTZoLTIuMzhjLTIuNjk2LS4xMzEtMy4yOC0uOTItMy4xOS00LjQ2OHYtNy43NTNoNy40bDYuODUzIDE2Ljc3Nmg2LjQ2OWw5LjQzNC0yMS4zMzJoLTYuMzhsLTYuMTEgMTQuNjc1LTUuNTctMTQuNjc1aC0xMi4wOTd6IiBmaWxsPSIjNmNkNTkxIi8+PHBhdGggZD0ibTg4LjYxIDE4LjI3N2MzLjkwOCAwIDYuNTEzLjk2NCA4LjQ5IDMuMTU0IDEuNjE3IDEuNzUyIDIuNDcgNC4zMzcgMi40NyA3LjIyNyAwIDQuMjA1LTEuNzA2IDcuNDktNC44MDYgOS4zNzQtMi4wMjIgMS4yMjctMy43NzMgMS41NzctNy43MjcgMS41NzdoLTkuNzkzdi0yOS4yMTZoNi4wMnY3Ljg4NHptLTUuMzQ2IDE2Ljc3N2g0LjQ0N2MzLjU0OSAwIDUuNzA1LTIuMzY2IDUuNzA1LTYuMjIgMC00LjAzLTEuODg2LTYuMDAyLTUuNzA1LTYuMDAyaC00LjQ0N3ptLTE1LjI1LTI0LjY2MWg2LjAydjI5LjIxNmgtMTAuMzc4Yy00LjIyMiAwLTYuMjQ0LS42MTMtOC40LTIuNDk2LTIuMjAyLTEuOTcxLTMuMzI1LTQuNzc1LTMuMzI1LTguMzIzIDAtMy42OCAxLjIxMy02LjQ4MyAzLjc3NC04LjU0MiAxLjYxNy0xLjMxNCAzLjgxOS0xLjk3IDYuNjQ5LTEuOTdoNS42NnptMCAxMi40NGgtNC4zNThjLTMuNTA0IDAtNS42MTUgMi4yMzQtNS42MTUgNS45NTcgMCAzLjg5OCAyLjE1NiA2LjI2NCA1LjYxNSA2LjI2NGg0LjM1OHoiIGZpbGw9IiNmZmYiLz48L3N2Zz4=';
const trakt_logo = 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAxOC4wLjAsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+DQo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4Ig0KCSB2aWV3Qm94PSIwIDAgMTQ0LjggMTQ0LjgiIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDE0NC44IDE0NC44IiB4bWw6c3BhY2U9InByZXNlcnZlIj4NCjxnPg0KCTxjaXJjbGUgZmlsbD0iI0ZGRkZGRiIgY3g9IjcyLjQiIGN5PSI3Mi40IiByPSI3Mi40Ii8+DQoJPHBhdGggZmlsbD0iI0VEMjIyNCIgZD0iTTI5LjUsMTExLjhjMTAuNiwxMS42LDI1LjksMTguOCw0Mi45LDE4LjhjOC43LDAsMTYuOS0xLjksMjQuMy01LjNMNTYuMyw4NUwyOS41LDExMS44eiIvPg0KCTxwYXRoIGZpbGw9IiNFRDIyMjQiIGQ9Ik01Ni4xLDYwLjZMMjUuNSw5MS4xTDIxLjQsODdsMzIuMi0zMi4yaDBsMzcuNi0zNy42Yy01LjktMi0xMi4yLTMuMS0xOC44LTMuMWMtMzIuMiwwLTU4LjMsMjYuMS01OC4zLDU4LjMNCgkJYzAsMTMuMSw0LjMsMjUuMiwxMS43LDM1bDMwLjUtMzAuNWwyLjEsMmw0My43LDQzLjdjMC45LTAuNSwxLjctMSwyLjUtMS42TDU2LjMsNzIuN0wyNywxMDJsLTQuMS00LjFsMzMuNC0zMy40bDIuMSwybDUxLDUwLjkNCgkJYzAuOC0wLjYsMS41LTEuMywyLjItMS45bC01NS01NUw1Ni4xLDYwLjZ6Ii8+DQoJPHBhdGggZmlsbD0iI0VEMUMyNCIgZD0iTTExNS43LDExMS40YzkuMy0xMC4zLDE1LTI0LDE1LTM5YzAtMjMuNC0xMy44LTQzLjUtMzMuNi01Mi44TDYwLjQsNTYuMkwxMTUuNywxMTEuNHogTTc0LjUsNjYuOGwtNC4xLTQuMQ0KCQlsMjguOS0yOC45bDQuMSw0LjFMNzQuNSw2Ni44eiBNMTAxLjksMjcuMUw2OC42LDYwLjRsLTQuMS00LjFMOTcuOCwyM0wxMDEuOSwyNy4xeiIvPg0KCTxnPg0KCQk8Zz4NCgkJCTxwYXRoIGZpbGw9IiNFRDIyMjQiIGQ9Ik03Mi40LDE0NC44QzMyLjUsMTQ0LjgsMCwxMTIuMywwLDcyLjRDMCwzMi41LDMyLjUsMCw3Mi40LDBzNzIuNCwzMi41LDcyLjQsNzIuNA0KCQkJCUMxNDQuOCwxMTIuMywxMTIuMywxNDQuOCw3Mi40LDE0NC44eiBNNzIuNCw3LjNDMzYuNSw3LjMsNy4zLDM2LjUsNy4zLDcyLjRzMjkuMiw2NS4xLDY1LjEsNjUuMXM2NS4xLTI5LjIsNjUuMS02NS4xDQoJCQkJUzEwOC4zLDcuMyw3Mi40LDcuM3oiLz4NCgkJPC9nPg0KCTwvZz4NCjwvZz4NCjwvc3ZnPg0K';
const tvmaze_logo = 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMjUwIiBoZWlnaHQ9IjI1MCIgdmlld0JveD0iMCAwIDI1MCAyNTAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMF8yMTUyXzQxMjQpIj4KPHBhdGggZD0iTTIyNi45NDQgOTkuOTgxNEMyMjYuNTk0IDk4LjM2MDYgMjI2LjIyMiA5Ni43NDg4IDIyNS44MyA5NS4xNDU4QzIyNC4yNSA4OC43MjQ2IDIyMC43NzYgODIuOTI5OSAyMTUuODYxIDc4LjUxNzlDMjEwLjk0NiA3NC4xMDYgMjA0LjgxOCA3MS4yODE3IDE5OC4yNzcgNzAuNDEzOEwxOTEuODc1IDY5LjYxODNDMTc5LjYyNCA2OC4xNDM1IDE2Ni45MDkgNjcuMDI2MiAxNTMuODE5IDY2LjMxMTFMMjA0LjI3OCAzNi42ODk5QzIwNS40MDQgMzYuMDMwNSAyMDYuMzgzIDM1LjE0NTkgMjA3LjE1MyAzNC4wOTExQzIwNy45MjMgMzMuMDM2NCAyMDguNDY5IDMxLjgzNCAyMDguNzU2IDMwLjU1ODhDMjA5LjA0MyAyOS4yODM3IDIwOS4wNjUgMjcuOTYzIDIwOC44MjEgMjYuNjc4OUMyMDguNTc3IDI1LjM5NDggMjA4LjA3MSAyNC4xNzQ4IDIwNy4zMzcgMjMuMDk0OEMyMDUuOTc4IDIxLjA5OTIgMjAzLjkxNSAxOS42OTQzIDIwMS41NjMgMTkuMTYzNUMxOTkuMjEyIDE4LjYzMjcgMTk2Ljc0NyAxOS4wMTU1IDE5NC42NjYgMjAuMjM0NkwxMjQuMzQ5IDYxLjUzODFMNTIuMjA0NiAxOS4yNjkzQzUwLjEwNyAxOC4wNDE5IDQ3LjYxMzYgMTcuNjg1OCA0NS4yNTc1IDE4LjI3NzFDNDIuOTAxNSAxOC44NjgzIDQwLjg2OTYgMjAuMzYwMSAzOS41OTY0IDIyLjQzMzRDMzguOTQ1NyAyMy40OTE2IDM4LjUxMjcgMjQuNjY5NSAzOC4zMjI5IDI1Ljg5NzhDMzguMTMzIDI3LjEyNjIgMzguMTkwMiAyOC4zODAyIDM4LjQ5MDkgMjkuNTg2MkMzOC43OTE3IDMwLjc5MjEgMzkuMzMgMzEuOTI1NSA0MC4wNzQyIDMyLjkxOThDNDAuODE4MyAzMy45MTQxIDQxLjc1MzMgMzQuNzQ5MiA0Mi44MjQyIDM1LjM3NTlMOTQuODI2MiA2NS44NDYzQzgyLjUwOTMgNjYuMjg3MyA3MC41MTY0IDY3LjEwMzYgNTguODQ3NSA2OC4yOTU0TDUyLjg0NjYgNjguOTAzMkM0Ni4xNDY4IDY5LjY1NzQgMzkuODM4MyA3Mi40NTA5IDM0Ljc3MDQgNzYuOTA3OUMyOS43MDI1IDgxLjM2NDkgMjYuMTE4MyA4Ny4yNzEyIDI0LjUwMDUgOTMuODMxOUMyNC4yNTk3IDk0Ljc4ODMgMjQuMDE5IDk1LjczNTggMjMuNzk2MSA5Ni42OTIxQzE1Ljc2MDQgMTMxLjMzNCAxNi4wNjYgMTY3LjM5NSAyNC42ODc3IDIwMS44OTVDMjUuMDI2NiAyMDMuMjI3IDI1LjM3NDMgMjA0LjU3NiAyNS43NDg4IDIwNS44NjRDMjcuNTIxOSAyMTIuMTkgMzEuMTUwMSAyMTcuODM3IDM2LjE2MzEgMjIyLjA3MUM0MS4xNzYxIDIyNi4zMDYgNDcuMzQyNiAyMjguOTMzIDUzLjg2MzEgMjI5LjYxMkM1Ni4yNDA4IDIyOS44NjMgNTguNjE4NiAyMzAuMTAxIDYwLjk5NjQgMjMwLjMyN0M3OS41NTIgMjMyLjExNSA5OS4wMjYgMjMzLjAwOSAxMTkuMDg4IDIzMy4wMDlDMTQyLjY4IDIzMy4wNDIgMTY2LjI1MyAyMzEuNzA4IDE4OS42OTEgMjI5LjAxNEMxOTIuMTY5IDIyOC43MzYgMTk0LjYyMiAyMjguNDI0IDE5Ny4wNzQgMjI4LjEyQzIwMy41NzIgMjI3LjI3MiAyMDkuNjcyIDIyNC41MDQgMjE0LjU5NSAyMjAuMTY4QzIxOS41MTggMjE1LjgzMSAyMjMuMDQxIDIxMC4xMjQgMjI0LjcxNSAyMDMuNzcyQzIyNS4xNyAyMDIuMDgzIDIyNS42MDcgMjAwLjM4NCAyMjUuOTk5IDE5OC42NTlDMjI5Ljk3MSAxODEuODc5IDIzMS45NTggMTY0LjY4OSAyMzEuOTIgMTQ3LjQ0M0MyMzEuOTc4IDEzMS40ODkgMjMwLjMxIDExNS41NzUgMjI2Ljk0NCA5OS45ODE0Wk0yMDUuOTE5IDE4OC4xNzVDMjA0LjczNSAxOTMuMDk3IDIwMi4xMSAxOTcuNTUyIDE5OC4zODEgMjAwLjk2N0MxOTQuNjUxIDIwNC4zODIgMTg5Ljk4OSAyMDYuNjAxIDE4NC45OTIgMjA3LjMzOEMxNjMuMTkgMjEwLjUyNCAxNDEuMTgzIDIxMi4wOTUgMTE5LjE1MSAyMTIuMDRDMTAxLjY5OSAyMTIuMDYgODQuMjU5NyAyMTEuMDkxIDY2LjkxNzEgMjA5LjEzNUM2MS41NzE0IDIwOC41MjYgNTYuNTQwMyAyMDYuMjkgNTIuNTAwNiAyMDIuNzI4QzQ4LjQ2MSAxOTkuMTY2IDQ1LjYwNjggMTk0LjQ0OSA0NC4zMjIyIDE4OS4yMTJDMzcuODc5MSAxNjIuMjQ2IDM3Ljc2MDEgMTM0LjE1MSA0My45NzQ1IDEwNy4xMzJDNDUuMDYyIDEwMi41NTIgNDcuNTI2NyA5OC40MTY5IDUxLjAzNTIgOTUuMjg2OEM1NC41NDM3IDkyLjE1NjcgNTguOTI2NyA5MC4xODI0IDYzLjU5MTEgODkuNjMxQzY4LjcwOTMgODkuMDA1MyA3My45MzQ1IDg4LjQ1MTEgNzkuMjU3NyA4OC4wMTMxQzkyLjA2MiA4Ni44OTU5IDEwNS40MjggODYuMzIzOCAxMTkuMTUxIDg2LjMyMzhIMTE5Ljc3NUMxMjMuMTk5IDg2LjMyMzggMTI2LjYwMiA4Ni4zNjU1IDEyOS45ODUgODYuNDQ4OUMxNDQuNDIxIDg2Ljc3MDcgMTU4LjQwMiA4Ny43MjcxIDE3MS42NjEgODkuMjU1NUMxNzcuMTYzIDg5Ljg5OTEgMTgyLjU1NyA5MC42MzIgMTg3LjgwOSA5MS40NTQ0QzE5Mi4yOTYgOTIuMTQ0NCAxOTYuNDcyIDk0LjE3MjcgMTk5Ljc5MiA5Ny4yNzVDMjAzLjExMyAxMDAuMzc3IDIwNS40MjUgMTA0LjQxIDIwNi40MjcgMTA4Ljg0OEMyMDkuMjI1IDEyMS41MTUgMjEwLjYyNCAxMzQuNDUyIDIxMC42IDE0Ny40MjVDMjEwLjYyNyAxNjEuMTQ1IDIwOS4wNTYgMTc0LjgyMSAyMDUuOTE5IDE4OC4xNzVaIiBmaWxsPSIjMDEwMTAxIi8+CjxwYXRoIGQ9Ik04MC41NTkyIDE3MC44NzlDOTUuMDkxNSAxNzAuODc5IDEwNi44NzIgMTU5LjA3IDEwNi44NzIgMTQ0LjUwM0MxMDYuODcyIDEyOS45MzUgOTUuMDkxNSAxMTguMTI2IDgwLjU1OTIgMTE4LjEyNkM2Ni4wMjY5IDExOC4xMjYgNTQuMjQ2MSAxMjkuOTM1IDU0LjI0NjEgMTQ0LjUwM0M1NC4yNDYxIDE1OS4wNyA2Ni4wMjY5IDE3MC44NzkgODAuNTU5MiAxNzAuODc5WiIgZmlsbD0iIzNGOTE4QiIvPgo8cGF0aCBkPSJNMTY5LjQ0MSAxNzIuMDVDMTgzLjk3MyAxNzIuMDUgMTk1Ljc1NCAxNjAuMjQxIDE5NS43NTQgMTQ1LjY3NEMxOTUuNzU0IDEzMS4xMDYgMTgzLjk3MyAxMTkuMjk3IDE2OS40NDEgMTE5LjI5N0MxNTQuOTA5IDExOS4yOTcgMTQzLjEyOCAxMzEuMTA2IDE0My4xMjggMTQ1LjY3NEMxNDMuMTI4IDE2MC4yNDEgMTU0LjkwOSAxNzIuMDUgMTY5LjQ0MSAxNzIuMDVaIiBmaWxsPSIjM0Y5MThCIi8+CjwvZz4KPGRlZnM+CjxjbGlwUGF0aCBpZD0iY2xpcDBfMjE1Ml80MTI0Ij4KPHJlY3Qgd2lkdGg9IjIxNCIgaGVpZ2h0PSIyMTUiIGZpbGw9IndoaXRlIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxOCAxOCkiLz4KPC9jbGlwUGF0aD4KPC9kZWZzPgo8L3N2Zz4K';
const metadata_map = {
  'Wikidata': {
    'icon': 'https://upload.wikimedia.org/wikipedia/commons/6/66/Wikidata-logo-en.svg',
    'uri': '{id}',
    'row': metadataType.METADATA,
    'title': 'Wikidata ({id})' // Needs URL replaced.
  },
  'IMDb': {
    'icon': 'https://upload.wikimedia.org/wikipedia/commons/6/69/IMDB_Logo_2016.svg',
    'uri': 'https://www.imdb.com/title/{id}',
    'row': metadataType.METADATA,
    'title': 'Internet Movie DB ({id})'
  },
  'Trakt': {
    'icon': trakt_logo,
    'uri': 'https://trakt.tv/{id}',
    'row': metadataType.METADATA,
    'title': 'Trakt ({id})'
  },
  'TMDbTV': {
    'icon': 'https://upload.wikimedia.org/wikipedia/commons/8/89/Tmdb.new.logo.svg',
    'uri': 'https://www.themoviedb.org/tv/{id}',
    'row': metadataType.METADATA,
    'title': 'The Movie DB (tv/{id})'
  },
  'TMDbMovie': {
    'icon': 'https://upload.wikimedia.org/wikipedia/commons/8/89/Tmdb.new.logo.svg',
    'uri': 'https://www.themoviedb.org/movie/{id}',
    'row': metadataType.METADATA,
    'title': 'The Movie DB (movie/{id})'
  },
  'TVDbTV': {
    'icon': tvdb_logo,
    'uri': 'https://thetvdb.com/dereferrer/series/{id}',
    'row': metadataType.METADATA,
    'title': 'TheTV DB (series/{id})'
  },
  'TVDbMovie': {
    'icon': tvdb_logo,
    'uri': 'https://thetvdb.com/dereferrer/movie/{id}',
    'row': metadataType.METADATA,
    'title': 'TheTV DB (movie/{id})'
  },
  'TVMaze': {
    'icon': tvmaze_logo,
    'uri': 'https://tvmaze.com/shows/{id}',
    'row': metadataType.METADATA,
    'title': 'TVMaze (shows/{id})'
  },
  'Wikipedia': {
    'icon': 'https://upload.wikimedia.org/wikipedia/en/8/80/Wikipedia-logo-v2.svg',
    'uri': '{id}',
    'row': metadataType.METADATA,
    'title': 'Wikipedia'
  },
  'RottenTomatoes': {
    'icon': 'https://upload.wikimedia.org/wikipedia/commons/6/6f/Rotten_Tomatoes_logo.svg',
    'uri': 'https://www.rottentomatoes.com/{id}',
    'row': metadataType.METADATA,
    'title': 'Rotten Tomatoes ({id})'
  },
  'Google': {
    'icon': 'https://upload.wikimedia.org/wikipedia/commons/archive/c/c1/20230822192910%21Google_%22G%22_logo.svg',
    'uri': 'https://www.google.com/search?kgmid={id}',
    'row': metadataType.METADATA,
    'title': 'Google ({id})'
  },
  'TheNumbers': {
    'icon': 'https://upload.wikimedia.org/wikipedia/commons/6/69/The_Numbers_logo.svg',
    'uri': 'https://www.the-numbers.com/movie/{id}',
    'row': metadataType.METADATA,
    'title': 'The Numbers ({id})'
  },
  'Letterboxd': {
    'icon': 'https://upload.wikimedia.org/wikipedia/commons/9/93/Letterboxd_2018_logo_%28vertical%29.svg',
    'uri': 'https://letterboxd.com/film/{id}',
    'row': metadataType.METADATA,
    'title': 'Letterboxd ({id})'
  },
  'IGN': {
    'icon': 'https://upload.wikimedia.org/wikipedia/commons/4/47/IGN_logo.svg',
    'uri': 'https://www.ign.com/movies/{id}',
    'row': metadataType.METADATA,
    'title': 'IGN ({id})'
  },
  'Wolfram': {
    'icon': 'https://upload.wikimedia.org/wikipedia/commons/2/20/Mathematica_Logo.svg',
    'uri': 'https://www.wolframalpha.com/input/?i={id}',
    'row': metadataType.METADATA,
    'title': 'WolframAlphha ({id})'
  },
  'Vudu': {
    'icon': 'https://upload.wikimedia.org/wikipedia/commons/e/ef/Vudu_2014_logo.svg',
    'uri': 'https://www.vudu.com/content/browse/details/wd/{id}',
    'row': metadataType.STREAMING,
    'title': 'Vudu ({id})'
  },
  'HuluMovie': {
    'icon': 'https://upload.wikimedia.org/wikipedia/commons/2/20/Hulu_2019.svg',
    'uri': 'https://www.hulu.com/movie/{id}',
    'row': metadataType.STREAMING,
    'title': 'Hulu (movie/{id})'
  },
  'HuluSeries': {
    'icon': 'https://upload.wikimedia.org/wikipedia/commons/2/20/Hulu_2019.svg',
    'uri': 'https://www.hulu.com/series/{id}',
    'row': metadataType.STREAMING,
    'title': 'Hulu (series/{id})'
  },
  'Netflix': {
    'icon': 'https://upload.wikimedia.org/wikipedia/commons/0/0c/Netflix_2015_N_logo.svg',
    'uri': 'https://www.netflix.com/title/{id}',
    'row': metadataType.STREAMING,
    'title': 'Netflix ({id})'
  },
  'DisneyPlusSeries': {
    'icon': 'https://upload.wikimedia.org/wikipedia/commons/6/64/Disney%2B_2024.svg',
    'uri': 'https://www.disneyplus.com/series/wp/{id}',
    'row': metadataType.STREAMING,
    'title': 'Disney+ (series/{id})'
  },
  'DisneyPlusMovie': {
    'icon': 'https://upload.wikimedia.org/wikipedia/commons/6/64/Disney%2B_2024.svg',
    'uri': 'https://www.disneyplus.com/movies/wd/{id}',
    'row': metadataType.STREAMING,
    'title': 'Disney+ (movies/{id})'
  },
  'AmazonPrimeUS': {
    'icon': 'https://upload.wikimedia.org/wikipedia/commons/1/11/Amazon_Prime_Video_logo.svg',
    'uri': 'https://www.amazon.com/gp/video/detail/{id}',
    'row': metadataType.STREAMING,
    'title': 'Amazon Prime US ({id})'
  },
  'AppleTV': {
    'icon': 'https://upload.wikimedia.org/wikipedia/commons/8/8a/Apple_Logo.svg',
    'uri': 'https://tv.apple.com/show/{id}',
    'row': metadataType.STREAMING,
    'title': 'iTunes (show/{id})'
  },
  'AppleMovie': {
    'icon': 'https://upload.wikimedia.org/wikipedia/commons/8/8a/Apple_Logo.svg',
    'uri': 'https://tv.apple.com/movie/{id}',
    'row': metadataType.STREAMING,
    'title': 'iTunes (movie/{id})'
  },
  'Plex': {
    'icon': 'https://upload.wikimedia.org/wikipedia/commons/7/7b/Plex_logo_2022.svg',
    'uri': 'https://app.plex.tv/desktop/#!/provider/tv.plex.provider.metadata/details?key=/library/metadata/{id}',
    'row': metadataType.STREAMING,
    'title': 'Plex ({id})'
  },
  'Youtube': {
    'icon': 'https://upload.wikimedia.org/wikipedia/commons/0/09/YouTube_full-color_icon_%282017%29.svg',
    'uri': 'https://www.youtube.com/watch?v={id}',
    'row': metadataType.STREAMING,
    'title': 'YouTube ({id})'
  },
  'Xfinity': {
    'icon': 'https://upload.wikimedia.org/wikipedia/commons/5/58/Xfinity_logo.svg',
    'uri': 'https://www.xfinity.com/stream/entity/{id}',
    'row': metadataType.STREAMING,
    'title': 'Xfinity ({id})'
  },
  'Facebook': {
    'icon': 'https://upload.wikimedia.org/wikipedia/commons/b/b9/2023_Facebook_icon.svg',
    'uri': 'https://www.facebook.com/{id}',
    'row': metadataType.SOCIAL,
    'title': 'Facebook ({id})'
  },
  'Tumblr': {
    'icon': 'https://upload.wikimedia.org/wikipedia/commons/2/2c/Tumblr_Logo.svg',
    'uri': 'https://{id}.tumblr.com/',
    'row': metadataType.SOCIAL,
    'title': 'Tumblr ({id})'
  },
  'Twitter': {
    'icon': 'https://upload.wikimedia.org/wikipedia/commons/6/6f/Logo_of_Twitter.svg',
    'uri': 'https://twitter.com/{id}',
    'row': metadataType.SOCIAL,
    'title': 'Twitter/X ({id})'
  },
  'Instagram': {
    'icon': 'https://upload.wikimedia.org/wikipedia/commons/e/e7/Instagram_logo_2016.svg',
    'uri': 'https://www.instagram.com/{id}',
    'row': metadataType.SOCIAL,
    'title': 'Instagram ({id})'
  },
}

const settings_page_body = `
<h1>Wikidata Settings</h1>
<ul>
   <li>Row numbers, 0 means do not display/show.
   <li>Custom Sites, searh by IMDb. {ttimdb} and {imdb}, ex:
      <ul>
         <li>{ttimdb}: tt7907922
         <li>{imdb}: 7907922
      </ul>
   <li>Pipe separated required "|"
   <li>One site per line.
   <li>Order:
   <ol>
      <li>Site Name (hover over)
      <li>Search Pattern, eg:
         <ul>
            <li>https://mysite.com/search/imdb/{ttimdb}
         </ul>
      <li>Icon
   </ol>
   <li>Example:<br>
      <pre>Best site ever | https://mysite.com/search/imdb/{ttimdb} | https://mysite.com/mylogo.png</pre>
</ul>
<hr/>

  <label for="social">Social Media Row:</label>
  <input type="number" step="1" pattern="\\d+" min="0" max="3" id="social" name="social"><br><br>
  <label for="streaming">Streaming Services Row:</label>
  <input type="number" step="1" pattern="\\d+" min="0" max="4" id="streaming" name="streaming"><br><br>
  <label for="customPos">Custom Sites Row:</label>
  <input type="number" step="1" pattern="\\d+" min="0" max="4" id="customPos" name="customPos"><br><br>
  <label for="custom">Custom Sites (by IMDb):</label><br>
  <textarea id="custom" name="custom" rows="10" cols="100"></textarea><br><br>
  <input type="submit" id="update_wikidata" value="Update Settings">
`

async function search_wikidata_com(imdb, original_language_code) {
  let url = base_query.replace(`{IMDB}`, imdb);
  url += metadata_links_sparql + base_order_by
  if (original_language_code != '') {
    url = url.replace(`{ORIGINAL_LANGUAGE}`, '');
    let original_language_order = original_language_sparql.replace(`{LANGUAGE_CODE}`, original_language_code);
    url += original_language_sparql
  }
  url += final_order_by
  url = encodeURIComponent(url)
  console.log(sparql_log + url);
  url = sparql + url
  const headers = {
    "Accept": "application/json"
  };
  let resolver;
  let rejecter;
  const p = new Promise((resolveFn, rejectFn) => {
    resolver = resolveFn;
    rejecter = rejectFn;
  });
  const final = GM_xmlhttpRequest({
    method: "GET",
    url: url,
    headers: headers,
    onload: (response) => resolver(response),
    onerror: (response) => rejecter(response),
    ontimeout: (response) => rejecter(response)
  });
  return p;
}

function get_imdb_id() {
  let imdb_id = ''
  try {
    let imdb_url = document.getElementsByClassName("meta__imdb")[0].getElementsByTagName("a")[0].href;
    let url_regex = /^(.*)(tt\d+)([^\d]*)$/;
    imdb_id = imdb_url.match(url_regex)[2];
  }
  catch {}
  return imdb_id;
}

function get_primary_language() {
  let primary_language = ''
  try {
    primary_language = document.getElementsByClassName("meta__language")[0].getElementsByClassName("meta-chip__value")[0].innerText;
    if (primary_language.length != 2) {
      primary_language = ''
    }
  }
  catch {}
  return primary_language;
}

function removeDuplicates(arr) {
  let unique = arr.reduce(function (acc, curr) {
    if (!acc.includes(curr)) {
      acc.push(curr);
    }
    return acc;
  }, []);
  return unique;
}

function get_custom_sites_html() {
  var custom_html = "";
  var sites = customSites.trim().split(`\n`);
  var site;
  var site_title;
  var site_uri;
  var site_icon;
  var site_class;
  for (var s = 0; s < sites.length; s++) {
    site = sites[s].split(`|`);
    if (site.length != 3) {
      continue;
    }
    site_title = site[0].trim().replace('"', "'");
    site_class = site_title.replace(" ", "")
    site_uri = site[1].trim().replace("{ttimdb}", imdb_id).replace("{imdb}", imdb_id.replace("tt", ""));
    site_icon = site[2].trim();
    custom_html = custom_html + `
                   <li class="meta__${site_class}">
                      <a class="meta-id-tag" href="${site_uri}" title="${site_title}" target="_blank">
                         <img src="${site_icon}">
                      </a>
                   </li>`;
  }
  return custom_html;
}

function add_to_site(wikidata) {
  let meta_html_tag = document.getElementsByClassName("meta__ids")[0];
  var meta_row;
  var meta_url;
  var meta_img;
  var meta_title;
  var social_found = false;
  var streaming_found = false;
  var meta_html;
  let url = '';
  var metadata_html = '';
  var streaming_html = "";
  var social_html = "";
  var custom_html = "";
  for (var w in wikidata) {
    if (metadata_map.hasOwnProperty(w)) {
      meta_url = metadata_map[w].uri.replace("{id}", wikidata[w].value);
      meta_row = metadata_map[w].row;
      meta_img = metadata_map[w].icon;
      meta_title = metadata_map[w].title.replace("{id}", wikidata[w].value);
      meta_html = `<li class="meta__${w}">
                      <a class="meta-id-tag" href="${meta_url}" title="${meta_title}" target="_blank">
                         <img src="${meta_img}">
                      </a>
                   </li>`;
      if (meta_row === metadataType.METADATA && !(
          (w == "IMDb" && document.getElementsByClassName("meta__imdb").length != 0) ||
          ((w == "TMDbTV" || w == "TMDbMovie") && document.getElementsByClassName("meta__tmdb").length != 0) ||
          ((w == "TVDbTV" || w == "TVDbTV") && document.getElementsByClassName("meta__tvdb").length != 0) ||
          (w == "RottenTomatoes" && document.getElementsByClassName("meta__rotten").length != 0)
        )) {
        metadata_html += meta_html;
      }
      else if (meta_row === metadataType.STREAMING) {
        streaming_html += meta_html;
        streaming_found = true;
      }
      else if (meta_row === metadataType.SOCIAL) {
        social_html += meta_html;
        social_found = true;
      }
      else if (w == "RottenTomatoes" && document.getElementsByClassName("meta__rotten").length != 0) {
        document.getElementsByClassName("meta__rotten")[0].getElementsByTagName("a")[0].href = meta_url;
      }
    }
    else if (w == 'subject') {
      meta_url = metadata_map.Wikidata.uri.replace("{id}", wikidata[w].value)
      meta_row = metadata_map.Wikidata.row;
      meta_img = metadata_map.Wikidata.icon;
      meta_title = metadata_map.Wikidata.title.replace("{id}", wikidata[w].value.replace("http://www.wikidata.org/entity/", ""));
      meta_html = `
                   <li class="meta__wikidata">
                      <a class="meta-id-tag" href="${meta_url}" title="${meta_title}" target="_blank">
                         <img src="${meta_img}">
                      </a>
                   </li>`;
      document.getElementsByClassName("meta__ids")[0].innerHTML += meta_html
    }
  }
  var num_rows_added = 0;
  var rows_needed = [];
  if (streamingRow > 1 && streaming_found) {
    rows_needed.push(streamingRow)
  }
  if (socialRow > 1 && social_found) {
    rows_needed.push(socialRow)
  }
  if (customSites.length > 0 && customRow > 1) {
    rows_needed.push(customRow)
  }
  rows_needed = removeDuplicates(rows_needed);
  num_rows_added = rows_needed.length;

  custom_html = get_custom_sites_html();

  var current_row = 0;
  rows_needed.sort();

  meta_html_tag.innerHTML += metadata_html
  if (streaming_found && streamingRow == 1) {
    meta_html_tag.innerHTML += streaming_html
  }
  if (social_found && socialRow == 1) {
    meta_html_tag.innerHTML += social_html
  }
  if (custom_html.length > 0 && customRow == 1) {
    meta_html_tag.innerHTML += custom_html
  }
  var row_html = '';
  if (num_rows_added >= 1) {
    row_html = ''
    row_html += (streaming_found && streamingRow == rows_needed[current_row]) ? streaming_html : "";
    row_html += (social_found && socialRow == rows_needed[current_row]) ? social_html : "";
    row_html += (custom_html.length > 0 && customRow == rows_needed[current_row]) ? custom_html : "";
    document.getElementsByClassName("meta__ids")[0].innerHTML += break_html;
    document.getElementsByClassName("meta__ids")[0].innerHTML += row_html;
  }
  current_row++;
  if (num_rows_added >= 2) {
    row_html = "";
    row_html += (streaming_found && streamingRow == rows_needed[current_row]) ? streaming_html : "";
    row_html += (social_found && socialRow == rows_needed[current_row]) ? social_html : "";
    row_html += (custom_html.length > 0 && customRow == rows_needed[current_row]) ? custom_html : "";
    document.getElementsByClassName("meta__ids")[0].innerHTML += break_html;
    document.getElementsByClassName("meta__ids")[0].innerHTML += row_html;
  }
  current_row++;
  if (num_rows_added >= 3) {
    row_html = "";
    row_html += (streaming_found && streamingRow == rows_needed[current_row]) ? streaming_html : "";
    row_html += (social_found && socialRow == rows_needed[current_row]) ? social_html : "";
    row_html += (custom_html.length > 0 && customRow == rows_needed[current_row]) ? custom_html : "";
    document.getElementsByClassName("meta__ids")[0].innerHTML += break_html;
    document.getElementsByClassName("meta__ids")[0].innerHTML += row_html;
  }
}

function set_from_user_settings() {
  try {
    streamingRow = Number(JSON.parse(window.localStorage.getItem(`wikidata-settings-streaming`)).row)
  }
  catch {}
  try {
    socialRow = Number(JSON.parse(window.localStorage.getItem(`wikidata-settings-social`)).row)
  }
  catch {}
  try {
    customRow = JSON.parse(window.localStorage.getItem(`wikidata-settings-custom`)).row
  }
  catch {}
  try {
    customSites = JSON.parse(window.localStorage.getItem(`wikidata-settings-custom`)).sites
  }
  catch {}
  streamingRow = streamingRow >= 0 ? streamingRow : 2;
  socialRow = socialRow >= 0 ? socialRow : 3;
  customRow = customRow >= 0 ? customRow : 4;
  customSites = customSites ? customSites : '';
}

function settings_page() {
  if (streamingRow >= 0) {
    document.getElementById("streaming").value = streamingRow
  }
  if (socialRow >= 0) {
    document.getElementById("social").value = socialRow
  }
  if (customRow >= 0) {
    document.getElementById("customPos").value = customRow
  }
  if (customSites) {
    document.getElementById("custom").value = customSites
  }
}

function update_settings_page() {
  var streaming_value;
  var social_value;
  var custom_value;
  var customPos_value;
  try {
    streaming_value = document.getElementById("streaming").value >= 0 ? document.getElementById("streaming").value : 2
  }
  catch {
    streaming_value = 2;
  }
  try {
    social_value = document.getElementById("social").value >= 0 ? document.getElementById("social").value : 3
  }
  catch {
    social_value = 3;
  }
  try {
    customPos_value = document.getElementById("customPos").value >= 0 ? document.getElementById("customPos").value : 4
  }
  catch {
    customPos_value = 4;
  }
  try {
    custom_value = document.getElementById("custom").value ? document.getElementById("custom").value : ''
  }
  catch {
    custom_value = '';
  }
  window.localStorage.setItem('wikidata-settings-streaming', JSON.stringify({
    'row': streaming_value
  }));
  window.localStorage.setItem('wikidata-settings-social', JSON.stringify({
    'row': social_value
  }));
  window.localStorage.setItem('wikidata-settings-custom', JSON.stringify({
    'sites': custom_value,
    'row': customPos_value
  }));
}

const currentUrl = window.location.href;
(async function () {
  set_from_user_settings();
  if (currentUrl.endsWith("/wikidata")) {
    document.head.innerHTML = '';
    document.body.innerHTML = settings_page_body;
    settings_page();
    document.getElementById("update_wikidata").onclick = update_settings_page;
    return;
  }
  'use strict';
  imdb_id = get_imdb_id();
  if (imdb_id === '') {
    return; // No IMDb to search by
  }
  let primary_language = get_primary_language();
  // Set flex-wrap so meta doesn't overflow.
  document.getElementsByClassName("meta__ids")[0].style["flex-wrap"] = "wrap"
  var wikidata;
  try {
    let updateCache = true;
    try {
      let oldTime = JSON.parse(window.localStorage.getItem(`wikidata-${imdb_id}-time`)).time
      let difference = (new Date).getTime() - oldTime;
      updateCache = difference > CACHE_TIME
    }
    catch {}
    if (updateCache) {
      try {
        window.localStorage.removeItem(`wikidata-${imdb_id}-wikidata`);
        window.localStorage.removeItem(`wikidata-${imdb_id}-time`);
      }
      catch {}
      let wikidata_response = await search_wikidata_com(imdb_id, primary_language)
      let wikidata_json = JSON.parse(wikidata_response.response);
      wikidata = wikidata_json.results.bindings[0];
      window.localStorage.setItem(`wikidata-${imdb_id}-wikidata`, JSON.stringify(wikidata));
      window.localStorage.setItem(`wikidata-${imdb_id}-time`, JSON.stringify({
        'time': (new Date).getTime()
      }));
    }
    else {
      wikidata = JSON.parse(window.localStorage.getItem(`wikidata-${imdb_id}-wikidata`));
    }
    add_to_site(wikidata);
  }
  catch {
    //No Wikidata found, try adding custom
    let meta_html_tag = document.getElementsByClassName("meta__ids")[0];
    var custom_html = get_custom_sites_html();
    if (custom_html.length > 0 && customRow == 1) {
      meta_html_tag.innerHTML += custom_html
    }
    else if (custom_html.length > 0 && customRow >= 1) {
      document.getElementsByClassName("meta__ids")[0].innerHTML += `<div style="flex-basis: 100%; height: 0;"></div>`;
      document.getElementsByClassName("meta__ids")[0].innerHTML += custom_html;
    }
  }
})();