breadcrumbs
This commit is contained in:
parent
0f6a5c3a9e
commit
24badd5cb8
@ -132,6 +132,41 @@ button .tooltip {
|
|||||||
.timestamp .original {
|
.timestamp .original {
|
||||||
color: gray;
|
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) {
|
@media screen and (max-width: 1200px) {
|
||||||
.inner {
|
.inner {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
165
autoindex.js
165
autoindex.js
@ -1,6 +1,7 @@
|
|||||||
const allLinks = [...document.getElementsByTagName('a')];
|
const allLinks = [...document.getElementsByTagName('a')];
|
||||||
const bodyInner = document.querySelector('.inner');
|
const bodyInner = document.querySelector('.inner');
|
||||||
const tableElem = document.querySelector('table');
|
const tableElem = document.querySelector('table');
|
||||||
|
const dirnameElem = document.querySelector('#dirname');
|
||||||
const infoRegex = /(-thumb)?\.(jpg|nfo|srt)$/;
|
const infoRegex = /(-thumb)?\.(jpg|nfo|srt)$/;
|
||||||
let infoFiles = 0;
|
let infoFiles = 0;
|
||||||
let infoFilesAccounted = 0;
|
let infoFilesAccounted = 0;
|
||||||
@ -31,19 +32,21 @@ function getHref(tag) {
|
|||||||
|
|
||||||
function findByHref(href) {
|
function findByHref(href) {
|
||||||
return allLinks.filter((link) => {
|
return allLinks.filter((link) => {
|
||||||
return reencodeURI(getHref(link)) === reencodeURI(href)
|
return reencodeURI(getHref(link)) === reencodeURI(href);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function findWholeNumberInText(text, matchNumber) {
|
function findWholeNumberInText(text, matchNumber) {
|
||||||
const numbers = text.match(/\d+/g);
|
const numbers = text.match(/\d+/g);
|
||||||
return numbers
|
return (
|
||||||
&& numbers.length
|
numbers &&
|
||||||
&& numbers.map((x) => parseInt(x, 10)).some((number) => number === matchNumber);
|
numbers.length &&
|
||||||
|
numbers.map((x) => parseInt(x, 10)).some((number) => number === matchNumber)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function findByInfoFile(infoFile) {
|
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];
|
const element = findByHref(infoFile.replace(infoRegex, '.' + current))[0];
|
||||||
return element || last;
|
return element || last;
|
||||||
}, null);
|
}, null);
|
||||||
@ -53,9 +56,20 @@ function findByInfoFile(infoFile) {
|
|||||||
const pZ = (n) => n.toString().padStart(2, '0');
|
const pZ = (n) => n.toString().padStart(2, '0');
|
||||||
function restampDateTime(datetime, includeTime = true) {
|
function restampDateTime(datetime, includeTime = true) {
|
||||||
const date = new Date(datetime);
|
const date = new Date(datetime);
|
||||||
const [month, day, year] = [date.getMonth() + 1, date.getDate(), date.getFullYear()];
|
const [month, day, year] = [
|
||||||
const [hour, minutes, seconds] = [date.getHours(), date.getMinutes(), date.getSeconds()];
|
date.getMonth() + 1,
|
||||||
return `${pZ(day)}/${pZ(month)}/${year}${includeTime ? ` ${pZ(hour)}:${pZ(minutes)}:${pZ(seconds)}` : ''}`;
|
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) {
|
function deduplicateJointEpisode(textContent) {
|
||||||
@ -86,7 +100,12 @@ async function createXMLRequest(nfo) {
|
|||||||
|
|
||||||
// Fill metadata boxes, generate buttons
|
// Fill metadata boxes, generate buttons
|
||||||
function fillMeta(
|
function fillMeta(
|
||||||
content, original, nfo, movieTitle, movieDescription, movieButtons
|
content,
|
||||||
|
original,
|
||||||
|
nfo,
|
||||||
|
movieTitle,
|
||||||
|
movieDescription,
|
||||||
|
movieButtons
|
||||||
) {
|
) {
|
||||||
infoFilesAccounted += 1;
|
infoFilesAccounted += 1;
|
||||||
if (!content) {
|
if (!content) {
|
||||||
@ -105,10 +124,14 @@ function fillMeta(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (airedEl || premieredEl) {
|
if (airedEl || premieredEl) {
|
||||||
const timestamp = original.parentElement.parentElement.querySelector('.timestamp');
|
const timestamp =
|
||||||
|
original.parentElement.parentElement.querySelector('.timestamp');
|
||||||
if (timestamp) {
|
if (timestamp) {
|
||||||
const original = timestamp.innerText;
|
const original = timestamp.innerText;
|
||||||
const airdate = restampDateTime((airedEl || premieredEl).textContent, false);
|
const airdate = restampDateTime(
|
||||||
|
(airedEl || premieredEl).textContent,
|
||||||
|
false
|
||||||
|
);
|
||||||
const wrapper = `<span class="airdate">${airdate}</span>`;
|
const wrapper = `<span class="airdate">${airdate}</span>`;
|
||||||
const originalWrapper = ` <span class="original">(${original})</span>`;
|
const originalWrapper = ` <span class="original">(${original})</span>`;
|
||||||
timestamp.innerHTML = wrapper + originalWrapper;
|
timestamp.innerHTML = wrapper + originalWrapper;
|
||||||
@ -117,7 +140,7 @@ function fillMeta(
|
|||||||
|
|
||||||
const episode = content.querySelector('episode');
|
const episode = content.querySelector('episode');
|
||||||
const season = content.querySelector('season');
|
const season = content.querySelector('season');
|
||||||
let title = titleEl.textContent
|
let title = titleEl.textContent;
|
||||||
if (episode && season) {
|
if (episode && season) {
|
||||||
title = `(S${season.textContent} E${episode.textContent}) ` + title;
|
title = `(S${season.textContent} E${episode.textContent}) ` + title;
|
||||||
original.parentElement.setAttribute('data-episode', episode.textContent);
|
original.parentElement.setAttribute('data-episode', episode.textContent);
|
||||||
@ -144,10 +167,10 @@ function fillMeta(
|
|||||||
|
|
||||||
// Add "copy direct url" button
|
// Add "copy direct url" button
|
||||||
if (
|
if (
|
||||||
getHref(nfo) !== 'tvshow.nfo'
|
getHref(nfo) !== 'tvshow.nfo' &&
|
||||||
&& navigator
|
navigator &&
|
||||||
&& navigator.clipboard
|
navigator.clipboard &&
|
||||||
&& navigator.clipboard.writeText
|
navigator.clipboard.writeText
|
||||||
) {
|
) {
|
||||||
const copybutton = document.createElement('button');
|
const copybutton = document.createElement('button');
|
||||||
copybutton.innerText = 'Copy direct URL';
|
copybutton.innerText = 'Copy direct URL';
|
||||||
@ -169,7 +192,9 @@ function fillMeta(
|
|||||||
// Replace list entry with a metadata box
|
// Replace list entry with a metadata box
|
||||||
// Either create or populate
|
// Either create or populate
|
||||||
function createOrImproveMovieMeta(original, thumbnail, nfo) {
|
function createOrImproveMovieMeta(original, thumbnail, nfo) {
|
||||||
let imgTag = `<img class="thumbnail" src="${thumbnail ? reencodeURI(getHref(thumbnail)) : ''}"/>`;
|
let imgTag = `<img class="thumbnail" src="${
|
||||||
|
thumbnail ? reencodeURI(getHref(thumbnail)) : ''
|
||||||
|
}"/>`;
|
||||||
let movieTitle = `<div class="movie-title">${original.innerText}</div>`;
|
let movieTitle = `<div class="movie-title">${original.innerText}</div>`;
|
||||||
let movieDescription = `<div class="movie-description"></div>`;
|
let movieDescription = `<div class="movie-description"></div>`;
|
||||||
let movieButtons = `<div class="movie-buttons"></div>`;
|
let movieButtons = `<div class="movie-buttons"></div>`;
|
||||||
@ -188,7 +213,7 @@ function createOrImproveMovieMeta(original, thumbnail, nfo) {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
original.classList.add('movie');
|
original.classList.add('movie');
|
||||||
original.innerHTML = imgTag + '<div class="meta-wrap">' + movieTitle + movieDescription + movieButtons + '</div>';
|
original.innerHTML = `${imgTag}<div class="meta-wrap">${movieTitle}${movieDescription}${movieButtons}</div>`;
|
||||||
original.parentElement.classList.add('enhanced');
|
original.parentElement.classList.add('enhanced');
|
||||||
// quick innerHTML hack to create elements here lol
|
// quick innerHTML hack to create elements here lol
|
||||||
imgTag = original.querySelector('.thumbnail');
|
imgTag = original.querySelector('.thumbnail');
|
||||||
@ -200,7 +225,16 @@ function createOrImproveMovieMeta(original, thumbnail, nfo) {
|
|||||||
// Fetch metadata from the nfo file and populate the info box
|
// Fetch metadata from the nfo file and populate the info box
|
||||||
if (nfo) {
|
if (nfo) {
|
||||||
createXMLRequest(reencodeURI(getHref(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) => {
|
.catch((e) => {
|
||||||
infoFilesAccounted += 1;
|
infoFilesAccounted += 1;
|
||||||
console.error(e);
|
console.error(e);
|
||||||
@ -236,20 +270,25 @@ function waitUntilInfoComplete() {
|
|||||||
|
|
||||||
// Get episode name or name of file
|
// Get episode name or name of file
|
||||||
const getCellValue = (tr, idx) =>
|
const getCellValue = (tr, idx) =>
|
||||||
tr.children[idx].getAttribute('data-episode')
|
tr.children[idx].getAttribute('data-episode') ||
|
||||||
|| tr.children[idx].innerText
|
tr.children[idx].innerText ||
|
||||||
|| tr.children[idx].textContent;
|
tr.children[idx].textContent;
|
||||||
|
|
||||||
// Compare numeric or (locale-aware, numeric-aware) by string values
|
// Compare numeric or (locale-aware, numeric-aware) by string values
|
||||||
const comparer = (idx, asc) => (a, b) => ((v1, v2) =>
|
const comparer = (idx, asc) => (a, b) =>
|
||||||
v1 !== '' && v2 !== '' && !isNaN(v1) && !isNaN(v2) ? v1 - v2 : v1.toString().localeCompare(v2, 'et', { numeric: true })
|
((v1, v2) =>
|
||||||
)(getCellValue(asc ? a : b, idx), getCellValue(asc ? b : a, idx));
|
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
|
// Table sort by column index
|
||||||
const sortTableValues = (index) =>
|
const sortTableValues = (index) =>
|
||||||
Array.from(tableElem.querySelectorAll('tr:nth-child(n+2):not(.btn-back)'))
|
Array.from(tableElem.querySelectorAll('tr:nth-child(n+2):not(.btn-back)'))
|
||||||
.sort(comparer(index, this.asc = !this.asc))
|
.sort(comparer(index, (this.asc = !this.asc)))
|
||||||
.forEach((tr) => tableElem.appendChild(tr));
|
.forEach((tr) => tableElem.appendChild(tr));
|
||||||
|
|
||||||
let tvShow;
|
let tvShow;
|
||||||
function accountForMetadata() {
|
function accountForMetadata() {
|
||||||
@ -260,10 +299,13 @@ function accountForMetadata() {
|
|||||||
if (url.match(infoRegex)) {
|
if (url.match(infoRegex)) {
|
||||||
// Hide metadata file
|
// Hide metadata file
|
||||||
link.parentElement.parentElement.style.display = 'none';
|
link.parentElement.parentElement.style.display = 'none';
|
||||||
let findOriginal
|
let findOriginal;
|
||||||
// Place TV show information in the beginning of the table
|
// Place TV show information in the beginning of the table
|
||||||
if (url === 'tvshow.nfo' || url === 'poster.jpg') {
|
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=".."])');
|
findOriginal = document.querySelector('a:not([href=".."])');
|
||||||
} else {
|
} else {
|
||||||
if (tvShow) {
|
if (tvShow) {
|
||||||
@ -278,25 +320,29 @@ function accountForMetadata() {
|
|||||||
findOriginal = tvShow;
|
findOriginal = tvShow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Give posters to seasons
|
// Give posters to seasons
|
||||||
} else if (url.startsWith('season') && url.includes('poster')) {
|
} else if (url.startsWith('season') && url.includes('poster')) {
|
||||||
// Find season by number
|
// Find season by number
|
||||||
const sn = url.match(/season(\d+)/);
|
const sn = url.match(/season(\d+)/);
|
||||||
if (sn) {
|
if (sn) {
|
||||||
findOriginal = allLinks.find(
|
findOriginal = allLinks.find(({ innerText }) => {
|
||||||
({ innerText }) => {
|
return (
|
||||||
return innerText.endsWith('/')
|
innerText.endsWith('/') &&
|
||||||
&& findWholeNumberInText(innerText, parseInt(sn[1], 10));
|
findWholeNumberInText(innerText, parseInt(sn[1], 10))
|
||||||
});
|
);
|
||||||
// Find specials folder
|
});
|
||||||
|
// Find specials folder
|
||||||
} else if (url.includes('specials')) {
|
} else if (url.includes('specials')) {
|
||||||
findOriginal = allLinks.find(({ innerText }) => {
|
findOriginal = allLinks.find(({ innerText }) => {
|
||||||
const matchAgainst = innerText.toLowerCase();
|
const matchAgainst = innerText.toLowerCase();
|
||||||
return (matchAgainst.startsWith('specials') || matchAgainst.startsWith('extras'))
|
return (
|
||||||
&& innerText.endsWith('/');
|
(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 {
|
} else {
|
||||||
findOriginal = findByInfoFile(url);
|
findOriginal = findByInfoFile(url);
|
||||||
}
|
}
|
||||||
@ -306,9 +352,10 @@ function accountForMetadata() {
|
|||||||
if (url.match(/.nfo$/)) {
|
if (url.match(/.nfo$/)) {
|
||||||
infoFiles += 1;
|
infoFiles += 1;
|
||||||
}
|
}
|
||||||
createOrImproveMovieMeta(findOriginal,
|
createOrImproveMovieMeta(
|
||||||
|
findOriginal,
|
||||||
url.match(/.jpg$/) ? link : undefined,
|
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');
|
const label = document.createElement('label');
|
||||||
form.className = 'form ' + field;
|
form.className = 'form ' + field;
|
||||||
checkbox.type = 'checkbox';
|
checkbox.type = 'checkbox';
|
||||||
checkbox.id = field
|
checkbox.id = field;
|
||||||
label.innerText = description;
|
label.innerText = description;
|
||||||
label.setAttribute('for', field);
|
label.setAttribute('for', field);
|
||||||
form.appendChild(checkbox);
|
form.appendChild(checkbox);
|
||||||
form.appendChild(label)
|
form.appendChild(label);
|
||||||
bodyInner.appendChild(form);
|
bodyInner.appendChild(form);
|
||||||
|
|
||||||
checkbox.addEventListener('change', (e) => {
|
checkbox.addEventListener('change', (e) => {
|
||||||
@ -346,18 +393,42 @@ function createToggleCheckbox(field, value, description) {
|
|||||||
checkbox.checked = value === 'true';
|
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) {
|
if ('localStorage' in window) {
|
||||||
const plainMode = window.localStorage.getItem('plainMode');
|
const plainMode = window.localStorage.getItem('plainMode');
|
||||||
if (plainMode !== 'true') {
|
if (plainMode !== 'true') {
|
||||||
timestampify();
|
timestampify();
|
||||||
accountForMetadata();
|
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 {
|
} else {
|
||||||
timestampify();
|
timestampify();
|
||||||
accountForMetadata();
|
accountForMetadata();
|
||||||
}
|
}
|
||||||
|
|
||||||
document.querySelectorAll('th').forEach((th) => th.addEventListener('click', (() => {
|
document.querySelectorAll('th').forEach((th) =>
|
||||||
sortTableValues(Array.from(th.parentNode.children).indexOf(th));
|
th.addEventListener('click', () => {
|
||||||
})));
|
sortTableValues(Array.from(th.parentNode.children).indexOf(th));
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
createDirTree();
|
||||||
|
Loading…
Reference in New Issue
Block a user