diff --git a/autoindex.css b/autoindex.css
index a761c89..5c0a69a 100644
--- a/autoindex.css
+++ b/autoindex.css
@@ -132,6 +132,41 @@ button .tooltip {
.timestamp .original {
color: gray;
}
+#dirname {
+ padding: 1rem;
+}
+.directory#dirname {
+ padding: 0;
+ display: flex;
+ flex-wrap: wrap;
+ margin-bottom: 0.5rem;
+}
+.directory-link {
+ padding: 0.5rem 1rem 0.5rem 3rem;
+ background-color: #05275b;
+ display: flex;
+ position: relative;
+ text-decoration: none;
+ white-space: nowrap;
+}
+.directory-link:nth-child(2n+1) {
+ background-color: #0b3a80;
+}
+.directory-link::after {
+ content: '';
+ width: 0;
+ height: 0;
+ border-top: 29px solid transparent;
+ border-bottom: 29px solid transparent;
+ border-left: 29px solid #05275b;
+ position: absolute;
+ right: -29px;
+ top: 0;
+ z-index: 10;
+}
+.directory-link:nth-child(2n+1)::after {
+ border-left-color: #0b3a80;
+}
@media screen and (max-width: 1200px) {
.inner {
width: 100%;
diff --git a/autoindex.js b/autoindex.js
index 563898c..5a5f18f 100644
--- a/autoindex.js
+++ b/autoindex.js
@@ -1,6 +1,7 @@
const allLinks = [...document.getElementsByTagName('a')];
const bodyInner = document.querySelector('.inner');
const tableElem = document.querySelector('table');
+const dirnameElem = document.querySelector('#dirname');
const infoRegex = /(-thumb)?\.(jpg|nfo|srt)$/;
let infoFiles = 0;
let infoFilesAccounted = 0;
@@ -31,19 +32,21 @@ function getHref(tag) {
function findByHref(href) {
return allLinks.filter((link) => {
- return reencodeURI(getHref(link)) === reencodeURI(href)
+ return reencodeURI(getHref(link)) === reencodeURI(href);
});
}
function findWholeNumberInText(text, matchNumber) {
const numbers = text.match(/\d+/g);
- return numbers
- && numbers.length
- && numbers.map((x) => parseInt(x, 10)).some((number) => number === matchNumber);
+ return (
+ numbers &&
+ numbers.length &&
+ numbers.map((x) => parseInt(x, 10)).some((number) => number === matchNumber)
+ );
}
function findByInfoFile(infoFile) {
- const found = ['mkv', 'mp4', 'avi'].reduce((last, current) => {
+ const found = ['mkv', 'mp4', 'avi', 'webm'].reduce((last, current) => {
const element = findByHref(infoFile.replace(infoRegex, '.' + current))[0];
return element || last;
}, null);
@@ -53,9 +56,20 @@ function findByInfoFile(infoFile) {
const pZ = (n) => n.toString().padStart(2, '0');
function restampDateTime(datetime, includeTime = true) {
const date = new Date(datetime);
- const [month, day, year] = [date.getMonth() + 1, date.getDate(), date.getFullYear()];
- const [hour, minutes, seconds] = [date.getHours(), date.getMinutes(), date.getSeconds()];
- return `${pZ(day)}/${pZ(month)}/${year}${includeTime ? ` ${pZ(hour)}:${pZ(minutes)}:${pZ(seconds)}` : ''}`;
+ const [month, day, year] = [
+ date.getMonth() + 1,
+ date.getDate(),
+ date.getFullYear(),
+ ];
+ const [hour, minutes, seconds] = [
+ date.getHours(),
+ date.getMinutes(),
+ date.getSeconds(),
+ ];
+ return `${year}/${pZ(month)}/${pZ(day)}${
+ includeTime ? ` ${pZ(hour)}:${pZ(minutes)}:${pZ(seconds)}` : ''
+ }`;
+ // return `${pZ(day)}/${pZ(month)}/${year}${includeTime ? ` ${pZ(hour)}:${pZ(minutes)}:${pZ(seconds)}` : ''}`;
}
function deduplicateJointEpisode(textContent) {
@@ -86,7 +100,12 @@ async function createXMLRequest(nfo) {
// Fill metadata boxes, generate buttons
function fillMeta(
- content, original, nfo, movieTitle, movieDescription, movieButtons
+ content,
+ original,
+ nfo,
+ movieTitle,
+ movieDescription,
+ movieButtons
) {
infoFilesAccounted += 1;
if (!content) {
@@ -105,10 +124,14 @@ function fillMeta(
}
if (airedEl || premieredEl) {
- const timestamp = original.parentElement.parentElement.querySelector('.timestamp');
+ const timestamp =
+ original.parentElement.parentElement.querySelector('.timestamp');
if (timestamp) {
const original = timestamp.innerText;
- const airdate = restampDateTime((airedEl || premieredEl).textContent, false);
+ const airdate = restampDateTime(
+ (airedEl || premieredEl).textContent,
+ false
+ );
const wrapper = `${airdate}`;
const originalWrapper = ` (${original})`;
timestamp.innerHTML = wrapper + originalWrapper;
@@ -117,7 +140,7 @@ function fillMeta(
const episode = content.querySelector('episode');
const season = content.querySelector('season');
- let title = titleEl.textContent
+ let title = titleEl.textContent;
if (episode && season) {
title = `(S${season.textContent} E${episode.textContent}) ` + title;
original.parentElement.setAttribute('data-episode', episode.textContent);
@@ -144,10 +167,10 @@ function fillMeta(
// Add "copy direct url" button
if (
- getHref(nfo) !== 'tvshow.nfo'
- && navigator
- && navigator.clipboard
- && navigator.clipboard.writeText
+ getHref(nfo) !== 'tvshow.nfo' &&
+ navigator &&
+ navigator.clipboard &&
+ navigator.clipboard.writeText
) {
const copybutton = document.createElement('button');
copybutton.innerText = 'Copy direct URL';
@@ -169,7 +192,9 @@ function fillMeta(
// Replace list entry with a metadata box
// Either create or populate
function createOrImproveMovieMeta(original, thumbnail, nfo) {
- let imgTag = ``;
+ let imgTag = ``;
let movieTitle = `
${original.innerText}
`;
let movieDescription = ``;
let movieButtons = ``;
@@ -188,7 +213,7 @@ function createOrImproveMovieMeta(original, thumbnail, nfo) {
}
} else {
original.classList.add('movie');
- original.innerHTML = imgTag + '' + movieTitle + movieDescription + movieButtons + '
';
+ original.innerHTML = `${imgTag}${movieTitle}${movieDescription}${movieButtons}
`;
original.parentElement.classList.add('enhanced');
// quick innerHTML hack to create elements here lol
imgTag = original.querySelector('.thumbnail');
@@ -200,7 +225,16 @@ function createOrImproveMovieMeta(original, thumbnail, nfo) {
// Fetch metadata from the nfo file and populate the info box
if (nfo) {
createXMLRequest(reencodeURI(getHref(nfo)))
- .then((content) => fillMeta(content, original, nfo, movieTitle, movieDescription, movieButtons))
+ .then((content) =>
+ fillMeta(
+ content,
+ original,
+ nfo,
+ movieTitle,
+ movieDescription,
+ movieButtons
+ )
+ )
.catch((e) => {
infoFilesAccounted += 1;
console.error(e);
@@ -236,20 +270,25 @@ function waitUntilInfoComplete() {
// Get episode name or name of file
const getCellValue = (tr, idx) =>
- tr.children[idx].getAttribute('data-episode')
- || tr.children[idx].innerText
- || tr.children[idx].textContent;
+ tr.children[idx].getAttribute('data-episode') ||
+ tr.children[idx].innerText ||
+ tr.children[idx].textContent;
// Compare numeric or (locale-aware, numeric-aware) by string values
-const comparer = (idx, asc) => (a, b) => ((v1, v2) =>
- v1 !== '' && v2 !== '' && !isNaN(v1) && !isNaN(v2) ? v1 - v2 : v1.toString().localeCompare(v2, 'et', { numeric: true })
- )(getCellValue(asc ? a : b, idx), getCellValue(asc ? b : a, idx));
+const comparer = (idx, asc) => (a, b) =>
+ ((v1, v2) =>
+ v1 !== '' && v2 !== '' && !isNaN(v1) && !isNaN(v2)
+ ? v1 - v2
+ : v1.toString().localeCompare(v2, 'et', { numeric: true }))(
+ getCellValue(asc ? a : b, idx),
+ getCellValue(asc ? b : a, idx)
+ );
// Table sort by column index
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));
+ .sort(comparer(index, (this.asc = !this.asc)))
+ .forEach((tr) => tableElem.appendChild(tr));
let tvShow;
function accountForMetadata() {
@@ -260,10 +299,13 @@ function accountForMetadata() {
if (url.match(infoRegex)) {
// Hide metadata file
link.parentElement.parentElement.style.display = 'none';
- let findOriginal
+ let findOriginal;
// Place TV show information in the beginning of the table
if (url === 'tvshow.nfo' || url === 'poster.jpg') {
- if (url === 'poster.jpg' && !allLinks.find((found) => getHref(found) === 'tvshow.nfo')) {
+ if (
+ url === 'poster.jpg' &&
+ !allLinks.find((found) => getHref(found) === 'tvshow.nfo')
+ ) {
findOriginal = document.querySelector('a:not([href=".."])');
} else {
if (tvShow) {
@@ -278,25 +320,29 @@ function accountForMetadata() {
findOriginal = tvShow;
}
}
- // Give posters to seasons
+ // Give posters to seasons
} else if (url.startsWith('season') && url.includes('poster')) {
// Find season by number
const sn = url.match(/season(\d+)/);
if (sn) {
- findOriginal = allLinks.find(
- ({ innerText }) => {
- return innerText.endsWith('/')
- && findWholeNumberInText(innerText, parseInt(sn[1], 10));
- });
- // Find specials folder
+ findOriginal = allLinks.find(({ innerText }) => {
+ return (
+ innerText.endsWith('/') &&
+ findWholeNumberInText(innerText, parseInt(sn[1], 10))
+ );
+ });
+ // Find specials folder
} else if (url.includes('specials')) {
findOriginal = allLinks.find(({ innerText }) => {
const matchAgainst = innerText.toLowerCase();
- return (matchAgainst.startsWith('specials') || matchAgainst.startsWith('extras'))
- && innerText.endsWith('/');
+ return (
+ (matchAgainst.startsWith('specials') ||
+ matchAgainst.startsWith('extras')) &&
+ innerText.endsWith('/')
+ );
});
}
- // Find the video file associated with this info file
+ // Find the video file associated with this info file
} else {
findOriginal = findByInfoFile(url);
}
@@ -306,9 +352,10 @@ function accountForMetadata() {
if (url.match(/.nfo$/)) {
infoFiles += 1;
}
- createOrImproveMovieMeta(findOriginal,
+ createOrImproveMovieMeta(
+ findOriginal,
url.match(/.jpg$/) ? link : undefined,
- url.match(/.nfo$/) ? link : undefined,
+ url.match(/.nfo$/) ? link : undefined
);
}
}
@@ -328,11 +375,11 @@ function createToggleCheckbox(field, value, description) {
const label = document.createElement('label');
form.className = 'form ' + field;
checkbox.type = 'checkbox';
- checkbox.id = field
+ checkbox.id = field;
label.innerText = description;
label.setAttribute('for', field);
form.appendChild(checkbox);
- form.appendChild(label)
+ form.appendChild(label);
bodyInner.appendChild(form);
checkbox.addEventListener('change', (e) => {
@@ -346,18 +393,42 @@ function createToggleCheckbox(field, value, description) {
checkbox.checked = value === 'true';
}
+function createDirTree() {
+ dirnameElem.classList.add('directory');
+ const dirSplit = dirnameElem.innerText.split('/').slice(1).filter((item) => !!item);
+ dirSplit.unshift('');
+ dirnameElem.innerHTML = '';
+ let lastPath = '';
+ for (const dir of dirSplit) {
+ lastPath += dir + '/';
+ const atag = document.createElement('a');
+ atag.innerText = dir;
+ atag.classList.add('directory-link');
+ atag.href = lastPath;
+ dirnameElem.appendChild(atag);
+ }
+}
+
if ('localStorage' in window) {
const plainMode = window.localStorage.getItem('plainMode');
if (plainMode !== 'true') {
timestampify();
accountForMetadata();
}
- createToggleCheckbox('plainMode', plainMode, 'Disable metadata / display plain index listing (less bandwidth required)');
+ createToggleCheckbox(
+ 'plainMode',
+ plainMode,
+ 'Disable metadata / display plain index listing (less bandwidth required)'
+ );
} else {
timestampify();
accountForMetadata();
}
-document.querySelectorAll('th').forEach((th) => th.addEventListener('click', (() => {
- sortTableValues(Array.from(th.parentNode.children).indexOf(th));
-})));
+document.querySelectorAll('th').forEach((th) =>
+ th.addEventListener('click', () => {
+ sortTableValues(Array.from(th.parentNode.children).indexOf(th));
+ })
+);
+
+createDirTree();