some more features and fixes (title and show info)
This commit is contained in:
@@ -66,61 +66,119 @@ App.ui = App.ui || {};
|
||||
}, 4000);
|
||||
};
|
||||
|
||||
App.ui.showInfo = function(video) {
|
||||
// Which video the panel is currently showing, so a slow resolve that lands
|
||||
// after the user moved on doesn't redraw someone else's panel.
|
||||
let infoVideo = null;
|
||||
|
||||
const appendInfoHeading = function(list, label) {
|
||||
const heading = document.createElement('div');
|
||||
heading.className = 'info-section';
|
||||
heading.textContent = label;
|
||||
list.appendChild(heading);
|
||||
};
|
||||
|
||||
// One row per field, whatever the field is. Objects and arrays are printed
|
||||
// as JSON rather than summarised: the panel is the place to see exactly what
|
||||
// the server said, so nothing is dropped or abbreviated here.
|
||||
const appendInfoRows = function(list, data) {
|
||||
let count = 0;
|
||||
Object.entries(data || {}).forEach(([key, value]) => {
|
||||
const row = document.createElement('div');
|
||||
row.className = 'info-row';
|
||||
|
||||
const label = document.createElement('span');
|
||||
label.className = 'info-label';
|
||||
label.textContent = key;
|
||||
|
||||
let valueNode;
|
||||
if (value && typeof value === 'object') {
|
||||
valueNode = document.createElement('pre');
|
||||
valueNode.className = 'info-json';
|
||||
valueNode.textContent = JSON.stringify(value, null, 2);
|
||||
} else {
|
||||
valueNode = document.createElement('span');
|
||||
valueNode.className = 'info-value';
|
||||
valueNode.textContent = value === undefined || value === null || value === '' ? '—' : String(value);
|
||||
}
|
||||
|
||||
row.appendChild(label);
|
||||
row.appendChild(valueNode);
|
||||
list.appendChild(row);
|
||||
count++;
|
||||
});
|
||||
return count;
|
||||
};
|
||||
|
||||
// Shows every field the client holds for a video: the listing item's own
|
||||
// (id, title, uploader, duration, tags, ...) and then the extractor's, which
|
||||
// arrive separately. It used to show `video.meta` *instead of* the item once
|
||||
// one had been resolved, which silently hid everything the listing knew the
|
||||
// moment a card had been hovered.
|
||||
// `options.info` is the full extractor payload (App.videos.fetchFullInfo);
|
||||
// `options.pending` notes that it's still on its way.
|
||||
App.ui.showInfo = function(video, options) {
|
||||
const modal = document.getElementById('info-modal');
|
||||
if (!modal) return;
|
||||
const opts = options || {};
|
||||
const title = document.getElementById('info-title');
|
||||
const list = document.getElementById('info-list');
|
||||
const empty = document.getElementById('info-empty');
|
||||
|
||||
const data = video && video.meta ? video.meta : video;
|
||||
const titleText = data && data.title ? data.title : 'Video Info';
|
||||
if (title) title.textContent = titleText;
|
||||
const item = (video && typeof video === 'object') ? video : {};
|
||||
// `meta` is the trimmed playback payload; the full extractor info is a
|
||||
// superset of it, so only one of the two is ever shown.
|
||||
const resolved = opts.info || item.meta || null;
|
||||
|
||||
if (title) title.textContent = item.title || (resolved && resolved.title) || 'Video Info';
|
||||
|
||||
let rows = 0;
|
||||
if (list) {
|
||||
list.innerHTML = "";
|
||||
}
|
||||
// `meta` gets its own section below rather than a row of JSON.
|
||||
const own = Object.assign({}, item);
|
||||
delete own.meta;
|
||||
rows += appendInfoRows(list, own);
|
||||
|
||||
let hasRows = false;
|
||||
if (data && typeof data === 'object') {
|
||||
Object.entries(data).forEach(([key, value]) => {
|
||||
if (!list) return;
|
||||
const row = document.createElement('div');
|
||||
row.className = 'info-row';
|
||||
if (resolved && typeof resolved === 'object') {
|
||||
appendInfoHeading(list, opts.info ? 'Extractor' : 'Resolved');
|
||||
rows += appendInfoRows(list, resolved);
|
||||
}
|
||||
|
||||
const label = document.createElement('span');
|
||||
label.className = 'info-label';
|
||||
label.textContent = key;
|
||||
|
||||
let valueNode;
|
||||
if (value && typeof value === 'object') {
|
||||
valueNode = document.createElement('pre');
|
||||
valueNode.className = 'info-json';
|
||||
valueNode.textContent = JSON.stringify(value, null, 2);
|
||||
} else {
|
||||
valueNode = document.createElement('span');
|
||||
valueNode.className = 'info-value';
|
||||
valueNode.textContent = value === undefined || value === null || value === '' ? '—' : String(value);
|
||||
}
|
||||
|
||||
row.appendChild(label);
|
||||
row.appendChild(valueNode);
|
||||
list.appendChild(row);
|
||||
hasRows = true;
|
||||
});
|
||||
if (opts.pending) {
|
||||
const pending = document.createElement('div');
|
||||
pending.className = 'info-pending';
|
||||
pending.textContent = 'Resolving full metadata…';
|
||||
list.appendChild(pending);
|
||||
}
|
||||
}
|
||||
|
||||
if (empty) {
|
||||
empty.style.display = hasRows ? 'none' : 'block';
|
||||
empty.style.display = rows ? 'none' : 'block';
|
||||
}
|
||||
|
||||
modal.classList.add('open');
|
||||
modal.setAttribute('aria-hidden', 'false');
|
||||
};
|
||||
|
||||
// Opens the panel on what the client already has, then redraws it with the
|
||||
// extractor's full payload once that lands. Resolution runs yt-dlp against
|
||||
// the source site and can take seconds; there's no reason to stare at
|
||||
// nothing (or at a spinner) while it does.
|
||||
App.ui.openInfo = function(video) {
|
||||
infoVideo = video;
|
||||
const canResolve = !!(App.videos && typeof App.videos.fetchFullInfo === 'function');
|
||||
App.ui.showInfo(video, { pending: canResolve });
|
||||
if (!canResolve) return;
|
||||
App.videos.fetchFullInfo(video).then((info) => {
|
||||
if (infoVideo !== video) return; // the panel moved on, or closed
|
||||
App.ui.showInfo(video, { info: info });
|
||||
});
|
||||
};
|
||||
|
||||
App.ui.closeInfo = function() {
|
||||
const modal = document.getElementById('info-modal');
|
||||
if (!modal) return;
|
||||
infoVideo = null;
|
||||
modal.classList.remove('open');
|
||||
modal.setAttribute('aria-hidden', 'true');
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user