diff --git a/static/app.css b/static/app.css index fed72d4..1656f6d 100644 --- a/static/app.css +++ b/static/app.css @@ -1251,3 +1251,8 @@ footer .audio-player-container { background-color: rgba(246, 195, 68, 0.25); border-radius: 4px; } + +.file-access-actions { + flex-wrap: nowrap; + white-space: nowrap; +} diff --git a/static/app.js b/static/app.js index 4fc68a7..1fbdf42 100644 --- a/static/app.js +++ b/static/app.js @@ -639,10 +639,21 @@ function initAppPage() { // Load initial directory based on URL. let initialSubpath = ''; + const pendingFolderOpen = getPendingFolderOpen(); if (window.location.pathname.indexOf('/path/') === 0) { initialSubpath = window.location.pathname.substring(6); // remove "/path/" } - loadDirectory(initialSubpath); + if (pendingFolderOpen?.folder !== undefined) { + initialSubpath = pendingFolderOpen.folder || ''; + } + loadDirectory(initialSubpath).then(() => { + if (pendingFolderOpen?.file) { + highlightPendingFile(pendingFolderOpen.file); + } + if (pendingFolderOpen) { + clearPendingFolderOpen(); + } + }); } @@ -731,6 +742,32 @@ function syncThemeColor() { // syncThemeColor is called during app init. +function getPendingFolderOpen() { + try { + const raw = sessionStorage.getItem('pendingFolderOpen'); + return raw ? JSON.parse(raw) : null; + } catch { + return null; + } +} + +function clearPendingFolderOpen() { + try { + sessionStorage.removeItem('pendingFolderOpen'); + } catch { + // ignore + } +} + +function highlightPendingFile(filePath) { + if (!filePath) return; + const target = document.querySelector(`.play-file[data-url=\"${filePath}\"]`); + if (target) { + target.classList.add('search-highlight'); + target.scrollIntoView({ behavior: 'smooth', block: 'center' }); + } +} + if (window.PageRegistry && typeof window.PageRegistry.register === 'function') { window.PageRegistry.register('app', { init: initAppPage }); } else { diff --git a/static/file_access.js b/static/file_access.js new file mode 100644 index 0000000..edd0f68 --- /dev/null +++ b/static/file_access.js @@ -0,0 +1,98 @@ +(() => { + const audioExts = ['.mp3', '.wav', '.ogg', '.m4a', '.flac']; + + const safeDecode = (value) => { + try { + return decodeURIComponent(value); + } catch { + return value; + } + }; + + const normalizePath = (path) => { + if (!path) return ''; + return path + .replace(/\\/g, '/') + .replace(/^\/+/, ''); + }; + + const encodePath = (path) => { + const normalized = normalizePath(path); + if (!normalized) return ''; + return normalized + .split('/') + .map(segment => encodeURIComponent(safeDecode(segment))) + .join('/'); + }; + + const isAudio = (path) => + audioExts.some(ext => path.toLowerCase().endsWith(ext)); + + const highlightFile = (filePath) => { + const normalized = normalizePath(filePath); + const target = document.querySelector(`.play-file[data-url="${normalized}"]`); + if (target) { + target.classList.add('search-highlight'); + target.scrollIntoView({ behavior: 'smooth', block: 'center' }); + } + }; + + const openFolder = (folderPath, filePath) => { + const normalizedFolder = normalizePath(folderPath); + const normalizedFile = normalizePath(filePath); + const payload = { folder: normalizedFolder || '', file: normalizedFile || '' }; + sessionStorage.setItem('pendingFolderOpen', JSON.stringify(payload)); + + const url = normalizedFolder ? `/path/${encodePath(normalizedFolder)}` : '/'; + if (window.PageRouter && typeof window.PageRouter.loadPage === 'function') { + window.PageRouter.loadPage(url, { push: true, scroll: true }); + return; + } + window.location.href = url; + }; + + const initFileAccess = () => { + const table = document.getElementById('file-access-table'); + if (!table || table.dataset.bound) return; + table.dataset.bound = '1'; + + table.querySelectorAll('.play-access-btn').forEach(btn => { + const path = normalizePath(btn.dataset.path || ''); + if (!path) return; + + if (!isAudio(path)) { + btn.innerHTML = ''; + btn.setAttribute('title', 'Download'); + btn.setAttribute('aria-label', 'Download'); + btn.addEventListener('click', () => { + window.location.href = `/media/${encodePath(path)}`; + }); + return; + } + + btn.addEventListener('click', () => { + const activePlayer = (typeof player !== 'undefined' && player) ? player : window.player; + if (activePlayer && typeof activePlayer.loadTrack === 'function') { + activePlayer.loadTrack(path); + } + }); + }); + + table.querySelectorAll('.folder-open-btn').forEach(btn => { + const folder = normalizePath(btn.dataset.folder || ''); + const file = normalizePath(btn.dataset.file || ''); + btn.addEventListener('click', () => { + // If we're already on the app page, load directly. + if (window.PageRegistry?.getCurrent?.() === 'app' && typeof loadDirectory === 'function') { + loadDirectory(folder).then(() => highlightFile(file)); + } else { + openFolder(folder, file); + } + }); + }); + }; + + if (window.PageRegistry && typeof window.PageRegistry.register === 'function') { + window.PageRegistry.register('file_access', { init: initFileAccess }); + } +})(); diff --git a/static/page_router.js b/static/page_router.js index 9936ece..a4391b3 100644 --- a/static/page_router.js +++ b/static/page_router.js @@ -125,6 +125,8 @@ } }; + window.PageRouter = { loadPage }; + if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', boot); } else { diff --git a/templates/base.html b/templates/base.html index 711cf16..f972b62 100644 --- a/templates/base.html +++ b/templates/base.html @@ -114,6 +114,7 @@ + diff --git a/templates/file_access.html b/templates/file_access.html index 32ae5fb..92c612a 100644 --- a/templates/file_access.html +++ b/templates/file_access.html @@ -93,18 +93,30 @@
| Access Count | File Path | +Aktion |
|---|---|---|
| {{ row.access_count }} | {{ row.rel_path }} | +
+
+
+
+
+ |