docs: rebuild HTML + WASM doc after identity PATCH update

This commit is contained in:
M5Max128
2026-05-22 10:08:08 +08:00
parent 2b025a014e
commit 3e81f7c16b
4 changed files with 511 additions and 3 deletions

View File

@@ -132,6 +132,93 @@ a { color: #0066cc; }
<strong>Scope</strong>: identity-level</p>
<p>Delete an identity permanently.</p>
<hr />
<h3><code>PATCH /api/v1/identity/:identity_uuid</code></h3>
<p><strong>Auth</strong>: Required
<strong>Scope</strong>: identity-level</p>
<p>Partially update an identity. Only provided fields are modified. The <code>name</code> field is a display label and may repeat across identities (removed UNIQUE constraint). Aliases for multilingual display are stored in <code>metadata.aliases</code> (see BCP 47 reference below).</p>
<h4>Request (JSON, all fields optional)</h4>
<table class="table">
<thead>
<tr>
<th>Field</th>
<th>Type</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>name</code></td>
<td>string</td>
<td>New display name</td>
</tr>
<tr>
<td><code>metadata</code></td>
<td>object</td>
<td>Merged into existing metadata. Use <code>"aliases"</code> key for locale-tagged names</td>
</tr>
<tr>
<td><code>status</code></td>
<td>string</td>
<td><code>"confirmed"</code>, <code>"pending"</code>, or <code>"skipped"</code></td>
</tr>
<tr>
<td><code>identity_type</code></td>
<td>string</td>
<td><code>"people"</code>, <code>"brand"</code>, <code>"object"</code>, <code>"concept"</code>, etc.</td>
</tr>
</tbody>
</table>
<h4>Example</h4>
<div class="codehilite"><pre><span></span><code><span class="c1"># Update name and add aliases</span>
curl<span class="w"> </span>-s<span class="w"> </span>-X<span class="w"> </span>PATCH<span class="w"> </span><span class="s2">&quot;</span><span class="nv">$API</span><span class="s2">/api/v1/identity/</span><span class="nv">$IDENTITY_UUID</span><span class="s2">&quot;</span><span class="w"> </span><span class="se">\</span>
<span class="w"> </span>-H<span class="w"> </span><span class="s2">&quot;X-API-Key: </span><span class="nv">$KEY</span><span class="s2">&quot;</span><span class="w"> </span><span class="se">\</span>
<span class="w"> </span>-H<span class="w"> </span><span class="s2">&quot;Content-Type: application/json&quot;</span><span class="w"> </span><span class="se">\</span>
<span class="w"> </span>-d<span class="w"> </span><span class="s1">&#39;{</span>
<span class="s1"> &quot;name&quot;: &quot;John Smith&quot;,</span>
<span class="s1"> &quot;metadata&quot;: {</span>
<span class="s1"> &quot;aliases&quot;: [</span>
<span class="s1"> {&quot;locale&quot;: &quot;en&quot;, &quot;name&quot;: &quot;John Smith&quot;},</span>
<span class="s1"> {&quot;locale&quot;: &quot;zh-TW&quot;, &quot;name&quot;: &quot;約翰·史密斯&quot;},</span>
<span class="s1"> {&quot;locale&quot;: &quot;ja&quot;, &quot;name&quot;: &quot;ジョン・スミス&quot;}</span>
<span class="s1"> ]</span>
<span class="s1"> }</span>
<span class="s1"> }&#39;</span>
<span class="c1"># Update status only</span>
curl<span class="w"> </span>-s<span class="w"> </span>-X<span class="w"> </span>PATCH<span class="w"> </span><span class="s2">&quot;</span><span class="nv">$API</span><span class="s2">/api/v1/identity/</span><span class="nv">$IDENTITY_UUID</span><span class="s2">&quot;</span><span class="w"> </span><span class="se">\</span>
<span class="w"> </span>-H<span class="w"> </span><span class="s2">&quot;X-API-Key: </span><span class="nv">$KEY</span><span class="s2">&quot;</span><span class="w"> </span><span class="se">\</span>
<span class="w"> </span>-H<span class="w"> </span><span class="s2">&quot;Content-Type: application/json&quot;</span><span class="w"> </span><span class="se">\</span>
<span class="w"> </span>-d<span class="w"> </span><span class="s1">&#39;{&quot;status&quot;: &quot;confirmed&quot;}&#39;</span>
</code></pre></div>
<h4>Response (200)</h4>
<div class="codehilite"><pre><span></span><code><span class="p">{</span>
<span class="w"> </span><span class="nt">&quot;success&quot;</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span>
<span class="w"> </span><span class="nt">&quot;identity_uuid&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;a9a901056d6b46ff92da0c3c1a57dff4&quot;</span><span class="p">,</span>
<span class="w"> </span><span class="nt">&quot;updated_fields&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">&quot;name&quot;</span><span class="p">,</span><span class="w"> </span><span class="s2">&quot;metadata&quot;</span><span class="p">]</span>
<span class="p">}</span>
</code></pre></div>
<h4>Error Responses</h4>
<table class="table">
<thead>
<tr>
<th>HTTP</th>
<th>When</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>400</code></td>
<td>No fields to update or invalid UUID format</td>
</tr>
<tr>
<td><code>404</code></td>
<td>Identity not found</td>
</tr>
</tbody>
</table>
<hr />
<h3><code>GET /api/v1/identity/:identity_uuid/files</code></h3>
<p><strong>Auth</strong>: Required
<strong>Scope</strong>: identity-level</p>
@@ -349,7 +436,7 @@ a { color: #0066cc; }
<p><strong>Auth</strong>: Required
<strong>Scope</strong>: identity-level</p>
<p>Upload an identity.json file to create or update an identity. Accepts the same format as the identity.json files stored on disk.</p>
<p>If an identity with the same <code>name</code> already exists, it will be updated with the new values.</p>
<p>If an identity with the same <code>identity_uuid</code> already exists, it will be updated with the new values.</p>
<h4>Request</h4>
<p>The request body is an <code>IdentityFile</code> object:</p>
<table class="table">
@@ -512,7 +599,107 @@ a { color: #0066cc; }
</tbody>
</table>
<hr />
<p><em>Updated: 2026-05-19 12:49:24</em></p>
<h2>Alias System (BCP 47 Locale Tags)</h2>
<p>Identity aliases support multilingual display names. Aliases are stored in <code>metadata.aliases</code> as an array of <code>{locale, name}</code> objects.</p>
<h3>BCP 47 Locale Tags Reference</h3>
<table class="table">
<thead>
<tr>
<th>Locale</th>
<th>Tag</th>
<th>Example</th>
</tr>
</thead>
<tbody>
<tr>
<td>English</td>
<td><code>en</code></td>
<td>John Smith</td>
</tr>
<tr>
<td>Traditional Chinese</td>
<td><code>zh-TW</code></td>
<td>約翰·史密斯</td>
</tr>
<tr>
<td>Simplified Chinese</td>
<td><code>zh-CN</code></td>
<td>约翰·史密斯</td>
</tr>
<tr>
<td>Japanese</td>
<td><code>ja</code></td>
<td>ジョン・スミス</td>
</tr>
<tr>
<td>Korean</td>
<td><code>ko</code></td>
<td>존 스미스</td>
</tr>
<tr>
<td>Cantonese</td>
<td><code>yue</code></td>
<td>約翰·史密夫</td>
</tr>
<tr>
<td>French</td>
<td><code>fr</code></td>
<td>John Smith (French spelling)</td>
</tr>
<tr>
<td>Spanish</td>
<td><code>es</code></td>
<td>Juan Smith</td>
</tr>
<tr>
<td>Arabic</td>
<td><code>ar</code></td>
<td>جون سميث</td>
</tr>
<tr>
<td>Russian</td>
<td><code>ru</code></td>
<td>Джон Смит</td>
</tr>
<tr>
<td>Thai</td>
<td><code>th</code></td>
<td>จอห์น สมิธ</td>
</tr>
</tbody>
</table>
<p>BCP 47 is the IETF standard for language tags. Format: <code>language</code> (e.g. <code>en</code>, <code>ja</code>) or <code>language-Region</code> (e.g. <code>zh-TW</code>, <code>zh-CN</code>). Region suffix distinguishes regional variants.</p>
<h3>Frontend Display Logic</h3>
<div class="codehilite"><pre><span></span><code><span class="kd">function</span><span class="w"> </span><span class="nx">getDisplayName</span><span class="p">(</span><span class="nx">identity</span><span class="p">,</span><span class="w"> </span><span class="nx">preferredLocale</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="c1">// 1. Exact locale match</span>
<span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">match</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">identity</span><span class="p">.</span><span class="nx">metadata</span><span class="o">?</span><span class="p">.</span><span class="nx">aliases</span><span class="o">?</span><span class="p">.</span><span class="nx">find</span><span class="p">(</span><span class="nx">a</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="nx">a</span><span class="p">.</span><span class="nx">locale</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="nx">preferredLocale</span><span class="p">);</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">match</span><span class="p">)</span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">match</span><span class="p">.</span><span class="nx">name</span><span class="p">;</span>
<span class="w"> </span><span class="c1">// 2. Language-only match (zh-TW → zh)</span>
<span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">lang</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">preferredLocale</span><span class="p">.</span><span class="nx">split</span><span class="p">(</span><span class="s1">&#39;-&#39;</span><span class="p">)[</span><span class="mf">0</span><span class="p">];</span>
<span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">langMatch</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">identity</span><span class="p">.</span><span class="nx">metadata</span><span class="o">?</span><span class="p">.</span><span class="nx">aliases</span><span class="o">?</span><span class="p">.</span><span class="nx">find</span><span class="p">(</span><span class="nx">a</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="nx">a</span><span class="p">.</span><span class="nx">locale</span><span class="p">.</span><span class="nx">startsWith</span><span class="p">(</span><span class="nx">lang</span><span class="p">));</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">langMatch</span><span class="p">)</span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">langMatch</span><span class="p">.</span><span class="nx">name</span><span class="p">;</span>
<span class="w"> </span><span class="c1">// 3. Fallback to identity.name</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">identity</span><span class="p">.</span><span class="nx">name</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>
<h3>Updating Aliases via PATCH</h3>
<div class="codehilite"><pre><span></span><code><span class="err">PATCH</span><span class="w"> </span><span class="err">/api/v</span><span class="mi">1</span><span class="err">/ide</span><span class="kc">nt</span><span class="err">i</span><span class="kc">t</span><span class="err">y/</span><span class="p">:</span><span class="err">ide</span><span class="kc">nt</span><span class="err">i</span><span class="kc">t</span><span class="err">y_uuid</span>
<span class="p">{</span>
<span class="w"> </span><span class="nt">&quot;metadata&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nt">&quot;aliases&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">[</span>
<span class="w"> </span><span class="p">{</span><span class="nt">&quot;locale&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;en&quot;</span><span class="p">,</span><span class="w"> </span><span class="nt">&quot;name&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;John Smith&quot;</span><span class="p">},</span>
<span class="w"> </span><span class="p">{</span><span class="nt">&quot;locale&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;zh-TW&quot;</span><span class="p">,</span><span class="w"> </span><span class="nt">&quot;name&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;約翰·史密斯&quot;</span><span class="p">}</span>
<span class="w"> </span><span class="p">]</span>
<span class="w"> </span><span class="p">}</span>
<span class="p">}</span>
</code></pre></div>
<p>This <strong>replaces</strong> the entire <code>aliases</code> array. To add to existing aliases, include all existing entries in the request.</p>
<hr />
<p>*Updated: 2026-05-22</p>
</div>
</body>
</html>

View File

@@ -81,6 +81,211 @@ a { color: #0066cc; }
<p><strong>Auth</strong>: Required
<strong>Scope</strong>: file-level</p>
<hr />
<h3><code>GET /api/v1/file/:file_uuid/trace/:trace_id/representative-face</code></h3>
<p>Find the best single face to represent this trace. Uses a two-stage selection: SQL (area × confidence → top 10) then FFmpeg <code>blurdetect</code> (sharpness → pick the least blurry).</p>
<p><strong>Auth</strong>: Required
<strong>Scope</strong>: file-level</p>
<h4>Example</h4>
<div class="codehilite"><pre><span></span><code>curl<span class="w"> </span>-s<span class="w"> </span><span class="s2">&quot;</span><span class="nv">$API</span><span class="s2">/api/v1/file/</span><span class="nv">$FILE_UUID</span><span class="s2">/trace/1939/representative-face&quot;</span><span class="w"> </span><span class="se">\</span>
<span class="w"> </span>-H<span class="w"> </span><span class="s2">&quot;X-API-Key: </span><span class="nv">$KEY</span><span class="s2">&quot;</span>
</code></pre></div>
<h4>Response (200)</h4>
<div class="codehilite"><pre><span></span><code><span class="p">{</span>
<span class="w"> </span><span class="nt">&quot;success&quot;</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span>
<span class="w"> </span><span class="nt">&quot;file_uuid&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;aeed71342a899fe4b4c57b7d41bcb692&quot;</span><span class="p">,</span>
<span class="w"> </span><span class="nt">&quot;trace_id&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">1939</span><span class="p">,</span>
<span class="w"> </span><span class="nt">&quot;face_count&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">538</span><span class="p">,</span>
<span class="w"> </span><span class="nt">&quot;representative&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nt">&quot;frame_number&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">68193</span><span class="p">,</span>
<span class="w"> </span><span class="nt">&quot;timestamp_secs&quot;</span><span class="p">:</span><span class="w"> </span><span class="mf">2727.72</span><span class="p">,</span>
<span class="w"> </span><span class="nt">&quot;bbox&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nt">&quot;x&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">347</span><span class="p">,</span><span class="w"> </span><span class="nt">&quot;y&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">378</span><span class="p">,</span><span class="w"> </span><span class="nt">&quot;width&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">427</span><span class="p">,</span><span class="w"> </span><span class="nt">&quot;height&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">427</span><span class="w"> </span><span class="p">},</span>
<span class="w"> </span><span class="nt">&quot;confidence&quot;</span><span class="p">:</span><span class="w"> </span><span class="mf">0.760</span><span class="p">,</span>
<span class="w"> </span><span class="nt">&quot;quality_score&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">138516</span><span class="p">,</span>
<span class="w"> </span><span class="nt">&quot;blur_score&quot;</span><span class="p">:</span><span class="w"> </span><span class="mf">9.46</span>
<span class="w"> </span><span class="p">}</span>
<span class="p">}</span>
</code></pre></div>
<h4>Response Fields</h4>
<table class="table">
<thead>
<tr>
<th>Field</th>
<th>Type</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>trace_id</code></td>
<td>integer</td>
<td>Face trace ID</td>
</tr>
<tr>
<td><code>face_count</code></td>
<td>integer</td>
<td>Total face detections in this trace</td>
</tr>
<tr>
<td><code>representative.frame_number</code></td>
<td>integer</td>
<td>Frame number of the selected face (primary coordinate)</td>
</tr>
<tr>
<td><code>representative.timestamp_secs</code></td>
<td>float</td>
<td>Time in seconds (derived from <code>frame_number / fps</code>)</td>
</tr>
<tr>
<td><code>representative.bbox</code></td>
<td>object</td>
<td>Bounding box <code>{x, y, width, height}</code></td>
</tr>
<tr>
<td><code>representative.confidence</code></td>
<td>float</td>
<td>Detection confidence (0.01.0)</td>
</tr>
<tr>
<td><code>representative.quality_score</code></td>
<td>float</td>
<td>Pre-selection score (<code>area × confidence</code>)</td>
</tr>
<tr>
<td><code>representative.blur_score</code></td>
<td>float</td>
<td>FFmpeg blurdetect result (lower = sharper)</td>
</tr>
</tbody>
</table>
<h4>Error Responses</h4>
<hr />
<h3><code>GET /api/v1/file/:file_uuid/trace/:trace_id/thumbnail</code></h3>
<p>Extract the best face image for a trace as JPEG (320×320). Internally selects the face using the same two-stage algorithm as <code>representative-face</code>, then crops via FFmpeg. The result is cacheable for 24 hours.</p>
<p><strong>Auth</strong>: Required
<strong>Scope</strong>: file-level</p>
<h4>Example</h4>
<div class="codehilite"><pre><span></span><code>curl<span class="w"> </span>-s<span class="w"> </span><span class="s2">&quot;</span><span class="nv">$API</span><span class="s2">/api/v1/file/</span><span class="nv">$FILE_UUID</span><span class="s2">/trace/1939/thumbnail&quot;</span><span class="w"> </span><span class="se">\</span>
<span class="w"> </span>-H<span class="w"> </span><span class="s2">&quot;X-API-Key: </span><span class="nv">$KEY</span><span class="s2">&quot;</span><span class="w"> </span>-o<span class="w"> </span>trace_1939_face.jpg
</code></pre></div>
<h4>Response</h4>
<ul>
<li><strong>200</strong>: <code>image/jpeg</code> binary data (320×320 cropped face)</li>
<li><strong>404</strong>: File, trace not found, or no suitable face</li>
<li><strong>500</strong>: FFmpeg or database error</li>
</ul>
<hr />
<h3><code>GET /api/v1/file/:file_uuid/identities/:identity_uuid_a/co-occur-with/:identity_uuid_b</code></h3>
<p>Find the first frame where two identities appear together, with representative face thumbnails for both.</p>
<p><strong>Auth</strong>: Required
<strong>Scope</strong>: file-level</p>
<h4>Example</h4>
<div class="codehilite"><pre><span></span><code><span class="c1"># Audrey Hepburn &amp; Cary Grant 第一次同框</span>
curl<span class="w"> </span>-s<span class="w"> </span><span class="s2">&quot;</span><span class="nv">$API</span><span class="s2">/api/v1/file/</span><span class="nv">$FILE_UUID</span><span class="s2">/identities/</span><span class="nv">$AUDREY_UUID</span><span class="s2">/co-occur-with/</span><span class="nv">$CARY_UUID</span><span class="s2">&quot;</span><span class="w"> </span><span class="se">\</span>
<span class="w"> </span>-H<span class="w"> </span><span class="s2">&quot;X-API-Key: </span><span class="nv">$KEY</span><span class="s2">&quot;</span><span class="w"> </span><span class="p">|</span><span class="w"> </span>jq<span class="w"> </span><span class="s1">&#39;{identity_a: .identity_a.name, identity_b: .identity_b.name, first_frame: .first_cooccurrence.frame_number}&#39;</span>
</code></pre></div>
<h4>Response (200)</h4>
<div class="codehilite"><pre><span></span><code><span class="p">{</span>
<span class="w"> </span><span class="nt">&quot;success&quot;</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span>
<span class="w"> </span><span class="nt">&quot;file_uuid&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;aeed71342a899fe4b4c57b7d41bcb692&quot;</span><span class="p">,</span>
<span class="w"> </span><span class="nt">&quot;identity_a&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nt">&quot;identity_uuid&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;c3545906-c82d-4b66-aa1d-150bc02decce&quot;</span><span class="p">,</span>
<span class="w"> </span><span class="nt">&quot;name&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;Audrey Hepburn&quot;</span><span class="p">,</span>
<span class="w"> </span><span class="nt">&quot;trace_id&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">920</span>
<span class="w"> </span><span class="p">},</span>
<span class="w"> </span><span class="nt">&quot;identity_b&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nt">&quot;identity_uuid&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;2b0ddefe-e2a9-4533-9308-b375594604d5&quot;</span><span class="p">,</span>
<span class="w"> </span><span class="nt">&quot;name&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;Cary Grant&quot;</span><span class="p">,</span>
<span class="w"> </span><span class="nt">&quot;trace_id&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">919</span>
<span class="w"> </span><span class="p">},</span>
<span class="w"> </span><span class="nt">&quot;first_cooccurrence&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nt">&quot;frame_number&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">38165</span><span class="p">,</span>
<span class="w"> </span><span class="nt">&quot;timestamp_secs&quot;</span><span class="p">:</span><span class="w"> </span><span class="mf">1526.60</span><span class="p">,</span>
<span class="w"> </span><span class="nt">&quot;total_cooccurrence_frames&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">3136</span><span class="p">,</span>
<span class="w"> </span><span class="nt">&quot;representative_face_a&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nt">&quot;frame_number&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">38199</span><span class="p">,</span>
<span class="w"> </span><span class="nt">&quot;bbox&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nt">&quot;x&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">122</span><span class="p">,</span><span class="w"> </span><span class="nt">&quot;y&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">339</span><span class="p">,</span><span class="w"> </span><span class="nt">&quot;width&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">176</span><span class="p">,</span><span class="w"> </span><span class="nt">&quot;height&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">176</span><span class="w"> </span><span class="p">},</span>
<span class="w"> </span><span class="nt">&quot;confidence&quot;</span><span class="p">:</span><span class="w"> </span><span class="mf">0.832</span><span class="p">,</span>
<span class="w"> </span><span class="nt">&quot;thumbnail_url&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;/api/v1/file/aeed71342.../trace/920/thumbnail&quot;</span>
<span class="w"> </span><span class="p">},</span>
<span class="w"> </span><span class="nt">&quot;representative_face_b&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nt">&quot;frame_number&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">38291</span><span class="p">,</span>
<span class="w"> </span><span class="nt">&quot;bbox&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nt">&quot;x&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">511</span><span class="p">,</span><span class="w"> </span><span class="nt">&quot;y&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">315</span><span class="p">,</span><span class="w"> </span><span class="nt">&quot;width&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">192</span><span class="p">,</span><span class="w"> </span><span class="nt">&quot;height&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">192</span><span class="w"> </span><span class="p">},</span>
<span class="w"> </span><span class="nt">&quot;confidence&quot;</span><span class="p">:</span><span class="w"> </span><span class="mf">0.791</span><span class="p">,</span>
<span class="w"> </span><span class="nt">&quot;thumbnail_url&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;/api/v1/file/aeed71342.../trace/919/thumbnail&quot;</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="p">}</span>
<span class="p">}</span>
</code></pre></div>
<h4>Response Fields</h4>
<table class="table">
<thead>
<tr>
<th>Field</th>
<th>Type</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>identity_a.name</code></td>
<td>string</td>
<td>First identity name</td>
</tr>
<tr>
<td><code>identity_b.name</code></td>
<td>string</td>
<td>Second identity name</td>
</tr>
<tr>
<td><code>first_cooccurrence.frame_number</code></td>
<td>int</td>
<td>First frame where both appear</td>
</tr>
<tr>
<td><code>first_cooccurrence.timestamp_secs</code></td>
<td>float</td>
<td>Time in seconds</td>
</tr>
<tr>
<td><code>first_cooccurrence.total_cooccurrence_frames</code></td>
<td>int</td>
<td>Total frames with both present</td>
</tr>
<tr>
<td><code>first_cooccurrence.representative_face_a/b</code></td>
<td>object</td>
<td>Best face thumbnail data for each identity</td>
</tr>
</tbody>
</table>
<h4>Error Responses</h4>
<table class="table">
<thead>
<tr>
<th>HTTP</th>
<th>When</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>404</code></td>
<td>File or identity not found</td>
</tr>
<tr>
<td><code>404</code></td>
<td>The two identities never co-occur in this file</td>
</tr>
<tr>
<td><code>500</code></td>
<td>Database or FFmpeg error</td>
</tr>
</tbody>
</table>
<h3><code>GET /api/v1/file/:file_uuid/video/bbox</code></h3>
<p>Stream video with bounding box overlay for all detected objects/faces.</p>
<p><strong>Auth</strong>: Required

