Deployed b107bca with MkDocs version: 1.6.1

This commit is contained in:
2026-10-14 17:05:03 +00:00
parent c90b976366
commit bf1a26d065
9 changed files with 912 additions and 704 deletions
+74 -3
View File
@@ -2037,6 +2037,17 @@
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#post-apisessionsession_idsamples" class="md-nav__link">
<span class="md-ellipsis">
POST /api/session/{session_id}/samples
</span>
</a>
</li>
<li class="md-nav__item">
@@ -2941,6 +2952,17 @@
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#post-apisessionsession_idsamples" class="md-nav__link">
<span class="md-ellipsis">
POST /api/session/{session_id}/samples
</span>
</a>
</li>
<li class="md-nav__item">
@@ -4826,9 +4848,10 @@ answers the prompt within 10 minutes.</p>
</span><span id="__span-30-4"><a id="__codelineno-30-4" name="__codelineno-30-4" href="#__codelineno-30-4"></a>POST /api/session/{id}/tracks → tracks + chapters (with URLs) for one title
</span><span id="__span-30-5"><a id="__codelineno-30-5" name="__codelineno-30-5" href="#__codelineno-30-5"></a>POST /api/session/{id}/segments → resolve segment URLs / DRM / headers per track
</span><span id="__span-30-6"><a id="__codelineno-30-6" name="__codelineno-30-6" href="#__codelineno-30-6"></a>POST /api/session/{id}/segment_filter → HLS segments the service drops (ads, bumpers)
</span><span id="__span-30-7"><a id="__codelineno-30-7" name="__codelineno-30-7" href="#__codelineno-30-7"></a>POST /api/session/{id}/license → proxy or server-CDM DRM licensing
</span><span id="__span-30-8"><a id="__codelineno-30-8" name="__codelineno-30-8" href="#__codelineno-30-8"></a>POST /api/session/{id}/keys/bad → report a server-vault content key that did not decrypt
</span><span id="__span-30-9"><a id="__codelineno-30-9" name="__codelineno-30-9" href="#__codelineno-30-9"></a>DELETE /api/session/{id} → tear down, harvest updated cache
</span><span id="__span-30-7"><a id="__codelineno-30-7" name="__codelineno-30-7" href="#__codelineno-30-7"></a>POST /api/session/{id}/samples → decrypt cbcs samples of a track on the server
</span><span id="__span-30-8"><a id="__codelineno-30-8" name="__codelineno-30-8" href="#__codelineno-30-8"></a>POST /api/session/{id}/license → proxy or server-CDM DRM licensing
</span><span id="__span-30-9"><a id="__codelineno-30-9" name="__codelineno-30-9" href="#__codelineno-30-9"></a>POST /api/session/{id}/keys/bad → report a server-vault content key that did not decrypt
</span><span id="__span-30-10"><a id="__codelineno-30-10" name="__codelineno-30-10" href="#__codelineno-30-10"></a>DELETE /api/session/{id} → tear down, harvest updated cache
</span></code></pre></div>
<h3 id="slow-responses">Slow responses<a class="headerlink" href="#slow-responses" title="Permanent link">&para;</a></h3>
<p>The <code>titles</code>, <code>tracks</code> and <code>license</code> routes can take minutes on a large catalogue. When the
@@ -5157,6 +5180,54 @@ A slow answer arrives as a <a href="#slow-responses">slow response</a>.</p>
</tr>
</tbody>
</table>
<h3 id="post-apisessionsession_idsamples"><code>POST /api/session/{session_id}/samples</code><a class="headerlink" href="#post-apisessionsession_idsamples" title="Permanent link">&para;</a></h3>
<p>Decrypt the samples of a track that the service decrypts itself after the download. The tracks response lists those track IDs in <code>sample_decrypt_tracks</code>. The client downloads the file, then sends each <code>stsd</code> sample entry's samples here, and writes the plaintexts back in place. The query string names the track and the 1-based entry: <code>?track_id=a-1&amp;entry=2</code>.</p>
<ul>
<li>An empty body asks how the entry decrypts. The JSON response is <code>{ "key": "&lt;hex&gt;" }</code>, which the client then uses itself, or <code>{ "decrypter": true }</code>.</li>
<li>An <code>application/octet-stream</code> body is a sample frame: a big-endian <code>uint32</code> count, one <code>uint32</code> length per sample, then the samples. Each sample is a ciphertext cut to whole AES blocks. The response is a frame of the same shape with the plaintexts in the same order.</li>
</ul>
<p>The client sends at most 8 MiB of samples per request, and the route accepts a body of up to 16 MiB. Binary frames keep the upload the size of the samples, and the server does not compress them. The route needs the <code>server_cdm</code> grant for the service, because the decrypter can use a process or an account of the server.</p>
<table>
<thead>
<tr>
<th>Status</th>
<th>Error code</th>
<th>Meaning</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>200</code></td>
<td>-</td>
<td>The content key, <code>decrypter</code>, or the plaintext frame.</td>
</tr>
<tr>
<td><code>400</code></td>
<td><code>INVALID_INPUT</code></td>
<td>Missing <code>track_id</code>, a bad <code>entry</code>, a malformed frame, or a track the service does not decrypt.</td>
</tr>
<tr>
<td><code>413</code></td>
<td>-</td>
<td>The body is larger than 16 MiB.</td>
</tr>
<tr>
<td><code>403</code></td>
<td><code>FORBIDDEN</code></td>
<td>The API key has no <code>server_cdm</code> grant for the service.</td>
</tr>
<tr>
<td><code>404</code></td>
<td><code>TRACK_NOT_FOUND</code></td>
<td>An unknown track ID was requested.</td>
</tr>
<tr>
<td><code>500</code></td>
<td><code>SERVICE_ERROR</code></td>
<td>The service's decrypter failed.</td>
</tr>
</tbody>
</table>
<h3 id="post-apisessionsession_idlicense"><code>POST /api/session/{session_id}/license</code><a class="headerlink" href="#post-apisessionsession_idlicense" title="Permanent link">&para;</a></h3>
<p>Get the content keys for the DRM. The <code>mode</code> field selects one of two modes.
A slow answer arrives as a <a href="#slow-responses">slow response</a>.</p>
+5
View File
@@ -2484,6 +2484,11 @@ request and response in full.</p>
</tr>
<tr>
<td><code>POST</code></td>
<td><code>/api/session/{session_id}/samples</code></td>
<td>Server-side cbcs sample decryption for one track.</td>
</tr>
<tr>
<td><code>POST</code></td>
<td><code>/api/session/{session_id}/license</code></td>
<td>Proxy or server-side DRM licensing.</td>
</tr>
+5
View File
@@ -2853,6 +2853,11 @@ from a browser origin, not when you want only the local HTTP API.</p>
</tr>
<tr>
<td><code>POST</code></td>
<td><code>/api/session/{id}/samples</code></td>
<td>Decrypt the cbcs samples of a track the service decrypts itself</td>
</tr>
<tr>
<td><code>POST</code></td>
<td><code>/api/session/{id}/license</code></td>
<td>DRM licensing (proxy or server CDM)</td>
</tr>
+18 -9
View File
@@ -2907,8 +2907,10 @@ automatically.</p>
needs no <code>year</code>.</p>
<p>A <code>Song</code> also takes optional metadata keywords that the tagger and the naming templates
read: <code>album_artist</code>, <code>release_type</code>, <code>total_tracks</code>, <code>total_discs</code>, <code>genre</code>,
<code>explicit</code>, <code>isrc</code>, <code>upc</code>, <code>copyright</code>, <code>label</code>, <code>lyrics</code>, and <code>artwork_url</code>. Read the
class for the current list.</p>
<code>explicit</code>, <code>isrc</code>, <code>upc</code>, <code>copyright</code>, <code>label</code>, <code>lyrics</code>, <code>lyrics_suffix</code>, and
<code>artwork_url</code>. Read the class for the current list. With <code>lyrics_suffix</code> (<code>".lrc"</code>,
<code>".ttml"</code>), unshackle also saves <code>lyrics</code> beside the final audio file under that suffix.
You can set these fields in <code>get_tracks()</code> too, and a remote client gets them back.</p>
<p>The tagger also falls back to <code>Song.data</code> for fields the constructor did not get, such
as the composer or a full release date. It reads the top level of that dict and a
nested <code>metadata</code> sub-dict, with the nested keys winning, so either shape works.</p>
@@ -3198,13 +3200,20 @@ classes drive the CDM handshake and call the decryption backend. Your hook is on
<h3 id="decrypting-a-cbcs-file-yourself">Decrypting a cbcs file yourself<a class="headerlink" href="#decrypting-a-cbcs-file-yourself" title="Permanent link">&para;</a></h3>
<p>Some tracks cannot use the framework's decryption: a single cbcs file whose sample entries share
a zero default KID, or a sample entry whose content key no CDM can license and only a separate
process can use. For these, call <code>unshackle.core.drm.cbcs.decrypt_file(track.path, key_for)</code> from
<code>on_track_downloaded</code>. Set no <code>drm</code> on the track, so the framework does not try to license it.</p>
<p><code>key_for(track, entry)</code> gets the track layout and one protected <code>stsd</code> entry. It returns the
16-byte content key, or a <code>cbcs.SampleDecrypter</code>. A <code>SampleDecrypter</code> gets the protected bytes of
each sample of one fragment, cut to whole AES blocks, and returns plaintexts of the same lengths.
It handles full-sample cbcs only, not pattern encryption. <code>decrypt_file</code> writes the plaintext
back in place and removes the protection boxes, so the muxer reads a clear file.</p>
process can use. For these, set no <code>drm</code> on the track and override two methods:</p>
<ul>
<li><code>decrypts_samples(track)</code> returns <code>True</code> for such a track.</li>
<li><code>get_sample_decrypter(track, entry)</code> returns, for the 1-based <code>stsd</code> entry, the 16-byte
content key or a <code>cbcs.SampleDecrypter</code>. A <code>SampleDecrypter</code> gets the protected bytes of each sample of
one fragment, cut to whole AES blocks, and returns plaintexts of the same lengths. It handles
full-sample cbcs only, not pattern encryption.</li>
</ul>
<p>When the download of the track ends, unshackle calls <code>unshackle.core.drm.cbcs.decrypt_file</code>, and
the progress row shows "Decrypting". Your <code>on_track_downloaded</code> runs before this and gets the
encrypted file. <code>decrypt_file</code> writes the plaintext back in place and removes the
protection boxes, so the muxer reads a clear file. In a remote session <code>get_sample_decrypter</code>
runs on the server: the client sends the samples through the <code>samples</code> route, so a decrypter
that needs a process on the server also works for a remote client.</p>
<h3 id="how-unshackle-chooses-a-cdm-and-finds-keys">How unshackle chooses a CDM and finds keys<a class="headerlink" href="#how-unshackle-chooses-a-cdm-and-finds-keys" title="Permanent link">&para;</a></h3>
<p>CDM selection is config-driven: the <code>cdm</code> map resolves a service tag (and optionally the
track quality or DRM type) to a device name, which <code>load_cdm</code> instantiates as a local
+3
View File
@@ -3027,6 +3027,9 @@ and puts it in the file.</p>
<p>unshackle writes lyrics when the service supplies them: a <code>LYRICS</code> Vorbis comment for FLAC, a
<code>USLT</code> frame for MP3, and a <code>©lyr</code> atom for M4A. Ogg uses the same <code>LYRICS</code> field as
FLAC. unshackle does not write synchronized lyrics (<code>SYLT</code>).</p>
<p>A service can also ask for a lyrics file beside the audio, for example
<code>08.Get.Lucky.lrc</code> next to <code>08.Get.Lucky.m4a</code>. A music player reads the timed lines from
it. The file also comes back to the client from a remote server.</p>
<p>A metadata fault does not stop the run. The file is already in place, so unshackle writes a
warning and continues.</p>
<div class="admonition warning">
BIN
View File
Binary file not shown.
File diff suppressed because it is too large Load Diff
+4 -1
View File
@@ -2819,7 +2819,8 @@ takes every part. A dated episode also answers to its ISO air date.
</span><span id="__span-0-21"><a id="__codelineno-0-21" name="__codelineno-0-21" href="#__codelineno-0-21"></a> <span class="n">label</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
</span><span id="__span-0-22"><a id="__codelineno-0-22" name="__codelineno-0-22" href="#__codelineno-0-22"></a> <span class="n">lyrics</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
</span><span id="__span-0-23"><a id="__codelineno-0-23" name="__codelineno-0-23" href="#__codelineno-0-23"></a> <span class="n">artwork_url</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
</span><span id="__span-0-24"><a id="__codelineno-0-24" name="__codelineno-0-24" href="#__codelineno-0-24"></a><span class="p">)</span>
</span><span id="__span-0-24"><a id="__codelineno-0-24" name="__codelineno-0-24" href="#__codelineno-0-24"></a> <span class="n">lyrics_suffix</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
</span><span id="__span-0-25"><a id="__codelineno-0-25" name="__codelineno-0-25" href="#__codelineno-0-25"></a><span class="p">)</span>
</span></code></pre></div>
<div class="doc doc-contents ">
@@ -2828,6 +2829,8 @@ takes every part. A dated episode also answers to its ISO air date.
<p><code>lyrics_suffix</code> (".lrc", ".ttml") also saves <code>lyrics</code> beside the audio file with that suffix.</p>
File diff suppressed because one or more lines are too long