Deployed 84b0a92 with MkDocs version: 1.6.1

This commit is contained in:
2026-09-07 23:04:35 +00:00
parent ad65788183
commit 79e96079ae
6 changed files with 239 additions and 4 deletions
+1 -1
View File
@@ -2241,7 +2241,7 @@ dashboard UI that wants history must keep its own copy.</p>
</tr>
<tr>
<td><code>/api/dashboard/sessions</code></td>
<td>Every live remote session: <code>id</code>, <code>owner</code> (username or masked key), <code>creator_ip</code>, <code>service</code>, <code>title_id</code>, <code>titles</code>, <code>tracks</code>, <code>title</code> (the first resolved title's display name), <code>auth_status</code>, <code>auth_error</code>, <code>server_account</code> (the server profile lent to the remote session, else <code>null</code>), <code>client</code>, <code>actions</code>, <code>created_at</code> and <code>last_accessed</code> (ISO 8601) with <code>created_ts</code> and <code>last_accessed_ts</code> (Unix epoch, the same clock as log <code>ts</code>), <code>age_seconds</code>, <code>idle_seconds</code>, <code>log_seq</code>. <code>client</code> is whatever the remote client sent as <code>client</code> in its session create request: the CLI sends <code>version</code> and <code>platform</code>, an older client sends nothing and the field is <code>{}</code>. <code>log_seq</code> is the last sequence number in the remote session's service log - poll <code>/api/dashboard/sessions/{id}/logs</code> when it changes. <code>actions</code> is the session's request log, newest last, capped at 500: <code>ts</code>, <code>method</code>, <code>action</code> (<code>titles</code>, <code>tracks</code>, <code>segments</code>, <code>license</code>, <code>prompt</code>, …), <code>query</code>, <code>status</code>, <code>ms</code>, <code>bytes_in</code>, <code>bytes_out</code>.</td>
<td>Every live remote session: <code>id</code>, <code>owner</code> (username or masked key), <code>creator_ip</code>, <code>service</code>, <code>title_id</code>, <code>titles</code>, <code>tracks</code>, <code>title</code> (the display name of the title the client is on: the last one it asked tracks for, else the first resolved title), <code>auth_status</code>, <code>auth_error</code>, <code>server_account</code> (the server profile lent to the remote session, else <code>null</code>), <code>client</code>, <code>actions</code>, <code>created_at</code> and <code>last_accessed</code> (ISO 8601) with <code>created_ts</code> and <code>last_accessed_ts</code> (Unix epoch, the same clock as log <code>ts</code>), <code>age_seconds</code>, <code>idle_seconds</code>, <code>log_seq</code>. <code>client</code> is whatever the remote client sent as <code>client</code> in its session create request. The CLI sends <code>version</code>, <code>code_hash</code> (the commit the client runs, <code>null</code> when its source cannot be read), <code>platform</code> and <code>argv</code>. <code>argv</code> is the command line the user ran: proxy and URL userinfo, secret query parameters and credential values become <code>***</code>, home and install paths shorten as in the logs, and the line is cut at 3000 characters. An older client sends less or nothing and the field is <code>{}</code>. <code>log_seq</code> is the last sequence number in the remote session's service log - poll <code>/api/dashboard/sessions/{id}/logs</code> when it changes. <code>actions</code> is the session's request log, newest last, capped at 500: <code>ts</code>, <code>method</code>, <code>action</code> (<code>titles</code>, <code>tracks</code>, <code>segments</code>, <code>license</code>, <code>prompt</code>, …), <code>query</code>, <code>status</code>, <code>ms</code>, <code>bytes_in</code>, <code>bytes_out</code>.</td>
</tr>
<tr>
<td><code>/api/dashboard/jobs</code></td>
+1 -1
View File
@@ -4541,7 +4541,7 @@ an HTTP caller would be remote code execution. You configure post-scripts only i
</span><span id="__span-28-8"><a id="__codelineno-28-8" name="__codelineno-28-8" href="#__codelineno-28-8"></a>DELETE /api/session/{id} → tear down, harvest updated cache
</span></code></pre></div>
<h3 id="post-apisessioncreate"><code>POST /api/session/create</code><a class="headerlink" href="#post-apisessioncreate" title="Permanent link">&para;</a></h3>
<p>Make a remote session for a service and title. Authentication runs asynchronously in the background. This call returns immediately with <code>status: "authenticating"</code>, and you then poll the prompt endpoint. The body accepts <code>service</code> and <code>title_id</code> (both required). It also accepts a broad set of optional keys, because the body allows <code>additionalProperties</code>. These are <code>credentials</code> (<code>{username, password, extra?}</code>), <code>cookies</code> (base64 of zlib-compressed Netscape cookie file), <code>proxy</code>, <code>no_proxy</code>, <code>profile</code>, <code>cache</code> (map of forwarded cache files), <code>client_region</code>, <code>proxy_region</code>, <code>cdm_type</code>, <code>range_</code>, <code>vcodec</code>, <code>quality</code>, <code>best_available</code>, and any service CLI options. <code>proxy_region</code> is the country code the client resolved <code>proxy</code> from; the server matches it against its own accounts.</p>
<p>Make a remote session for a service and title. Authentication runs asynchronously in the background. This call returns immediately with <code>status: "authenticating"</code>, and you then poll the prompt endpoint. The body accepts <code>service</code> and <code>title_id</code> (both required). It also accepts a broad set of optional keys, because the body allows <code>additionalProperties</code>. These are <code>credentials</code> (<code>{username, password, extra?}</code>), <code>cookies</code> (base64 of zlib-compressed Netscape cookie file), <code>proxy</code>, <code>no_proxy</code>, <code>profile</code>, <code>cache</code> (map of forwarded cache files), <code>client_region</code>, <code>proxy_region</code>, <code>cdm_type</code>, <code>range_</code>, <code>vcodec</code>, <code>quality</code>, <code>best_available</code>, <code>client</code>, and any service CLI options. <code>proxy_region</code> is the country code the client resolved <code>proxy</code> from; the server matches it against its own accounts. <code>client</code> is a freeform object the dashboard shows as sent (the CLI puts <code>version</code>, <code>code_hash</code>, <code>platform</code> and a redacted <code>argv</code> in it); the server ignores it above 4096 bytes of JSON.</p>
<p>Service CLI options also travel in a nested <code>service_params</code> object, which wins over a flat key with the same name. <code>profile</code> at the top level always means the credentials profile, never a service's own <code>--profile</code> option.</p>
<p>The <code>proxy</code> value must be a full proxy URI, unless the operator gives your API key <code>server_proxy</code> in the <code>serve.users</code> config. Without it, the server does not resolve country codes with its own proxy providers. It rejects the request with <code>INVALID_PROXY</code> when no <code>proxy</code> is set and the reported <code>client_region</code> differs from the server's region. A request that reports no <code>client_region</code> is not blocked. Pass <code>proxy</code> with your own proxy, or <code>no_proxy</code> to accept the server's own connection. With <code>server_proxy</code>, the server resolves a country code and picks a proxy for your <code>client_region</code> itself.</p>
<div class="tabbed-set tabbed-alternate" data-tabs="19:1"><input checked="checked" id="__tabbed_19_1" name="__tabbed_19" type="radio" /><div class="tabbed-labels"><label for="__tabbed_19_1">Response <code>200</code></label></div>
+2 -1
View File
@@ -2753,7 +2753,8 @@ from a browser origin, not when you want only the local HTTP API.</p>
<p><code>POST /api/session/create</code> requires <code>service</code> and <code>title_id</code>. It also accepts
<code>credentials</code>, <code>cookies</code> (base64 of zlib-compressed Netscape cookie file), <code>proxy</code>,
<code>no_proxy</code>, <code>profile</code>, <code>cache</code> (a map of <code>filename → base64(zlib(bytes))</code>),
<code>client_region</code>, <code>cdm_type</code>, and the track-selection hints <code>range_</code>, <code>vcodec</code>,
<code>client_region</code>, <code>cdm_type</code>, <code>client</code> (a freeform identity object the dashboard
shows as sent), and the track-selection hints <code>range_</code>, <code>vcodec</code>,
<code>quality</code>, <code>best_available</code>, plus arbitrary service CLI options
(<code>additionalProperties: true</code>).</p>
<p>The response returns <strong>before</strong> authentication finishes:</p>
BIN
View File
Binary file not shown.
+234
View File
@@ -2332,6 +2332,50 @@
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#unshackle.core.drm.PlayReady.header_kids" class="md-nav__link">
<span class="md-ellipsis">
<code class="doc-symbol doc-symbol-toc doc-symbol-method"></code>&nbsp;header_kids
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#unshackle.core.drm.PlayReady.absorb" class="md-nav__link">
<span class="md-ellipsis">
<code class="doc-symbol doc-symbol-toc doc-symbol-method"></code>&nbsp;absorb
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#unshackle.core.drm.PlayReady.distinct_headers" class="md-nav__link">
<span class="md-ellipsis">
<code class="doc-symbol doc-symbol-toc doc-symbol-method"></code>&nbsp;distinct_headers
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#unshackle.core.drm.PlayReady.pro_b64_for" class="md-nav__link">
<span class="md-ellipsis">
<code class="doc-symbol doc-symbol-toc doc-symbol-method"></code>&nbsp;pro_b64_for
</span>
</a>
</li>
<li class="md-nav__item">
@@ -2343,6 +2387,17 @@
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#unshackle.core.drm.PlayReady.get_content_keys" class="md-nav__link">
<span class="md-ellipsis">
<code class="doc-symbol doc-symbol-toc doc-symbol-method"></code>&nbsp;get_content_keys
</span>
</a>
</li>
<li class="md-nav__item">
@@ -3071,6 +3126,50 @@
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#unshackle.core.drm.PlayReady.header_kids" class="md-nav__link">
<span class="md-ellipsis">
<code class="doc-symbol doc-symbol-toc doc-symbol-method"></code>&nbsp;header_kids
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#unshackle.core.drm.PlayReady.absorb" class="md-nav__link">
<span class="md-ellipsis">
<code class="doc-symbol doc-symbol-toc doc-symbol-method"></code>&nbsp;absorb
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#unshackle.core.drm.PlayReady.distinct_headers" class="md-nav__link">
<span class="md-ellipsis">
<code class="doc-symbol doc-symbol-toc doc-symbol-method"></code>&nbsp;distinct_headers
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#unshackle.core.drm.PlayReady.pro_b64_for" class="md-nav__link">
<span class="md-ellipsis">
<code class="doc-symbol doc-symbol-toc doc-symbol-method"></code>&nbsp;pro_b64_for
</span>
</a>
</li>
<li class="md-nav__item">
@@ -3082,6 +3181,17 @@
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#unshackle.core.drm.PlayReady.get_content_keys" class="md-nav__link">
<span class="md-ellipsis">
<code class="doc-symbol doc-symbol-toc doc-symbol-method"></code>&nbsp;get_content_keys
</span>
</a>
</li>
<li class="md-nav__item">
@@ -4780,6 +4890,102 @@ duplicate them here.</p>
<div class="doc doc-object doc-function">
<h3 id="unshackle.core.drm.PlayReady.header_kids" class="doc doc-heading">
<code class="doc-symbol doc-symbol-heading doc-symbol-method"></code> <span class="doc doc-object-name doc-function-name">header_kids</span>
<span class="doc doc-labels">
<small class="doc doc-label doc-label-staticmethod"><code>staticmethod</code></small>
</span>
<a href="#unshackle.core.drm.PlayReady.header_kids" class="headerlink" title="Permanent link">&para;</a></h3>
<div class="language-python doc-signature highlight"><pre><span></span><code><span id="__span-0-1"><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a><span class="nf">header_kids</span><span class="p">(</span><span class="n">header</span><span class="p">)</span>
</span></code></pre></div>
<div class="doc doc-contents ">
<p>Return the KIDs one WRM header names, in header order.</p>
</div>
</div>
<div class="doc doc-object doc-function">
<h3 id="unshackle.core.drm.PlayReady.absorb" class="doc doc-heading">
<code class="doc-symbol doc-symbol-heading doc-symbol-method"></code> <span class="doc doc-object-name doc-function-name">absorb</span>
<a href="#unshackle.core.drm.PlayReady.absorb" class="headerlink" title="Permanent link">&para;</a></h3>
<div class="language-python doc-signature highlight"><pre><span></span><code><span id="__span-0-1"><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a><span class="nf">absorb</span><span class="p">(</span><span class="o">*</span><span class="n">others</span><span class="p">)</span>
</span></code></pre></div>
<div class="doc doc-contents ">
<p>Fold sibling PlayReady objects into this one so their headers and KIDs get licensed too.</p>
<p>A manifest can attach one PlayReady object per WRM header to the same track
(ISM shares its Protection list across tracks; HLS emits one per EXT-X-KEY). The
caller keeps this object's identity, which the decrypt path and drm_lock depend on.</p>
</div>
</div>
<div class="doc doc-object doc-function">
<h3 id="unshackle.core.drm.PlayReady.distinct_headers" class="doc doc-heading">
<code class="doc-symbol doc-symbol-heading doc-symbol-method"></code> <span class="doc doc-object-name doc-function-name">distinct_headers</span>
<a href="#unshackle.core.drm.PlayReady.distinct_headers" class="headerlink" title="Permanent link">&para;</a></h3>
<div class="language-python doc-signature highlight"><pre><span></span><code><span id="__span-0-1"><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a><span class="nf">distinct_headers</span><span class="p">()</span>
</span></code></pre></div>
<div class="doc doc-contents ">
<p>Return the WRM headers to license, deduplicated by the KID set each one names.</p>
<p>Two headers naming the same KIDs (a v4.0 header beside its v4.3 twin, or one PRH per
rendition for one content key) cost one challenge. A header with no readable KID is kept: it
may still be the only route to a content key nothing else names.</p>
</div>
</div>
<div class="doc doc-object doc-function">
<h3 id="unshackle.core.drm.PlayReady.pro_b64_for" class="doc doc-heading">
<code class="doc-symbol doc-symbol-heading doc-symbol-method"></code> <span class="doc doc-object-name doc-function-name">pro_b64_for</span>
<span class="doc doc-labels">
<small class="doc doc-label doc-label-staticmethod"><code>staticmethod</code></small>
</span>
<a href="#unshackle.core.drm.PlayReady.pro_b64_for" class="headerlink" title="Permanent link">&para;</a></h3>
<div class="language-python doc-signature highlight"><pre><span></span><code><span id="__span-0-1"><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a><span class="nf">pro_b64_for</span><span class="p">(</span><span class="n">header</span><span class="p">)</span>
</span></code></pre></div>
<div class="doc doc-contents ">
<p>Build a base64 one-record PlayReady Object holding only this header.</p>
<p>Remote CDMs send the value given to set_pssh_b64 rather than the header passed
to get_license_challenge, so a per-header challenge needs a per-header PRO.</p>
</div>
</div>
<div class="doc doc-object doc-function">
<h3 id="unshackle.core.drm.PlayReady.extract_keys_from_cdm" class="doc doc-heading">
<code class="doc-symbol doc-symbol-heading doc-symbol-method"></code> <span class="doc doc-object-name doc-function-name">extract_keys_from_cdm</span>
@@ -4870,6 +5076,34 @@ duplicate them here.</p>
<div class="doc doc-object doc-function">
<h3 id="unshackle.core.drm.PlayReady.get_content_keys" class="doc doc-heading">
<code class="doc-symbol doc-symbol-heading doc-symbol-method"></code> <span class="doc doc-object-name doc-function-name">get_content_keys</span>
<a href="#unshackle.core.drm.PlayReady.get_content_keys" class="headerlink" title="Permanent link">&para;</a></h3>
<div class="language-python doc-signature highlight"><pre><span></span><code><span id="__span-0-1"><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a><span class="nf">get_content_keys</span><span class="p">(</span><span class="n">cdm</span><span class="p">,</span> <span class="n">certificate</span><span class="p">,</span> <span class="n">licence</span><span class="p">)</span>
</span></code></pre></div>
<div class="doc doc-contents ">
<p>License every distinct WRM header whose KIDs still lack a content key.</p>
<p>A v4.2+ header may list several KIDs and one response may stack several licences,
so one challenge often covers everything. Content that ships several headers (ISM
with one ProtectionHeader per track type, HLS with one PRH per playlist) gets one
challenge per header that is not yet covered. Headers are sent as-is: Microsoft lets
the server ignore the header, so a merged header could ask for keys it never grants.</p>
<p>A header that a round refused, or that yielded nothing, is not sent again by this
object: every track of a title runs this method, and a repeat would only repeat the
refusal.</p>
</div>
</div>
<div class="doc doc-object doc-function">
<h3 id="unshackle.core.drm.PlayReady.detect_revocation" class="doc doc-heading">
<code class="doc-symbol doc-symbol-heading doc-symbol-method"></code> <span class="doc doc-object-name doc-function-name">detect_revocation</span>
File diff suppressed because one or more lines are too long