web/desktop: grid + subtitle fixes, comment toggle, faster startup
Several related UI and startup fixes from one session: - Web grid: channel and music views rendered in a single column because their content was wrapped in one nested div that became a single cell of the outer CSS grid. Span the wrappers (and .empty) across all columns with grid-column:1/-1. - Subtitles: route every track through /api/sub-vtt and strip per-cue position/alignment settings (align:start position:0% etc.) from both SRT and VTT, so auto-generated captions render centered instead of left- aligned. New strip_cue_settings/normalize_vtt + sub_vtt_url helpers. - Comment downloads: add a global backup.fetch_comments toggle (the per-channel option already existed; it's now a tri-state override that defers to the global). Full five-touchpoint wiring across config, download_options, the downloader's apply_comments resolver, both UIs' global + per-channel controls, and downloader seeding. - Desktop startup: run the initial library scan + search-index sync on a background thread (mirrors the web server's deferred bind) so the window appears immediately instead of blocking on a cold-cache scan; update() swaps the library in when it lands. - Perceptual dedup: cap worker threads via fingerprint::default_workers() (~half the cores, always leaving at least one free) instead of using every core, so the hashing pass doesn't starve the UI. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
fdc7493eb9
commit
aed577ea2f
7 changed files with 245 additions and 50 deletions
|
|
@ -142,7 +142,7 @@
|
|||
.dl-new-flags{display:flex;gap:12px;flex-wrap:wrap;font-size:12px;color:var(--muted)}
|
||||
.dl-new-flags label{display:flex;align-items:center;gap:4px;cursor:pointer;white-space:nowrap}
|
||||
.preview-thumb{width:100%;max-width:280px;aspect-ratio:16/9;object-fit:cover;border-radius:4px;background:#000;flex-shrink:0}
|
||||
.empty{text-align:center;color:var(--muted);padding:40px;font-size:13px}
|
||||
.empty{grid-column:1/-1;text-align:center;color:var(--muted);padding:40px;font-size:13px}
|
||||
@media(max-width:640px){
|
||||
#menu-btn{display:block}
|
||||
aside{position:fixed;top:0;left:0;height:100%;z-index:50;transform:translateX(-100%);width:240px}
|
||||
|
|
@ -776,7 +776,7 @@ function renderChannelGrid(){
|
|||
const chs=q?library.filter(ch=>ch.name.toLowerCase().includes(q)||ch.uploader?.toLowerCase().includes(q)):library;
|
||||
setStatus(chs.length+' channel'+(chs.length!==1?'s':''));
|
||||
if(!chs.length){grid.innerHTML='<div class="empty">Nothing here.</div>';return}
|
||||
grid.innerHTML='<div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:12px;padding:4px">'+chs.map((ch,i)=>{
|
||||
grid.innerHTML='<div style="grid-column:1/-1;display:grid;grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:12px;padding:4px">'+chs.map((ch,i)=>{
|
||||
const idx=library.indexOf(ch);
|
||||
const thumb=ch.thumb_url?`<img src="${ch.thumb_url}" loading="lazy" alt="">`:'<div class="nothumb">📺</div>';
|
||||
const size=ch.size_bytes>0?` · ${fmtSize(ch.size_bytes)}`:'';
|
||||
|
|
@ -799,7 +799,7 @@ function renderMusicGrid(){
|
|||
const tracks=q?musicTracks.filter(t=>t.title.toLowerCase().includes(q)||t.artist.toLowerCase().includes(q)):musicTracks;
|
||||
setStatus(tracks.length+' track'+(tracks.length!==1?'s':''));
|
||||
if(!tracks.length){grid.innerHTML='<div class="empty">No music yet. Use 🎵 Music mode in the download bar.</div>';return}
|
||||
let currentArtist='',h='<div style="width:100%;padding:4px">';
|
||||
let currentArtist='',h='<div style="grid-column:1/-1;width:100%;padding:4px">';
|
||||
for(const t of tracks){
|
||||
if(t.artist!==currentArtist){
|
||||
currentArtist=t.artist;
|
||||
|
|
@ -994,9 +994,9 @@ async function openChannelOptions(idx){
|
|||
<div class="settings-row"><label>Audio-only</label>
|
||||
<input type="checkbox" id="opt-audio" ${opts.audio_only?'checked':''}></div>
|
||||
<div class="settings-hint" style="margin-bottom:4px">Useful for music channels — saves disk + skips video re-encode.</div>
|
||||
<div class="settings-row"><label>Fetch comments</label>
|
||||
<input type="checkbox" id="opt-comments" ${opts.fetch_comments?'checked':''}></div>
|
||||
<div class="settings-hint" style="margin-bottom:4px">Adds <code>--write-comments</code>. Embeds the comment tree into info.json — slow for popular videos but enables the Comments tab in the player.</div>
|
||||
<div class="settings-row"><label>Fetch comments (blank = global)</label>
|
||||
${triSelect('opt-comments',opts.fetch_comments)}</div>
|
||||
<div class="settings-hint" style="margin-bottom:4px">Adds <code>--write-comments</code>. Embeds the comment tree into info.json — slow for popular videos but enables the Comments tab in the player. "Default" uses the global setting.</div>
|
||||
<div class="settings-row"><label>Skip auth check</label>
|
||||
<input type="checkbox" id="opt-skipauth" ${opts.skip_auth_check?'checked':''}></div>
|
||||
<div class="settings-hint" style="margin-bottom:4px">Adds <code>--extractor-args youtubetab:skip=authcheck</code>. Safe for <strong>public</strong> channels — silences the "playlists that require authentication may not extract correctly" warning without changing which videos are found. Leave off for members-only/private channels, where that warning means your cookies may not be working.</div>
|
||||
|
|
@ -1058,7 +1058,7 @@ function readChannelOptionsForm(){
|
|||
return {
|
||||
quality:q?q:null,
|
||||
audio_only:document.getElementById('opt-audio')?.checked||false,
|
||||
fetch_comments:document.getElementById('opt-comments')?.checked||false,
|
||||
fetch_comments:triValue('opt-comments'),
|
||||
skip_auth_check:document.getElementById('opt-skipauth')?.checked||false,
|
||||
limit_rate_kb:intOrNull('opt-rate'),
|
||||
min_filesize_mb:intOrNull('opt-minsz'),
|
||||
|
|
@ -1785,6 +1785,11 @@ async function openSettings(){
|
|||
</select>
|
||||
</div>
|
||||
<div class="settings-hint" style="margin-bottom:6px">Uses the community SponsorBlock database for sponsor / intro / self-promo segments. <b>Mark</b> adds skippable chapter markers; <b>Remove</b> cuts the segments out of the saved file. Per-channel overrides live in each channel's options.</div>
|
||||
<div class="settings-row" style="margin-top:8px">
|
||||
<label for="cf-comments">Fetch comments</label>
|
||||
<input type="checkbox" id="cf-comments" ${cur.fetch_comments?'checked':''}>
|
||||
</div>
|
||||
<div class="settings-hint" style="margin-bottom:6px">Adds <code>--write-comments</code> to every download — fetches each video's comment tree into its info.json so the player's Comments tab works. Slow on popular videos. Per-channel overrides live in each channel's options.</div>
|
||||
<hr style="border-color:var(--border);margin:12px 0">
|
||||
<div style="font-weight:700;margin-bottom:8px">Subtitles (global defaults)</div>
|
||||
<div class="settings-hint" style="margin-bottom:6px">Applied to every download. Individual channels can override these in their Channel options dialog.</div>
|
||||
|
|
@ -2027,12 +2032,13 @@ async function saveSettings(btn){
|
|||
const subsFormat=document.getElementById('cf-subs-format')?.value||'';
|
||||
const playerClients=document.getElementById('cf-player-clients')?.value||'';
|
||||
const sponsorblock=document.getElementById('cf-sponsorblock')?.value||'mark';
|
||||
const fetchComments=document.getElementById('cf-comments')?.checked||false;
|
||||
const convMode=document.getElementById('cf-convert-mode')?.value||'';
|
||||
const convCrf=parseInt(document.getElementById('cf-convert-crf')?.value||'23',10);
|
||||
const convPreset=document.getElementById('cf-convert-preset')?.value||'';
|
||||
const convAudio=document.getElementById('cf-convert-audio')?.value||'';
|
||||
const convKeep=document.getElementById('cf-convert-keep')?.checked||false;
|
||||
const payload={transcode,scheduler_enabled:schedEnabled,scheduler_interval_hours:schedInterval,max_concurrent:maxConcurrent,use_bundled_ytdlp:useBundledYtdlp,use_pot_provider:usePotProvider,subtitles_enabled:subsEnabled,subtitles_auto:subsAuto,subtitles_embed:subsEmbed,subtitle_langs:subsLangs,subtitle_format:subsFormat,youtube_player_clients:playerClients,sponsorblock_mode:sponsorblock,convert_mode:convMode,convert_crf:convCrf,convert_preset:convPreset,convert_audio_format:convAudio,convert_keep_original:convKeep};
|
||||
const payload={transcode,scheduler_enabled:schedEnabled,scheduler_interval_hours:schedInterval,max_concurrent:maxConcurrent,use_bundled_ytdlp:useBundledYtdlp,use_pot_provider:usePotProvider,subtitles_enabled:subsEnabled,subtitles_auto:subsAuto,subtitles_embed:subsEmbed,subtitle_langs:subsLangs,subtitle_format:subsFormat,youtube_player_clients:playerClients,sponsorblock_mode:sponsorblock,fetch_comments:fetchComments,convert_mode:convMode,convert_crf:convCrf,convert_preset:convPreset,convert_audio_format:convAudio,convert_keep_original:convKeep};
|
||||
if(bindMode)payload.bind_mode=bindMode;
|
||||
if(plexPath!==undefined)payload.plex_library_path=plexPath;
|
||||
if(sourceUrl!==undefined)payload.source_url=sourceUrl;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue