summaryrefslogtreecommitdiff
path: root/web/js/views/media-info.js
diff options
context:
space:
mode:
Diffstat (limited to 'web/js/views/media-info.js')
-rw-r--r--web/js/views/media-info.js142
1 files changed, 0 insertions, 142 deletions
diff --git a/web/js/views/media-info.js b/web/js/views/media-info.js
deleted file mode 100644
index bbb1f98..0000000
--- a/web/js/views/media-info.js
+++ /dev/null
@@ -1,142 +0,0 @@
-import { API } from '../api.js';
-import { fmtDateTime, fmtSize } from '../dom.js';
-import { escapeHtml, fmtDur, toast } from '../utils.js';
-import { selectedMediaId } from './media-actions.js';
-
-let callbacks = {};
-
-export function initMediaInfo(options = {}) {
- callbacks = options;
- const modal = document.getElementById('media-info-modal');
- const closeBtn = document.getElementById('media-info-close');
- closeBtn?.addEventListener('click', closeMediaInfo);
- modal?.addEventListener('click', (e) => {
- if (e.target === modal) closeMediaInfo();
- });
- modal?.addEventListener('click', async (e) => {
- const button = e.target.closest('[data-media-info-action]');
- if (!button) return;
- const id = button.dataset.mediaId;
- if (!id) return;
- let updated = false;
- if (button.dataset.mediaInfoAction === 'mark-finished') {
- updated = await callbacks.markAsFinished?.(id);
- } else if (button.dataset.mediaInfoAction === 'mark-not-started') {
- updated = await callbacks.markAsNotStarted?.(id);
- }
- if (updated) await openMediaInfo(id);
- });
-}
-
-export function closeMediaInfo() {
- document.getElementById('media-info-modal')?.classList.remove('open');
-}
-
-export function isMediaInfoOpen() {
- return document.getElementById('media-info-modal')?.classList.contains('open');
-}
-
-export function scrollMediaInfo(delta) {
- const panel = document.getElementById('media-info-panel')
- || document.querySelector('#media-info-modal .modal');
- if (!panel) return;
- const step = Math.max(96, Math.round(panel.clientHeight * 0.25));
- panel.scrollTop += delta * step;
-}
-
-export async function toggleMediaInfo() {
- const modal = document.getElementById('media-info-modal');
- if (!modal) return;
- if (modal.classList.contains('open')) {
- closeMediaInfo();
- return;
- }
- const id = selectedMediaId();
- if (!id) {
- toast('No media selected', 'error');
- return;
- }
- await openMediaInfo(id);
-}
-
-async function openMediaInfo(id) {
- const modal = document.getElementById('media-info-modal');
- const body = document.getElementById('media-info-body');
- if (!modal || !body) return;
- body.innerHTML = '<p class="text-muted text-sm">Loading...</p>';
- modal.classList.add('open');
- (document.getElementById('media-info-panel')
- || document.querySelector('#media-info-modal .modal'))?.focus({ preventScroll: true });
- try {
- const detail = await API.mediaDetail(id);
- body.innerHTML = renderMediaInfo(detail);
- } catch (err) {
- body.innerHTML = `<p class="error-message">${escapeHtml(err.message || 'Failed to load media info')}</p>`;
- }
-}
-
-function renderMediaInfo(detail) {
- const media = detail?.media || {};
- const tags = Array.isArray(detail?.tags) ? detail.tags.map((t) => t.name).filter(Boolean).join(', ') : '';
- const ext = media.file_name?.includes('.') ? media.file_name.split('.').pop().toUpperCase() : '';
- const progress = detail?.progress;
- const note = detail?.note;
- let rows = [
- ['Title', media.file_name],
- ['Format', ext],
- ['Type', media.type],
- ['File size', media.file_size_bytes ? `${fmtSize(media.file_size_bytes)} (${media.file_size_bytes} bytes)` : ''],
- ['Relative path', media.rel_path],
- ['Absolute path', media.abs_path],
- ['Media ID', media.id],
- ['Set ID', media.set_id],
- ['Play count', media.play_count],
- ['Added', fmtDateTime(media.created_at)],
- ['Thumbnail', media.thumbnail_path],
- ['Favorite', detail?.favorite ? 'Yes' : 'No'],
- ['Tags', tags],
- ];
- if (media.type === 'image') {
- rows = rows.concat([
- ['Dimensions', media.resolution],
- ['Width', media.width],
- ['Height', media.height],
- ['Camera', media.exif_camera],
- ['Lens', media.exif_lens],
- ['Date Taken', media.exif_date],
- ['ISO', media.exif_iso],
- ['F-Number', media.exif_f_number],
- ['Exposure', media.exif_exposure],
- ['Focal Length', media.exif_focal_length],
- ]);
- } else {
- rows = rows.concat([
- ['Duration', media.duration ? `${fmtDur(media.duration)} (${Math.round(media.duration)} seconds)` : ''],
- ['Bitrate', media.bitrate ? `${Math.round(media.bitrate / 1000)} kbps (${media.bitrate} bps)` : ''],
- ['Codec', media.codec],
- ['Resolution', media.resolution],
- ['Saved position', progress ? `${fmtDur(progress.position_seconds)} (${Math.round(progress.position_seconds || 0)} seconds)` : ''],
- ['Progress updated', fmtDateTime(progress?.updated_at)],
- ]);
- }
- rows = rows.concat([
- ['Note updated', fmtDateTime(note?.updated_at)],
- ['Note length', note?.content ? `${note.content.length} characters` : ''],
- ]);
- const table = rows
- .filter(([, value]) => value !== undefined && value !== null && value !== '')
- .map(([label, value]) => `<tr><th scope="row">${escapeHtml(label)}</th><td>${escapeHtml(String(value))}</td></tr>`)
- .join('');
- const raw = escapeHtml(JSON.stringify(detail || {}, null, 2));
- return `
- <div class="flex flex-wrap gap-2 mt-1 mb-2">
- <button class="btn btn-ghost btn-sm" type="button" data-media-info-action="mark-finished" data-media-id="${escapeHtml(String(media.id || ''))}">✓ Finished</button>
- <button class="btn btn-ghost btn-sm" type="button" data-media-info-action="mark-not-started" data-media-id="${escapeHtml(String(media.id || ''))}">↺ Not started</button>
- </div>
- <table class="media-info-table">${table}</table>
- <details class="media-info-raw">
- <summary>Raw API detail</summary>
- <pre>${raw}</pre>
- </details>
- `;
-}