Skip to content

Commit 8af4e0f

Browse files
authored
Address secondary Deno, remux, and release metadata notes (#537)
* Address secondary Deno, remux, and release metadata notes * Fix formatting and preserve packaging script modes * Update live remux argv test for full stream mapping
1 parent 7f3f15c commit 8af4e0f

9 files changed

Lines changed: 260 additions & 48 deletions

‎build-aux/check-bundle-contents.py‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,11 @@ def fail(msg: str) -> Mismatch:
5656
def version_key(version: str) -> tuple:
5757
# AppStream ordering: '~' sorts before everything, so 5.6.7~beta.3 < 5.6.7.
5858
# Normalize Cargo's '-' prerelease separator to AppStream's '~'.
59-
version = version.replace("-beta.", "~beta.").replace("-alpha.", "~alpha.")
59+
version = (
60+
version.replace("-beta.", "~beta.")
61+
.replace("-alpha.", "~alpha.")
62+
.replace("-rc.", "~rc.")
63+
)
6064
if "~" in version:
6165
core, pre = version.split("~", 1)
6266
core_tuple = tuple(int(p) if p.isdigit() else 0 for p in core.split("."))

‎build-aux/check-release-consistency.py‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,11 @@ def metainfo_release_versions() -> list[str]:
7979
def version_key(version: str):
8080
# AppStream ordering: '~' sorts before everything, so 5.6.7~beta.3 < 5.6.7.
8181
# Normalize Cargo's '-' prerelease separator to AppStream's '~'.
82-
version = version.replace("-beta.", "~beta.").replace("-alpha.", "~alpha.")
82+
version = (
83+
version.replace("-beta.", "~beta.")
84+
.replace("-alpha.", "~alpha.")
85+
.replace("-rc.", "~rc.")
86+
)
8387
if "~" in version:
8488
core, pre = version.split("~", 1)
8589
core_tuple = tuple(int(p) if p.isdigit() else 0 for p in core.split("."))

‎src/application.rs‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ fn appstream_release_version(version: &str) -> String {
3030
version
3131
.replace("-beta.", "~beta.")
3232
.replace("-alpha.", "~alpha.")
33+
.replace("-rc.", "~rc.")
3334
}
3435

3536
/// True for an http(s) URL pointing at a .torrent file (case-insensitive).
@@ -1573,6 +1574,7 @@ mod tests {
15731574
fn appstream_release_version_uses_tilde_for_prereleases_only() {
15741575
assert_eq!(appstream_release_version("6.3.0-beta.1"), "6.3.0~beta.1");
15751576
assert_eq!(appstream_release_version("6.3.0-alpha.2"), "6.3.0~alpha.2");
1577+
assert_eq!(appstream_release_version("6.3.0-rc.3"), "6.3.0~rc.3");
15761578
assert_eq!(appstream_release_version("6.2.2"), "6.2.2");
15771579
}
15781580

‎src/gallery_tools.rs‎

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -87,11 +87,11 @@ pub fn managed_gallerydl_sha() -> Option<Option<String>> {
8787
Some(sha256_file(&path).ok())
8888
}
8989

90-
/// Whether Grab's own gallery-dl differs from the latest upstream build. It
91-
/// compares the installed file's SHA-256 with the upstream asset digest, the
92-
/// same value the installer verifies, so the answer follows the binary on
93-
/// disk. A binary Grab does not manage, or a missing upstream digest, never
94-
/// prompts.
90+
/// Whether Grab's own gallery-dl differs from the latest stable Codeberg build.
91+
/// The managed channel is intentionally stable even if a prior build installed a
92+
/// newer nightly: a hash mismatch offers the user an explicit stable-channel
93+
/// replacement from Preferences. A binary Grab does not manage, or a missing
94+
/// upstream digest, never prompts.
9595
pub fn gallerydl_needs_update(managed: Option<Option<&str>>, latest_digest: Option<&str>) -> bool {
9696
match (managed, latest_digest) {
9797
(Some(Some(installed)), Some(latest)) => !installed.eq_ignore_ascii_case(latest),
@@ -105,7 +105,9 @@ pub fn gallerydl_needs_update(managed: Option<Option<&str>>, latest_digest: Opti
105105
/// Any API failure, draft/prerelease, unexpected asset URL, or missing checksum
106106
/// makes the release unavailable rather than weakening verification.
107107
fn valid_release_tag(tag: &str) -> bool {
108-
!tag.is_empty()
108+
tag.as_bytes()
109+
.first()
110+
.is_some_and(|b| b.is_ascii_alphanumeric())
109111
&& tag.bytes().all(|b| {
110112
matches!(
111113
b,
@@ -357,6 +359,9 @@ mod tests {
357359
.is_none()
358360
);
359361
assert!(codeberg_asset_url("v1.32.17/../../evil", LINUX_ASSET).is_none());
362+
assert!(codeberg_asset_url(".", LINUX_ASSET).is_none());
363+
assert!(codeberg_asset_url("..", LINUX_ASSET).is_none());
364+
assert!(codeberg_asset_url(".hidden", LINUX_ASSET).is_none());
360365
}
361366

362367
#[test]

‎src/video_argv.rs‎

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -388,15 +388,18 @@ pub(crate) fn live_remux_argv(
388388
argv.extend(["-i".to_string(), ts_path.to_string_lossy().into_owned()]);
389389
if audio_only {
390390
argv.extend(["-map".to_string(), "0:a?".to_string()]);
391-
} else {
392-
// A live HLS capture may expose subtitle/data tracks that MP4 cannot
393-
// mux. Only carry its actual media streams into the finished file.
391+
} else if repair_timestamps {
392+
// The repair pass narrows mapping to A/V streams in case subtitle/data
393+
// tracks are part of the muxer failure.
394394
argv.extend([
395395
"-map".to_string(),
396396
"0:v?".to_string(),
397397
"-map".to_string(),
398398
"0:a?".to_string(),
399399
]);
400+
} else {
401+
// Preserve the normal remux's full-stream behavior, including subtitles.
402+
argv.extend(["-map".to_string(), "0".to_string()]);
400403
}
401404
argv.extend([
402405
"-dn".to_string(),
@@ -621,7 +624,7 @@ mod live_remux_argv_tests {
621624
use std::path::Path;
622625

623626
#[test]
624-
fn live_video_remux_maps_only_video_and_audio_streams() {
627+
fn normal_live_video_remux_preserves_all_input_streams() {
625628
let args = live_remux_argv(
626629
Path::new("capture.mp4"),
627630
Path::new("finished.mp4.part"),
@@ -630,6 +633,24 @@ mod live_remux_argv_tests {
630633
false,
631634
"https://example.com/live",
632635
);
636+
let maps: Vec<&str> = args
637+
.windows(2)
638+
.filter(|pair| pair[0] == "-map")
639+
.map(|pair| pair[1].as_str())
640+
.collect();
641+
assert_eq!(maps, vec!["0"]);
642+
}
643+
644+
#[test]
645+
fn timestamp_repair_remux_narrows_mapping_to_audio_and_video() {
646+
let args = live_remux_argv(
647+
Path::new("capture.ts"),
648+
Path::new("finished.mp4.part"),
649+
false,
650+
true,
651+
true,
652+
"https://example.com/live",
653+
);
633654
let maps: Vec<&str> = args
634655
.windows(2)
635656
.filter(|pair| pair[0] == "-map")

‎src/video_runner.rs‎

Lines changed: 33 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -935,6 +935,23 @@ fn needs_live_timestamp_repair(log: &str) -> bool {
935935
.any(|needle| log.contains(needle))
936936
}
937937

938+
/// Return the next (AAC bitstream filter, timestamp repair) configuration.
939+
/// Timestamp repair gets one priority retry; otherwise the AAC filter is
940+
/// dropped once. Pure logic so retry ordering can be regression-tested.
941+
fn next_live_remux_attempt(
942+
with_bsf: bool,
943+
repair_timestamps: bool,
944+
log_tail: &str,
945+
) -> Option<(bool, bool)> {
946+
if !repair_timestamps && needs_live_timestamp_repair(log_tail) {
947+
Some((true, true))
948+
} else if with_bsf {
949+
Some((false, repair_timestamps))
950+
} else {
951+
None
952+
}
953+
}
954+
938955
/// Remux a stopped live capture into place. ffmpeg writes `<dest>.part` and the result is renamed to `dest` only on success: a bare `final.<n>.<ext>` is always worth keeping, a `.part` never is.
939956
pub(crate) async fn remux_live_capture(
940957
ffmpeg_bin: &Path,
@@ -1016,29 +1033,28 @@ pub(crate) async fn remux_live_capture(
10161033
};
10171034
}
10181035
let detail = last_log_line(&log_tail, "ffmpeg reported failure");
1019-
if !repair_timestamps && needs_live_timestamp_repair(&log_tail) {
1036+
let Some((next_with_bsf, next_repair_timestamps)) =
1037+
next_live_remux_attempt(with_bsf, repair_timestamps, &log_tail)
1038+
else {
1039+
let _ = tokio::fs::remove_file(&partial).await;
1040+
break Err(VideoError::combine(detail));
1041+
};
1042+
if !repair_timestamps && next_repair_timestamps {
10201043
tracing::warn!(
10211044
error = %detail,
10221045
"live remux retrying with timestamp recovery"
10231046
);
1024-
// FFmpeg failed on an input timestamp pattern that can be repaired.
1025-
// Start with the AAC fixup enabled; if that still fails, the loop
1026-
// will also test the repaired path without the bitstream filter.
1027-
let _ = tokio::fs::remove_file(&partial).await;
1028-
repair_timestamps = true;
1029-
with_bsf = true;
1030-
continue;
1031-
}
1032-
if with_bsf {
1033-
tracing::debug!(error = %detail, "live remux without bsf, retrying bare");
1034-
// The failed attempt may have left a partial output. The retry must
1035-
// start clean; the partial is ours because this loop reserved its slot.
1036-
let _ = tokio::fs::remove_file(&partial).await;
1037-
with_bsf = false;
1038-
continue;
1047+
// Keep the AAC fixup for the first repair attempt; the helper drops
1048+
// it if the repaired attempt also fails.
1049+
} else {
1050+
tracing::debug!(error = %detail, "live remux retrying with adjusted flags");
10391051
}
1052+
// The failed attempt may have left a partial output. The retry must
1053+
// start clean; the partial is ours because this loop reserved its slot.
10401054
let _ = tokio::fs::remove_file(&partial).await;
1041-
break Err(VideoError::combine(detail));
1055+
with_bsf = next_with_bsf;
1056+
repair_timestamps = next_repair_timestamps;
1057+
continue;
10421058
}
10431059
}
10441060

‎src/video_runner_tests.rs‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,34 @@ use super::*;
66

77
use crate::video_argv::VideoJob;
88

9+
#[test]
10+
fn live_remux_retry_policy_repairs_timestamps_before_dropping_aac_filter() {
11+
assert_eq!(
12+
next_live_remux_attempt(true, false, "non-monotonous DTS in output"),
13+
Some((true, true))
14+
);
15+
assert_eq!(
16+
next_live_remux_attempt(true, true, "non-monotonous DTS in output"),
17+
Some((false, true))
18+
);
19+
assert_eq!(
20+
next_live_remux_attempt(false, true, "generic failure"),
21+
None
22+
);
23+
}
24+
25+
#[test]
26+
fn live_remux_retry_policy_drops_aac_filter_once_for_other_errors() {
27+
assert_eq!(
28+
next_live_remux_attempt(true, false, "disk I/O error"),
29+
Some((false, false))
30+
);
31+
assert_eq!(
32+
next_live_remux_attempt(false, false, "disk I/O error"),
33+
None
34+
);
35+
}
36+
937
/// `Exit::RenameFailed` keeps the user's only copy: nothing re-records, nothing is
1038
/// swept except `.ytdl` state. Also pins the non-recursive staging sweep, which is
1139
/// what keeps an earlier attempt's unplaceable remux durable.

‎src/video_tests.rs‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3798,7 +3798,7 @@ fn live_argv_pins_planner_id_in_mpegts() {
37983798

37993799
#[test]
38003800
fn live_remux_argv_copies_with_fixup_and_carries_provenance() {
3801-
// Map media streams only, stream-copy, ADTS fixup, faststart.
3801+
// Preserve normal input stream mapping, stream-copy, ADTS fixup, faststart.
38023802
// Dests are the `.part` remux targets production passes: ffmpeg can't guess a
38033803
// container from that name, so `-f` must be pinned from the real extension.
38043804
let argv = live_remux_argv(
@@ -3809,8 +3809,9 @@ fn live_remux_argv_copies_with_fixup_and_carries_provenance() {
38093809
false,
38103810
"https://x.com/watch?v=abc123",
38113811
);
3812-
assert!(argv.windows(2).any(|w| w == ["-map", "0:v?"]));
3813-
assert!(argv.windows(2).any(|w| w == ["-map", "0:a?"]));
3812+
assert!(argv.windows(2).any(|w| w == ["-map", "0"]));
3813+
assert!(!argv.windows(2).any(|w| w == ["-map", "0:v?"]));
3814+
assert!(!argv.windows(2).any(|w| w == ["-map", "0:a?"]));
38143815
assert!(argv.windows(2).any(|w| w == ["-c", "copy"]));
38153816
assert!(argv.windows(2).any(|w| w == ["-bsf:a", "aac_adtstoasc"]));
38163817
assert!(argv.windows(2).any(|w| w == ["-f", "mp4"]));

0 commit comments

Comments
 (0)