View File

@@ -74,6 +74,66 @@ Delete an identity permanently.
---
### `PATCH /api/v1/identity/:identity_uuid`
**Auth**: Required
**Scope**: identity-level
Partially update an identity. Only provided fields are modified. The `name` field is a display label and may repeat across identities (removed UNIQUE constraint). Aliases for multilingual display are stored in `metadata.aliases` (see BCP 47 reference below).
#### Request (JSON, all fields optional)
| Field | Type | Description |
|-------|------|-------------|
| `name` | string | New display name |
| `metadata` | object | Merged into existing metadata. Use `"aliases"` key for locale-tagged names |
| `status` | string | `"confirmed"`, `"pending"`, or `"skipped"` |
| `identity_type` | string | `"people"`, `"brand"`, `"object"`, `"concept"`, etc. |
#### Example
```bash
# Update name and add aliases
curl -s -X PATCH "$API/api/v1/identity/$IDENTITY_UUID" \
-H "X-API-Key: $KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "John Smith",
"metadata": {
"aliases": [
{"locale": "en", "name": "John Smith"},
{"locale": "zh-TW", "name": "約翰·史密斯"},
{"locale": "ja", "name": "ジョン・スミス"}
]
}
}'
# Update status only
curl -s -X PATCH "$API/api/v1/identity/$IDENTITY_UUID" \
-H "X-API-Key: $KEY" \
-H "Content-Type: application/json" \
-d '{"status": "confirmed"}'
```
#### Response (200)
```json
{
"success": true,
"identity_uuid": "a9a901056d6b46ff92da0c3c1a57dff4",
"updated_fields": ["name", "metadata"]
}
```
#### Error Responses
| HTTP | When |
|------|------|
| `400` | No fields to update or invalid UUID format |
| `404` | Identity not found |
---
### `GET /api/v1/identity/:identity_uuid/files`
**Auth**: Required
@@ -330,7 +390,63 @@ curl -s "$API/api/v1/identity/$IDENTITY_UUID/profile-image" \
|----------------|-------|
| `content-type` | `image/jpeg` or `image/png` |
---
## Alias System (BCP 47 Locale Tags)
Identity aliases support multilingual display names. Aliases are stored in `metadata.aliases` as an array of `{locale, name}` objects.
### BCP 47 Locale Tags Reference
| Locale | Tag | Example |
|--------|-----|---------|
| English | `en` | John Smith |
| Traditional Chinese | `zh-TW` | 約翰·史密斯 |
| Simplified Chinese | `zh-CN` | 约翰·史密斯 |
| Japanese | `ja` | ジョン・スミス |
| Korean | `ko` | 존 스미스 |
| Cantonese | `yue` | 約翰·史密夫 |
| French | `fr` | John Smith (French spelling) |
| Spanish | `es` | Juan Smith |
| Arabic | `ar` | جون سميث |
| Russian | `ru` | Джон Смит |
| Thai | `th` | จอห์น สมิธ |
BCP 47 is the IETF standard for language tags. Format: `language` (e.g. `en`, `ja`) or `language-Region` (e.g. `zh-TW`, `zh-CN`). Region suffix distinguishes regional variants.
### Frontend Display Logic
```javascript
function getDisplayName(identity, preferredLocale) {
// 1. Exact locale match
const match = identity.metadata?.aliases?.find(a => a.locale === preferredLocale);
if (match) return match.name;
// 2. Language-only match (zh-TW → zh)
const lang = preferredLocale.split('-')[0];
const langMatch = identity.metadata?.aliases?.find(a => a.locale.startsWith(lang));
if (langMatch) return langMatch.name;
// 3. Fallback to identity.name
return identity.name;
}
```
### Updating Aliases via PATCH
```json
PATCH /api/v1/identity/:identity_uuid
{
"metadata": {
"aliases": [
{"locale": "en", "name": "John Smith"},
{"locale": "zh-TW", "name": "約翰·史密斯"}
]
}
}
```
This **replaces** the entire `aliases` array. To add to existing aliases, include all existing entries in the request.
---
*Updated: 2026-05-19 12:49:24*
*Updated: 2026-05-22