388 lines
28 KiB
HTML
388 lines
28 KiB
HTML
<!DOCTYPE HTML>
|
||
<html lang="en" class="navy" dir="ltr">
|
||
<head>
|
||
<!-- Book generated using mdBook -->
|
||
<meta charset="UTF-8">
|
||
<title>libmpv2 Migration - JellyTau Documentation</title>
|
||
|
||
|
||
<!-- Custom HTML head -->
|
||
|
||
<meta name="description" content="Requirements, traceability, and architecture for the JellyTau Jellyfin client.">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<meta name="theme-color" content="#ffffff">
|
||
|
||
<link rel="icon" href="../favicon.svg">
|
||
<link rel="shortcut icon" href="../favicon.png">
|
||
<link rel="stylesheet" href="../css/variables.css">
|
||
<link rel="stylesheet" href="../css/general.css">
|
||
<link rel="stylesheet" href="../css/chrome.css">
|
||
<link rel="stylesheet" href="../css/print.css" media="print">
|
||
|
||
<!-- Fonts -->
|
||
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
|
||
<link rel="stylesheet" href="../fonts/fonts.css">
|
||
|
||
<!-- Highlight.js Stylesheets -->
|
||
<link rel="stylesheet" href="../highlight.css">
|
||
<link rel="stylesheet" href="../tomorrow-night.css">
|
||
<link rel="stylesheet" href="../ayu-highlight.css">
|
||
|
||
<!-- Custom theme stylesheets -->
|
||
|
||
</head>
|
||
<body class="sidebar-visible no-js">
|
||
<div id="body-container">
|
||
<!-- Provide site root to javascript -->
|
||
<script>
|
||
var path_to_root = "../";
|
||
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "navy" : "navy";
|
||
</script>
|
||
|
||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||
<script>
|
||
try {
|
||
var theme = localStorage.getItem('mdbook-theme');
|
||
var sidebar = localStorage.getItem('mdbook-sidebar');
|
||
|
||
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||
}
|
||
|
||
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||
}
|
||
} catch (e) { }
|
||
</script>
|
||
|
||
<!-- Set the theme before any content is loaded, prevents flash -->
|
||
<script>
|
||
var theme;
|
||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||
var html = document.querySelector('html');
|
||
html.classList.remove('navy')
|
||
html.classList.add(theme);
|
||
var body = document.querySelector('body');
|
||
body.classList.remove('no-js')
|
||
body.classList.add('js');
|
||
</script>
|
||
|
||
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
|
||
|
||
<!-- Hide / unhide sidebar before it is displayed -->
|
||
<script>
|
||
var body = document.querySelector('body');
|
||
var sidebar = null;
|
||
var sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
|
||
if (document.body.clientWidth >= 1080) {
|
||
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||
sidebar = sidebar || 'visible';
|
||
} else {
|
||
sidebar = 'hidden';
|
||
}
|
||
sidebar_toggle.checked = sidebar === 'visible';
|
||
body.classList.remove('sidebar-visible');
|
||
body.classList.add("sidebar-" + sidebar);
|
||
</script>
|
||
|
||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||
<div class="sidebar-scrollbox">
|
||
<ol class="chapter"><li class="chapter-item expanded affix "><a href="../index.html">Introduction</a></li><li class="chapter-item expanded affix "><li class="part-title">Requirements & Traceability</li><li class="chapter-item expanded "><a href="../requirements.html"><strong aria-hidden="true">1.</strong> Requirements Specification</a></li><li class="chapter-item expanded "><a href="../traceability.html"><strong aria-hidden="true">2.</strong> Traceability Matrix</a></li><li class="chapter-item expanded "><a href="../traceability-ci.html"><strong aria-hidden="true">3.</strong> Traceability CI</a></li><li class="chapter-item expanded "><a href="../traces-quick-ref.html"><strong aria-hidden="true">4.</strong> Traces Quick Reference</a></li><li class="chapter-item expanded affix "><li class="part-title">Architecture</li><li class="chapter-item expanded "><a href="../architecture/index.html"><strong aria-hidden="true">5.</strong> Overview</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../architecture/01-rust-backend.html"><strong aria-hidden="true">5.1.</strong> Rust Backend</a></li><li class="chapter-item "><a href="../architecture/02-svelte-frontend.html"><strong aria-hidden="true">5.2.</strong> Svelte Frontend</a></li><li class="chapter-item "><a href="../architecture/03-data-flow.html"><strong aria-hidden="true">5.3.</strong> Data Flow</a></li><li class="chapter-item "><a href="../architecture/04-type-sync-and-threading.html"><strong aria-hidden="true">5.4.</strong> Type Sync & Threading</a></li><li class="chapter-item "><a href="../architecture/05-platform-backends.html"><strong aria-hidden="true">5.5.</strong> Platform Backends</a></li><li class="chapter-item "><a href="../architecture/06-downloads-and-offline.html"><strong aria-hidden="true">5.6.</strong> Downloads & Offline</a></li><li class="chapter-item "><a href="../architecture/07-connectivity.html"><strong aria-hidden="true">5.7.</strong> Connectivity</a></li><li class="chapter-item "><a href="../architecture/08-database-design.html"><strong aria-hidden="true">5.8.</strong> Database Design</a></li><li class="chapter-item "><a href="../architecture/09-security.html"><strong aria-hidden="true">5.9.</strong> Security</a></li></ol></li><li class="chapter-item expanded "><li class="part-title">UX</li><li class="chapter-item expanded "><a href="../ux-flows.html"><strong aria-hidden="true">6.</strong> UX Flows</a></li><li class="chapter-item expanded affix "><li class="part-title">Specs — Pending Work</li><li class="chapter-item expanded "><a href="../specs/index.html"><strong aria-hidden="true">7.</strong> Specs Index</a></li><li class="chapter-item expanded "><a href="../specs/SPEC-TEMPLATE.html"><strong aria-hidden="true">8.</strong> Spec Template</a></li><li class="chapter-item expanded "><a href="../specs/SPEC-REVIEW-CHECKLIST.html"><strong aria-hidden="true">9.</strong> Spec Review Checklist</a></li><li class="chapter-item expanded "><a href="../specs/playback-backend-unification.html"><strong aria-hidden="true">10.</strong> Playback Backend Unification</a></li><li class="chapter-item expanded "><a href="../specs/linux-native-video-spike.html"><strong aria-hidden="true">11.</strong> Linux Native Video Spike</a></li><li class="chapter-item expanded "><a href="../specs/backend-owned-stream-selection.html"><strong aria-hidden="true">12.</strong> Backend-Owned Stream Selection</a></li><li class="chapter-item expanded "><a href="../specs/player-facade-enforcement.html"><strong aria-hidden="true">13.</strong> Player Facade Enforcement</a></li><li class="chapter-item expanded "><a href="../specs/windows-native-audio-backend.html"><strong aria-hidden="true">14.</strong> Windows Native Audio Backend</a></li><li class="chapter-item expanded "><a href="../specs/libmpv2-migration.html" class="active"><strong aria-hidden="true">15.</strong> libmpv2 Migration</a></li><li class="chapter-item expanded "><a href="../specs/read-through-media-cache.html"><strong aria-hidden="true">16.</strong> Read-Through Media Cache</a></li><li class="chapter-item expanded "><a href="../specs/scoped-search.html"><strong aria-hidden="true">17.</strong> Scoped Search</a></li><li class="chapter-item expanded "><a href="../specs/scoped-search-boundary.html"><strong aria-hidden="true">18.</strong> Scoped Search Boundary</a></li><li class="chapter-item expanded "><a href="../specs/scoped-search-boundary-implementation.html"><strong aria-hidden="true">19.</strong> Scoped Search Boundary — Implementation</a></li><li class="chapter-item expanded "><a href="../specs/frontend-domain-model.html"><strong aria-hidden="true">20.</strong> Frontend Domain Model</a></li><li class="chapter-item expanded "><a href="../specs/desktop-native-video.html"><strong aria-hidden="true">21.</strong> Desktop Native Video</a></li><li class="chapter-item expanded "><a href="../specs/build-provenance.html"><strong aria-hidden="true">22.</strong> Build Provenance</a></li><li class="chapter-item expanded affix "><li class="part-title">Build & Release</li><li class="chapter-item expanded "><a href="../build/build-release.html"><strong aria-hidden="true">23.</strong> Build & Release</a></li><li class="chapter-item expanded "><a href="../release-checklist.html"><strong aria-hidden="true">24.</strong> Release Checklist</a></li><li class="chapter-item expanded "><a href="../native-player-verification.html"><strong aria-hidden="true">25.</strong> Native Player Verification</a></li><li class="chapter-item expanded "><a href="../build/build-desktop-packages.html"><strong aria-hidden="true">26.</strong> Desktop Packaging</a></li><li class="chapter-item expanded "><a href="../build/build-windows.html"><strong aria-hidden="true">27.</strong> Windows Build</a></li><li class="chapter-item expanded "><a href="../defect-windows.html"><strong aria-hidden="true">28.</strong> Defect Windows</a></li><li class="chapter-item expanded "><a href="../build/docker.html"><strong aria-hidden="true">29.</strong> Docker</a></li><li class="chapter-item expanded "><a href="../build/build-builder-image.html"><strong aria-hidden="true">30.</strong> Builder Image</a></li><li class="chapter-item expanded affix "><li class="spacer"></li><li class="chapter-item expanded affix "><a href="../api-redirect.html">Rust API Reference (rustdoc)</a></li></ol>
|
||
</div>
|
||
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
|
||
<div class="sidebar-resize-indicator"></div>
|
||
</div>
|
||
</nav>
|
||
|
||
<!-- Track and set sidebar scroll position -->
|
||
<script>
|
||
var sidebarScrollbox = document.querySelector('#sidebar .sidebar-scrollbox');
|
||
sidebarScrollbox.addEventListener('click', function(e) {
|
||
if (e.target.tagName === 'A') {
|
||
sessionStorage.setItem('sidebar-scroll', sidebarScrollbox.scrollTop);
|
||
}
|
||
}, { passive: true });
|
||
var sidebarScrollTop = sessionStorage.getItem('sidebar-scroll');
|
||
sessionStorage.removeItem('sidebar-scroll');
|
||
if (sidebarScrollTop) {
|
||
// preserve sidebar scroll position when navigating via links within sidebar
|
||
sidebarScrollbox.scrollTop = sidebarScrollTop;
|
||
} else {
|
||
// scroll sidebar to current active section when navigating via "next/previous chapter" buttons
|
||
var activeSection = document.querySelector('#sidebar .active');
|
||
if (activeSection) {
|
||
activeSection.scrollIntoView({ block: 'center' });
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<div id="page-wrapper" class="page-wrapper">
|
||
|
||
<div class="page">
|
||
<div id="menu-bar-hover-placeholder"></div>
|
||
<div id="menu-bar" class="menu-bar sticky">
|
||
<div class="left-buttons">
|
||
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||
<i class="fa fa-bars"></i>
|
||
</label>
|
||
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||
<i class="fa fa-paint-brush"></i>
|
||
</button>
|
||
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
|
||
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
||
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
||
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
||
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
||
</ul>
|
||
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
|
||
<i class="fa fa-search"></i>
|
||
</button>
|
||
</div>
|
||
|
||
<h1 class="menu-title">JellyTau Documentation</h1>
|
||
|
||
<div class="right-buttons">
|
||
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
||
<i id="print-button" class="fa fa-print"></i>
|
||
</a>
|
||
<a href="https://gitea.tourolle.paris/dtourolle/jellytau" title="Git repository" aria-label="Git repository">
|
||
<i id="git-repository-button" class="fa fa-github"></i>
|
||
</a>
|
||
<a href="https://gitea.tourolle.paris/dtourolle/jellytau/_edit/master/docs/../docs/specs/libmpv2-migration.md" title="Suggest an edit" aria-label="Suggest an edit">
|
||
<i id="git-edit-button" class="fa fa-edit"></i>
|
||
</a>
|
||
|
||
</div>
|
||
</div>
|
||
|
||
<div id="search-wrapper" class="hidden">
|
||
<form id="searchbar-outer" class="searchbar-outer">
|
||
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
||
</form>
|
||
<div id="searchresults-outer" class="searchresults-outer hidden">
|
||
<div id="searchresults-header" class="searchresults-header"></div>
|
||
<ul id="searchresults">
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||
<script>
|
||
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||
});
|
||
</script>
|
||
|
||
<div id="content" class="content">
|
||
<main>
|
||
<h1 id="spec-migrate-to-libmpv2-and-declare-the-project-licence"><a class="header" href="#spec-migrate-to-libmpv2-and-declare-the-project-licence">Spec: Migrate to libmpv2 and declare the project licence</a></h1>
|
||
<p><strong>Status:</strong> Partially implemented — the <code>LICENSE</code> file has landed (part 2). The
|
||
<code>libmpv</code> → <code>libmpv2</code> swap (part 1) is <strong>not</strong> done: <code>src-tauri/Cargo.toml</code> still
|
||
pins the abandoned crate to a git branch.
|
||
<strong>Requirements:</strong> UR-003 → IR-003 (revises the MPV integration); no new user-facing behaviour
|
||
<strong>UX spec:</strong> n/a
|
||
<strong>Supersedes / revises:</strong> dependency and licensing housekeeping identified in <a href="playback-backend-unification.html">playback-backend-unification.md</a></p>
|
||
<h2 id="summary"><a class="header" href="#summary">Summary</a></h2>
|
||
<p>Two related pieces of housekeeping that block or complicate later work:</p>
|
||
<ol>
|
||
<li>Replace the abandoned <code>libmpv</code> crate (pinned to a git branch) with the
|
||
maintained <code>libmpv2</code>.</li>
|
||
<li>Add a <code>LICENSE</code> file. The project has none, which leaves its legal status
|
||
undefined while it links GPL-licensed libmpv.</li>
|
||
</ol>
|
||
<p>Neither changes user-visible behaviour. Both are prerequisites for
|
||
<a href="windows-native-audio-backend.html">windows-native-audio-backend.md</a>.</p>
|
||
<h2 id="motivation"><a class="header" href="#motivation">Motivation</a></h2>
|
||
<h3 id="the-dependency-is-dead"><a class="header" href="#the-dependency-is-dead">The dependency is dead</a></h3>
|
||
<pre><code class="language-toml"># src-tauri/Cargo.toml
|
||
libmpv = { git = "https://github.com/ParadoxSpiral/libmpv-rs.git", branch = "master" }
|
||
</code></pre>
|
||
<ul>
|
||
<li>crates.io <code>libmpv</code> 2.0.1 was published <strong>2020-09-29</strong>.</li>
|
||
<li>The upstream repo's last commit was <strong>2023-01-08</strong>; nothing since was released.</li>
|
||
<li>We pin a git <em>branch</em>, so builds are not reproducible — the same lockfile-less
|
||
checkout can resolve differently over time, and CI has no protection if the
|
||
branch moves or the repo disappears.</li>
|
||
</ul>
|
||
<p><code>libmpv2</code> (kohsine/libmpv2-rs) is a maintained fork of exactly this crate:
|
||
6.0.0 released <strong>2026-05-12</strong>, ~23.5k recent downloads against the original's
|
||
~1.1k, releases roughly quarterly since 2024.</p>
|
||
<h3 id="the-project-has-no-licence"><a class="header" href="#the-project-has-no-licence">The project has no licence</a></h3>
|
||
<p>There is no <code>LICENSE</code>/<code>COPYING</code> file and <code>src-tauri/Cargo.toml</code> has no <code>license</code>
|
||
field. The project is open source and will never be commercial, so this is purely
|
||
an omission — but it matters because we link libmpv, and "no licence" defaults to
|
||
<em>all rights reserved</em>, which is incompatible with distributing a GPL-derived
|
||
work.</p>
|
||
<h2 id="design"><a class="header" href="#design">Design</a></h2>
|
||
<h3 id="part-1--licence"><a class="header" href="#part-1--licence">Part 1 — licence</a></h3>
|
||
<p><strong>Use GPLv3.</strong> This is forced, not chosen:</p>
|
||
<ul>
|
||
<li>mpv's default build is <strong>GPLv2-or-later</strong>, so the combined work must be
|
||
GPL-compatible.</li>
|
||
<li>Apache-2.0 is <strong>GPLv2-incompatible</strong> (patent-termination and indemnification
|
||
clauses) but GPLv3-compatible.</li>
|
||
<li>A scan of the dependency tree found Apache-2.0-<strong>only</strong> crates with no
|
||
alternative arm — most importantly <strong><code>tao</code></strong> (Tauri's own windowing crate),
|
||
plus <code>sync_wrapper</code>, <code>gethostname</code>, and <code>ring</code> (Apache-2.0 AND ISC).</li>
|
||
</ul>
|
||
<p><code>tao</code> is unavoidable in a Tauri app, so GPLv2 is unavailable. Exercising mpv's
|
||
"or later" option puts the combination at <strong>GPLv3</strong>.</p>
|
||
<p>Actions:</p>
|
||
<ul>
|
||
<li>Add <code>LICENSE</code> containing the GPLv3 text.</li>
|
||
<li>Add <code>license = "GPL-3.0-or-later"</code> to <code>src-tauri/Cargo.toml</code> and <code>license</code> to
|
||
<code>package.json</code>.</li>
|
||
<li>Note in the README that the binary links libmpv (GPLv2+) and FFmpeg.</li>
|
||
</ul>
|
||
<p>Because the project is open source, we use mpv's <strong>default GPL build</strong> — no
|
||
<code>-Dgpl=false</code>, no LGPL FFmpeg build, and none of the LGPL §6 relinking analysis
|
||
that a proprietary app would need. We keep VAAPI/VDPAU/X11 and every GPL FFmpeg
|
||
filter.</p>
|
||
<p>🔴 Never build FFmpeg with <code>--enable-nonfree</code> — that produces a binary that is
|
||
<strong>unredistributable under any licence</strong>, open source or not.</p>
|
||
<h3 id="part-2--libmpv--libmpv2"><a class="header" href="#part-2--libmpv--libmpv2">Part 2 — libmpv → libmpv2</a></h3>
|
||
<pre><code class="language-toml"># Linux (and later Windows, per the Windows audio spec)
|
||
libmpv2 = "=6.0.0"
|
||
</code></pre>
|
||
<p>Pin exactly: <code>libmpv2</code> has broken its API in <strong>every</strong> major release.</p>
|
||
<p>Breaking changes to expect, from the changelog:</p>
|
||
<div class="table-wrapper"><table><thead><tr><th>Version</th><th>Change</th><th>Impact here</th></tr></thead><tbody>
|
||
<tr><td>4.0.0</td><td>Removed command helper methods — call <code>mpv.command(...)</code> directly</td><td>Low; we already use <code>command</code>/<code>set_property</code></td></tr>
|
||
<tr><td>5.0.0</td><td>Removed <code>mpv_node</code> support entirely (properties return strings; parse JSON yourself); <code>EventContext</code> folded into <code>Mpv</code>; <code>ProtocolContext</code> → <code>Protocol</code></td><td><strong>Medium</strong> — <code>start_event_loop</code> uses <code>create_event_context()</code>; check whether that call still exists</td></tr>
|
||
<tr><td>6.0.0</td><td><code>RenderContext::new()</code> → <code>Mpv::create_render_context()</code>; <code>'static</code> bound on <code>OpenGLInitParams</code>; render context now borrows <code>Mpv</code> (fixes a use-after-free)</td><td><strong>None</strong> — we do not use the render API</td></tr>
|
||
</tbody></table>
|
||
</div>
|
||
<p>The last row matters: we run mpv audio-only (<code>video = no</code>), so the entire render
|
||
surface is irrelevant to us. Consider disabling the default <code>render</code> feature to
|
||
reduce build surface.</p>
|
||
<p>The main porting work is the event loop in <code>mpv_backend.rs</code> — <code>wait_event</code>,
|
||
<code>disable_deprecated_events</code>, and the <code>FileLoaded</code> / <code>PlaybackRestart</code> /
|
||
<code>PropertyChange</code> / <code>EndFile</code> handling, given 5.0.0 folded <code>EventContext</code> into
|
||
<code>Mpv</code>.</p>
|
||
<p>Everything else — <code>set_property</code> calls, the <code>af</code> filter graph, the 250ms position
|
||
thread, the seek-suppression window — should port unchanged.</p>
|
||
<h2 id="layer-assignment"><a class="header" href="#layer-assignment">Layer assignment</a></h2>
|
||
<p>No logic moves. This is a dependency swap plus a licence file; the
|
||
<code>PlayerBackend</code> trait boundary is untouched.</p>
|
||
<div class="table-wrapper"><table><thead><tr><th>Logic / responsibility</th><th>Layer</th><th>Why it belongs there</th></tr></thead><tbody>
|
||
<tr><td>mpv event → <code>PlayerStatusEvent</code> mapping</td><td>Rust (unchanged)</td><td>Already correct; only the binding API beneath it changes.</td></tr>
|
||
</tbody></table>
|
||
</div>
|
||
<h2 id="out-of-scope"><a class="header" href="#out-of-scope">Out of scope</a></h2>
|
||
<ul>
|
||
<li>Any behaviour change. If playback behaves differently after this, that is a bug.</li>
|
||
<li>Windows support — separate spec, but this must land first.</li>
|
||
<li>Adopting the render API. We are audio-only on mpv.</li>
|
||
<li>Re-licensing decisions beyond adding the file the project already implies.</li>
|
||
</ul>
|
||
<h2 id="acceptance-criteria"><a class="header" href="#acceptance-criteria">Acceptance criteria</a></h2>
|
||
<ul>
|
||
<li><input disabled="" type="checkbox"/>
|
||
<code>LICENSE</code> (GPLv3) present; <code>license</code> field set in <code>Cargo.toml</code> and <code>package.json</code>.</li>
|
||
<li><input disabled="" type="checkbox"/>
|
||
A full dependency-licence audit has been run (<code>cargo install cargo-license && cargo license</code>) and confirms no GPLv3-incompatible dependency. <em>(The scan behind this spec resolved 441 of 575 crates from the local registry cache; the remaining 134 are unverified.)</em></li>
|
||
<li><input disabled="" type="checkbox"/>
|
||
<code>libmpv</code> git dependency removed; <code>libmpv2</code> pinned to an exact version.</li>
|
||
<li><input disabled="" type="checkbox"/>
|
||
Linux audio playback works identically: play/pause/seek/volume, queue advance, gapless, EQ, normalization, sleep timer.</li>
|
||
<li><input disabled="" type="checkbox"/>
|
||
Position updates still arrive at 250ms; the 150ms post-seek suppression still prevents the jump-to-zero glitch.</li>
|
||
<li><input disabled="" type="checkbox"/>
|
||
<code>EndFile</code> still emits <code>PlaybackEnded</code> only for EOF (not STOP/QUIT/ERROR) — autoplay depends on this.</li>
|
||
<li><input disabled="" type="checkbox"/>
|
||
Builder image updated if the libmpv dev package requirement changed; <strong>no toolchain install added to any CI step</strong>.</li>
|
||
<li><input disabled="" type="checkbox"/>
|
||
<code>bun run check</code>, <code>bun run test</code>, <code>bun run check:boundary</code> pass.</li>
|
||
<li><input disabled="" type="checkbox"/>
|
||
<code>cargo fmt</code> clean, <code>cargo clippy</code> clean, <code>bun run test:rust</code> passes.</li>
|
||
</ul>
|
||
<h2 id="testing"><a class="header" href="#testing">Testing</a></h2>
|
||
<p>The existing <code>mpv_backend_test.rs</code> plus the <code>build_af_filter</code>,
|
||
<code>eq_filter_entries</code>, and <code>normalize_filter_entry</code> tests are the regression net —
|
||
they must pass unchanged, since none of them touch the binding API.</p>
|
||
<p>The event loop has no unit tests and is where the risk concentrates. Verify
|
||
manually on Linux:</p>
|
||
<ol>
|
||
<li>Play → pause → play; confirm position does not flash to 0:00 (the known
|
||
playing-event regression).</li>
|
||
<li>Seek mid-track; confirm no jump-to-zero within 150ms.</li>
|
||
<li>Let a track end naturally; confirm autoplay advances (exercises <code>EndFile</code> EOF).</li>
|
||
<li>Press stop; confirm autoplay does <strong>not</strong> advance.</li>
|
||
<li>Sleep-timer expiry; confirm it stops without triggering autoplay.</li>
|
||
</ol>
|
||
<p>Cases 3–5 are the ones most likely to break silently, and each corresponds to a
|
||
bug already fixed once in this codebase.</p>
|
||
<h2 id="traces"><a class="header" href="#traces">TRACES</a></h2>
|
||
<ul>
|
||
<li><code>MpvBackend</code> construction / event loop → existing <code>// TRACES: UR-003 | IR-003</code>, unchanged</li>
|
||
<li>No new requirement IDs; this is a dependency migration.</li>
|
||
</ul>
|
||
<h2 id="notes-for-the-implementer"><a class="header" href="#notes-for-the-implementer">Notes for the implementer</a></h2>
|
||
<ul>
|
||
<li>Do this <strong>before</strong> the Windows audio backend.</li>
|
||
<li>Read the 4.0/5.0/6.0 changelogs before writing code — the crate has broken API
|
||
in every major release, most recently two months before this spec.</li>
|
||
<li>The crates.io <code>repository</code> field for <code>libmpv2</code> points at <code>kohsine/libmpv-rs</code>,
|
||
but the repo was renamed to <strong><code>libmpv2-rs</code></strong>; the old raw URLs 404.</li>
|
||
<li><code>libmpv2-sys</code> ships pregenerated bindings and vendored headers, so no libclang
|
||
is needed at build time — relevant to keeping the builder image thin.</li>
|
||
<li>A parallel Claude session may be active — <code>git diff</code> before "repairing"
|
||
unexpected changes.</li>
|
||
</ul>
|
||
|
||
</main>
|
||
|
||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||
<!-- Mobile navigation buttons -->
|
||
<a rel="prev" href="../specs/windows-native-audio-backend.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||
<i class="fa fa-angle-left"></i>
|
||
</a>
|
||
|
||
<a rel="next prefetch" href="../specs/read-through-media-cache.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||
<i class="fa fa-angle-right"></i>
|
||
</a>
|
||
|
||
<div style="clear: both"></div>
|
||
</nav>
|
||
</div>
|
||
</div>
|
||
|
||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||
<a rel="prev" href="../specs/windows-native-audio-backend.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||
<i class="fa fa-angle-left"></i>
|
||
</a>
|
||
|
||
<a rel="next prefetch" href="../specs/read-through-media-cache.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||
<i class="fa fa-angle-right"></i>
|
||
</a>
|
||
</nav>
|
||
|
||
</div>
|
||
|
||
|
||
|
||
|
||
<script>
|
||
window.playground_copyable = true;
|
||
</script>
|
||
|
||
|
||
<script src="../elasticlunr.min.js"></script>
|
||
<script src="../mark.min.js"></script>
|
||
<script src="../searcher.js"></script>
|
||
|
||
<script src="../clipboard.min.js"></script>
|
||
<script src="../highlight.js"></script>
|
||
<script src="../book.js"></script>
|
||
|
||
<!-- Custom JS scripts -->
|
||
|
||
|
||
</div>
|
||
</body>
|
||
</html>
|