From 6a310d7825f12fe682ef8a6ad03bc89a78a54e7a Mon Sep 17 00:00:00 2001 From: Mrminecope Date: Thu, 24 Sep 2026 11:41:18 +0530 Subject: [PATCH 1/7] ci: enable v0.1.24 release publication --- .github/workflows/release.yml | 75 ++++++++++++++++++++++++++++++++--- 1 file changed, 69 insertions(+), 6 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 577acc0..783f08b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -4,27 +4,67 @@ on: push: tags: - 'v*' + branches: + - 'v*' + workflow_dispatch: permissions: contents: read +concurrency: + group: moviebox-release-${{ github.ref_name }} + cancel-in-progress: true + jobs: create-release: name: Create GitHub release + if: startsWith(github.ref_name, 'v') runs-on: ubuntu-latest permissions: contents: write steps: - name: Checkout uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Align release tag with branch commit + if: github.ref_type == 'branch' + shell: bash + env: + RELEASE_TAG: ${{ github.ref_name }} + RELEASE_SHA: ${{ github.sha }} + run: | + set -euo pipefail + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git tag -fa "${RELEASE_TAG}" "${RELEASE_SHA}" -m "MovieBox ${RELEASE_TAG}" + git push origin "refs/tags/${RELEASE_TAG}" --force + + - name: Remove previous release assets + shell: bash + env: + GH_TOKEN: ${{ github.token }} + RELEASE_TAG: ${{ github.ref_name }} + run: | + set -euo pipefail + if gh release view "${RELEASE_TAG}" --repo "${GITHUB_REPOSITORY}" >/dev/null 2>&1; then + gh release view "${RELEASE_TAG}" --repo "${GITHUB_REPOSITORY}" --json assets --jq '.assets[].name' | + while IFS= read -r asset_name; do + [ -z "${asset_name}" ] && continue + gh release delete-asset "${RELEASE_TAG}" "${asset_name}" --repo "${GITHUB_REPOSITORY}" --yes || + echo "Asset ${asset_name} disappeared before cleanup; continuing." + done + fi - name: Create release uses: taiki-e/create-gh-release-action@v1 with: changelog: CHANGELOG.md - + ref: refs/tags/${{ github.ref_name }} upload-assets: name: Build ${{ matrix.name }} + if: github.ref_type == 'branch' || github.ref_type == 'tag' needs: create-release strategy: fail-fast: false @@ -47,11 +87,10 @@ jobs: target: x86_64-pc-windows-msvc os: windows-latest archive: MovieBox_Windows_x64 - - name: Android arm64 - target: aarch64-linux-android - os: ubuntu-latest - archive: MovieBox_Android_arm64 - build-tool: cross + - name: Windows arm64 + target: aarch64-pc-windows-msvc + os: windows-latest + archive: MovieBox_Windows_arm64 runs-on: ${{ matrix.os }} permissions: @@ -69,6 +108,30 @@ jobs: build-tool: ${{ matrix.build-tool || '' }} locked: true checksum: sha256 + ref: refs/tags/${{ github.ref_name }} include: LICENSE-MIT,LICENSE-APACHE,README.md tar: unix zip: windows + publish-checksums: + name: Publish SHA256SUMS + if: github.ref_type == 'branch' || github.ref_type == 'tag' + needs: upload-assets + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Download per-asset checksums + env: + GH_TOKEN: ${{ github.token }} + run: | + mkdir -p checksums + gh release download "${GITHUB_REF_NAME}" --pattern "*.sha256" --dir checksums --repo "${GITHUB_REPOSITORY}" + - name: Build aggregate checksum file + run: | + set -euo pipefail + cat checksums/*.sha256 | sort > SHA256SUMS + - name: Upload aggregate checksum file + env: + GH_TOKEN: ${{ github.token }} + run: | + gh release upload "${GITHUB_REF_NAME}" SHA256SUMS --clobber --repo "${GITHUB_REPOSITORY}" From 03c34bdefbc3a00183c189ffffc0584fe8e5d425 Mon Sep 17 00:00:00 2001 From: Mrminecope Date: Thu, 24 Sep 2026 11:41:43 +0530 Subject: [PATCH 2/7] docs: finalize v0.1.24 credit --- AUTHORS.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/AUTHORS.md b/AUTHORS.md index 94e3b5d..99abb53 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -4,3 +4,5 @@ MovieBox is created and maintained by **Mrminecope**. Project repository: https://github.com/Mrminecope/MovieBox Project website: https://mrminecope.github.io/MovieBox/ + +Release 0.1.24 packaging and publication: **Mrminecope**. From 8dcdc5297810da24b3cbda307c3ef58ae133a380 Mon Sep 17 00:00:00 2001 From: Mrminecope Date: Thu, 24 Sep 2026 11:46:24 +0530 Subject: [PATCH 3/7] fix: repair proxy header test escaping --- src/proxy.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/proxy.rs b/src/proxy.rs index 9eaac2b..55f8540 100644 --- a/src/proxy.rs +++ b/src/proxy.rs @@ -432,7 +432,7 @@ fn format_proxy_response_headers( if is_partial_content && !headers .keys() - .any(|name| name.eq_ignore_ascii_case("accept-ranges")) + .any(|name| name.as_str().eq_ignore_ascii_case("accept-ranges")) { out.extend_from_slice(b"Accept-Ranges: bytes\r\n"); } @@ -687,7 +687,7 @@ mod tests { headers.insert("content-type", "video/mp4".parse().unwrap()); headers.insert("content-length", "1000".parse().unwrap()); headers.insert("content-range", "bytes 1000-1999/10000".parse().unwrap()); - headers.insert("etag", ""movie-v1"".parse().unwrap()); + headers.insert("etag", "\"movie-v1\"".parse().unwrap()); headers.insert("last-modified", "Wed, 23 Sep 2026 08:00:00 GMT".parse().unwrap()); let url = "https://cdn.example.com/video.mp4"; @@ -695,7 +695,7 @@ mod tests { String::from_utf8(format_proxy_response_headers(&headers, url, true)).unwrap(); assert!(out.contains("content-range: bytes 1000-1999/10000")); - assert!(out.contains("etag: "movie-v1"")); + assert!(out.contains("etag: \"movie-v1\"")); assert!(out.contains("last-modified: Wed, 23 Sep 2026 08:00:00 GMT")); assert!(out.contains("Accept-Ranges: bytes")); } From 2d4221309644f99ab06641aa7af485f93d41f05d Mon Sep 17 00:00:00 2001 From: Mrminecope Date: Thu, 24 Sep 2026 12:01:18 +0530 Subject: [PATCH 4/7] fix(v0.1.24): restore src/player.rs playback baseline --- src/player.rs | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/src/player.rs b/src/player.rs index 42b018c..c041106 100644 --- a/src/player.rs +++ b/src/player.rs @@ -630,10 +630,6 @@ fn vlc_command( } command.arg("--play-and-exit"); command.arg("--adaptive-logic=highest"); - // Give VLC enough network headroom for remote DASH seeks and reconnect - // automatically if a seek causes the current HTTP connection to drop. - command.arg("--network-caching=5000"); - command.arg("--http-reconnect"); if let Some(start) = resume_seconds { if start > 0 { command.arg(format!("--start-time={start}")); @@ -1452,21 +1448,20 @@ mod tests { #[test] fn test_format_mpv_script_opts_windows_paths() { - let win_path = - PathBuf::from(r"C:\Users\User\AppData\Local\MovieBox\playback\moviebox_123_1_1.json"); + let win_path = PathBuf::from( + r"C:\Users\User\AppData\Local\MovieBox-Tui\playback\moviebox_123_1_1.json", + ); let opts = format_mpv_script_opts("moviebox", "123", 1, 1, &win_path); assert!(!opts.contains(r"\")); - assert!(opts.contains("moviebox-state_file=C:/Users/User/AppData/Local/MovieBox/playback/moviebox_123_1_1.json")); + assert!(opts.contains("moviebox-state_file=C:/Users/User/AppData/Local/MovieBox-Tui/playback/moviebox_123_1_1.json")); } #[test] fn test_format_mpv_script_opts_unix_paths() { let unix_path = - PathBuf::from("/home/user/.local/share/moviebox/playback/moviebox_123_1_1.json"); + PathBuf::from("/home/user/.local/share/moviebox-tui/playback/moviebox_123_1_1.json"); let opts = format_mpv_script_opts("moviebox", "123", 1, 1, &unix_path); - assert!(opts.contains( - "moviebox-state_file=/home/user/.local/share/moviebox/playback/moviebox_123_1_1.json" - )); + assert!(opts.contains("moviebox-state_file=/home/user/.local/share/moviebox-tui/playback/moviebox_123_1_1.json")); } #[test] @@ -1490,9 +1485,6 @@ mod tests { assert!(args.contains(&"--width=1280".into())); assert!(args.contains(&"--height=720".into())); assert!(args.contains(&"--play-and-exit".into())); - assert!(args.contains(&"--adaptive-logic=highest".into())); - assert!(args.contains(&"--network-caching=5000".into())); - assert!(args.contains(&"--http-reconnect".into())); assert!(args.contains(&"--start-time=42".into())); assert!(args.contains(&"--http-referrer=https://example.test/".into())); assert!(args.contains(&"--http-user-agent=MovieBox-Test".into())); @@ -1508,7 +1500,7 @@ mod tests { fn vlc_command_normalizes_windows_subtitle_paths() { let command = vlc_command( "https://example.test/video.mp4", - Some(r"C:\Users\User\AppData\Local\MovieBox\subs\sub.srt"), + Some(r"C:\Users\User\AppData\Local\MovieBox-Tui\subs\sub.srt"), &[], None, None, @@ -1518,7 +1510,9 @@ mod tests { .map(|arg| arg.to_string_lossy().into_owned()) .collect::>(); assert!( - args.contains(&"--sub-file=C:/Users/User/AppData/Local/MovieBox/subs/sub.srt".into()) + args.contains( + &"--sub-file=C:/Users/User/AppData/Local/MovieBox-Tui/subs/sub.srt".into() + ) ); } #[test] From e7e9d45f1fb2e295b3f8aba3f9ddddf3454b5e1e Mon Sep 17 00:00:00 2001 From: Mrminecope Date: Thu, 24 Sep 2026 12:01:22 +0530 Subject: [PATCH 5/7] fix(v0.1.24): restore src/proxy.rs playback baseline --- src/proxy.rs | 47 ++++------------------------------------------- 1 file changed, 4 insertions(+), 43 deletions(-) diff --git a/src/proxy.rs b/src/proxy.rs index 55f8540..d7d7d9c 100644 --- a/src/proxy.rs +++ b/src/proxy.rs @@ -231,7 +231,6 @@ async fn handle_connection( let path_and_query = parts.next().unwrap_or("/"); let mut range_header = None; - let mut if_range_header = None; let mut header_count = 0usize; loop { let mut header_line = String::new(); @@ -252,8 +251,6 @@ async fn handle_connection( if let Some((name, val)) = trimmed.split_once(':') { if name.trim().eq_ignore_ascii_case("range") { range_header = Some(val.trim().to_string()); - } else if name.trim().eq_ignore_ascii_case("if-range") { - if_range_header = Some(val.trim().to_string()); } } } @@ -299,9 +296,6 @@ async fn handle_connection( if let Some(range) = range_header { req = req.header("Range", range); } - if let Some(if_range) = if_range_header { - req = req.header("If-Range", if_range); - } let upstream_res = match req.send().await { Ok(res) => res, @@ -369,8 +363,7 @@ async fn handle_connection( return Ok(()); } - let headers_bytes = - format_proxy_response_headers(upstream_res.headers(), &target_url, status == reqwest::StatusCode::PARTIAL_CONTENT); + let headers_bytes = format_proxy_response_headers(upstream_res.headers(), &target_url); writer.write_all(&headers_bytes).await?; let mut stream = upstream_res.bytes_stream(); @@ -391,7 +384,6 @@ async fn handle_connection( fn format_proxy_response_headers( headers: &reqwest::header::HeaderMap, target_url: &str, - is_partial_content: bool, ) -> Vec { let mut out = Vec::new(); let clean_path = target_url @@ -414,8 +406,6 @@ fn format_proxy_response_headers( || name_str.eq_ignore_ascii_case("content-length") || name_str.eq_ignore_ascii_case("content-range") || name_str.eq_ignore_ascii_case("accept-ranges") - || name_str.eq_ignore_ascii_case("etag") - || name_str.eq_ignore_ascii_case("last-modified") { if let Ok(val_str) = header_val.to_str() { out.extend_from_slice(format!("{name_str}: {val_str}\r\n").as_bytes()); @@ -429,13 +419,6 @@ fn format_proxy_response_headers( } else if is_vtt { out.extend_from_slice(b"Content-Type: text/vtt\r\n"); } - if is_partial_content - && !headers - .keys() - .any(|name| name.as_str().eq_ignore_ascii_case("accept-ranges")) - { - out.extend_from_slice(b"Accept-Ranges: bytes\r\n"); - } out.extend_from_slice(b"Connection: close\r\n\r\n"); out } @@ -638,8 +621,7 @@ mod tests { headers.insert("server", "cloudflare".parse().unwrap()); let url = "https://cdn.example.com/subs.SRT?token=abc123&expires=999#top"; - let out = - String::from_utf8(format_proxy_response_headers(&headers, url, false)).unwrap(); + let out = String::from_utf8(format_proxy_response_headers(&headers, url)).unwrap(); assert_eq!(out.matches("Access-Control-Allow-Origin: *").count(), 1); assert_eq!(out.matches("Content-Type: application/x-subrip").count(), 1); @@ -656,8 +638,7 @@ mod tests { headers.insert("content-length", "500".parse().unwrap()); let url = "https://cdn.example.com/subs.vtt"; - let out = - String::from_utf8(format_proxy_response_headers(&headers, url, false)).unwrap(); + let out = String::from_utf8(format_proxy_response_headers(&headers, url)).unwrap(); assert_eq!(out.matches("Access-Control-Allow-Origin: *").count(), 1); assert_eq!(out.matches("Content-Type: text/vtt").count(), 1); @@ -672,31 +653,11 @@ mod tests { headers.insert("content-length", "10000000".parse().unwrap()); let url = "https://cdn.example.com/video.mp4"; - let out = - String::from_utf8(format_proxy_response_headers(&headers, url, false)).unwrap(); + let out = String::from_utf8(format_proxy_response_headers(&headers, url)).unwrap(); assert_eq!(out.matches("Access-Control-Allow-Origin: *").count(), 1); assert!(out.contains("content-type: video/mp4")); assert!(!out.contains("application/x-subrip")); assert!(!out.contains("text/vtt")); } - - #[test] - fn test_format_proxy_response_headers_for_partial_content() { - let mut headers = reqwest::header::HeaderMap::new(); - headers.insert("content-type", "video/mp4".parse().unwrap()); - headers.insert("content-length", "1000".parse().unwrap()); - headers.insert("content-range", "bytes 1000-1999/10000".parse().unwrap()); - headers.insert("etag", "\"movie-v1\"".parse().unwrap()); - headers.insert("last-modified", "Wed, 23 Sep 2026 08:00:00 GMT".parse().unwrap()); - - let url = "https://cdn.example.com/video.mp4"; - let out = - String::from_utf8(format_proxy_response_headers(&headers, url, true)).unwrap(); - - assert!(out.contains("content-range: bytes 1000-1999/10000")); - assert!(out.contains("etag: \"movie-v1\"")); - assert!(out.contains("last-modified: Wed, 23 Sep 2026 08:00:00 GMT")); - assert!(out.contains("Accept-Ranges: bytes")); - } } From 92023be0e64113e138fe8bc22562cb033ee4986e Mon Sep 17 00:00:00 2001 From: Mrminecope Date: Thu, 24 Sep 2026 12:01:31 +0530 Subject: [PATCH 6/7] docs(v0.1.24): document playback compatibility rebuild --- CHANGELOG.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e418d3c..c8d9401 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,14 @@ # Changelog -## [0.1.24] - 2026-09-23 +## [0.1.24] - 2026-09-24 ### Fixed -- **VLC Seek / Buffer Recovery**: - - Increased VLC network caching to 5 seconds for remote MovieBox streams so seeking has enough buffer headroom instead of appearing frozen. - - Enabled VLC HTTP reconnect so dropped HTTP connections during seeks can recover automatically. - - Preserved `If-Range`, `ETag`, and `Last-Modified` metadata through the MovieBox loopback proxy and advertised byte-range support for `206 Partial Content` responses, improving reliable HTTP seeking for proxied DASH/media streams. +- **Playback compatibility rebuild**: + - Rebuilt v0.1.24 from the working v0.1.23 playback baseline after the first 0.1.24 build could fail to launch media on some player/client combinations. + - Restored the proven VLC and MovieBox proxy playback paths from v0.1.23. + - Removed the experimental VLC seek/reconnect flags and proxy validator changes from the previous 0.1.24 build. +- **Release identity**: + - Package, binary, repository, and updater identity remain `moviebox`, `moviebox.exe`, and `Mrminecope/MovieBox`. ## [0.1.23] - 2026-09-21 From 8dba9d3d7973cab3d3bf620d8dc41cf3cdbe1291 Mon Sep 17 00:00:00 2001 From: Mrminecope Date: Thu, 24 Sep 2026 12:01:47 +0530 Subject: [PATCH 7/7] ci(v0.1.24): rebuild release cleanly --- .github/workflows/release.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 783f08b..75aaa84 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -28,6 +28,16 @@ jobs: with: fetch-depth: 0 + - name: Delete existing v0.1.24 release and tag for rebuild + if: github.ref_name == 'v0.1.24' + shell: bash + env: + GH_TOKEN: ${{ github.token }} + RELEASE_TAG: ${{ github.ref_name }} + run: | + set -euo pipefail + gh release delete "${RELEASE_TAG}" --repo "${GITHUB_REPOSITORY}" --yes --cleanup-tag=true || true + - name: Align release tag with branch commit if: github.ref_type == 'branch' shell: bash