{% if chunk.video_codec and chunk.width and chunk.height %}Video: {{ chunk.video_codec }} {{ chunk.width }}x{{ chunk.height }}
+
{% if chunk.video_codec and chunk.width and chunk.height %}Video: {{ chunk.video_codec }} {{ chunk.width }}x{{ chunk.height }}
{% elif chunk.video_codec %}Video: {{ chunk.video_codec }}
{% endif %}Size: {{ chunk.size_mb }} MB
Created: {{ chunk.created_at }}
@@ -336,10 +338,34 @@
Audio Files
isOpen: false,
title: '',
body: '',
- show(chunk, items, type) {
+ show(chunk, items, type, taUrl) {
this.title = type === 'sources' ? 'Source files: ' + (chunk || '') : 'Models: ' + (chunk || '');
if (type === 'sources') {
- this.body = (items || []).map(p => '
' + (p || '').replace(//g, '>') + '
').join('') || '
No source files.
';
+ var list = items || [];
+ this.body = list.map(function(item) {
+ var path = typeof item === 'string' ? item : (item && item.path);
+ var model = typeof item === 'object' && item && item.model;
+ var thumb = typeof item === 'object' && item && item.thumbnail_url;
+ var title = typeof item === 'object' && item && item.title;
+ var channel = typeof item === 'object' && item && item.channel;
+ if (!path) return '';
+ var escaped = (path || '').replace(//g, '>');
+ var stem = path.replace(/^.*\//, '').replace(/\.(mp4|mkv|avi)$/i, '');
+ var videoId = (stem.length === 11 && /^[a-zA-Z0-9_-]+$/.test(stem)) ? stem : null;
+ var link = (taUrl && videoId) ? (taUrl + '/video/' + videoId + '/') : null;
+ var linkHtml = link ? '
' + escaped + '' : escaped;
+ var label = model || title || channel;
+ var modelHtml = '';
+ if (label) {
+ var m = ('' + label).replace(//g, '>');
+ var href = (label.indexOf('http') === 0 ? label : (label.indexOf('www.') === 0 ? 'https://' + label : (label.indexOf('/') === 0 && taUrl ? taUrl + label : null)));
+ modelHtml = '
— ' + (href ? '
' + m + '' : m) + '
';
+ }
+ var thumbSrc = videoId ? ('https://img.youtube.com/vi/' + videoId + '/hqdefault.jpg') : (thumb || '');
+ var thumbHtml = thumbSrc ? '

' : '';
+ var thumbBlock = thumbSrc ? (link ? '
' + thumbHtml + '' : '
' + thumbHtml + '') : '';
+ return '
' + linkHtml + modelHtml + '
' + thumbBlock + '
';
+ }).join('') || '
No source files.
';
} else {
this.body = (items || []).map(m => {
const href = (m.indexOf('http') === 0 ? m : 'https://' + m);
@@ -361,6 +387,7 @@
Audio Files
let currentPage = 1;
const currentChunkExclude = {{ (current_chunk or '') | tojson }};
const showModelColumn = {{ (show_model_column | default(false)) | tojson }};
+ const tubearchivistUrl = {{ (tubearchivist_url or '') | tojson }};
let rows = [];
let filteredSortedChunks = [];
@@ -374,11 +401,11 @@
Audio Files
metaLines.push('Size: ' + (chunk.size_mb != null ? chunk.size_mb + ' MB' : '—'));
metaLines.push('Created: ' + (chunk.created_at || '—'));
var metaContent = metaLines.join('\n');
- var metaCell = '
';
+ var metaCell = '
';
var expiresCell = '~' + (chunk.days_to_expire != null ? chunk.days_to_expire : '?') + ' days';
var sourcesBtn = (chunk.source_videos && chunk.source_videos.length) ? '
' : '';
var modelBtn = (showModelColumn && chunk.model_info && chunk.model_info.length) ? '
' : '';
- return '
▶' + chunk.name + '' + sourcesBtn + modelBtn + ' | ' + expiresCell + ' | ' + metaCell + ' |
';
+ return '
▶' + chunk.name + '' + sourcesBtn + modelBtn + ' | ' + expiresCell + ' | ' + metaCell + ' |
';
}
function getFilteredSortedChunks() {
@@ -499,11 +526,11 @@
Audio Files
tr.setAttribute('data-chunk-name', chunkName);
tr.className = 'chunk-row now-playing border-b-2 border-blue-500/50';
var metaContent = buildMetadataContent(meta);
- var metaCell = '
';
+ var metaCell = '
';
var expiresCell = '~' + (meta && meta.days_to_expire != null ? meta.days_to_expire : '?') + ' days';
var sourcesBtn = (meta && meta.source_videos && meta.source_videos.length) ? '
' : '';
var modelBtn = (showModelColumn && meta && meta.model_info && meta.model_info.length) ? '
' : '';
- tr.innerHTML = '
▶' + chunkName + '' + sourcesBtn + modelBtn + ' Now Playing ▶ | ' + expiresCell + ' | ' + metaCell + ' | ';
+ tr.innerHTML = '
▶' + chunkName + '' + sourcesBtn + modelBtn + ' Now Playing ▶ | ' + expiresCell + ' | ' + metaCell + ' | ';
if (tbody && tbody.firstChild) tbody.insertBefore(tr, tbody.firstChild); else if (tbody) tbody.appendChild(tr);
playingRow = tr;
} else {
@@ -512,16 +539,16 @@
Audio Files
var nameCell = playingRow.querySelector('td:first-child');
var nameSpan = playingRow.querySelector('.chunk-name');
if (nameSpan) nameSpan.textContent = chunkName;
- var playLink = playingRow.querySelector('a[href^="/chunks/"]');
- if (playLink) playLink.href = '/chunks/' + chunkName;
- var metaCell = '
i' + buildMetadataContent(meta) + '
';
+ var playBtn = playingRow.querySelector('.btn-play-chunk');
+ if (playBtn) playBtn.setAttribute('data-chunk-name', chunkName);
+ var metaCell = '
i' + buildMetadataContent(meta) + '
';
var expiresCell = '~' + (meta && meta.days_to_expire != null ? meta.days_to_expire : '?') + ' days';
var sourcesBtn = (meta && meta.source_videos && meta.source_videos.length) ? '
' : '';
var modelBtn = (showModelColumn && meta && meta.model_info && meta.model_info.length) ? '
' : '';
var nameCell = playingRow.querySelector('td:first-child');
if (nameCell) {
var nameDiv = nameCell.querySelector('.flex');
- if (nameDiv) nameDiv.innerHTML = '
▶' + chunkName + '' + sourcesBtn + modelBtn + '
Now Playing ▶';
+ if (nameDiv) nameDiv.innerHTML = '
▶' + chunkName + '' + sourcesBtn + modelBtn + '
Now Playing ▶';
var progDiv = nameCell.querySelector('.now-playing-progress');
if (progDiv) progDiv.className = 'now-playing-progress mt-1.5 ' + (startedAt != null && duration != null && duration > 0 ? '' : 'hidden');
}
@@ -793,7 +820,7 @@
Audio Files
var raw = srcBtn.getAttribute('data-sources') || '[]';
var sources = [];
try { sources = JSON.parse(raw.replace(/"/g, '"')); } catch (_) {}
- window.dispatchEvent(new CustomEvent('open-sources', { detail: { chunk: chunk, sources: sources } }));
+ window.dispatchEvent(new CustomEvent('open-sources', { detail: { chunk: chunk, sources: sources, tubearchivist_url: typeof tubearchivistUrl !== 'undefined' ? tubearchivistUrl : '' } }));
return;
}
var modelBtn = e.target && e.target.closest('.chunk-model-btn');
@@ -821,6 +848,31 @@
Audio Files
}, durationMs || 4000);
}
+ document.addEventListener('click', async function(e) {
+ var btn = e.target && e.target.closest('.btn-play-chunk');
+ if (!btn) return;
+ var chunkName = btn.getAttribute('data-chunk-name');
+ if (!chunkName) return;
+ var orig = btn.textContent;
+ btn.textContent = '…';
+ btn.disabled = true;
+ try {
+ var res = await fetch('/api/play_chunk', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ chunk_name: chunkName }) });
+ var data = await res.json();
+ if (data.success) {
+ showToast('Switched to next video. It may take a few seconds to appear in the live stream.');
+ setTimeout(function() { window.dispatchEvent(new CustomEvent('refresh-stream-status')); }, 2000);
+ setTimeout(function() { window.dispatchEvent(new CustomEvent('refresh-stream-status')); }, 5000);
+ } else {
+ showToast(data.error || 'Failed to play chunk.');
+ }
+ } catch (err) {
+ showToast('Request failed.');
+ }
+ btn.textContent = orig;
+ btn.disabled = false;
+ });
+
document.getElementById('btn-skip-next').addEventListener('click', async () => {
const btn = document.getElementById('btn-skip-next');
const orig = btn.textContent;
diff --git a/templates/stats.html b/templates/stats.html
index 7c6659c..2f20889 100644
--- a/templates/stats.html
+++ b/templates/stats.html
@@ -52,7 +52,7 @@
Mode
{% if m.image %}
-
+
{% endif %}
{{ m.title }}