Library sorting: download-date sort + grouped sort options (both UIs)

Add "download date" sorting (file mtime, distinct from upload date) plus a
broader set of options. Web select is now grouped via <optgroup>; desktop
gains SortMode::DownloadDesc/DownloadAsc/ChannelAsc with matching arms and
toolbar entries.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Luna 2026-06-15 20:15:27 -07:00
parent 015d03751c
commit 8b2578783a
2 changed files with 48 additions and 9 deletions

View file

@ -71,6 +71,9 @@ enum SortMode {
SizeDesc,
DateDesc,
DateAsc,
DownloadDesc,
DownloadAsc,
ChannelAsc,
}
#[derive(Clone, PartialEq)]
@ -883,6 +886,19 @@ impl App {
}
});
}
SortMode::DownloadDesc => {
// Most-recently-downloaded first (file mtime); missing → end.
cards.sort_by(|a, b| b.mtime_unix.unwrap_or(0).cmp(&a.mtime_unix.unwrap_or(0)));
}
SortMode::DownloadAsc => {
cards.sort_by(|a, b| a.mtime_unix.unwrap_or(u64::MAX).cmp(&b.mtime_unix.unwrap_or(u64::MAX)));
}
SortMode::ChannelAsc => {
cards.sort_by(|a, b| {
a.channel_name.to_lowercase().cmp(&b.channel_name.to_lowercase())
.then_with(|| a.title.to_lowercase().cmp(&b.title.to_lowercase()))
});
}
}
cards
@ -4131,13 +4147,16 @@ impl App {
}
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
ui.selectable_value(&mut self.sort_mode, SortMode::DateDesc, "Newest");
ui.selectable_value(&mut self.sort_mode, SortMode::DateAsc, "Oldest");
ui.selectable_value(&mut self.sort_mode, SortMode::SizeDesc, "Largest");
ui.selectable_value(&mut self.sort_mode, SortMode::SizeAsc, "Smallest");
ui.selectable_value(&mut self.sort_mode, SortMode::DurationDesc, "Longest");
ui.selectable_value(&mut self.sort_mode, SortMode::DurationAsc, "Shortest");
ui.selectable_value(&mut self.sort_mode, SortMode::ChannelAsc, "Channel");
ui.selectable_value(&mut self.sort_mode, SortMode::Title, "Title");
ui.selectable_value(&mut self.sort_mode, SortMode::DateAsc, "Oldest");
ui.selectable_value(&mut self.sort_mode, SortMode::DateDesc, "Newest");
ui.selectable_value(&mut self.sort_mode, SortMode::DownloadAsc, "Oldest DL");
ui.selectable_value(&mut self.sort_mode, SortMode::DownloadDesc, "Recent DL");
ui.label(egui::RichText::new("Sort:").weak());
});
});

View file

@ -216,13 +216,29 @@
<h1>yt-offline</h1>
<input type="search" id="search" placeholder="Filter…" oninput="saveUiState();renderGrid()">
<select id="sort" onchange="saveUiState();renderGrid()">
<optgroup label="Download date">
<option value="dl-desc">Recently downloaded</option>
<option value="dl-asc">Oldest download</option>
</optgroup>
<optgroup label="Upload date">
<option value="date-desc">Newest</option>
<option value="date-asc">Oldest</option>
<option value="title">Title</option>
</optgroup>
<optgroup label="Title">
<option value="title">Title (A→Z)</option>
<option value="title-desc">Title (Z→A)</option>
</optgroup>
<optgroup label="Channel">
<option value="channel">Channel (A→Z)</option>
</optgroup>
<optgroup label="Duration">
<option value="dur-asc">Shortest</option>
<option value="dur-desc">Longest</option>
</optgroup>
<optgroup label="Size">
<option value="size-asc">Smallest</option>
<option value="size-desc">Largest</option>
</optgroup>
</select>
<span id="hdr-stats"></span>
<button onclick="openSearch()" title="Search titles, descriptions & transcripts (f)">🔍</button>
@ -617,6 +633,8 @@ function currentVideos(){
for(const v of pool)
if((!q||matchesSearch(v,ch?.name||v.channel,q))&&passesFilter(v))vids.push({...v,channel:ch.name});
}
if(sort==='dl-desc')vids.sort((a,b)=>(b.mtime_unix||0)-(a.mtime_unix||0));
if(sort==='dl-asc')vids.sort((a,b)=>(a.mtime_unix||0)-(b.mtime_unix||0));
if(sort==='date-desc')vids.sort((a,b)=>(b.upload_date||'').localeCompare(a.upload_date||''));
if(sort==='date-asc')vids.sort((a,b)=>{const ax=a.upload_date||'￿',bx=b.upload_date||'￿';return ax.localeCompare(bx)});
if(sort==='dur-asc')vids.sort((a,b)=>(a.duration_secs??0)-(b.duration_secs??0));
@ -624,6 +642,8 @@ function currentVideos(){
if(sort==='size-asc')vids.sort((a,b)=>(a.file_size??0)-(b.file_size??0));
if(sort==='size-desc')vids.sort((a,b)=>(b.file_size??0)-(a.file_size??0));
if(sort==='title')vids.sort((a,b)=>a.title.localeCompare(b.title));
if(sort==='title-desc')vids.sort((a,b)=>b.title.localeCompare(a.title));
if(sort==='channel')vids.sort((a,b)=>(a.channel||'').localeCompare(b.channel||'')||a.title.localeCompare(b.title));
return vids;
}