davinci-resolve-mcp 2.63.2 → 2.65.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +81 -0
- package/README.md +1 -1
- package/docs/SKILL.md +8 -2
- package/docs/guides/media-analysis-guide.md +17 -0
- package/docs/install.md +23 -1
- package/install.py +30 -3
- package/package.json +1 -1
- package/scripts/doctor.py +73 -15
- package/src/analysis_dashboard.py +156 -18
- package/src/control_panel_i18n.py +780 -0
- package/src/granular/common.py +4 -3
- package/src/server.py +3 -2
- package/src/utils/media_analysis.py +146 -3
- package/src/utils/resolve_connection.py +46 -13
|
@@ -44,10 +44,12 @@ from src.utils.media_analysis_jobs import (
|
|
|
44
44
|
run_batch_job_slice,
|
|
45
45
|
)
|
|
46
46
|
from src.utils.platform import setup_environment
|
|
47
|
+
from src.utils.resolve_connection import connect_resolve
|
|
47
48
|
from src.utils.analysis_memory import read_panel_state, write_panel_state
|
|
48
49
|
from src.utils import brain_edits as _brain_edits
|
|
49
50
|
from src.utils import timeline_versioning as _timeline_versioning
|
|
50
51
|
from src.utils import timeline_brain_db as _timeline_brain_db
|
|
52
|
+
from src.control_panel_i18n import localization_script
|
|
51
53
|
|
|
52
54
|
|
|
53
55
|
HTML = r"""<!doctype html>
|
|
@@ -615,6 +617,33 @@ HTML = r"""<!doctype html>
|
|
|
615
617
|
gap: var(--space-2);
|
|
616
618
|
min-width: 0;
|
|
617
619
|
}
|
|
620
|
+
.language-switch {
|
|
621
|
+
display: inline-flex;
|
|
622
|
+
align-items: center;
|
|
623
|
+
min-height: 32px;
|
|
624
|
+
padding: 2px;
|
|
625
|
+
border: 1px solid var(--border-default);
|
|
626
|
+
border-radius: var(--radius-sm);
|
|
627
|
+
background: var(--bg-elevated-1);
|
|
628
|
+
}
|
|
629
|
+
.language-switch button {
|
|
630
|
+
min-height: 26px;
|
|
631
|
+
padding: 0 8px;
|
|
632
|
+
border: 0;
|
|
633
|
+
border-radius: 2px;
|
|
634
|
+
background: transparent;
|
|
635
|
+
color: var(--text-tertiary);
|
|
636
|
+
font-size: 11px;
|
|
637
|
+
font-weight: 600;
|
|
638
|
+
}
|
|
639
|
+
.language-switch button:hover {
|
|
640
|
+
background: var(--bg-hover);
|
|
641
|
+
color: var(--text-primary);
|
|
642
|
+
}
|
|
643
|
+
.language-switch button.active {
|
|
644
|
+
background: var(--accent-brand-muted);
|
|
645
|
+
color: var(--accent-brand-hover);
|
|
646
|
+
}
|
|
618
647
|
.version-badge {
|
|
619
648
|
position: relative;
|
|
620
649
|
display: inline-flex;
|
|
@@ -2504,16 +2533,57 @@ HTML = r"""<!doctype html>
|
|
|
2504
2533
|
.ai-op-btn.danger { background: #b4452f; }
|
|
2505
2534
|
.ai-caps-grid {
|
|
2506
2535
|
display: grid;
|
|
2507
|
-
grid-template-columns: repeat(auto-
|
|
2536
|
+
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
|
2508
2537
|
gap: var(--space-2);
|
|
2509
2538
|
margin-top: var(--space-2);
|
|
2510
2539
|
}
|
|
2511
2540
|
.ai-caps-item {
|
|
2512
|
-
display:
|
|
2541
|
+
display: grid;
|
|
2542
|
+
grid-template-columns: 9px minmax(0, 1fr) auto;
|
|
2513
2543
|
align-items: center;
|
|
2514
|
-
gap: 8px;
|
|
2544
|
+
column-gap: 8px;
|
|
2545
|
+
row-gap: 2px;
|
|
2546
|
+
padding: var(--space-2) 0;
|
|
2547
|
+
border-bottom: 1px solid var(--border-subtle);
|
|
2515
2548
|
font-size: var(--text-xs);
|
|
2516
2549
|
}
|
|
2550
|
+
.ai-build-summary {
|
|
2551
|
+
display: flex;
|
|
2552
|
+
align-items: center;
|
|
2553
|
+
justify-content: space-between;
|
|
2554
|
+
gap: var(--space-3);
|
|
2555
|
+
padding: var(--space-3);
|
|
2556
|
+
border: 1px solid var(--border-default);
|
|
2557
|
+
border-radius: var(--radius-sm);
|
|
2558
|
+
background: var(--bg-elevated-1);
|
|
2559
|
+
margin-bottom: var(--space-3);
|
|
2560
|
+
}
|
|
2561
|
+
.ai-build-summary strong { color: var(--text-primary); }
|
|
2562
|
+
.ai-build-count { color: var(--text-secondary); font-size: var(--ops-text-label); }
|
|
2563
|
+
.ai-caps-extra { grid-column: 2 / -1; }
|
|
2564
|
+
.ai-caps-status {
|
|
2565
|
+
grid-column: 3;
|
|
2566
|
+
grid-row: 1;
|
|
2567
|
+
color: var(--text-tertiary);
|
|
2568
|
+
font-size: 10px;
|
|
2569
|
+
white-space: nowrap;
|
|
2570
|
+
}
|
|
2571
|
+
.ai-caps-status.available { color: var(--accent-success); }
|
|
2572
|
+
.ai-section-unavailable > :not(.caps-section-head),
|
|
2573
|
+
.ai-section-unavailable .caps-section-hint {
|
|
2574
|
+
display: none;
|
|
2575
|
+
}
|
|
2576
|
+
.ai-section-unavailable .caps-section-head {
|
|
2577
|
+
flex-direction: row;
|
|
2578
|
+
align-items: center;
|
|
2579
|
+
justify-content: space-between;
|
|
2580
|
+
margin-bottom: 0;
|
|
2581
|
+
}
|
|
2582
|
+
.ai-section-status {
|
|
2583
|
+
color: var(--accent-warning);
|
|
2584
|
+
font-size: 10px;
|
|
2585
|
+
font-weight: 600;
|
|
2586
|
+
}
|
|
2517
2587
|
.ai-caps-dot { width: 9px; height: 9px; border-radius: 50%; flex: 0 0 auto; }
|
|
2518
2588
|
.ai-caps-dot.on { background: #34a853; }
|
|
2519
2589
|
.ai-caps-dot.off { background: rgba(255,255,255,0.25); }
|
|
@@ -3902,7 +3972,19 @@ HTML = r"""<!doctype html>
|
|
|
3902
3972
|
.settings-grid { grid-template-columns: 1fr; }
|
|
3903
3973
|
}
|
|
3904
3974
|
@media (max-width: 620px) {
|
|
3975
|
+
main { padding-bottom: var(--space-3); }
|
|
3976
|
+
.lab-footer {
|
|
3977
|
+
position: static;
|
|
3978
|
+
height: auto;
|
|
3979
|
+
min-height: 112px;
|
|
3980
|
+
}
|
|
3905
3981
|
.overview-grid { grid-template-columns: 1fr; }
|
|
3982
|
+
.nav-left { flex: 0 0 auto; }
|
|
3983
|
+
.wordmark span:first-child { display: none; }
|
|
3984
|
+
.nav-links > .github-icon-link { display: none; }
|
|
3985
|
+
.version-badge .version-label { display: none; }
|
|
3986
|
+
.version-badge { padding: 0 8px; }
|
|
3987
|
+
.language-switch button { padding: 0 6px; }
|
|
3906
3988
|
}
|
|
3907
3989
|
</style>
|
|
3908
3990
|
</head>
|
|
@@ -3968,6 +4050,10 @@ HTML = r"""<!doctype html>
|
|
|
3968
4050
|
</div>
|
|
3969
4051
|
</nav>
|
|
3970
4052
|
<div class="nav-links">
|
|
4053
|
+
<div class="language-switch" role="group" aria-label="Interface language">
|
|
4054
|
+
<button type="button" data-locale="zh-CN" aria-pressed="false">中文</button>
|
|
4055
|
+
<button type="button" data-locale="en" aria-pressed="false">EN</button>
|
|
4056
|
+
</div>
|
|
3971
4057
|
<button id="versionBadge" class="version-badge" type="button" aria-label="MCP version and updates" title="MCP version">
|
|
3972
4058
|
<span class="version-label">MCP</span>
|
|
3973
4059
|
<span class="version-number" id="versionNumber">…</span>
|
|
@@ -4295,7 +4381,7 @@ HTML = r"""<!doctype html>
|
|
|
4295
4381
|
<section class="span-12">
|
|
4296
4382
|
<div class="section-top">
|
|
4297
4383
|
<div>
|
|
4298
|
-
<h2>Resolve
|
|
4384
|
+
<h2>Resolve AI Console</h2>
|
|
4299
4385
|
<p class="section-sub">Run Resolve's local AI operations on the current Media Pool folder or a specific clip. These run on Resolve's GPU/AI engine — the analysis and slate ops are safe and reversible; <strong>motion-deblur</strong> and <strong>speech generation</strong> create new media files and ask for confirmation first. Source media is never modified. Every run is recorded in the <em>Resolve 21 AI ops</em> ledger (Preferences → Caps + Safety).</p>
|
|
4300
4386
|
</div>
|
|
4301
4387
|
</div>
|
|
@@ -4304,8 +4390,8 @@ HTML = r"""<!doctype html>
|
|
|
4304
4390
|
<div class="caps-section-hint">Checking which AI methods this Resolve build exposes…</div>
|
|
4305
4391
|
</div>
|
|
4306
4392
|
|
|
4307
|
-
<div class="caps-section" style="margin-top:12px;">
|
|
4308
|
-
<div class="caps-section-head"><div class="caps-section-title">Governance
|
|
4393
|
+
<div class="caps-section" id="aiGovernanceSection" style="margin-top:12px;">
|
|
4394
|
+
<div class="caps-section-head"><div class="caps-section-title">Governance <span class="ai-section-status"></span></div>
|
|
4309
4395
|
<div class="caps-section-hint">Per-session limits for the two media-creating ops (deblur, speech). In <strong>Advisory</strong> mode you're warned in the confirm dialog but never blocked; in <strong>Enforce</strong> mode an over-tier run is refused until you raise the tier, relax the mode, or consciously override. Pick the tier that matches the job.</div></div>
|
|
4310
4396
|
<div id="aiGovTiers" class="caps-preset-cards" role="radiogroup" aria-label="AI governance tier"></div>
|
|
4311
4397
|
<div class="review-view-toggle" id="aiGovMode" role="radiogroup" aria-label="Governance mode" style="margin-top:10px;">
|
|
@@ -4348,8 +4434,8 @@ HTML = r"""<!doctype html>
|
|
|
4348
4434
|
</div>
|
|
4349
4435
|
</div>
|
|
4350
4436
|
|
|
4351
|
-
<div class="caps-section" style="margin-top:12px;">
|
|
4352
|
-
<div class="caps-section-head"><div class="caps-section-title">Motion deblur
|
|
4437
|
+
<div class="caps-section" id="aiMotionDeblurSection" style="margin-top:12px;">
|
|
4438
|
+
<div class="caps-section-head"><div class="caps-section-title">Motion deblur <span class="ai-section-status"></span></div>
|
|
4353
4439
|
<div class="caps-section-hint">Renders new deblurred media. Creates files; asks for confirmation. Leave fields blank for Resolve defaults.</div></div>
|
|
4354
4440
|
<div class="settings-grid">
|
|
4355
4441
|
<label>Format <input id="aiDeblurFormat" type="text" placeholder="mov"></label>
|
|
@@ -4361,8 +4447,8 @@ HTML = r"""<!doctype html>
|
|
|
4361
4447
|
</div>
|
|
4362
4448
|
</div>
|
|
4363
4449
|
|
|
4364
|
-
<div class="caps-section" style="margin-top:12px;">
|
|
4365
|
-
<div class="caps-section-head"><div class="caps-section-title">Speech generator
|
|
4450
|
+
<div class="caps-section" id="aiSpeechSection" style="margin-top:12px;">
|
|
4451
|
+
<div class="caps-section-head"><div class="caps-section-title">Speech generator <span class="ai-section-status"></span></div>
|
|
4366
4452
|
<div class="caps-section-hint">AI text-to-speech. Requires the AI Speech Generator Extra. Creates a new audio item; asks for confirmation.</div></div>
|
|
4367
4453
|
<div class="settings-grid">
|
|
4368
4454
|
<label style="grid-column:1/-1;">Text <textarea id="aiSpeechText" rows="2" placeholder="Text to synthesize"></textarea></label>
|
|
@@ -4377,8 +4463,8 @@ HTML = r"""<!doctype html>
|
|
|
4377
4463
|
</div>
|
|
4378
4464
|
</div>
|
|
4379
4465
|
|
|
4380
|
-
<div class="caps-section" style="margin-top:12px;">
|
|
4381
|
-
<div class="caps-section-head"><div class="caps-section-title">Session
|
|
4466
|
+
<div class="caps-section" id="aiSessionSection" style="margin-top:12px;">
|
|
4467
|
+
<div class="caps-section-head"><div class="caps-section-title">Session <span class="ai-section-status"></span></div>
|
|
4382
4468
|
<div class="caps-section-hint">Quiet Resolve's background tasks for this session before heavy work. Resets on restart.</div></div>
|
|
4383
4469
|
<div class="ai-op-row"><button class="ai-op-btn ghost" data-ai-op="disable_background_tasks">Disable background tasks</button></div>
|
|
4384
4470
|
</div>
|
|
@@ -4959,6 +5045,8 @@ HTML = r"""<!doctype html>
|
|
|
4959
5045
|
</footer>
|
|
4960
5046
|
|
|
4961
5047
|
<script>
|
|
5048
|
+
/* CONTROL_PANEL_I18N */
|
|
5049
|
+
|
|
4962
5050
|
const state = {
|
|
4963
5051
|
boot: null,
|
|
4964
5052
|
projects: null,
|
|
@@ -5845,6 +5933,7 @@ HTML = r"""<!doctype html>
|
|
|
5845
5933
|
renderOverview();
|
|
5846
5934
|
renderDiagnostics();
|
|
5847
5935
|
renderProjects();
|
|
5936
|
+
if (_aiConsoleInit) renderAiConsole();
|
|
5848
5937
|
refreshRecentRootsDropdown();
|
|
5849
5938
|
}
|
|
5850
5939
|
|
|
@@ -7794,23 +7883,33 @@ HTML = r"""<!doctype html>
|
|
|
7794
7883
|
clear_audio_classification: 'clear_audio_classification',
|
|
7795
7884
|
analyze_for_intellisearch: 'analyze_for_intellisearch',
|
|
7796
7885
|
analyze_for_slate: 'analyze_for_slate',
|
|
7886
|
+
transcribe_audio: 'transcribe_audio',
|
|
7887
|
+
clear_transcription: 'clear_transcription',
|
|
7797
7888
|
remove_motion_blur: 'remove_motion_blur',
|
|
7798
7889
|
generate_speech: 'generate_speech',
|
|
7799
7890
|
disable_background_tasks: 'disable_background_tasks',
|
|
7800
7891
|
};
|
|
7892
|
+
const AI_OP_REQUIRES_21 = new Set([
|
|
7893
|
+
'perform_audio_classification', 'clear_audio_classification',
|
|
7894
|
+
'analyze_for_intellisearch', 'analyze_for_slate', 'remove_motion_blur',
|
|
7895
|
+
'generate_speech', 'disable_background_tasks',
|
|
7896
|
+
]);
|
|
7801
7897
|
let _aiConsoleInit = false;
|
|
7802
7898
|
|
|
7803
7899
|
function renderAiConsole() {
|
|
7804
7900
|
const feats = (state.boot?.resolve?.ai_features) || {};
|
|
7805
7901
|
const features = feats.features || {};
|
|
7806
7902
|
const requiresExtra = feats.requires_extra || {};
|
|
7903
|
+
const version = state.boot?.resolve?.version_string || 'unknown';
|
|
7807
7904
|
const capsEl = $('aiConsoleCaps');
|
|
7808
7905
|
if (capsEl) {
|
|
7809
7906
|
if (state.boot?.resolve?.available !== true) {
|
|
7810
7907
|
capsEl.innerHTML = '<div class="caps-section-hint">Resolve is not connected. Open a project in DaVinci Resolve, then reload.</div>';
|
|
7811
7908
|
} else {
|
|
7812
|
-
const
|
|
7813
|
-
|
|
7909
|
+
const ops = Object.keys(AI_OP_LABELS).filter(op => op in AI_OP_FEATURE);
|
|
7910
|
+
const availableCount = ops.filter(op => !!features[AI_OP_FEATURE[op]]).length;
|
|
7911
|
+
const items = ops
|
|
7912
|
+
.sort((a, b) => Number(!!features[AI_OP_FEATURE[b]]) - Number(!!features[AI_OP_FEATURE[a]]))
|
|
7814
7913
|
.map(op => {
|
|
7815
7914
|
const key = AI_OP_FEATURE[op];
|
|
7816
7915
|
const on = !!features[key];
|
|
@@ -7818,13 +7917,45 @@ HTML = r"""<!doctype html>
|
|
|
7818
7917
|
return `<div class="ai-caps-item"><span class="ai-caps-dot ${on ? 'on' : 'off'}"></span>`
|
|
7819
7918
|
+ `<span>${escapeHtml(AI_OP_LABELS[op])}</span>`
|
|
7820
7919
|
+ (extra ? `<span class="ai-caps-extra">· needs ${escapeHtml(extra)}</span>` : '')
|
|
7920
|
+
+ `<span class="ai-caps-status ${on ? 'available' : ''}">${on ? 'Available now' : (AI_OP_REQUIRES_21.has(op) ? 'Requires Resolve 21+' : 'Unavailable on this build')}</span>`
|
|
7821
7921
|
+ `</div>`;
|
|
7822
7922
|
}).join('');
|
|
7823
|
-
capsEl.innerHTML = `<div class="
|
|
7824
|
-
+ `<div class="caps-section-
|
|
7923
|
+
capsEl.innerHTML = `<div class="ai-build-summary"><strong>DaVinci Resolve ${escapeHtml(version)}</strong><span class="ai-build-count">${availableCount} of ${ops.length} AI console actions available now</span></div>`
|
|
7924
|
+
+ `<div class="caps-section-head"><div class="caps-section-title">Available on this Resolve build</div>`
|
|
7925
|
+
+ `<div class="caps-section-hint">Unavailable actions are disabled. Resolve 21 methods may also require the named Extra from Extras Download Manager.</div></div>`
|
|
7825
7926
|
+ `<div class="ai-caps-grid">${items}</div>`;
|
|
7826
7927
|
}
|
|
7827
7928
|
}
|
|
7929
|
+
document.querySelectorAll('#panel-aiconsole .ai-op-btn').forEach(btn => {
|
|
7930
|
+
const feature = AI_OP_FEATURE[btn.dataset.aiOp];
|
|
7931
|
+
const available = feature ? !!features[feature] : false;
|
|
7932
|
+
btn.disabled = !available;
|
|
7933
|
+
btn.title = available ? '' : (AI_OP_REQUIRES_21.has(btn.dataset.aiOp)
|
|
7934
|
+
? 'Requires DaVinci Resolve 21 or newer'
|
|
7935
|
+
: 'Unavailable on this Resolve build');
|
|
7936
|
+
});
|
|
7937
|
+
const speakerDetection = $('aiSpeakerDetection');
|
|
7938
|
+
if (speakerDetection) {
|
|
7939
|
+
const resolveMajor = Number(state.boot?.resolve?.version?.[0] || 0);
|
|
7940
|
+
speakerDetection.disabled = resolveMajor < 21;
|
|
7941
|
+
speakerDetection.title = resolveMajor < 21 ? 'Speaker detection requires DaVinci Resolve 21 or newer' : '';
|
|
7942
|
+
}
|
|
7943
|
+
const sectionAvailability = {
|
|
7944
|
+
aiGovernanceSection: !!features.remove_motion_blur || !!features.generate_speech,
|
|
7945
|
+
aiMotionDeblurSection: !!features.remove_motion_blur,
|
|
7946
|
+
aiSpeechSection: !!features.generate_speech,
|
|
7947
|
+
aiSessionSection: !!features.disable_background_tasks,
|
|
7948
|
+
};
|
|
7949
|
+
Object.entries(sectionAvailability).forEach(([id, available]) => {
|
|
7950
|
+
const section = $(id);
|
|
7951
|
+
if (!section) return;
|
|
7952
|
+
section.classList.toggle('ai-section-unavailable', !available);
|
|
7953
|
+
const status = section.querySelector('.ai-section-status');
|
|
7954
|
+
if (status) status.textContent = available ? '' : 'Requires Resolve 21+';
|
|
7955
|
+
section.querySelectorAll('input, textarea, select, button').forEach(control => {
|
|
7956
|
+
control.disabled = !available;
|
|
7957
|
+
});
|
|
7958
|
+
});
|
|
7828
7959
|
// Slate color dropdown (once).
|
|
7829
7960
|
const sel = $('aiSlateColor');
|
|
7830
7961
|
if (sel && !sel.options.length) {
|
|
@@ -7924,7 +8055,7 @@ HTML = r"""<!doctype html>
|
|
|
7924
8055
|
refreshResolveAiOps().catch(() => {});
|
|
7925
8056
|
refreshGovernance().catch(() => {});
|
|
7926
8057
|
} finally {
|
|
7927
|
-
|
|
8058
|
+
renderAiConsole();
|
|
7928
8059
|
}
|
|
7929
8060
|
}
|
|
7930
8061
|
|
|
@@ -7960,6 +8091,9 @@ HTML = r"""<!doctype html>
|
|
|
7960
8091
|
<div class="caps-preset-card-stats">${stats}</div>
|
|
7961
8092
|
</button>`;
|
|
7962
8093
|
}).join('');
|
|
8094
|
+
if ($('aiGovernanceSection')?.classList.contains('ai-section-unavailable')) {
|
|
8095
|
+
el.querySelectorAll('button').forEach(button => { button.disabled = true; });
|
|
8096
|
+
}
|
|
7963
8097
|
}
|
|
7964
8098
|
function renderGovUsage() {
|
|
7965
8099
|
const el = $('aiGovUsage');
|
|
@@ -11779,6 +11913,8 @@ HTML = r"""<!doctype html>
|
|
|
11779
11913
|
</html>
|
|
11780
11914
|
"""
|
|
11781
11915
|
|
|
11916
|
+
HTML = HTML.replace("/* CONTROL_PANEL_I18N */", localization_script())
|
|
11917
|
+
|
|
11782
11918
|
|
|
11783
11919
|
def _safe_call(obj: Any, method_name: str, *args: Any) -> Tuple[Any, Optional[str]]:
|
|
11784
11920
|
if obj is None or not hasattr(obj, method_name):
|
|
@@ -11838,7 +11974,7 @@ def _connect_resolve_read_only() -> Tuple[Any, Optional[str]]:
|
|
|
11838
11974
|
except Exception as exc:
|
|
11839
11975
|
return None, f"Resolve scripting API unavailable: {exc}"
|
|
11840
11976
|
try:
|
|
11841
|
-
resolve = dvr_script
|
|
11977
|
+
resolve = connect_resolve(dvr_script)
|
|
11842
11978
|
except Exception as exc:
|
|
11843
11979
|
return None, f"Resolve connection failed: {exc}"
|
|
11844
11980
|
if resolve is None:
|
|
@@ -11890,6 +12026,8 @@ def _resolve_ai_features(resolve: Any) -> Dict[str, Any]:
|
|
|
11890
12026
|
"generate_speech": has(project, "GenerateSpeech"),
|
|
11891
12027
|
"perform_audio_classification": has(folder, "PerformAudioClassification"),
|
|
11892
12028
|
"clear_audio_classification": has(folder, "ClearAudioClassification"),
|
|
12029
|
+
"transcribe_audio": has(folder, "TranscribeAudio"),
|
|
12030
|
+
"clear_transcription": has(folder, "ClearTranscription"),
|
|
11893
12031
|
"analyze_for_intellisearch": has(folder, "AnalyzeForIntellisearch"),
|
|
11894
12032
|
"analyze_for_slate": has(folder, "AnalyzeForSlate"),
|
|
11895
12033
|
"remove_motion_blur": has(folder, "RemoveMotionBlur"),
|