mirror of
https://github.com/thegeneralist01/archivr
synced 2026-07-21 18:55:36 +02:00
fix(core): playlist total size includes children; per_item_quality as include-set
archive.rs: total_artifact_bytes for root entries now adds a correlated subquery summing children's blob bytes so playlist/channel containers show the real download size instead of 0. capture.rs: non-empty per_item_quality map now acts as an include-set — items whose yt-dlp ID is absent are skipped entirely. This wires the UI's per-video delete button to actual capture exclusion. Empty map preserves the existing behaviour (download everything). routes.rs + capture.rs doc: comments updated to reflect both semantics (empty = all / non-empty = only listed IDs) accurately.
This commit is contained in:
parent
cca4742f89
commit
e2f50a9856
3 changed files with 24 additions and 10 deletions
|
|
@ -214,7 +214,7 @@ pub fn list_root_entries(
|
|||
e.visibility,
|
||||
si.canonical_url,
|
||||
COUNT(ea.id) AS artifact_count,
|
||||
COALESCE(SUM(b.byte_size), 0) AS total_artifact_bytes,
|
||||
COALESCE(SUM(b.byte_size), 0) + COALESCE((SELECT SUM(b2.byte_size) FROM archived_entries c2 JOIN entry_artifacts ea2 ON ea2.entry_id = c2.id JOIN blobs b2 ON b2.id = ea2.blob_id WHERE c2.parent_entry_id = e.id), 0) AS total_artifact_bytes,
|
||||
NULL AS parent_entry_uid,
|
||||
EXISTS(SELECT 1 FROM entry_artifacts fav WHERE fav.entry_id = e.id AND fav.artifact_role = 'favicon') AS has_favicon,
|
||||
e.cached_bytes,
|
||||
|
|
@ -689,7 +689,7 @@ pub fn parse_search_query(raw: &str) -> Result<SearchEntriesQuery, String> {
|
|||
|
||||
const ENTRY_SELECT_COLS: &str = "SELECT e.entry_uid, e.archived_at, e.source_kind, e.entity_kind, e.title, \
|
||||
e.visibility, si.canonical_url, COUNT(ea.id) AS artifact_count, \
|
||||
COALESCE(SUM(b.byte_size), 0) AS total_artifact_bytes, \
|
||||
COALESCE(SUM(b.byte_size), 0) + COALESCE((SELECT SUM(b2.byte_size) FROM archived_entries c2 JOIN entry_artifacts ea2 ON ea2.entry_id = c2.id JOIN blobs b2 ON b2.id = ea2.blob_id WHERE c2.parent_entry_id = e.id), 0) AS total_artifact_bytes, \
|
||||
parent.entry_uid AS parent_entry_uid, \
|
||||
EXISTS(SELECT 1 FROM entry_artifacts fav WHERE fav.entry_id = e.id AND fav.artifact_role = 'favicon') AS has_favicon, \
|
||||
e.cached_bytes, \
|
||||
|
|
|
|||
|
|
@ -90,10 +90,13 @@ pub struct CaptureConfig {
|
|||
/// Route WebPage captures through the Freedium mirror to bypass paywalls.
|
||||
/// The original locator is still recorded in the DB; only the fetch URL changes.
|
||||
pub via_freedium: bool,
|
||||
/// Per-item quality overrides for playlist captures.
|
||||
/// Per-item quality overrides and include-set for playlist captures.
|
||||
/// Keys are yt-dlp video IDs (e.g. "dQw4w9WgXcQ"); values are quality strings
|
||||
/// accepted by `quality_format` ("best", "audio", "1080p", etc.).
|
||||
/// Empty map = apply the global `quality` to all items.
|
||||
/// - Empty map: all playlist items are downloaded using the global `quality`.
|
||||
/// - Non-empty map: ONLY items whose ID appears as a key are downloaded;
|
||||
/// items absent from the map are skipped entirely. This is how the UI's
|
||||
/// per-video delete button excludes specific videos from a capture.
|
||||
pub per_item_quality: HashMap<String, String>,
|
||||
/// When true, skip playlist items whose URL is already archived as a child
|
||||
/// of any container entry with the same canonical playlist URL.
|
||||
|
|
@ -1139,6 +1142,16 @@ pub fn perform_capture(
|
|||
if config.sync && already_archived.contains(&playlist_item.url) {
|
||||
continue;
|
||||
}
|
||||
// Per-item exclusion: when per_item_quality is non-empty (frontend
|
||||
// probe ran and the user confirmed the item list), only download
|
||||
// items whose ID appears in the map. IDs absent from a non-empty
|
||||
// map were removed by the user via the delete button before submit.
|
||||
if !config.per_item_quality.is_empty()
|
||||
&& !config.per_item_quality.contains_key(&playlist_item.id)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
let child_timestamp = format!(
|
||||
"{}-{}",
|
||||
|
|
|
|||
|
|
@ -872,13 +872,14 @@ async fn capture_handler(
|
|||
)));
|
||||
}
|
||||
}
|
||||
// NOTE: the server does not enforce that every playlist item has a per_item_quality
|
||||
// entry. Items without an override receive the global `quality` value, which
|
||||
// yt-dlp applies as a format-selector cap (e.g. bestvideo[height<=1080]) and
|
||||
// falls back gracefully to the best available format below that cap. The
|
||||
// "must choose quality for unsupported videos" invariant is enforced by the
|
||||
// per_item_quality semantics (enforced in capture.rs):
|
||||
// - Absent or empty map: all playlist items are downloaded; quality is the
|
||||
// global `quality` field applied as a yt-dlp cap with graceful fallback.
|
||||
// - Non-empty map: ONLY items whose yt-dlp ID appears as a key are downloaded;
|
||||
// absent IDs are skipped. This is how the frontend's delete-item button works.
|
||||
// The "must choose quality for unsupported videos" invariant is enforced by the
|
||||
// frontend before submission; a direct API caller bypassing the UI accepts
|
||||
// yt-dlp's standard cap-and-fallback behavior.
|
||||
// yt-dlp's standard cap-and-fallback behavior for items it includes.
|
||||
let mounted = mounted_archive(&state, &archive_id)?;
|
||||
let archive_paths =
|
||||
archive::read_archive_paths(&mounted.archive_path).map_err(ApiError::from)?;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue