const allLinks = [...document.getElementsByTagName('a')]; const bodyInner = document.querySelector('.inner'); const tableElem = document.querySelector('table'); const infoRegex = /(-thumb)?\.(jpg|nfo|srt)$/; let infoFiles = 0; let infoFilesAccounted = 0; const arrow = ` ` function reencodeURI(uri) { return encodeURIComponent(decodeURIComponent(uri)); } function getHref(tag) { return tag.getAttribute('href'); } function findByHref(href) { return allLinks.filter((link) => { return reencodeURI(getHref(link)) === reencodeURI(href) }); } function findByInfoFile(infoFile) { const found = ['avi', 'mp4', 'mkv'].reduce((last, current) => { const element = findByHref(infoFile.replace(infoRegex, '.' + current))[0]; return element ? element : last; }, null); return found; } function deduplicateJointEpisode(textContent) { return new Promise((resolve, reject) => { const lns = textContent.split('\n'); const result = []; let passed = 0; for (const line of lns) { if (line.startsWith('')) { if (passed === 1) { break; } passed = 1; } result.push(line); } resolve(result.join('\n')); }); } const parser = new window.DOMParser(); async function createXMLRequest(nfo) { return fetch(nfo) .then((response) => response.text()) .then((text) => deduplicateJointEpisode(text)) .then((str) => parser.parseFromString(str, 'text/xml')); } function createOrImproveMovieMeta(original, thumbnail, nfo) { let imgTag = ``; let movieTitle = `
${original.innerText}
`; let movieDescription = `
`; let movieButtons = `
`; if (!thumbnail && !nfo) { return; } if (original.parentElement.classList.contains('enhanced')) { imgTag = original.querySelector('.thumbnail'); movieTitle = original.querySelector('.movie-title'); movieDescription = original.querySelector('.movie-description'); movieButtons = original.querySelector('.movie-buttons'); if (thumbnail) { imgTag.src = reencodeURI(getHref(thumbnail)); } } else { original.classList.add('movie'); original.innerHTML = imgTag + '
' + movieTitle + movieDescription + movieButtons + '
'; original.parentElement.classList.add('enhanced'); // quick innerHTML hack to create elements here lol imgTag = original.querySelector('.thumbnail'); movieTitle = original.querySelector('.movie-title'); movieDescription = original.querySelector('.movie-description'); movieButtons = original.querySelector('.movie-buttons'); } if (nfo) { createXMLRequest(reencodeURI(getHref(nfo))) .then((content) => { infoFilesAccounted += 1; if (!content) { return; } const titleEl = content.querySelector('title'); const plotEl = content.querySelector('plot'); const airedEl = content.querySelector('aired'); const premieredEl = content.querySelector('premiered'); const imdbEl = content.querySelector('uniqueid[type="imdb"]'); const tvdbEl = content.querySelector('uniqueid[type="tvdb"]'); if (!titleEl || !plotEl) { return; } if (airedEl || premieredEl) { const timestamp = original.parentElement.parentElement.querySelector('.timestamp'); if (timestamp) { timestamp.innerText = (airedEl || premieredEl).textContent; } } const episode = content.querySelector('episode'); const season = content.querySelector('season'); let title = titleEl.textContent if (episode && season) { title = `(S${season.textContent} E${episode.textContent}) ` + title; original.parentElement.setAttribute('data-episode', episode.textContent); } movieTitle.innerText = title; movieDescription.innerText = plotEl.textContent; if (imdbEl) { const imlink = document.createElement('a'); imlink.target = '_blank'; imlink.innerText = 'IMDb'; imlink.href = `https://www.imdb.com/title/${imdbEl.textContent}`; movieButtons.appendChild(imlink); } if (tvdbEl) { const tvlink = document.createElement('a'); tvlink.target = '_blank'; tvlink.innerText = 'The TVDB'; tvlink.href = `http://www.thetvdb.com/?tab=series&id=${tvdbEl.textContent}`; movieButtons.appendChild(tvlink); } if (getHref(nfo) !== 'tvshow.nfo' && navigator && navigator.clipboard && navigator.clipboard.writeText) { const copybutton = document.createElement('button'); copybutton.innerText = 'Copy direct URL'; movieButtons.appendChild(copybutton); copybutton.addEventListener('click', (e) => { e.preventDefault(); const popup = document.createElement('div'); popup.className = 'tooltip'; popup.innerText = 'Copied!'; const url = window.location.href + reencodeURI(getHref(original)); navigator.clipboard.writeText(url).then(() => { copybutton.appendChild(popup); setTimeout(() => copybutton.removeChild(popup), 1000); }); }); } }) .catch((e) => { infoFilesAccounted += 1; console.error(e); }); } } function waitUntilInfoComplete() { return new Promise((resolve, reject) => { if (infoFilesAccounted >= infoFiles) { resolve(true); return; } let seconds = 0; const accountWait = setInterval(() => { if (infoFilesAccounted >= infoFiles || seconds > 5000) { clearInterval(accountWait); resolve(true); return; } seconds += 500; }, 500); }); } const getCellValue = (tr, idx) => tr.children[idx].getAttribute('data-episode') || tr.children[idx].innerText || tr.children[idx].textContent; const comparer = (idx, asc) => (a, b) => ((v1, v2) => v1 !== '' && v2 !== '' && !isNaN(v1) && !isNaN(v2) ? v1 - v2 : v1.toString().localeCompare(v2) )(getCellValue(asc ? a : b, idx), getCellValue(asc ? b : a, idx)); const sortTableValues = (index) => Array.from(tableElem.querySelectorAll('tr:nth-child(n+2):not(.btn-back)')) .sort(comparer(index, this.asc = !this.asc)) .forEach((tr) => tableElem.appendChild(tr)); let tvShow; function accountForMetadata() { allLinks.forEach((link) => { const url = getHref(link); if (url.match(infoRegex)) { link.parentElement.parentElement.style.display = 'none'; let findOriginal if (url === 'tvshow.nfo' || url === 'poster.jpg') { if (url === 'poster.jpg' && !allLinks.find((found) => getHref(found) === 'tvshow.nfo')) { findOriginal = document.querySelector('a:not([href=".."])'); } else { if (tvShow) { findOriginal = tvShow; } else { const wrapper = document.createElement('div'); wrapper.className = 'highlight'; tvShow = document.createElement('a'); tvShow.className = 'movie'; wrapper.appendChild(tvShow); bodyInner.insertBefore(wrapper, tableElem); findOriginal = tvShow; } } } else if (url.startsWith('season') && url.includes('poster')) { const sn = url.match(/season(\d+)/); if (sn) { findOriginal = allLinks.find((allurl) => allurl.innerText.includes(parseInt(sn[1], 10))); } else if (url.includes('specials')) { findOriginal = allLinks.find((allurl) => allurl.innerText.toLowerCase().startsWith('special')); } } else { findOriginal = findByInfoFile(url); } if (findOriginal) { if (url.match(/.nfo$/)) { infoFiles += 1; } createOrImproveMovieMeta(findOriginal, url.match(/.jpg$/) ? link : undefined, url.match(/.nfo$/) ? link : undefined, ); } } if (url === '..') { link.parentElement.parentElement.classList.add('btn-back'); link.innerHTML = arrow + 'Back (..)'; } }); waitUntilInfoComplete().then(() => sortTableValues(0)); } function createToggleCheckbox(field, value, description) { const form = document.createElement('div'); const checkbox = document.createElement('input'); const label = document.createElement('label'); form.className = 'form ' + field; checkbox.type = 'checkbox'; checkbox.id = field label.innerText = description; label.setAttribute('for', field); form.appendChild(checkbox); form.appendChild(label) bodyInner.appendChild(form); checkbox.addEventListener('change', (e) => { if (checkbox.checked) { window.localStorage.setItem(field, 'true'); } else { window.localStorage.removeItem(field); } window.location.reload(); }); checkbox.checked = value === 'true'; } if ('localStorage' in window) { const plainMode = window.localStorage.getItem('plainMode'); if (plainMode !== 'true') { accountForMetadata(); } createToggleCheckbox('plainMode', plainMode, 'Disable metadata / display plain index listing (less bandwidth required)'); } else { accountForMetadata(); } document.querySelectorAll('th').forEach((th) => th.addEventListener('click', (() => { sortTableValues(Array.from(th.parentNode.children).indexOf(th)); })));