diff --git a/autoindex.css b/autoindex.css new file mode 100644 index 0000000..c008eee --- /dev/null +++ b/autoindex.css @@ -0,0 +1,192 @@ +/* ========================= + Nginx AutoIndex Theme + ========================= */ + +:root { + --bg: #0f111a; + --bg-soft: #151926; + --row-hover: #1f2440; + --text: #e6e6eb; + --muted: #8b93a7; + --accent: #7aa2f7; + --folder: #9ece6a; + --parent: #f7768e; + --border: #2a2f4a; +} + +* { + box-sizing: border-box; +} + +html, body { + margin: 0; + padding: 0; + background: linear-gradient(135deg, #0f111a, #121626); + color: var(--text); + font-family: system-ui, -apple-system, BlinkMacSystemFont, + "Segoe UI", Roboto, Ubuntu, Cantarell, "Helvetica Neue", + Arial, sans-serif; + font-size: 14px; +} + +/* Title */ +h1 { + margin: 0; + padding: 18px 24px; + font-size: 18px; + font-weight: 600; + background: var(--bg-soft); + border-bottom: 1px solid var(--border); +} + +/* Listing container */ +pre { + margin: 0; + padding: 12px 0 24px; + white-space: pre; + overflow-x: auto; + line-height: 1.6; +} + +/* Make links feel like rows */ +pre a { + display: inline-block; + padding: 8px 24px 8px 44px; + position: relative; + + color: var(--accent); + text-decoration: none; + + transition: background 0.15s ease, color 0.15s ease; +} + +/* Hover & focus = full-row highlight */ +pre a:hover, +pre a:focus-visible { + background: var(--row-hover); + color: #ffffff; + outline: none; +} + +/* Parent directory */ +pre a[href="../"] { + color: var(--parent); + font-weight: 600; +} + +/* Folders */ +pre a[href$="/"] { + color: var(--folder); + font-weight: 600; +} + +/* === Icons (pure CSS, no emoji) === */ +pre a::before { + content: ""; + position: absolute; + left: 20px; + top: 50%; + width: 10px; + height: 10px; + transform: translateY(-50%); + border-radius: 2px; + background: var(--accent); + opacity: 0.85; +} + +/* Folder icon */ +pre a[href$="/"]::before { + background: var(--folder); +} + +/* Parent directory icon */ +pre a[href="../"]::before { + background: var(--parent); +} + +/* Footer note */ +pre::after { + content: "— nginx autoindex —"; + display: block; + margin: 24px 24px 0; + color: var(--muted); + font-size: 12px; +} + +/* Scrollbar (WebKit only, safe) */ +pre::-webkit-scrollbar { + height: 8px; +} + +pre::-webkit-scrollbar-thumb { + background: var(--border); + border-radius: 4px; +} + +/* Mobile improvements */ +@media (max-width: 600px) { + h1 { + font-size: 16px; + padding: 14px 16px; + } + + pre a { + padding: 10px 16px 10px 40px; + font-size: 13px; + } + + pre a::before { + left: 16px; + } +} + +/* ===== Header actions ===== */ + +h1 { + display: flex; + align-items: center; + justify-content: space-between; + gap: 16px; +} + +/* Button container */ +.autoindex-actions { + display: flex; + gap: 8px; +} + +/* Buttons */ +.ai-btn { + appearance: none; + border: 1px solid var(--border); + background: transparent; + color: var(--text); + font-size: 12px; + padding: 6px 10px; + border-radius: 6px; + cursor: pointer; + text-decoration: none; + line-height: 1; + + transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease; +} + +/* Hover */ +.ai-btn:hover { + background: var(--row-hover); + border-color: var(--accent); + color: #fff; +} + +/* Active */ +.ai-btn:active { + transform: translateY(1px); +} + +/* Mobile */ +@media (max-width: 600px) { + .ai-btn { + padding: 6px 8px; + font-size: 11px; + } +} diff --git a/autoindex.js b/autoindex.js new file mode 100644 index 0000000..b708f23 --- /dev/null +++ b/autoindex.js @@ -0,0 +1,141 @@ + +/** + * Check if a URL points to a file by extension + */ +function isFileLink(url) { + const fileExtensions = [ + 'pdf','zip','rar','7z', + 'mp3','mp4','mkv','avi', + 'jpg','jpeg','png','gif','webp', + 'doc','docx','xls','xlsx','ppt','pptx', + 'csv','txt','json' + ]; + + const cleanUrl = url.split('?')[0].toLowerCase(); + return fileExtensions.some(ext => cleanUrl.endsWith('.' + ext)); +} + +/** + * Get all file links from the page + */ +function getFileLinks() { + const links = document.querySelectorAll('a'); + const result = []; + + links.forEach(a => { + if (!a.href) return; + if (!isFileLink(a.href)) return; + + result.push({ + text: a.innerText.trim(), + href: a.href + }); + }); + + return result; +} + +/** + * Converts a string to kebab-case. + */ +function toKebabCase(str) { + return str + .toLowerCase() + .trim() + .replace(/['’]/g, '') + .replace(/[^a-z0-9]+/g, '-') + .replace(/^-+|-+$/g, ''); +} + +/** + * Extracts the last directory or file name from the current page URL + */ +function getDirectoryName() { + const url = new URL(window.location.href); + const lastPart = url.pathname + .split('/') + .filter(Boolean) + .pop() || 'file-links'; + + return decodeURIComponent(lastPart); +} + +/** + * Download file links as Playlist + */ +function downloadPlaylist() { + const files = getFileLinks(); + let content = 'DAUMPLAYLIST\n'; + + files.forEach((item, index) => { + const text = item.text.replace(/"/g, '""'); + const href = item.href.replace(/"/g, '""'); + content += `${index+1}*file*${href}\n`; + }); + + const blob = new Blob([content], { type: 'application/octet-stream;charset=utf-8;' }); + const url = URL.createObjectURL(blob); + + const a = document.createElement('a'); + a.href = url; + a.download = toKebabCase(getDirectoryName()) + '.dpl'; + a.style.display = 'none'; + + document.body.appendChild(a); + a.click(); + + document.body.removeChild(a); + URL.revokeObjectURL(url); +} + +/** + * Copy file links (one per line) + */ +function copyAllLinks() { + const files = getFileLinks(); + const textToCopy = files.map(f => f.href).join('\n'); + + // Modern browsers + if (navigator.clipboard && window.isSecureContext) { + navigator.clipboard.writeText(textToCopy); + return; + } + + // Fallback (older browsers, some mobiles) + const textarea = document.createElement('textarea'); + textarea.value = textToCopy; + textarea.style.position = 'fixed'; + textarea.style.opacity = '0'; + + document.body.appendChild(textarea); + textarea.focus(); + textarea.select(); + + document.execCommand('copy'); + document.body.removeChild(textarea); +} + +(function () { + const header = document.querySelector('h1'); + if (!header) return; + + // Wrapper for buttons + const actions = document.createElement('div'); + actions.className = 'autoindex-actions'; + + // Playlist button + const plb = document.createElement('button'); + plb.className = 'ai-btn'; + plb.textContent = 'Download Playlist'; + plb.onclick = downloadPlaylist; + + // Copy Links button + const clb = document.createElement('button'); + clb.className = 'ai-btn'; + clb.textContent = 'Copy Links'; + clb.onclick = copyAllLinks; + + actions.appendChild(clb); + actions.appendChild(plb); + header.appendChild(actions); +})(); diff --git a/favicon.ico b/favicon.ico new file mode 100644 index 0000000..4f60535 Binary files /dev/null and b/favicon.ico differ diff --git a/favicon.png b/favicon.png new file mode 100644 index 0000000..cdfaa7c Binary files /dev/null and b/favicon.png differ diff --git a/nais.conf b/nais.conf new file mode 100644 index 0000000..0197d2d --- /dev/null +++ b/nais.conf @@ -0,0 +1,41 @@ +server { + listen 443 ssl; + server_name <>; + + # SSL certificates + ssl_certificate <>/fullchain.pem; + ssl_certificate_key <>/privkey.pem; + + # Default root + root <>; + + # Autoindex for the main folder + location / { + autoindex on; + autoindex_exact_size off; # optional: show human-readable sizes + autoindex_localtime on; # optional: show local time + + # Add custom CSS & JS to autoindex pages + sub_filter '' ''; + sub_filter '' ''; + sub_filter_once on; + } + + # Serve the assets from a separate folder + location = /autoindex.css { + root <>; + } + location = /autoindex.js { + root <>; + } + location = /favicon.ico { + root <>; + } +} +server { + listen 80; + server_name <>; + + return 301 https://<>$request_uri; +} +