akari-video 0.1.55 → 0.1.56
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/package.json +1 -1
- package/vendor/packages/akari-launcher/package.json +1 -1
- package/vendor/packages/edit-lint/src/edit-lint.mjs +12 -15
- package/vendor/packages/edit-store/lib/audio-schedule.js +6 -3
- package/vendor/packages/edit-store/lib/edit-store.d.ts +4 -0
- package/vendor/packages/edit-store/lib/internal-model.d.ts +2 -3
- package/vendor/packages/edit-store/lib/internal-model.js +8 -7
- package/vendor/packages/edit-store/lib/migrate/index.js +17 -3
- package/vendor/packages/edit-store/lib/webview-kernel.js +8 -3
- package/vendor/packages/schemas/engine-capabilities.json +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "akari-video",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.56",
|
|
4
4
|
"description": "AKARI Video launcher CLI — start an AI-edited video project from any directory: scaffold, connection check, then hand over to Claude Code (or opencode). AKARI Video を opencode や Claude Code で、どのディレクトリからでも始めるための `akari` ランチャー CLI。接続確認(doctor)→ 未セットアップならプロジェクト雛形を作成 → AI エージェントを起動する。外部 npm 依存ゼロ(Node.js 組み込みモジュールのみ)。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "akari-video",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.56",
|
|
4
4
|
"description": "AKARI Video launcher CLI — start an AI-edited video project from any directory: scaffold, connection check, then hand over to Claude Code (or opencode). AKARI Video を opencode や Claude Code で、どのディレクトリからでも始めるための `akari` ランチャー CLI。接続確認(doctor)→ 未セットアップならプロジェクト雛形を作成 → AI エージェントを起動する。外部 npm 依存ゼロ(Node.js 組み込みモジュールのみ)。 [akari-video npm vendor: bin/akari.mjs is reference-only. These CLI entrypoints are not included in the akari-video npm package. Use `akari doctor --json` and run the path reported in `render_cut.path`. Full installations provide it in a monorepo checkout, ~/.akari/app, /Applications/AKARI Video.app/Contents/Resources/packages, or %LOCALAPPDATA%\\Programs\\@akari-videoshell\\resources\\packages.]",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -308,16 +308,19 @@ export async function lintProject(input, options = {}) {
|
|
|
308
308
|
paths,
|
|
309
309
|
structure.sourceIds,
|
|
310
310
|
);
|
|
311
|
-
//
|
|
312
|
-
//
|
|
313
|
-
// timeline を null にして下流の尺検証を止める。
|
|
311
|
+
// 全素材の最大終端を出力尺とする。外部字幕の output 区間も同じ出力軸へ加える。
|
|
312
|
+
// cuts が構造的に不正なら下流の尺検証は止める。
|
|
314
313
|
const timelineDuration = cutsStructureResult === null ? null : timelineDurationSeconds(internalEdit);
|
|
315
|
-
const
|
|
314
|
+
const captionRows = Array.isArray(captionsState.value) ? captionsState.value
|
|
315
|
+
: Array.isArray(captionsState.value?.captions) ? captionsState.value.captions : [];
|
|
316
|
+
const outputCaptionEnd = captionRows.reduce((end, cue) =>
|
|
317
|
+
cue?.time_domain === 'output' && Number.isFinite(cue.end) ? Math.max(end, cue.end) : end, 0);
|
|
318
|
+
const timeline = timelineDuration ? Math.max(timelineDuration.seconds, outputCaptionEnd) : null;
|
|
316
319
|
if (timelineDuration?.basis === "overlays-audio") {
|
|
317
320
|
addFinding(findings, {
|
|
318
321
|
severity: "info",
|
|
319
322
|
check: "timeline.duration-derived",
|
|
320
|
-
message:
|
|
323
|
+
message: `尺を overlays / 字幕 / 音声の終端 ${formatNumber(timeline)} 秒から導出した`,
|
|
321
324
|
path: "edit.json#tracks",
|
|
322
325
|
});
|
|
323
326
|
}
|
|
@@ -326,16 +329,10 @@ export async function lintProject(input, options = {}) {
|
|
|
326
329
|
validateStillImageCuts(edit, findings);
|
|
327
330
|
const cutTrackSegments = computeCutTrackSegments(edit.cuts);
|
|
328
331
|
validateTransitionAdjacency(edit.cuts, cutTrackSegments, edit.sources, edit.fps, findings);
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
check: "cuts.track-overlap",
|
|
334
|
-
message: `cut overlaps another cut on track ${segment.track} in the output axis`,
|
|
335
|
-
path: `edit.json#cuts[${segment.index}]`,
|
|
336
|
-
range: { start: segment.start, end: segment.end },
|
|
337
|
-
});
|
|
338
|
-
}
|
|
332
|
+
// v2.track-no-overlap above checks the actual track IDs before legacy projection.
|
|
333
|
+
// A mixed telop/media track may project its media into cuts with the same numeric
|
|
334
|
+
// ref as another physical track. Those per-kind aliases are not v2 track identity;
|
|
335
|
+
// checking projected cuts again would reject valid cross-track composition.
|
|
339
336
|
validateDurationMaximum(edit.outputs, timeline, findings, paths);
|
|
340
337
|
validateOutputAxisDurationMax(edit.outputs, cutTrackSegments, findings);
|
|
341
338
|
await validateOverlays(edit.overlays, timeline, findings, paths);
|
|
@@ -175,6 +175,9 @@ function scheduleBgm(spec, timelineDurationSec, startAtSec, duckIntervals, warni
|
|
|
175
175
|
const timelineT = typeof spec.t === 'number' && Number.isFinite(spec.t) && spec.t > 0 ? spec.t : 0;
|
|
176
176
|
if (timelineT >= timelineDurationSec)
|
|
177
177
|
return null;
|
|
178
|
+
const itemEndSec = finitePositive(spec.duration) ? Math.min(timelineDurationSec, timelineT + spec.duration) : timelineDurationSec;
|
|
179
|
+
if (startAtSec >= itemEndSec)
|
|
180
|
+
return null;
|
|
178
181
|
const sidecar = validSidecar(spec.sidecar);
|
|
179
182
|
if (spec.sidecar && !sidecar)
|
|
180
183
|
warnings.push(`${label}: sidecar declaration is invalid; using source`);
|
|
@@ -196,7 +199,7 @@ function scheduleBgm(spec, timelineDurationSec, startAtSec, duckIntervals, warni
|
|
|
196
199
|
return null;
|
|
197
200
|
}
|
|
198
201
|
const timelineStartSec = startAtSec + delaySec;
|
|
199
|
-
const timelineAvailableSec =
|
|
202
|
+
const timelineAvailableSec = itemEndSec - timelineStartSec;
|
|
200
203
|
const durationSec = Math.min(timelineAvailableSec, loop ? timelineAvailableSec : (materialDurationSec - sourceOffsetSec) / playbackRate);
|
|
201
204
|
if (!(durationSec > 0))
|
|
202
205
|
return null;
|
|
@@ -214,8 +217,8 @@ function scheduleBgm(spec, timelineDurationSec, startAtSec, duckIntervals, warni
|
|
|
214
217
|
sourceDurationSec: durationSec * playbackRate,
|
|
215
218
|
loop,
|
|
216
219
|
gainDb,
|
|
217
|
-
gainEvents: bgmFadeGainEvents(spec.fadeIn, spec.fadeOut,
|
|
218
|
-
envelopeEvents: scheduledEnvelopeEvents(spec, timelineT,
|
|
220
|
+
gainEvents: bgmFadeGainEvents(spec.fadeIn, spec.fadeOut, itemEndSec, timelineStartSec, durationSec, baseGain),
|
|
221
|
+
envelopeEvents: scheduledEnvelopeEvents(spec, timelineT, itemEndSec - timelineT, elapsedSec, durationSec, duckIntervals)
|
|
219
222
|
};
|
|
220
223
|
}
|
|
221
224
|
function scheduleSpeech(spec, timelineDurationSec, startAtSec, warnings) {
|
|
@@ -42,6 +42,8 @@ export interface EditOverlay {
|
|
|
42
42
|
}
|
|
43
43
|
export type LayerBlendMode = 'normal' | 'screen' | 'multiply' | 'add' | 'difference' | 'darken' | 'lighten' | 'overlay' | 'hardlight' | 'softlight';
|
|
44
44
|
export interface EditLayer {
|
|
45
|
+
in?: number;
|
|
46
|
+
speed?: number;
|
|
45
47
|
id: string;
|
|
46
48
|
t: number;
|
|
47
49
|
duration: number;
|
|
@@ -132,6 +134,8 @@ export interface CutTrackSegment {
|
|
|
132
134
|
end: number;
|
|
133
135
|
}
|
|
134
136
|
export interface EditAudioBgm {
|
|
137
|
+
t?: number;
|
|
138
|
+
duration?: number;
|
|
135
139
|
id: 'bgm';
|
|
136
140
|
path: string;
|
|
137
141
|
track?: number;
|
|
@@ -202,9 +202,8 @@ export declare function readInternalSources(source: string | unknown): InternalS
|
|
|
202
202
|
*/
|
|
203
203
|
export declare function visualContentEndSeconds(internal: InternalEdit): number;
|
|
204
204
|
/**
|
|
205
|
-
*
|
|
206
|
-
*
|
|
207
|
-
* BGM は総尺に合わせて切られる素材なので、後退尺には含めない。
|
|
205
|
+
* 出力タイムラインの総尺は全素材の最大終端。映像の後ろの HTML・字幕・音声も出力対象。
|
|
206
|
+
* 実尺未解決(duration=0)の音源はここでは延長せず、再生・書き出し時のプローブで補完する。
|
|
208
207
|
*/
|
|
209
208
|
export declare function timelineDurationSeconds(internal: InternalEdit): {
|
|
210
209
|
seconds: number;
|
|
@@ -77,21 +77,17 @@ function visualContentEndSeconds(internal) {
|
|
|
77
77
|
return maxEnd;
|
|
78
78
|
}
|
|
79
79
|
/**
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
* BGM は総尺に合わせて切られる素材なので、後退尺には含めない。
|
|
80
|
+
* 出力タイムラインの総尺は全素材の最大終端。映像の後ろの HTML・字幕・音声も出力対象。
|
|
81
|
+
* 実尺未解決(duration=0)の音源はここでは延長せず、再生・書き出し時のプローブで補完する。
|
|
83
82
|
*/
|
|
84
83
|
function timelineDurationSeconds(internal) {
|
|
85
84
|
const visualEnd = visualContentEndSeconds(internal);
|
|
86
|
-
if (visualEnd > 0) {
|
|
87
|
-
return { seconds: visualEnd, basis: 'visual' };
|
|
88
|
-
}
|
|
89
85
|
let fallbackEnd = 0;
|
|
90
86
|
const walk = (item, lane) => {
|
|
91
87
|
const isFallbackVisual = lane === 'visual'
|
|
92
88
|
&& ['html', 'group', 'captions', 'caption'].includes(item.source.kind);
|
|
93
89
|
const isFallbackAudio = lane === 'audio'
|
|
94
|
-
&&
|
|
90
|
+
&& item.duration > 0;
|
|
95
91
|
if (isFallbackVisual || isFallbackAudio) {
|
|
96
92
|
fallbackEnd = Math.max(fallbackEnd, item.at + item.duration);
|
|
97
93
|
}
|
|
@@ -102,6 +98,8 @@ function timelineDurationSeconds(internal) {
|
|
|
102
98
|
for (const item of track.items)
|
|
103
99
|
walk(item, track.lane);
|
|
104
100
|
}
|
|
101
|
+
if (visualEnd >= fallbackEnd && visualEnd > 0)
|
|
102
|
+
return { seconds: visualEnd, basis: 'visual' };
|
|
105
103
|
return fallbackEnd > 0
|
|
106
104
|
? { seconds: fallbackEnd, basis: 'overlays-audio' }
|
|
107
105
|
: { seconds: 0, basis: 'empty' };
|
|
@@ -655,6 +653,7 @@ function buildV2VisualItem(item, fps, ref, pathOf, chromaKeyOf, legacyIndexCount
|
|
|
655
653
|
if (needsLayersEngine(item, chromaKeyOf, hasOverlappingSibling)) {
|
|
656
654
|
const declaration = {
|
|
657
655
|
id: item.id, t: at, duration, kind: 'video', src: path ?? item.source.src,
|
|
656
|
+
in: item.source.in,
|
|
658
657
|
track: ref, ...common, ...copyMediaSourceFields(item.source),
|
|
659
658
|
...('audio' in item && item.audio === false ? { audio: false } : {})
|
|
660
659
|
};
|
|
@@ -892,6 +891,7 @@ function buildV2AudioItem(item, fps, ref, pathOf, legacyIndexCounters) {
|
|
|
892
891
|
if (role === 'bgm') {
|
|
893
892
|
const value = {
|
|
894
893
|
id: 'bgm',
|
|
894
|
+
...(duration > 0 ? { t: at, duration } : {}),
|
|
895
895
|
path: resolvedPath,
|
|
896
896
|
track: ref,
|
|
897
897
|
...(item.fade_in !== undefined ? { fadeIn: item.fade_in } : {}),
|
|
@@ -910,6 +910,7 @@ function buildV2AudioItem(item, fps, ref, pathOf, legacyIndexCounters) {
|
|
|
910
910
|
id: item.id, atFrames, durationFrames, at, duration, children: [], source,
|
|
911
911
|
declaration: {
|
|
912
912
|
path: resolvedPath,
|
|
913
|
+
...(duration > 0 ? { t: at, duration } : {}),
|
|
913
914
|
...(item.source.in !== undefined ? { in: item.source.in } : {}),
|
|
914
915
|
...(item.fade_in !== undefined ? { fadeIn: item.fade_in } : {}),
|
|
915
916
|
...(item.fade_out !== undefined ? { fadeOut: item.fade_out } : {}),
|
|
@@ -43,7 +43,7 @@ const CUT_KEYS = new Set([
|
|
|
43
43
|
]);
|
|
44
44
|
const OVERLAY_KEYS = new Set(['id', 'html', 'start', 'duration', 'vars', 'transform', 'track']);
|
|
45
45
|
const LAYER_KEYS = new Set([
|
|
46
|
-
'id', 't', 'duration', 'kind', 'src', 'transform', 'crop', 'perspective', 'opacity',
|
|
46
|
+
'id', 't', 'duration', 'kind', 'src', 'in', 'speed', 'transform', 'crop', 'perspective', 'opacity',
|
|
47
47
|
'keyframes', 'preset', 'params', 'track', 'blend', 'chroma_key', 'filter', 'mask'
|
|
48
48
|
]);
|
|
49
49
|
const AUDIO_ENVELOPE_KEYS = ['keyframes', 'ducking', 'duck_db', 'duck_attack', 'duck_release'];
|
|
@@ -217,7 +217,7 @@ function migrateEditToV2(raw, options = {}) {
|
|
|
217
217
|
let source;
|
|
218
218
|
let mask;
|
|
219
219
|
if (value.kind === 'filter') {
|
|
220
|
-
const forbidden = ['src', 'chroma_key', 'blend', 'crop', 'transform', 'mask'].filter(key => hasOwn(value, key));
|
|
220
|
+
const forbidden = ['src', 'in', 'speed', 'chroma_key', 'blend', 'crop', 'transform', 'mask'].filter(key => hasOwn(value, key));
|
|
221
221
|
if (forbidden.length > 0) {
|
|
222
222
|
blockers.push(`edit.json.layers[${index}] の kind filter は ${forbidden.join(' / ')} を持てません。`);
|
|
223
223
|
return;
|
|
@@ -229,6 +229,10 @@ function migrateEditToV2(raw, options = {}) {
|
|
|
229
229
|
return;
|
|
230
230
|
}
|
|
231
231
|
else if (value.kind === 'baked' && nonEmpty(value.preset)) {
|
|
232
|
+
if (hasOwn(value, 'in') || hasOwn(value, 'speed')) {
|
|
233
|
+
blockers.push(`edit.json.layers[${index}] の in / speed は baked telop へ変換できません。`);
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
232
236
|
if (hasOwn(value, 'mask')) {
|
|
233
237
|
blockers.push(`edit.json.layers[${index}].mask は baked telop へ変換できません。`);
|
|
234
238
|
return;
|
|
@@ -239,6 +243,13 @@ function migrateEditToV2(raw, options = {}) {
|
|
|
239
243
|
};
|
|
240
244
|
}
|
|
241
245
|
else {
|
|
246
|
+
if ((hasOwn(value, 'in') && !nonNegative(value.in))
|
|
247
|
+
|| (hasOwn(value, 'speed') && !positive(value.speed))) {
|
|
248
|
+
blockers.push(`edit.json.layers[${index}] の in / speed が不正です。`);
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
const sourceIn = hasOwn(value, 'in') ? value.in : 0;
|
|
252
|
+
const speed = hasOwn(value, 'speed') ? value.speed : 1;
|
|
242
253
|
let src = sourceIdByPath.get(value.src);
|
|
243
254
|
if (!src) {
|
|
244
255
|
do
|
|
@@ -248,7 +259,10 @@ function migrateEditToV2(raw, options = {}) {
|
|
|
248
259
|
sourceIdByPath.set(value.src, src);
|
|
249
260
|
sources.push({ id: src, path: value.src, proxy: null });
|
|
250
261
|
}
|
|
251
|
-
source = {
|
|
262
|
+
source = {
|
|
263
|
+
kind: 'media', src, in: sourceIn, out: sourceIn + value.duration * speed,
|
|
264
|
+
...copyPresent(value, ['speed', 'chroma_key'])
|
|
265
|
+
};
|
|
252
266
|
if (hasOwn(value, 'mask')) {
|
|
253
267
|
if (!nonEmpty(value.mask)) {
|
|
254
268
|
blockers.push(`edit.json.layers[${index}].mask が不正です。`);
|
|
@@ -1319,6 +1319,8 @@ var AkariEditKernel = (() => {
|
|
|
1319
1319
|
if (gainDb === null) return null;
|
|
1320
1320
|
const timelineT = typeof spec.t === "number" && Number.isFinite(spec.t) && spec.t > 0 ? spec.t : 0;
|
|
1321
1321
|
if (timelineT >= timelineDurationSec) return null;
|
|
1322
|
+
const itemEndSec = finitePositive(spec.duration) ? Math.min(timelineDurationSec, timelineT + spec.duration) : timelineDurationSec;
|
|
1323
|
+
if (startAtSec >= itemEndSec) return null;
|
|
1322
1324
|
const sidecar = validSidecar(spec.sidecar);
|
|
1323
1325
|
if (spec.sidecar && !sidecar) warnings.push(`${label}: sidecar declaration is invalid; using source`);
|
|
1324
1326
|
const materialDurationSec = sidecar ? sidecar.durationSec : spec.durationSec;
|
|
@@ -1338,7 +1340,7 @@ var AkariEditKernel = (() => {
|
|
|
1338
1340
|
return null;
|
|
1339
1341
|
}
|
|
1340
1342
|
const timelineStartSec = startAtSec + delaySec;
|
|
1341
|
-
const timelineAvailableSec =
|
|
1343
|
+
const timelineAvailableSec = itemEndSec - timelineStartSec;
|
|
1342
1344
|
const durationSec = Math.min(
|
|
1343
1345
|
timelineAvailableSec,
|
|
1344
1346
|
loop ? timelineAvailableSec : (materialDurationSec - sourceOffsetSec) / playbackRate
|
|
@@ -1361,7 +1363,7 @@ var AkariEditKernel = (() => {
|
|
|
1361
1363
|
gainEvents: bgmFadeGainEvents(
|
|
1362
1364
|
spec.fadeIn,
|
|
1363
1365
|
spec.fadeOut,
|
|
1364
|
-
|
|
1366
|
+
itemEndSec,
|
|
1365
1367
|
timelineStartSec,
|
|
1366
1368
|
durationSec,
|
|
1367
1369
|
baseGain
|
|
@@ -1369,7 +1371,7 @@ var AkariEditKernel = (() => {
|
|
|
1369
1371
|
envelopeEvents: scheduledEnvelopeEvents(
|
|
1370
1372
|
spec,
|
|
1371
1373
|
timelineT,
|
|
1372
|
-
|
|
1374
|
+
itemEndSec - timelineT,
|
|
1373
1375
|
elapsedSec,
|
|
1374
1376
|
durationSec,
|
|
1375
1377
|
duckIntervals
|
|
@@ -2911,6 +2913,7 @@ var AkariEditKernel = (() => {
|
|
|
2911
2913
|
duration,
|
|
2912
2914
|
kind: "video",
|
|
2913
2915
|
src: path ?? item.source.src,
|
|
2916
|
+
in: item.source.in,
|
|
2914
2917
|
track: ref,
|
|
2915
2918
|
...common,
|
|
2916
2919
|
...copyMediaSourceFields(item.source),
|
|
@@ -3233,6 +3236,7 @@ var AkariEditKernel = (() => {
|
|
|
3233
3236
|
if (role === "bgm") {
|
|
3234
3237
|
const value2 = {
|
|
3235
3238
|
id: "bgm",
|
|
3239
|
+
...duration > 0 ? { t: at, duration } : {},
|
|
3236
3240
|
path: resolvedPath,
|
|
3237
3241
|
track: ref,
|
|
3238
3242
|
...item.fade_in !== void 0 ? { fadeIn: item.fade_in } : {},
|
|
@@ -3257,6 +3261,7 @@ var AkariEditKernel = (() => {
|
|
|
3257
3261
|
source,
|
|
3258
3262
|
declaration: {
|
|
3259
3263
|
path: resolvedPath,
|
|
3264
|
+
...duration > 0 ? { t: at, duration } : {},
|
|
3260
3265
|
...item.source.in !== void 0 ? { in: item.source.in } : {},
|
|
3261
3266
|
...item.fade_in !== void 0 ? { fadeIn: item.fade_in } : {},
|
|
3262
3267
|
...item.fade_out !== void 0 ? { fadeOut: item.fade_out } : {},
|
|
@@ -81,7 +81,7 @@
|
|
|
81
81
|
{ "path": "tracks[].items[].source.freeze", "applies_to": ["layers"], "gpu": "ignored", "osr": "ignored", "runtime_warning": true, "evidence": "packages/edit-store/src/internal-model.ts copyMediaSourceFields; packages/frame-engine/src/timeline/plan.ts KNOWN_LAYER_KEYS omits freeze" },
|
|
82
82
|
{ "path": "tracks[].items[].source.fx", "applies_to": ["cuts", "layers"], "gpu": "ignored", "osr": "ignored", "runtime_warning": true, "evidence": "packages/edit-store/src/internal-model.ts copyMediaSourceFields; packages/frame-engine/src/timeline/plan.ts KNOWN_CUT_KEYS/KNOWN_LAYER_KEYS omit fx", "hint": "GPU / OSR frame-engine には登録済み cut FX が無い" },
|
|
83
83
|
{ "path": "tracks[].items[].source.speed", "applies_to": ["cuts"], "gpu": "consumed", "osr": "consumed", "evidence": "packages/edit-store/src/internal-model.ts buildV2VisualItem speed projection; packages/frame-engine/src/timeline/plan.ts buildResolvedTimelinePlan/layerFromPlacement" },
|
|
84
|
-
{ "path": "tracks[].items[].source.speed", "applies_to": ["layers"], "gpu": "
|
|
84
|
+
{ "path": "tracks[].items[].source.speed", "applies_to": ["layers"], "gpu": "consumed", "osr": "consumed", "evidence": "packages/edit-store/src/internal-model.ts copyMediaSourceFields; packages/frame-engine/src/timeline/plan.ts resolvedCompositeLayers preserves source trim and speed" },
|
|
85
85
|
{ "path": "tracks[].items[].source.gain_db", "applies_to": ["cuts"], "gpu": "other-subsystem", "osr": "other-subsystem", "evidence": "packages/render-cut/src/plan.mjs cutSpeechVolumeSuffix (appendSequentialAudioCutFilters / gap-aware); packages/edit-store/src/audio-schedule.ts projectSpeechDeclarations speechGainDb", "note": "埋め込み音声(speech)の減衰。映像エンジンではなく render-cut の cut 音声(全エンジン共通)とプレビューの音声供給が消費する" },
|
|
86
86
|
{ "path": "tracks[].items[].source.mute", "applies_to": ["cuts"], "gpu": "other-subsystem", "osr": "other-subsystem", "evidence": "packages/render-cut/src/plan.mjs cut.mute -> anullsrc branch; packages/edit-store/src/audio-schedule.ts projectSpeechDeclarations skips muted cuts", "note": "埋め込み音声(speech)のミュート。尺は保ち無音区間になる" },
|
|
87
87
|
{ "path": "tracks[].items[].source.chroma_key", "applies_to": ["cuts", "layers"], "gpu": "partial", "osr": "partial", "runtime_warning": true, "evidence": "packages/edit-store/src/internal-model.ts needsLayersEngine/copyMediaSourceFields; packages/frame-engine/src/timeline/plan.ts KNOWN_CUT_KEYS/KNOWN_LAYER_KEYS omit chroma_key", "note": "透過 intent は layers 振り分けに使うが、frame-engine 自体は chroma key 抽出や background 置換をしない" },
|