1652 lines
79 KiB
HTML
1652 lines
79 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<title>07 Identity - Momentry API Docs</title>
|
||
<style>
|
||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #f5f5f5; color: #333; padding: 40px; }
|
||
.container { max-width: 960px; margin: 0 auto; background: white; border-radius: 12px; box-shadow: 0 2px 12px rgba(0,0,0,0.08); padding: 40px; }
|
||
h1 { font-size: 24px; margin: 24px 0 12px; }
|
||
h2 { font-size: 20px; margin: 20px 0 10px; color: #222; }
|
||
h3 { font-size: 16px; margin: 16px 0 8px; color: #444; }
|
||
p { line-height: 1.6; margin: 8px 0; }
|
||
table { border-collapse: collapse; width: 100%; margin: 12px 0; font-size: 14px; }
|
||
th, td { border: 1px solid #ddd; padding: 8px 12px; text-align: left; }
|
||
th { background: #f0f0f0; font-weight: 600; }
|
||
code { background: #f0f0f0; padding: 2px 6px; border-radius: 3px; font-size: 13px; }
|
||
pre { background: #f8f8f8; border: 1px solid #ddd; border-radius: 6px; padding: 12px; overflow-x: auto; margin: 12px 0; }
|
||
pre code { background: none; padding: 0; }
|
||
a { color: #0066cc; }
|
||
.back { display: inline-block; margin-bottom: 20px; color: #666; }
|
||
.back:hover { color: #333; }
|
||
.topbar { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; }
|
||
.logout-btn { font-size: 13px; color: #999; text-decoration: none; }
|
||
.logout-btn:hover { color: #cc0000; }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="container">
|
||
<div class="topbar">
|
||
<a class="back" href="index.html">← Back to index</a>
|
||
<a class="logout-btn" href="#" onclick="fetch('/api/v1/auth/logout',{method:'POST'}).then(()=>window.location.reload());return false">Logout</a>
|
||
</div>
|
||
<!-- module: identity -->
|
||
<!-- description: Global identities — CRUD, detail, files, faces, bind, unbind, search -->
|
||
<!-- depends: 01_auth -->
|
||
|
||
<h2>Global Identities</h2>
|
||
<h3><code>GET /api/v1/identities</code></h3>
|
||
<p><strong>Auth</strong>: Required
|
||
<strong>Scope</strong>: identity-level</p>
|
||
<p>List all registered identities with pagination.</p>
|
||
<h4>Example</h4>
|
||
<div class="codehilite"><pre><span></span><code>curl<span class="w"> </span>-s<span class="w"> </span><span class="s2">"</span><span class="nv">$API</span><span class="s2">/api/v1/identities?page=1&page_size=20"</span><span class="w"> </span>-H<span class="w"> </span><span class="s2">"X-API-Key: </span><span class="nv">$KEY</span><span class="s2">"</span><span class="w"> </span><span class="p">|</span><span class="w"> </span>jq<span class="w"> </span><span class="s1">'{count, identities: [.identities[] | {name}]}'</span>
|
||
</code></pre></div>
|
||
|
||
<hr />
|
||
<h3><code>GET /api/v1/identity/:identity_uuid</code></h3>
|
||
<p><strong>Auth</strong>: Required
|
||
<strong>Scope</strong>: identity-level</p>
|
||
<p>Get detailed information for a specific identity, including metadata and TMDb references.</p>
|
||
<h4>Example</h4>
|
||
<div class="codehilite"><pre><span></span><code>curl<span class="w"> </span>-s<span class="w"> </span><span class="s2">"</span><span class="nv">$API</span><span class="s2">/api/v1/identity/</span><span class="nv">$IDENTITY_UUID</span><span class="s2">"</span><span class="w"> </span>-H<span class="w"> </span><span class="s2">"X-API-Key: </span><span class="nv">$KEY</span><span class="s2">"</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">"success"</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">"identity_uuid"</span><span class="p">:</span><span class="w"> </span><span class="s2">"a9a901056d6b46ff92da0c3c1a57dff4"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Cary Grant"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"identity_type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"people"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"source"</span><span class="p">:</span><span class="w"> </span><span class="s2">"tmdb"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"status"</span><span class="p">:</span><span class="w"> </span><span class="s2">"confirmed"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"tmdb_id"</span><span class="p">:</span><span class="w"> </span><span class="mi">112</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"tmdb_profile"</span><span class="p">:</span><span class="w"> </span><span class="s2">"{output}/identities/{identity_uuid}/profile.jpg"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"metadata"</span><span class="p">:</span><span class="w"> </span><span class="p">{},</span>
|
||
<span class="w"> </span><span class="nt">"reference_data"</span><span class="p">:</span><span class="w"> </span><span class="p">{},</span>
|
||
<span class="w"> </span><span class="nt">"created_at"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2026-05-16T12:00:00Z"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"updated_at"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span>
|
||
<span class="p">}</span>
|
||
</code></pre></div>
|
||
|
||
<table class="table">
|
||
<thead>
|
||
<tr>
|
||
<th>Field</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>identity_uuid</code></td>
|
||
<td>string</td>
|
||
<td>Identity identifier</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>name</code></td>
|
||
<td>string</td>
|
||
<td>Identity name</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>identity_type</code></td>
|
||
<td>string</td>
|
||
<td><code>"people"</code> or null</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>source</code></td>
|
||
<td>string</td>
|
||
<td><code>.json</code>, <code>auto</code>, <code>tmdb</code>, <code>user_defined</code>, or <code>merged</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>status</code></td>
|
||
<td>string</td>
|
||
<td><code>"confirmed"</code>, <code>"pending"</code>, or <code>"inactive"</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>tmdb_id</code></td>
|
||
<td>integer</td>
|
||
<td>TMDb person ID (only if source = tmdb)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>tmdb_profile</code></td>
|
||
<td>string</td>
|
||
<td>Local profile image path (<code>{output}/identities/{uuid}/profile.jpg</code>)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>metadata</code></td>
|
||
<td>object</td>
|
||
<td>Metadata JSON (tmdb_character, cast_order, etc.)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>created_at</code></td>
|
||
<td>string</td>
|
||
<td>Creation timestamp</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h3><code>DELETE /api/v1/identity/:identity_uuid</code></h3>
|
||
<p><strong>Auth</strong>: Required
|
||
<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">"</span><span class="nv">$API</span><span class="s2">/api/v1/identity/</span><span class="nv">$IDENTITY_UUID</span><span class="s2">"</span><span class="w"> </span><span class="se">\</span>
|
||
<span class="w"> </span>-H<span class="w"> </span><span class="s2">"X-API-Key: </span><span class="nv">$KEY</span><span class="s2">"</span><span class="w"> </span><span class="se">\</span>
|
||
<span class="w"> </span>-H<span class="w"> </span><span class="s2">"Content-Type: application/json"</span><span class="w"> </span><span class="se">\</span>
|
||
<span class="w"> </span>-d<span class="w"> </span><span class="s1">'{</span>
|
||
<span class="s1"> "name": "John Smith",</span>
|
||
<span class="s1"> "metadata": {</span>
|
||
<span class="s1"> "aliases": [</span>
|
||
<span class="s1"> {"locale": "en", "name": "John Smith"},</span>
|
||
<span class="s1"> {"locale": "zh-TW", "name": "約翰·史密斯"},</span>
|
||
<span class="s1"> {"locale": "ja", "name": "ジョン・スミス"}</span>
|
||
<span class="s1"> ]</span>
|
||
<span class="s1"> }</span>
|
||
<span class="s1"> }'</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">"</span><span class="nv">$API</span><span class="s2">/api/v1/identity/</span><span class="nv">$IDENTITY_UUID</span><span class="s2">"</span><span class="w"> </span><span class="se">\</span>
|
||
<span class="w"> </span>-H<span class="w"> </span><span class="s2">"X-API-Key: </span><span class="nv">$KEY</span><span class="s2">"</span><span class="w"> </span><span class="se">\</span>
|
||
<span class="w"> </span>-H<span class="w"> </span><span class="s2">"Content-Type: application/json"</span><span class="w"> </span><span class="se">\</span>
|
||
<span class="w"> </span>-d<span class="w"> </span><span class="s1">'{"status": "confirmed"}'</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">"success"</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">"identity_uuid"</span><span class="p">:</span><span class="w"> </span><span class="s2">"a9a901056d6b46ff92da0c3c1a57dff4"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"updated_fields"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"name"</span><span class="p">,</span><span class="w"> </span><span class="s2">"metadata"</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>
|
||
<h4>History & Undo/Redo</h4>
|
||
<p>Every PATCH records a before/after snapshot in the operation history. Up to 256 records per identity are kept (oldest auto-deleted). See <a href="14_identity_history.md"><code>14_identity_history.md</code></a> for:</p>
|
||
<ul>
|
||
<li><code>POST /api/v1/identity/:identity_uuid/undo</code> — Revert PATCH changes</li>
|
||
<li><code>POST /api/v1/identity/:identity_uuid/redo</code> — Reapply undone changes</li>
|
||
<li><code>GET /api/v1/identity/:identity_uuid/history</code> — Query operation log</li>
|
||
</ul>
|
||
<hr />
|
||
<h3><code>GET /api/v1/identity/:identity_uuid/files</code></h3>
|
||
<p><strong>Auth</strong>: Required
|
||
<strong>Scope</strong>: identity-level</p>
|
||
<p>Get all files where this identity appears. Returns per-file summary including face count, confidence, and appearance time range.</p>
|
||
<h4>Example</h4>
|
||
<div class="codehilite"><pre><span></span><code>curl<span class="w"> </span>-s<span class="w"> </span><span class="s2">"</span><span class="nv">$API</span><span class="s2">/api/v1/identity/</span><span class="nv">$IDENTITY_UUID</span><span class="s2">/files"</span><span class="w"> </span>-H<span class="w"> </span><span class="s2">"X-API-Key: </span><span class="nv">$KEY</span><span class="s2">"</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">"success"</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">"identity_uuid"</span><span class="p">:</span><span class="w"> </span><span class="s2">"a9a901056d6b46ff92da0c3c1a57dff4"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Cary Grant"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"total"</span><span class="p">:</span><span class="w"> </span><span class="mi">3</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"page"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"page_size"</span><span class="p">:</span><span class="w"> </span><span class="mi">20</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"data"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span>
|
||
<span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">"file_uuid"</span><span class="p">:</span><span class="w"> </span><span class="s2">"aeed71342a899fe4b4c57b7d41bcb692"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"file_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"charade.mp4"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"file_path"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/path/to/charade.mp4"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"status"</span><span class="p">:</span><span class="w"> </span><span class="s2">"done"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"face_count"</span><span class="p">:</span><span class="w"> </span><span class="mi">16335</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"speaker_count"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"first_appearance"</span><span class="p">:</span><span class="w"> </span><span class="mf">206.76</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"last_appearance"</span><span class="p">:</span><span class="w"> </span><span class="mf">6756.68</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"confidence"</span><span class="p">:</span><span class="w"> </span><span class="mf">0.8088</span>
|
||
<span class="w"> </span><span class="p">}</span>
|
||
<span class="w"> </span><span class="p">]</span>
|
||
<span class="p">}</span>
|
||
</code></pre></div>
|
||
|
||
<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>Identity display name</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>data[].file_uuid</code></td>
|
||
<td>string</td>
|
||
<td>File identifier</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>data[].file_name</code></td>
|
||
<td>string</td>
|
||
<td>File name</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>data[].face_count</code></td>
|
||
<td>integer</td>
|
||
<td>Number of face detections in this file</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>data[].first_appearance</code></td>
|
||
<td>float</td>
|
||
<td>First appearance time in seconds</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>data[].last_appearance</code></td>
|
||
<td>float</td>
|
||
<td>Last appearance time in seconds</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>data[].confidence</code></td>
|
||
<td>float</td>
|
||
<td>Average confidence (0.0–1.0)</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h3><code>GET /api/v1/identity/:identity_uuid/faces</code></h3>
|
||
<p><strong>Auth</strong>: Required
|
||
<strong>Scope</strong>: identity-level</p>
|
||
<p>Get all face detection records associated with this identity.</p>
|
||
<h4>Example</h4>
|
||
<div class="codehilite"><pre><span></span><code>curl<span class="w"> </span>-s<span class="w"> </span><span class="s2">"</span><span class="nv">$API</span><span class="s2">/api/v1/identity/</span><span class="nv">$IDENTITY_UUID</span><span class="s2">/faces"</span><span class="w"> </span>-H<span class="w"> </span><span class="s2">"X-API-Key: </span><span class="nv">$KEY</span><span class="s2">"</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">"success"</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">"identity_uuid"</span><span class="p">:</span><span class="w"> </span><span class="s2">"a9a901056d6b46ff92da0c3c1a57dff4"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Cary Grant"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"total"</span><span class="p">:</span><span class="w"> </span><span class="mi">963</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"page"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"page_size"</span><span class="p">:</span><span class="w"> </span><span class="mi">50</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"data"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span>
|
||
<span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">3902</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"file_uuid"</span><span class="p">:</span><span class="w"> </span><span class="s2">"aeed71342a899fe4b4c57b7d41bcb692"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"frame_number"</span><span class="p">:</span><span class="w"> </span><span class="mi">37974</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"timestamp_secs"</span><span class="p">:</span><span class="w"> </span><span class="mf">1518.96</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"face_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"37974_1"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"confidence"</span><span class="p">:</span><span class="w"> </span><span class="mf">0.8197</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"bbox"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nt">"x"</span><span class="p">:</span><span class="w"> </span><span class="mi">1097</span><span class="p">,</span><span class="w"> </span><span class="nt">"y"</span><span class="p">:</span><span class="w"> </span><span class="mi">310</span><span class="p">,</span><span class="w"> </span><span class="nt">"width"</span><span class="p">:</span><span class="w"> </span><span class="mi">177</span><span class="p">,</span><span class="w"> </span><span class="nt">"height"</span><span class="p">:</span><span class="w"> </span><span class="mi">177</span><span class="w"> </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>
|
||
|
||
<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>Identity display name</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>data[].file_uuid</code></td>
|
||
<td>string</td>
|
||
<td>File where face was detected</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>data[].frame_number</code></td>
|
||
<td>integer</td>
|
||
<td>Frame number of detection</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>data[].face_id</code></td>
|
||
<td>string</td>
|
||
<td>Face ID (format: <code>{frame}_{idx}</code>)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>data[].confidence</code></td>
|
||
<td>float</td>
|
||
<td>Detection confidence</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h3><code>GET /api/v1/file/:file_uuid/faces</code></h3>
|
||
<p><strong>Auth</strong>: Required
|
||
<strong>Scope</strong>: identity-level</p>
|
||
<p>List all face detections in a file with binding status. Each face is in one of four binding states:</p>
|
||
<table class="table">
|
||
<thead>
|
||
<tr>
|
||
<th>State</th>
|
||
<th><code>binding</code> response</th>
|
||
<th>Meaning</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>identity</strong></td>
|
||
<td><code>{"identity_id": 9, "identity_uuid": "...", "identity_name": "Audrey Hepburn"}</code></td>
|
||
<td>Face matched to a known TMDb or user-defined identity</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>stranger</strong></td>
|
||
<td><code>{"stranger_id": 845, "metadata": {}}</code></td>
|
||
<td>Face matched to an unknown person (trace not matched to any known identity)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>dangling</strong></td>
|
||
<td><code>{"old_identity_id": 18052}</code></td>
|
||
<td>Face was previously bound to an auto-generated identity that has been deleted (orphaned reference)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>unbound</strong></td>
|
||
<td><code>null</code></td>
|
||
<td>Face has no binding at all (identity_id and stranger_id are both NULL)</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h4>Query Parameters</h4>
|
||
<table class="table">
|
||
<thead>
|
||
<tr>
|
||
<th>Param</th>
|
||
<th>Type</th>
|
||
<th>Default</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>page</code></td>
|
||
<td>int</td>
|
||
<td>1</td>
|
||
<td>Page number</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>page_size</code></td>
|
||
<td>int</td>
|
||
<td>50</td>
|
||
<td>Items per page</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>binding</code></td>
|
||
<td>string</td>
|
||
<td>—</td>
|
||
<td>Filter by state: <code>identity</code>, <code>stranger</code>, <code>dangling</code>, or <code>unbound</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>trace_id</code></td>
|
||
<td>int</td>
|
||
<td>—</td>
|
||
<td>Filter by trace ID</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>min_confidence</code></td>
|
||
<td>float</td>
|
||
<td>—</td>
|
||
<td>Minimum detection confidence (0.0–1.0)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>start_frame</code></td>
|
||
<td>int</td>
|
||
<td>—</td>
|
||
<td>Starting frame number (inclusive)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>end_frame</code></td>
|
||
<td>int</td>
|
||
<td>—</td>
|
||
<td>Ending frame number (inclusive)</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h4>Example</h4>
|
||
<div class="codehilite"><pre><span></span><code>curl<span class="w"> </span>-s<span class="w"> </span><span class="s2">"</span><span class="nv">$API</span><span class="s2">/api/v1/file/aeed71342a899fe4b4c57b7d41bcb692/faces?page=1&page_size=2&binding=identity"</span><span class="w"> </span>-H<span class="w"> </span><span class="s2">"X-API-Key: </span><span class="nv">$KEY</span><span class="s2">"</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">"success"</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">"file_uuid"</span><span class="p">:</span><span class="w"> </span><span class="s2">"aeed71342a899fe4b4c57b7d41bcb692"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"total"</span><span class="p">:</span><span class="w"> </span><span class="mi">52244</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"page"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"page_size"</span><span class="p">:</span><span class="w"> </span><span class="mi">2</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"data"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span>
|
||
<span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">661508</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"file_uuid"</span><span class="p">:</span><span class="w"> </span><span class="s2">"aeed71342a899fe4b4c57b7d41bcb692"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"frame_number"</span><span class="p">:</span><span class="w"> </span><span class="mi">21297</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"timestamp_secs"</span><span class="p">:</span><span class="w"> </span><span class="mf">851.88</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"face_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"21297_0"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"trace_id"</span><span class="p">:</span><span class="w"> </span><span class="mi">485</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"bbox"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nt">"x"</span><span class="p">:</span><span class="w"> </span><span class="mi">1072</span><span class="p">,</span><span class="w"> </span><span class="nt">"y"</span><span class="p">:</span><span class="w"> </span><span class="mi">390</span><span class="p">,</span><span class="w"> </span><span class="nt">"width"</span><span class="p">:</span><span class="w"> </span><span class="mi">56</span><span class="p">,</span><span class="w"> </span><span class="nt">"height"</span><span class="p">:</span><span class="w"> </span><span class="mi">56</span><span class="w"> </span><span class="p">},</span>
|
||
<span class="w"> </span><span class="nt">"confidence"</span><span class="p">:</span><span class="w"> </span><span class="mf">0.6114</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"binding"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">"identity_id"</span><span class="p">:</span><span class="w"> </span><span class="mi">9</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"identity_uuid"</span><span class="p">:</span><span class="w"> </span><span class="s2">"c3545906-c82d-4b66-aa1d-150bc02decce"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"identity_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Audrey Hepburn"</span>
|
||
<span class="w"> </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>
|
||
|
||
<table class="table">
|
||
<thead>
|
||
<tr>
|
||
<th>Field</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>total</code></td>
|
||
<td>int</td>
|
||
<td>Number of faces matching the filter (not total in file)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>data[].trace_id</code></td>
|
||
<td>int</td>
|
||
<td>Face tracking trace ID</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>data[].timestamp_secs</code></td>
|
||
<td>float</td>
|
||
<td>Timestamp in seconds (<code>frame_number / fps</code>)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>data[].bbox</code></td>
|
||
<td>object</td>
|
||
<td>Bounding box <code>{x, y, width, height}</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>data[].binding</code></td>
|
||
<td>object/null</td>
|
||
<td>One of four binding states (see table above)</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h3><code>GET /api/v1/identity/:identity_uuid/chunks</code></h3>
|
||
<p><strong>Auth</strong>: Required
|
||
<strong>Scope</strong>: identity-level</p>
|
||
<p>Get all text chunks (sentences) spoken while this identity's face was on screen. Useful for finding what a person said.</p>
|
||
<h4>Example</h4>
|
||
<div class="codehilite"><pre><span></span><code>curl<span class="w"> </span>-s<span class="w"> </span><span class="s2">"</span><span class="nv">$API</span><span class="s2">/api/v1/identity/</span><span class="nv">$IDENTITY_UUID</span><span class="s2">/chunks"</span><span class="w"> </span>-H<span class="w"> </span><span class="s2">"X-API-Key: </span><span class="nv">$KEY</span><span class="s2">"</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">"success"</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">"identity_uuid"</span><span class="p">:</span><span class="w"> </span><span class="s2">"a9a901056d6b46ff92da0c3c1a57dff4"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Cary Grant"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"total"</span><span class="p">:</span><span class="w"> </span><span class="mi">20</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"page"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"page_size"</span><span class="p">:</span><span class="w"> </span><span class="mi">20</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"data"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span>
|
||
<span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"file_uuid"</span><span class="p">:</span><span class="w"> </span><span class="s2">"bd80fec92b0b6963d177a2c55bf713e2"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"chunk_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"bd80fec92b0b6963d177a2c55bf713e2_2"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"chunk_type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"sentence"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"start_frame"</span><span class="p">:</span><span class="w"> </span><span class="mi">5103</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"end_frame"</span><span class="p">:</span><span class="w"> </span><span class="mi">5127</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"fps"</span><span class="p">:</span><span class="w"> </span><span class="mf">24.0</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"start_time"</span><span class="p">:</span><span class="w"> </span><span class="mf">212.64</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"end_time"</span><span class="p">:</span><span class="w"> </span><span class="mf">213.64</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"text_content"</span><span class="p">:</span><span class="w"> </span><span class="s2">"[213s-214s] Cary Grant: \"Olá!\""</span>
|
||
<span class="w"> </span><span class="p">}</span>
|
||
<span class="w"> </span><span class="p">]</span>
|
||
<span class="p">}</span>
|
||
</code></pre></div>
|
||
|
||
<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>Identity display name</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>data[].file_uuid</code></td>
|
||
<td>string</td>
|
||
<td>File identifier</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>data[].chunk_id</code></td>
|
||
<td>string</td>
|
||
<td>Sentence chunk identifier</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>data[].start_frame</code></td>
|
||
<td>integer</td>
|
||
<td>Frame-accurate start position</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>data[].end_frame</code></td>
|
||
<td>integer</td>
|
||
<td>Frame-accurate end position</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>data[].fps</code></td>
|
||
<td>float</td>
|
||
<td>Frames per second</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>data[].start_time</code></td>
|
||
<td>float</td>
|
||
<td>Start time in seconds</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>data[].end_time</code></td>
|
||
<td>float</td>
|
||
<td>End time in seconds</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>data[].text_content</code></td>
|
||
<td>string</td>
|
||
<td>Spoken text content</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h3><code>POST /api/v1/identity/:identity_uuid/bind</code></h3>
|
||
<p><strong>Auth</strong>: Required
|
||
<strong>Scope</strong>: identity-level</p>
|
||
<p>Bind a face detection to an identity. Associates the face trace with the identity for future search and recognition.</p>
|
||
<h4>Request Parameters</h4>
|
||
<table class="table">
|
||
<thead>
|
||
<tr>
|
||
<th>Field</th>
|
||
<th>Type</th>
|
||
<th>Required</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>file_uuid</code></td>
|
||
<td>string</td>
|
||
<td>Yes</td>
|
||
<td>File where face is detected</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>face_id</code></td>
|
||
<td>string</td>
|
||
<td>Yes</td>
|
||
<td>Face ID (format: <code>{frame}_{idx}</code>)</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h4>Side Effects</h4>
|
||
<ul>
|
||
<li>清除該 face detection row 的 <code>stranger_id</code>(設為 NULL)</li>
|
||
<li>不影響 <code>identities</code> 表中原有的 stranger auto-identity 記錄</li>
|
||
</ul>
|
||
<h4>Example</h4>
|
||
<div class="codehilite"><pre><span></span><code>curl<span class="w"> </span>-s<span class="w"> </span>-X<span class="w"> </span>POST<span class="w"> </span><span class="s2">"</span><span class="nv">$API</span><span class="s2">/api/v1/identity/</span><span class="nv">$IDENTITY_UUID</span><span class="s2">/bind"</span><span class="w"> </span><span class="se">\</span>
|
||
<span class="w"> </span>-H<span class="w"> </span><span class="s2">"X-API-Key: </span><span class="nv">$KEY</span><span class="s2">"</span><span class="w"> </span><span class="se">\</span>
|
||
<span class="w"> </span>-H<span class="w"> </span><span class="s2">"Content-Type: application/json"</span><span class="w"> </span><span class="se">\</span>
|
||
<span class="w"> </span>-d<span class="w"> </span><span class="s1">'{"file_uuid": "'</span><span class="s2">"</span><span class="nv">$FILE_UUID</span><span class="s2">"</span><span class="s1">'", "face_id": "1_5"}'</span>
|
||
</code></pre></div>
|
||
|
||
<hr />
|
||
<h3><code>POST /api/v1/identity/:identity_uuid/bind/trace</code></h3>
|
||
<p><strong>Auth</strong>: Required
|
||
<strong>Scope</strong>: identity-level</p>
|
||
<p>Bind all face detections of a trace to an identity. Updates all rows in <code>face_detections</code> with the matching <code>file_uuid</code> and <code>trace_id</code>.</p>
|
||
<h4>Request Parameters</h4>
|
||
<table class="table">
|
||
<thead>
|
||
<tr>
|
||
<th>Field</th>
|
||
<th>Type</th>
|
||
<th>Required</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>file_uuid</code></td>
|
||
<td>string</td>
|
||
<td>Yes</td>
|
||
<td>File where trace exists</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>trace_id</code></td>
|
||
<td>integer</td>
|
||
<td>Yes</td>
|
||
<td>Trace ID (from <code>face_detections.trace_id</code>)</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h4>Side Effects</h4>
|
||
<ul>
|
||
<li>清除該 trace 所有 face detection rows 的 <code>stranger_id</code>(設為 NULL)</li>
|
||
<li>不影響 <code>identities</code> 表中原有的 stranger auto-identity 記錄</li>
|
||
</ul>
|
||
<h4>Example</h4>
|
||
<div class="codehilite"><pre><span></span><code>curl<span class="w"> </span>-s<span class="w"> </span>-X<span class="w"> </span>POST<span class="w"> </span><span class="s2">"</span><span class="nv">$API</span><span class="s2">/api/v1/identity/</span><span class="nv">$IDENTITY_UUID</span><span class="s2">/bind/trace"</span><span class="w"> </span><span class="se">\</span>
|
||
<span class="w"> </span>-H<span class="w"> </span><span class="s2">"X-API-Key: </span><span class="nv">$KEY</span><span class="s2">"</span><span class="w"> </span><span class="se">\</span>
|
||
<span class="w"> </span>-H<span class="w"> </span><span class="s2">"Content-Type: application/json"</span><span class="w"> </span><span class="se">\</span>
|
||
<span class="w"> </span>-d<span class="w"> </span><span class="s1">'{"file_uuid": "'</span><span class="s2">"</span><span class="nv">$FILE_UUID</span><span class="s2">"</span><span class="s1">'", "trace_id": 919}'</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">"success"</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">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Bound trace 919 of aeed71342... to Cary Grant"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"data"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nt">"rows_affected"</span><span class="p">:</span><span class="w"> </span><span class="mi">53</span><span class="w"> </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>404</code></td>
|
||
<td>Identity not found</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>500</code></td>
|
||
<td>Database error</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h3><code>GET /api/v1/identity/:identity_uuid/traces</code></h3>
|
||
<p><strong>Auth</strong>: Required
|
||
<strong>Scope</strong>: identity-level</p>
|
||
<p>Get paginated face traces (continuous tracking segments) associated with this identity across all files.</p>
|
||
<h4>Query Parameters</h4>
|
||
<table class="table">
|
||
<thead>
|
||
<tr>
|
||
<th>Field</th>
|
||
<th>Type</th>
|
||
<th>Required</th>
|
||
<th>Default</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>page</code></td>
|
||
<td>integer</td>
|
||
<td>No</td>
|
||
<td><code>1</code></td>
|
||
<td>Page number</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>page_size</code></td>
|
||
<td>integer</td>
|
||
<td>No</td>
|
||
<td><code>20</code></td>
|
||
<td>Items per page</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h4>Example</h4>
|
||
<div class="codehilite"><pre><span></span><code>curl<span class="w"> </span>-s<span class="w"> </span><span class="s2">"</span><span class="nv">$API</span><span class="s2">/api/v1/identity/</span><span class="nv">$IDENTITY_UUID</span><span class="s2">/traces?page=1&page_size=3"</span><span class="w"> </span><span class="se">\</span>
|
||
<span class="w"> </span>-H<span class="w"> </span><span class="s2">"X-API-Key: </span><span class="nv">$KEY</span><span class="s2">"</span><span class="w"> </span><span class="p">|</span><span class="w"> </span>jq<span class="w"> </span><span class="s1">'{total, total_faces, traces}'</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">"success"</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">"identity_uuid"</span><span class="p">:</span><span class="w"> </span><span class="s2">"a9a901056d6b46ff92da0c3c1a57dff4"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Cary Grant"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"total"</span><span class="p">:</span><span class="w"> </span><span class="mi">18</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"page"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"page_size"</span><span class="p">:</span><span class="w"> </span><span class="mi">3</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"total_faces"</span><span class="p">:</span><span class="w"> </span><span class="mi">542</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"traces"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span>
|
||
<span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">"file_uuid"</span><span class="p">:</span><span class="w"> </span><span class="s2">"aeed71342a899fe4b4c57b7d41bcb692"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"trace_id"</span><span class="p">:</span><span class="w"> </span><span class="mi">906</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"frame_count"</span><span class="p">:</span><span class="w"> </span><span class="mi">52</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"first_frame"</span><span class="p">:</span><span class="w"> </span><span class="mi">37974</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"last_frame"</span><span class="p">:</span><span class="w"> </span><span class="mi">38127</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"first_sec"</span><span class="p">:</span><span class="w"> </span><span class="mf">1519.0</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"last_sec"</span><span class="p">:</span><span class="w"> </span><span class="mf">1525.1</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"avg_confidence"</span><span class="p">:</span><span class="w"> </span><span class="mf">0.8254</span>
|
||
<span class="w"> </span><span class="p">}</span>
|
||
<span class="w"> </span><span class="p">]</span>
|
||
<span class="p">}</span>
|
||
</code></pre></div>
|
||
|
||
<table class="table">
|
||
<thead>
|
||
<tr>
|
||
<th>Field</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>success</code></td>
|
||
<td>bool</td>
|
||
<td>Always <code>true</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>identity_uuid</code></td>
|
||
<td>string</td>
|
||
<td>Identity UUID</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>name</code></td>
|
||
<td>string</td>
|
||
<td>Identity display name</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>total</code></td>
|
||
<td>integer</td>
|
||
<td>Total number of traces (across all pages)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>total_faces</code></td>
|
||
<td>integer</td>
|
||
<td>Sum of all face detections in returned traces</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>traces[].file_uuid</code></td>
|
||
<td>string</td>
|
||
<td>File where trace exists</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>traces[].trace_id</code></td>
|
||
<td>integer</td>
|
||
<td>Trace tracking ID</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>traces[].frame_count</code></td>
|
||
<td>integer</td>
|
||
<td>Number of frames in this trace</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>traces[].first_frame</code></td>
|
||
<td>integer</td>
|
||
<td>Start frame number</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>traces[].last_frame</code></td>
|
||
<td>integer</td>
|
||
<td>End frame number</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>traces[].first_sec</code></td>
|
||
<td>float</td>
|
||
<td>Start time in seconds</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>traces[].last_sec</code></td>
|
||
<td>float</td>
|
||
<td>End time in seconds</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>traces[].avg_confidence</code></td>
|
||
<td>float</td>
|
||
<td>Average detection confidence (0.0–1.0)</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>Identity not found</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>500</code></td>
|
||
<td>Database error</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h3><code>POST /api/v1/identity/:identity_uuid/unbind</code></h3>
|
||
<p><strong>Auth</strong>: Required
|
||
<strong>Scope</strong>: identity-level</p>
|
||
<p>Unbind a face detection from an identity. Removes the identity association from the face record.</p>
|
||
<h4>Side Effects</h4>
|
||
<ul>
|
||
<li>只清除 <code>identity_id</code>(設為 NULL),<strong>不會恢復 <code>stranger_id</code></strong></li>
|
||
<li>被 unbind 的 face 不會自動成為 stranger</li>
|
||
<li>要重新標記為 stranger 需重新跑 Agent API(<code>identity/analyze</code>)</li>
|
||
</ul>
|
||
<hr />
|
||
<h3><code>POST /api/v1/identity/:identity_uuid/mergeinto</code></h3>
|
||
<p><strong>Auth</strong>: Required
|
||
<strong>Scope</strong>: identity-level</p>
|
||
<p>Transfer all face bindings from this identity to another identity, then optionally delete or mark the source as merged.</p>
|
||
<h4>Two Merge Cases</h4>
|
||
<table class="table">
|
||
<thead>
|
||
<tr>
|
||
<th>Case</th>
|
||
<th>Description</th>
|
||
<th>Undo Support</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>stranger → identity</strong></td>
|
||
<td>Merge an auto-generated stranger identity into a known identity (TMDb or user-defined)</td>
|
||
<td>✅ 24hr undo</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>identity A → identity B</strong></td>
|
||
<td>Merge two known identities (e.g., duplicate entries)</td>
|
||
<td>✅ 24hr undo</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h4>Request Parameters</h4>
|
||
<table class="table">
|
||
<thead>
|
||
<tr>
|
||
<th>Field</th>
|
||
<th>Type</th>
|
||
<th>Required</th>
|
||
<th>Default</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>into_uuid</code></td>
|
||
<td>string</td>
|
||
<td>Yes</td>
|
||
<td>—</td>
|
||
<td>Target identity UUID to merge into</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>keep_history</code></td>
|
||
<td>bool</td>
|
||
<td>No</td>
|
||
<td><code>true</code></td>
|
||
<td>Keep source identity record with <code>status='merged'</code> (<code>true</code>) or delete it (<code>false</code>)</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h4>Side Effects</h4>
|
||
<ul>
|
||
<li>轉移所有 <code>face_detections.identity_id</code> 到目標 identity</li>
|
||
<li>同時清除所有被轉移 rows 的 <code>stranger_id</code></li>
|
||
<li>將 source name 加入 target aliases (with <code>source: "merge"</code> tag)</li>
|
||
<li>將 source aliases 加入 target aliases (if not already present)</li>
|
||
<li>將 source metadata fields 加入 target metadata (if not already present)</li>
|
||
<li><code>keep_history: true</code>(預設):source identity 設為 <code>status='merged'</code>,保留記錄</li>
|
||
<li><code>keep_history: false</code>:<strong>刪除</strong> source identity 及其 identity JSON 檔案</li>
|
||
<li><strong>記錄 merge history 到 MongoDB</strong>(支援 undo)</li>
|
||
</ul>
|
||
<h4>Example</h4>
|
||
<div class="codehilite"><pre><span></span><code>curl<span class="w"> </span>-s<span class="w"> </span>-X<span class="w"> </span>POST<span class="w"> </span><span class="s2">"</span><span class="nv">$API</span><span class="s2">/api/v1/identity/</span><span class="nv">$SOURCE_UUID</span><span class="s2">/mergeinto"</span><span class="w"> </span><span class="se">\</span>
|
||
<span class="w"> </span>-H<span class="w"> </span><span class="s2">"X-API-Key: </span><span class="nv">$KEY</span><span class="s2">"</span><span class="w"> </span><span class="se">\</span>
|
||
<span class="w"> </span>-H<span class="w"> </span><span class="s2">"Content-Type: application/json"</span><span class="w"> </span><span class="se">\</span>
|
||
<span class="w"> </span>-d<span class="w"> </span><span class="s1">'{"into_uuid": "'</span><span class="s2">"</span><span class="nv">$TARGET_UUID</span><span class="s2">"</span><span class="s1">'", "keep_history": true}'</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">"success"</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">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Merged 'stranger_13894' into 'Louis Viret' (52 faces transferred, history kept)"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"data"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">"merge_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"550e8400-e29b-41d4-a716-446655440000"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"faces_transferred"</span><span class="p">:</span><span class="w"> </span><span class="mi">52</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"aliases_added"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"metadata_fields_added"</span><span class="p">:</span><span class="w"> </span><span class="mi">2</span>
|
||
<span class="w"> </span><span class="p">}</span>
|
||
<span class="p">}</span>
|
||
</code></pre></div>
|
||
|
||
<table class="table">
|
||
<thead>
|
||
<tr>
|
||
<th>Field</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>merge_id</code></td>
|
||
<td>string</td>
|
||
<td>Unique merge operation ID (for undo)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>faces_transferred</code></td>
|
||
<td>integer</td>
|
||
<td>Number of face detections transferred</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>aliases_added</code></td>
|
||
<td>integer</td>
|
||
<td>Number of aliases added to target</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>metadata_fields_added</code></td>
|
||
<td>integer</td>
|
||
<td>Number of metadata fields added to target</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>Source or target identity not found</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>500</code></td>
|
||
<td>Database error</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h3><code>POST /api/v1/identity/merge/:merge_id/undo</code></h3>
|
||
<p><strong>Auth</strong>: Required
|
||
<strong>Scope</strong>: identity-level</p>
|
||
<p>Undo a merge operation within 24 hours. Restores the source identity and reverts face bindings.</p>
|
||
<h4>Undo Behavior</h4>
|
||
<table class="table">
|
||
<thead>
|
||
<tr>
|
||
<th>Action</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>Restore source identity</td>
|
||
<td>If <code>keep_history=true</code>: restore status to <code>confirmed</code><br>If <code>keep_history=false</code>: recreate identity from MongoDB snapshot</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Restore faces</td>
|
||
<td>Transfer faces back to source identity</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Remove aliases from target</td>
|
||
<td>Remove aliases with <code>source: "merge"</code> tag</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Remove metadata fields from target</td>
|
||
<td>Remove fields that were added from source</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Preserve manual changes</strong></td>
|
||
<td>Keep aliases/metadata manually added after merge</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h4>Example</h4>
|
||
<div class="codehilite"><pre><span></span><code>curl<span class="w"> </span>-s<span class="w"> </span>-X<span class="w"> </span>POST<span class="w"> </span><span class="s2">"</span><span class="nv">$API</span><span class="s2">/api/v1/identity/merge/550e8400-e29b-41d4-a716-446655440000/undo"</span><span class="w"> </span><span class="se">\</span>
|
||
<span class="w"> </span>-H<span class="w"> </span><span class="s2">"X-API-Key: </span><span class="nv">$KEY</span><span class="s2">"</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">"success"</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">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Undo merge completed: 'stranger_13894' restored, 52 faces reverted"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"data"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">"source_identity_restored"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">"uuid"</span><span class="p">:</span><span class="w"> </span><span class="s2">"a9a90105..."</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"stranger_13894"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"status"</span><span class="p">:</span><span class="w"> </span><span class="s2">"confirmed"</span>
|
||
<span class="w"> </span><span class="p">},</span>
|
||
<span class="w"> </span><span class="nt">"faces_reverted"</span><span class="p">:</span><span class="w"> </span><span class="mi">52</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"aliases_removed_from_target"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"metadata_fields_removed_from_target"</span><span class="p">:</span><span class="w"> </span><span class="mi">2</span>
|
||
<span class="w"> </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>Undo deadline expired (>24hr) or already undone</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>404</code></td>
|
||
<td>Merge record not found</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>500</code></td>
|
||
<td>Database error</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h3><code>GET /api/v1/identity/merge/history</code></h3>
|
||
<p><strong>Auth</strong>: Required
|
||
<strong>Scope</strong>: identity-level</p>
|
||
<p>Query merge history records from MongoDB.</p>
|
||
<h4>Query Parameters</h4>
|
||
<table class="table">
|
||
<thead>
|
||
<tr>
|
||
<th>Field</th>
|
||
<th>Type</th>
|
||
<th>Required</th>
|
||
<th>Default</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>source_uuid</code></td>
|
||
<td>string</td>
|
||
<td>No</td>
|
||
<td>—</td>
|
||
<td>Filter by source identity UUID</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>target_uuid</code></td>
|
||
<td>string</td>
|
||
<td>No</td>
|
||
<td>—</td>
|
||
<td>Filter by target identity UUID</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>merge_id</code></td>
|
||
<td>string</td>
|
||
<td>No</td>
|
||
<td>—</td>
|
||
<td>Filter by specific merge ID</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>undone</code></td>
|
||
<td>bool</td>
|
||
<td>No</td>
|
||
<td>—</td>
|
||
<td>Filter by undone status</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>page</code></td>
|
||
<td>int</td>
|
||
<td>No</td>
|
||
<td>1</td>
|
||
<td>Page number</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>page_size</code></td>
|
||
<td>int</td>
|
||
<td>No</td>
|
||
<td>20</td>
|
||
<td>Items per page</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h4>Example</h4>
|
||
<div class="codehilite"><pre><span></span><code>curl<span class="w"> </span>-s<span class="w"> </span><span class="s2">"</span><span class="nv">$API</span><span class="s2">/api/v1/identity/merge/history?page=1&page_size=10"</span><span class="w"> </span><span class="se">\</span>
|
||
<span class="w"> </span>-H<span class="w"> </span><span class="s2">"X-API-Key: </span><span class="nv">$KEY</span><span class="s2">"</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">"success"</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">"total"</span><span class="p">:</span><span class="w"> </span><span class="mi">5</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"page"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"page_size"</span><span class="p">:</span><span class="w"> </span><span class="mi">10</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"results"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span>
|
||
<span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">"merge_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"550e8400-e29b-41d4-a716-446655440000"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"source_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"stranger_13894"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"target_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Louis Viret"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"faces_transferred"</span><span class="p">:</span><span class="w"> </span><span class="mi">52</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"merged_at"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2026-05-27T10:00:00Z"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"undo_deadline"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2026-05-28T10:00:00Z"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"undone"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"undo_expired"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span>
|
||
<span class="w"> </span><span class="p">}</span>
|
||
<span class="w"> </span><span class="p">]</span>
|
||
<span class="p">}</span>
|
||
</code></pre></div>
|
||
|
||
<table class="table">
|
||
<thead>
|
||
<tr>
|
||
<th>Field</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>merge_id</code></td>
|
||
<td>string</td>
|
||
<td>Unique merge operation ID</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>source_name</code></td>
|
||
<td>string</td>
|
||
<td>Source identity name</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>target_name</code></td>
|
||
<td>string</td>
|
||
<td>Target identity name</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>faces_transferred</code></td>
|
||
<td>integer</td>
|
||
<td>Number of faces transferred</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>merged_at</code></td>
|
||
<td>datetime</td>
|
||
<td>When merge occurred</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>undo_deadline</code></td>
|
||
<td>datetime</td>
|
||
<td>24hr deadline for undo</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>undone</code></td>
|
||
<td>bool</td>
|
||
<td>Whether merge was undone</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>undo_expired</code></td>
|
||
<td>bool</td>
|
||
<td>Whether undo deadline passed</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h3><code>GET /api/v1/identities/search</code></h3>
|
||
<p><strong>Auth</strong>: Required
|
||
<strong>Scope</strong>: global / file-level</p>
|
||
<p>Search identity name → find associated chunks. Searches identity name and aliases, returns identities with their associated text chunks.</p>
|
||
<h4>Query Parameters</h4>
|
||
<table class="table">
|
||
<thead>
|
||
<tr>
|
||
<th>Field</th>
|
||
<th>Type</th>
|
||
<th>Required</th>
|
||
<th>Default</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>q</code></td>
|
||
<td>string</td>
|
||
<td>Yes</td>
|
||
<td>—</td>
|
||
<td>Search text (ILIKE match on name and aliases)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>file_uuid</code></td>
|
||
<td>string</td>
|
||
<td>No</td>
|
||
<td>—</td>
|
||
<td>Restrict to specific file. If omitted, searches all files (global search)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>limit</code></td>
|
||
<td>integer</td>
|
||
<td>No</td>
|
||
<td>50</td>
|
||
<td>Max results</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h4>Example (Global Search)</h4>
|
||
<div class="codehilite"><pre><span></span><code>curl<span class="w"> </span>-s<span class="w"> </span><span class="s2">"</span><span class="nv">$API</span><span class="s2">/api/v1/identities/search?q=Audrey"</span><span class="w"> </span>-H<span class="w"> </span><span class="s2">"X-API-Key: </span><span class="nv">$KEY</span><span class="s2">"</span>
|
||
</code></pre></div>
|
||
|
||
<h4>Example (File-specific Search)</h4>
|
||
<div class="codehilite"><pre><span></span><code>curl<span class="w"> </span>-s<span class="w"> </span><span class="s2">"</span><span class="nv">$API</span><span class="s2">/api/v1/identities/search?q=Audrey&file_uuid=</span><span class="nv">$FILE_UUID</span><span class="s2">"</span><span class="w"> </span>-H<span class="w"> </span><span class="s2">"X-API-Key: </span><span class="nv">$KEY</span><span class="s2">"</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">"success"</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">"total"</span><span class="p">:</span><span class="w"> </span><span class="mi">5</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"results"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span>
|
||
<span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">"identity_id"</span><span class="p">:</span><span class="w"> </span><span class="mi">9</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Audrey Hepburn"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"source"</span><span class="p">:</span><span class="w"> </span><span class="s2">"tmdb"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"tmdb_id"</span><span class="p">:</span><span class="w"> </span><span class="mi">1932</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"file_uuid"</span><span class="p">:</span><span class="w"> </span><span class="s2">"a6fb22eebefaef17e62af874997c5944"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"trace_id"</span><span class="p">:</span><span class="w"> </span><span class="mi">41</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"chunk_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"llm_parent_..._204_207"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"start_time"</span><span class="p">:</span><span class="w"> </span><span class="mf">204.162</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"text_content"</span><span class="p">:</span><span class="w"> </span><span class="s2">"...confrontation..."</span>
|
||
<span class="w"> </span><span class="p">}</span>
|
||
<span class="w"> </span><span class="p">]</span>
|
||
<span class="p">}</span>
|
||
</code></pre></div>
|
||
|
||
<table class="table">
|
||
<thead>
|
||
<tr>
|
||
<th>Field</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>results[].identity_id</code></td>
|
||
<td>integer</td>
|
||
<td>Identity ID</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>results[].name</code></td>
|
||
<td>string</td>
|
||
<td>Identity name</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>results[].source</code></td>
|
||
<td>string</td>
|
||
<td>Identity source (<code>tmdb</code>, <code>user_defined</code>, etc.)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>results[].tmdb_id</code></td>
|
||
<td>integer</td>
|
||
<td>TMDb person ID (if source = tmdb)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>results[].file_uuid</code></td>
|
||
<td>string</td>
|
||
<td>File where identity appears</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>results[].trace_id</code></td>
|
||
<td>integer</td>
|
||
<td>Face trace ID</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>results[].chunk_id</code></td>
|
||
<td>string</td>
|
||
<td>Associated chunk ID</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>results[].start_time</code></td>
|
||
<td>float</td>
|
||
<td>Chunk start time</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>results[].text_content</code></td>
|
||
<td>string</td>
|
||
<td>Chunk text content</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<hr />
|
||
<h3><code>POST /api/v1/identity/upload</code></h3>
|
||
<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>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">
|
||
<thead>
|
||
<tr>
|
||
<th>Field</th>
|
||
<th>Type</th>
|
||
<th>Required</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>identity_uuid</code></td>
|
||
<td>string</td>
|
||
<td>Yes</td>
|
||
<td>Identity identifier</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>name</code></td>
|
||
<td>string</td>
|
||
<td>Yes</td>
|
||
<td>Identity display name</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>identity_type</code></td>
|
||
<td>string</td>
|
||
<td>No</td>
|
||
<td><code>"people"</code> or null</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>source</code></td>
|
||
<td>string</td>
|
||
<td>No</td>
|
||
<td><code>.json</code>, <code>auto</code>, <code>tmdb</code>, <code>user_defined</code>, or <code>merged</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>status</code></td>
|
||
<td>string</td>
|
||
<td>No</td>
|
||
<td><code>"confirmed"</code>, <code>"pending"</code>, or <code>"inactive"</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>tmdb_id</code></td>
|
||
<td>integer</td>
|
||
<td>No</td>
|
||
<td>TMDb person ID</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>tmdb_profile</code></td>
|
||
<td>string</td>
|
||
<td>No</td>
|
||
<td>TMDb profile image URL</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>metadata</code></td>
|
||
<td>object</td>
|
||
<td>No</td>
|
||
<td>Arbitrary metadata JSON</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>file_bindings</code></td>
|
||
<td>array</td>
|
||
<td>No</td>
|
||
<td>Array of <code>{ file_uuid, trace_ids, face_count }</code> (informational)</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h4>Example</h4>
|
||
<div class="codehilite"><pre><span></span><code>curl<span class="w"> </span>-s<span class="w"> </span>-X<span class="w"> </span>POST<span class="w"> </span><span class="s2">"</span><span class="nv">$API</span><span class="s2">/api/v1/identity/upload"</span><span class="w"> </span><span class="se">\</span>
|
||
<span class="w"> </span>-H<span class="w"> </span><span class="s2">"X-API-Key: </span><span class="nv">$KEY</span><span class="s2">"</span><span class="w"> </span><span class="se">\</span>
|
||
<span class="w"> </span>-H<span class="w"> </span><span class="s2">"Content-Type: application/json"</span><span class="w"> </span><span class="se">\</span>
|
||
<span class="w"> </span>-d<span class="w"> </span><span class="s1">'{</span>
|
||
<span class="s1"> "version": 1,</span>
|
||
<span class="s1"> "identity_uuid": "a9a901056d6b46ff92da0c3c1a57dff4",</span>
|
||
<span class="s1"> "name": "Cary Grant",</span>
|
||
<span class="s1"> "identity_type": "people",</span>
|
||
<span class="s1"> "source": ".json",</span>
|
||
<span class="s1"> "status": "confirmed",</span>
|
||
<span class="s1"> "metadata": {},</span>
|
||
<span class="s1"> "file_bindings": []</span>
|
||
<span class="s1"> }'</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">"success"</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">"identity_uuid"</span><span class="p">:</span><span class="w"> </span><span class="s2">"a9a901056d6b46ff92da0c3c1a57dff4"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Cary Grant"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Identity uploaded successfully"</span>
|
||
<span class="p">}</span>
|
||
</code></pre></div>
|
||
|
||
<hr />
|
||
<hr />
|
||
<h3><code>POST /api/v1/identity/:identity_uuid/profile-image</code></h3>
|
||
<p><strong>Auth</strong>: Required
|
||
<strong>Scope</strong>: identity-level</p>
|
||
<p>Upload a profile image (JPEG or PNG) for an identity. The image is saved to <code>{output}/identities/{uuid}/profile.{ext}</code>.</p>
|
||
<p>Uses <code>multipart/form-data</code> with field name <code>image</code>.</p>
|
||
<h4>Example</h4>
|
||
<div class="codehilite"><pre><span></span><code>curl<span class="w"> </span>-s<span class="w"> </span>-X<span class="w"> </span>POST<span class="w"> </span><span class="s2">"</span><span class="nv">$API</span><span class="s2">/api/v1/identity/</span><span class="nv">$IDENTITY_UUID</span><span class="s2">/profile-image"</span><span class="w"> </span><span class="se">\</span>
|
||
<span class="w"> </span>-H<span class="w"> </span><span class="s2">"X-API-Key: </span><span class="nv">$KEY</span><span class="s2">"</span><span class="w"> </span><span class="se">\</span>
|
||
<span class="w"> </span>-F<span class="w"> </span><span class="s2">"image=@/path/to/photo.jpg"</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">"success"</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">"identity_uuid"</span><span class="p">:</span><span class="w"> </span><span class="s2">"a9a901056d6b46ff92da0c3c1a57dff4"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"path"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/path/to/output/identities/.../profile.jpg"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Profile image saved: profile.jpg"</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>Missing image field or unsupported format</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>404</code></td>
|
||
<td>Identity not found</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>415</code></td>
|
||
<td>Unsupported image type (use JPEG or PNG)</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h3><code>GET /api/v1/identity/:identity_uuid/profile-image</code></h3>
|
||
<p><strong>Auth</strong>: Required
|
||
<strong>Scope</strong>: identity-level</p>
|
||
<p>Retrieve the profile image for an identity. Returns the raw image data with appropriate Content-Type header.</p>
|
||
<div class="codehilite"><pre><span></span><code>curl<span class="w"> </span>-s<span class="w"> </span><span class="s2">"</span><span class="nv">$API</span><span class="s2">/api/v1/identity/</span><span class="nv">$IDENTITY_UUID</span><span class="s2">/profile-image"</span><span class="w"> </span><span class="se">\</span>
|
||
<span class="w"> </span>-H<span class="w"> </span><span class="s2">"X-API-Key: </span><span class="nv">$KEY</span><span class="s2">"</span><span class="w"> </span>-o<span class="w"> </span>profile.jpg
|
||
</code></pre></div>
|
||
|
||
<table class="table">
|
||
<thead>
|
||
<tr>
|
||
<th>Response Header</th>
|
||
<th>Value</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>content-type</code></td>
|
||
<td><code>image/jpeg</code> or <code>image/png</code></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<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">=></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">'-'</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">=></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">"metadata"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">"aliases"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span>
|
||
<span class="w"> </span><span class="p">{</span><span class="nt">"locale"</span><span class="p">:</span><span class="w"> </span><span class="s2">"en"</span><span class="p">,</span><span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"John Smith"</span><span class="p">},</span>
|
||
<span class="w"> </span><span class="p">{</span><span class="nt">"locale"</span><span class="p">:</span><span class="w"> </span><span class="s2">"zh-TW"</span><span class="p">,</span><span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"約翰·史密斯"</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-25 — Added <code>GET /api/v1/file/:file_uuid/faces</code> with 4 binding states, filters, strangers table split</p>
|
||
</div>
|
||
</body>
|
||
</html> |