akari-video 0.1.20 → 0.1.21
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/.akari-capability-sources.json +1 -0
- package/vendor/docs/contract-2026-07-23-analysis-person-matte.md +102 -2
- package/vendor/packages/akari-launcher/package.json +1 -1
- package/vendor/packages/edit-lint/src/edit-lint.mjs +31 -22
- package/vendor/packages/edit-store/lib/cut-adjacency.d.ts +17 -17
- package/vendor/packages/edit-store/lib/cut-adjacency.js +23 -36
- package/vendor/packages/edit-store/lib/edit-store.d.ts +8 -18
- package/vendor/packages/edit-store/lib/edit-store.js +24 -58
- package/vendor/packages/edit-store/lib/index.d.ts +1 -0
- package/vendor/packages/edit-store/lib/index.js +1 -0
- package/vendor/packages/edit-store/lib/internal-model.js +62 -0
- package/vendor/packages/edit-store/lib/migrate/legacy-parse.js +9 -6
- package/vendor/packages/edit-store/lib/timeline-map.d.ts +5 -0
- package/vendor/packages/edit-store/lib/timeline-map.js +40 -0
- package/vendor/packages/edit-store/lib/transition-vocabulary.d.ts +223 -0
- package/vendor/packages/edit-store/lib/transition-vocabulary.js +45 -0
- package/vendor/packages/edit-store/lib/webview-kernel.js +98 -0
- package/vendor/packages/matte-rvm/package.json +22 -0
- package/vendor/packages/schemas/bin/validate-edit.mjs +5 -2
- package/vendor/packages/schemas/edit.schema.json +2 -2
- package/vendor/packages/schemas/test/transition-vocabulary.test.mjs +64 -0
- package/vendor/packages/schemas/test/validate-edit.test.mjs +2 -2
- package/vendor/skills/analyze-footage/bin/person-matte/person-cutout.mjs +453 -0
- package/vendor/skills/analyze-footage/bin/person-matte/person-matte.mjs +163 -48
- package/vendor/skills/analyze-footage/person-matte.md +135 -5
|
@@ -12,6 +12,7 @@ exports.projectLegacyEdit = projectLegacyEdit;
|
|
|
12
12
|
exports.toLegacyTrack = toLegacyTrack;
|
|
13
13
|
exports.derivedLegacyTracks = derivedLegacyTracks;
|
|
14
14
|
const edit_v2_1 = require("./edit-v2");
|
|
15
|
+
const cut_adjacency_1 = require("./cut-adjacency");
|
|
15
16
|
const error_1 = require("./migrate/error");
|
|
16
17
|
/**
|
|
17
18
|
* edit.json v2 を内部表現へ読む。v0/v1 は凍結変換ユニットのみが読む。
|
|
@@ -172,6 +173,7 @@ function readV2Internal(raw) {
|
|
|
172
173
|
// 旧 top-level audio と tracks[] 音声が同居すると、後者に加えてこの fallback も射影される。
|
|
173
174
|
// どちらを優先するかは未裁定なので、旧 fixture の互換挙動を変えず二重計上の可能性を残す。
|
|
174
175
|
addV2AudioItems(tracks, edit.audio, fps, legacyIndexCounters);
|
|
176
|
+
synthesizeHiddenTransitionHandlesForRender(tracks, fps);
|
|
175
177
|
return {
|
|
176
178
|
output: {
|
|
177
179
|
width: edit.output.width,
|
|
@@ -191,6 +193,66 @@ function readV2Internal(raw) {
|
|
|
191
193
|
}
|
|
192
194
|
};
|
|
193
195
|
}
|
|
196
|
+
/**
|
|
197
|
+
* render-cut に加え shell プレビューの summary と Web UI の readRenderEdit も
|
|
198
|
+
* item.declaration を読む。そのため出荷プレビューはこの合成済み実重なりを既存窓として描く。
|
|
199
|
+
* UI / lint が読む item の at/duration/source と legacy.value は生タイムラインのまま保ち、
|
|
200
|
+
* 突き合わせ境界の隠れのりしろだけを declaration 上の物理重なりへ合成して既存
|
|
201
|
+
* xfade/acrossfade と 2 つのプレビューへ渡す。生 cuts を直接読む消費者の隠れのりしろ経路とは
|
|
202
|
+
* packages/edit-store/test/transition-window-path-equivalence.test.mjs で等価性を固定する。
|
|
203
|
+
*/
|
|
204
|
+
function synthesizeHiddenTransitionHandlesForRender(tracks, fps) {
|
|
205
|
+
const speedOf = (item) => {
|
|
206
|
+
const speed = item.declaration.speed;
|
|
207
|
+
return typeof speed === 'number' && Number.isFinite(speed) && speed > 0 ? speed : 1;
|
|
208
|
+
};
|
|
209
|
+
for (const track of tracks) {
|
|
210
|
+
if (track.lane !== 'visual')
|
|
211
|
+
continue;
|
|
212
|
+
const cuts = track.items.filter((item) => item.legacy.collection === 'cuts' && item.source.kind === 'media');
|
|
213
|
+
for (let index = 0; index + 1 < cuts.length; index++) {
|
|
214
|
+
const outgoing = cuts[index];
|
|
215
|
+
const incoming = cuts[index + 1];
|
|
216
|
+
if ((0, cut_adjacency_1.cutOverlapFrames)({ tlEnd: outgoing.at + outgoing.duration }, { tlStart: incoming.at }, fps) !== 0)
|
|
217
|
+
continue;
|
|
218
|
+
const transition = outgoing.declaration.transition_out;
|
|
219
|
+
if (!isRecord(transition)
|
|
220
|
+
|| typeof transition.duration !== 'number'
|
|
221
|
+
|| !Number.isFinite(transition.duration)
|
|
222
|
+
|| transition.duration <= 0)
|
|
223
|
+
continue;
|
|
224
|
+
const incomingSpeed = speedOf(incoming);
|
|
225
|
+
const incomingStill = (0, cut_adjacency_1.isStillImageSourcePath)(incoming.source.path);
|
|
226
|
+
const plan = (0, cut_adjacency_1.planTransitionHandleWindow)({
|
|
227
|
+
declaredSeconds: transition.duration,
|
|
228
|
+
outgoingTailRoomSeconds: Number.POSITIVE_INFINITY,
|
|
229
|
+
incomingHeadRoomSeconds: incomingStill
|
|
230
|
+
? Number.POSITIVE_INFINITY : incoming.source.in / incomingSpeed,
|
|
231
|
+
outgoingDurationSeconds: outgoing.duration,
|
|
232
|
+
incomingDurationSeconds: incoming.duration
|
|
233
|
+
});
|
|
234
|
+
if (plan.effectiveSeconds <= 0)
|
|
235
|
+
continue;
|
|
236
|
+
const outgoingSpeed = speedOf(outgoing);
|
|
237
|
+
outgoing.declaration = {
|
|
238
|
+
...outgoing.declaration,
|
|
239
|
+
out: Number(outgoing.declaration.out) + plan.halfSeconds * outgoingSpeed,
|
|
240
|
+
transition_out: { ...transition, duration: plan.effectiveSeconds }
|
|
241
|
+
};
|
|
242
|
+
incoming.declaration = incomingStill
|
|
243
|
+
? {
|
|
244
|
+
...incoming.declaration,
|
|
245
|
+
at: Number(incoming.declaration.at) - plan.halfSeconds,
|
|
246
|
+
out: Number(incoming.declaration.out) + plan.halfSeconds * incomingSpeed
|
|
247
|
+
}
|
|
248
|
+
: {
|
|
249
|
+
...incoming.declaration,
|
|
250
|
+
at: Number(incoming.declaration.at) - plan.halfSeconds,
|
|
251
|
+
in: Number(incoming.declaration.in) - plan.halfSeconds * incomingSpeed
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
194
256
|
function legacyKindOfV2Track(track, chromaKeyOf, overlappingItemIds) {
|
|
195
257
|
if (!('items' in track)) {
|
|
196
258
|
return 'captions';
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.parseEdit = parseEdit;
|
|
4
|
+
const transition_vocabulary_1 = require("../transition-vocabulary");
|
|
4
5
|
const LAYER_BLEND_MODES = [
|
|
5
6
|
'normal', 'screen', 'multiply', 'add', 'difference',
|
|
6
7
|
'darken', 'lighten', 'overlay', 'hardlight', 'softlight'
|
|
@@ -78,16 +79,18 @@ function parseEdit(source) {
|
|
|
78
79
|
let transitionOut;
|
|
79
80
|
if (rawCut.transition_out !== undefined && rawCut.transition_out !== null) {
|
|
80
81
|
const transition = rawCut.transition_out;
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|| transition?.type === '
|
|
85
|
-
|| transition?.type === 'reveal-up';
|
|
82
|
+
// 正準語彙に加え、手編集で先行した非空文字列も読み取りだけは保持する。
|
|
83
|
+
// 書き込み API は isTransitionType で閉じたまま、preview の汎用フォールバックへ渡す。
|
|
84
|
+
const validType = (0, transition_vocabulary_1.isTransitionType)(transition?.type)
|
|
85
|
+
|| (typeof transition?.type === 'string' && transition.type.trim().length > 0);
|
|
86
86
|
const validDuration = typeof transition?.duration === 'number'
|
|
87
87
|
&& Number.isFinite(transition.duration) && transition.duration > 0;
|
|
88
88
|
if (transition && typeof transition === 'object' && !Array.isArray(transition)
|
|
89
89
|
&& validType && validDuration) {
|
|
90
|
-
transitionOut = {
|
|
90
|
+
transitionOut = {
|
|
91
|
+
type: transition.type,
|
|
92
|
+
duration: transition.duration
|
|
93
|
+
};
|
|
91
94
|
}
|
|
92
95
|
else {
|
|
93
96
|
warnings.push(`${index + 1} 番目のクリップの transition_out が不正なため無視します。`);
|
|
@@ -56,6 +56,11 @@ export interface TimelineMapResult {
|
|
|
56
56
|
export declare function transitionProgressAt(window: TimelineTransitionWindow, outputT: number): number;
|
|
57
57
|
export declare function buildTimelineMap(cuts: readonly EditCut[], options?: {
|
|
58
58
|
trackZ?: (track: number) => number;
|
|
59
|
+
fps?: number;
|
|
60
|
+
handleRoom?: (cutIndex: number) => {
|
|
61
|
+
tailSeconds?: number;
|
|
62
|
+
headSeconds?: number;
|
|
63
|
+
} | undefined;
|
|
59
64
|
}): TimelineMapResult;
|
|
60
65
|
/**
|
|
61
66
|
* 出力秒 → ソース秒。トランジション重なり区間(隣接 src が重なる出力時刻)は
|
|
@@ -16,6 +16,7 @@ exports.transitionProgressAt = transitionProgressAt;
|
|
|
16
16
|
exports.buildTimelineMap = buildTimelineMap;
|
|
17
17
|
exports.outputToSource = outputToSource;
|
|
18
18
|
const edit_store_1 = require("./edit-store");
|
|
19
|
+
const cut_adjacency_1 = require("./cut-adjacency");
|
|
19
20
|
function transitionProgressAt(window, outputT) {
|
|
20
21
|
if (!(window.duration > 0))
|
|
21
22
|
return 0;
|
|
@@ -37,10 +38,49 @@ function buildTimelineMap(cuts, options) {
|
|
|
37
38
|
const resolved = trackSegments.map(segment => ({
|
|
38
39
|
start: segment.at,
|
|
39
40
|
end: segment.end,
|
|
41
|
+
baseStart: segment.at,
|
|
42
|
+
baseEnd: segment.end,
|
|
40
43
|
track: segment.track,
|
|
41
44
|
cut: usableCuts[segment.index],
|
|
42
45
|
cutIndex: usable[segment.index].index
|
|
43
46
|
}));
|
|
47
|
+
const fps = options?.fps ?? cut_adjacency_1.DEFAULT_CUT_ADJACENCY_FPS;
|
|
48
|
+
// 隠れのりしろは、フレーム量子化で突き合わせとなる境界にだけ合成する。
|
|
49
|
+
// 実重なり済みの境界は一切変更せず、従来の窓計算へそのまま流す。
|
|
50
|
+
for (let outgoingIndex = 0; outgoingIndex < resolved.length; outgoingIndex++) {
|
|
51
|
+
const outgoing = resolved[outgoingIndex];
|
|
52
|
+
const transition = outgoing.cut.transitionOut;
|
|
53
|
+
if (!transition || !(typeof transition.duration === 'number' && Number.isFinite(transition.duration)
|
|
54
|
+
&& transition.duration > 0))
|
|
55
|
+
continue;
|
|
56
|
+
const incoming = resolved.slice(outgoingIndex + 1).find(candidate => candidate.track === outgoing.track);
|
|
57
|
+
if (!incoming || (0, cut_adjacency_1.cutOverlapFrames)({ tlEnd: outgoing.end }, { tlStart: incoming.start }, fps) !== 0)
|
|
58
|
+
continue;
|
|
59
|
+
const outgoingRoom = options?.handleRoom?.(outgoing.cutIndex);
|
|
60
|
+
const incomingRoom = options?.handleRoom?.(incoming.cutIndex);
|
|
61
|
+
const incomingSpeed = typeof incoming.cut.speed === 'number' && incoming.cut.speed > 0
|
|
62
|
+
? incoming.cut.speed : 1;
|
|
63
|
+
const plan = (0, cut_adjacency_1.planTransitionHandleWindow)({
|
|
64
|
+
declaredSeconds: transition.duration,
|
|
65
|
+
outgoingTailRoomSeconds: outgoingRoom?.tailSeconds ?? Number.POSITIVE_INFINITY,
|
|
66
|
+
incomingHeadRoomSeconds: incomingRoom?.headSeconds ?? incoming.cut.in / incomingSpeed,
|
|
67
|
+
outgoingDurationSeconds: outgoing.baseEnd - outgoing.baseStart,
|
|
68
|
+
incomingDurationSeconds: incoming.baseEnd - incoming.baseStart
|
|
69
|
+
});
|
|
70
|
+
if (plan.effectiveSeconds <= 0)
|
|
71
|
+
continue;
|
|
72
|
+
const cutPoint = outgoing.end;
|
|
73
|
+
outgoing.end = cutPoint + plan.halfSeconds;
|
|
74
|
+
outgoing.cut = {
|
|
75
|
+
...outgoing.cut,
|
|
76
|
+
transitionOut: { ...transition, duration: plan.effectiveSeconds }
|
|
77
|
+
};
|
|
78
|
+
incoming.start = cutPoint - plan.halfSeconds;
|
|
79
|
+
incoming.cut = {
|
|
80
|
+
...incoming.cut,
|
|
81
|
+
in: Math.max(0, incoming.cut.in - plan.halfSeconds * incomingSpeed)
|
|
82
|
+
};
|
|
83
|
+
}
|
|
44
84
|
const segmentSlice = (entry, start, end, transitionOut = null) => {
|
|
45
85
|
const cut = entry.cut;
|
|
46
86
|
const speed = typeof cut.speed === 'number' && cut.speed > 0 ? cut.speed : 1;
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* edit.json の transition_out.type に使える正準語彙。
|
|
3
|
+
* JSON Schema はリテラル enum を保つため別置きだが、drift test でこの表と一致させる。
|
|
4
|
+
*/
|
|
5
|
+
export declare const TRANSITION_VOCABULARY: readonly [{
|
|
6
|
+
readonly id: "dissolve";
|
|
7
|
+
readonly xfadeName: "dissolve";
|
|
8
|
+
readonly labelJa: "ディゾルブ";
|
|
9
|
+
readonly category: "フェード";
|
|
10
|
+
readonly previewKind: "dissolve";
|
|
11
|
+
readonly glyph: "D";
|
|
12
|
+
}, {
|
|
13
|
+
readonly id: "fade";
|
|
14
|
+
readonly xfadeName: "fade";
|
|
15
|
+
readonly labelJa: "クロスフェード";
|
|
16
|
+
readonly category: "フェード";
|
|
17
|
+
readonly previewKind: "fade";
|
|
18
|
+
readonly glyph: "F";
|
|
19
|
+
}, {
|
|
20
|
+
readonly id: "fade-black";
|
|
21
|
+
readonly xfadeName: "fadeblack";
|
|
22
|
+
readonly labelJa: "黒フェード";
|
|
23
|
+
readonly category: "フェード";
|
|
24
|
+
readonly previewKind: "fade-black";
|
|
25
|
+
readonly glyph: "B";
|
|
26
|
+
}, {
|
|
27
|
+
readonly id: "fade-white";
|
|
28
|
+
readonly xfadeName: "fadewhite";
|
|
29
|
+
readonly labelJa: "白フェード";
|
|
30
|
+
readonly category: "フェード";
|
|
31
|
+
readonly previewKind: "fade-white";
|
|
32
|
+
readonly glyph: "W";
|
|
33
|
+
}, {
|
|
34
|
+
readonly id: "fade-grays";
|
|
35
|
+
readonly xfadeName: "fadegrays";
|
|
36
|
+
readonly labelJa: "モノクロフェード";
|
|
37
|
+
readonly category: "フェード";
|
|
38
|
+
readonly previewKind: "fade-grays";
|
|
39
|
+
readonly glyph: "G";
|
|
40
|
+
}, {
|
|
41
|
+
readonly id: "wipe-left";
|
|
42
|
+
readonly xfadeName: "wipeleft";
|
|
43
|
+
readonly labelJa: "ワイプ(左へ)";
|
|
44
|
+
readonly category: "ワイプ";
|
|
45
|
+
readonly previewKind: "wipe-left";
|
|
46
|
+
readonly glyph: "←";
|
|
47
|
+
}, {
|
|
48
|
+
readonly id: "wipe-right";
|
|
49
|
+
readonly xfadeName: "wiperight";
|
|
50
|
+
readonly labelJa: "ワイプ(右へ)";
|
|
51
|
+
readonly category: "ワイプ";
|
|
52
|
+
readonly previewKind: "wipe-right";
|
|
53
|
+
readonly glyph: "→";
|
|
54
|
+
}, {
|
|
55
|
+
readonly id: "wipe-up";
|
|
56
|
+
readonly xfadeName: "wipeup";
|
|
57
|
+
readonly labelJa: "ワイプ(上へ)";
|
|
58
|
+
readonly category: "ワイプ";
|
|
59
|
+
readonly previewKind: "wipe-up";
|
|
60
|
+
readonly glyph: "↑";
|
|
61
|
+
}, {
|
|
62
|
+
readonly id: "wipe-down";
|
|
63
|
+
readonly xfadeName: "wipedown";
|
|
64
|
+
readonly labelJa: "ワイプ(下へ)";
|
|
65
|
+
readonly category: "ワイプ";
|
|
66
|
+
readonly previewKind: "wipe-down";
|
|
67
|
+
readonly glyph: "↓";
|
|
68
|
+
}, {
|
|
69
|
+
readonly id: "radial";
|
|
70
|
+
readonly xfadeName: "radial";
|
|
71
|
+
readonly labelJa: "時計ワイプ";
|
|
72
|
+
readonly category: "ワイプ";
|
|
73
|
+
readonly previewKind: "radial";
|
|
74
|
+
readonly glyph: "◷";
|
|
75
|
+
}, {
|
|
76
|
+
readonly id: "slide-left";
|
|
77
|
+
readonly xfadeName: "slideleft";
|
|
78
|
+
readonly labelJa: "スライド(左へ)";
|
|
79
|
+
readonly category: "スライド";
|
|
80
|
+
readonly previewKind: "slide-left";
|
|
81
|
+
readonly glyph: "←";
|
|
82
|
+
}, {
|
|
83
|
+
readonly id: "slide-right";
|
|
84
|
+
readonly xfadeName: "slideright";
|
|
85
|
+
readonly labelJa: "スライド(右へ)";
|
|
86
|
+
readonly category: "スライド";
|
|
87
|
+
readonly previewKind: "slide-right";
|
|
88
|
+
readonly glyph: "→";
|
|
89
|
+
}, {
|
|
90
|
+
readonly id: "slide-up";
|
|
91
|
+
readonly xfadeName: "slideup";
|
|
92
|
+
readonly labelJa: "スライド(上へ)";
|
|
93
|
+
readonly category: "スライド";
|
|
94
|
+
readonly previewKind: "slide-up";
|
|
95
|
+
readonly glyph: "↑";
|
|
96
|
+
}, {
|
|
97
|
+
readonly id: "slide-down";
|
|
98
|
+
readonly xfadeName: "slidedown";
|
|
99
|
+
readonly labelJa: "スライド(下へ)";
|
|
100
|
+
readonly category: "スライド";
|
|
101
|
+
readonly previewKind: "slide-down";
|
|
102
|
+
readonly glyph: "↓";
|
|
103
|
+
}, {
|
|
104
|
+
readonly id: "cover-left";
|
|
105
|
+
readonly xfadeName: "coverleft";
|
|
106
|
+
readonly labelJa: "カバー(左へ)";
|
|
107
|
+
readonly category: "カバー";
|
|
108
|
+
readonly previewKind: "cover-left";
|
|
109
|
+
readonly glyph: "←";
|
|
110
|
+
}, {
|
|
111
|
+
readonly id: "cover-right";
|
|
112
|
+
readonly xfadeName: "coverright";
|
|
113
|
+
readonly labelJa: "カバー(右へ)";
|
|
114
|
+
readonly category: "カバー";
|
|
115
|
+
readonly previewKind: "cover-right";
|
|
116
|
+
readonly glyph: "→";
|
|
117
|
+
}, {
|
|
118
|
+
readonly id: "cover-up";
|
|
119
|
+
readonly xfadeName: "coverup";
|
|
120
|
+
readonly labelJa: "カバー(上へ)";
|
|
121
|
+
readonly category: "カバー";
|
|
122
|
+
readonly previewKind: "cover-up";
|
|
123
|
+
readonly glyph: "↑";
|
|
124
|
+
}, {
|
|
125
|
+
readonly id: "cover-down";
|
|
126
|
+
readonly xfadeName: "coverdown";
|
|
127
|
+
readonly labelJa: "カバー(下へ)";
|
|
128
|
+
readonly category: "カバー";
|
|
129
|
+
readonly previewKind: "cover-down";
|
|
130
|
+
readonly glyph: "↓";
|
|
131
|
+
}, {
|
|
132
|
+
readonly id: "reveal-left";
|
|
133
|
+
readonly xfadeName: "revealleft";
|
|
134
|
+
readonly labelJa: "リビール(左へ)";
|
|
135
|
+
readonly category: "リビール";
|
|
136
|
+
readonly previewKind: "reveal-left";
|
|
137
|
+
readonly glyph: "←";
|
|
138
|
+
}, {
|
|
139
|
+
readonly id: "reveal-right";
|
|
140
|
+
readonly xfadeName: "revealright";
|
|
141
|
+
readonly labelJa: "リビール(右へ)";
|
|
142
|
+
readonly category: "リビール";
|
|
143
|
+
readonly previewKind: "reveal-right";
|
|
144
|
+
readonly glyph: "→";
|
|
145
|
+
}, {
|
|
146
|
+
readonly id: "reveal-down";
|
|
147
|
+
readonly xfadeName: "revealdown";
|
|
148
|
+
readonly labelJa: "上からリビール";
|
|
149
|
+
readonly category: "リビール";
|
|
150
|
+
readonly previewKind: "reveal-down";
|
|
151
|
+
readonly glyph: "↓";
|
|
152
|
+
}, {
|
|
153
|
+
readonly id: "reveal-up";
|
|
154
|
+
readonly xfadeName: "revealup";
|
|
155
|
+
readonly labelJa: "下からリビール";
|
|
156
|
+
readonly category: "リビール";
|
|
157
|
+
readonly previewKind: "reveal-up";
|
|
158
|
+
readonly glyph: "↑";
|
|
159
|
+
}, {
|
|
160
|
+
readonly id: "circle-open";
|
|
161
|
+
readonly xfadeName: "circleopen";
|
|
162
|
+
readonly labelJa: "サークル(開く)";
|
|
163
|
+
readonly category: "形状";
|
|
164
|
+
readonly previewKind: "circle-open";
|
|
165
|
+
readonly glyph: "○";
|
|
166
|
+
}, {
|
|
167
|
+
readonly id: "circle-close";
|
|
168
|
+
readonly xfadeName: "circleclose";
|
|
169
|
+
readonly labelJa: "サークル(閉じる)";
|
|
170
|
+
readonly category: "形状";
|
|
171
|
+
readonly previewKind: "circle-close";
|
|
172
|
+
readonly glyph: "●";
|
|
173
|
+
}, {
|
|
174
|
+
readonly id: "zoom-in";
|
|
175
|
+
readonly xfadeName: "zoomin";
|
|
176
|
+
readonly labelJa: "ズームイン";
|
|
177
|
+
readonly category: "変形";
|
|
178
|
+
readonly previewKind: "zoom-in";
|
|
179
|
+
readonly glyph: "+";
|
|
180
|
+
}, {
|
|
181
|
+
readonly id: "squeeze-h";
|
|
182
|
+
readonly xfadeName: "squeezeh";
|
|
183
|
+
readonly labelJa: "スクイーズ(縦つぶし)";
|
|
184
|
+
readonly category: "変形";
|
|
185
|
+
readonly previewKind: "squeeze-h";
|
|
186
|
+
readonly glyph: "↕";
|
|
187
|
+
}, {
|
|
188
|
+
readonly id: "squeeze-v";
|
|
189
|
+
readonly xfadeName: "squeezev";
|
|
190
|
+
readonly labelJa: "スクイーズ(横つぶし)";
|
|
191
|
+
readonly category: "変形";
|
|
192
|
+
readonly previewKind: "squeeze-v";
|
|
193
|
+
readonly glyph: "↔";
|
|
194
|
+
}, {
|
|
195
|
+
readonly id: "blur";
|
|
196
|
+
readonly xfadeName: "hblur";
|
|
197
|
+
readonly labelJa: "ブラー";
|
|
198
|
+
readonly category: "質感";
|
|
199
|
+
readonly previewKind: "blur";
|
|
200
|
+
readonly glyph: "B";
|
|
201
|
+
}, {
|
|
202
|
+
readonly id: "pixelize";
|
|
203
|
+
readonly xfadeName: "pixelize";
|
|
204
|
+
readonly labelJa: "ピクセレート";
|
|
205
|
+
readonly category: "質感";
|
|
206
|
+
readonly previewKind: "pixelize";
|
|
207
|
+
readonly glyph: "P";
|
|
208
|
+
}];
|
|
209
|
+
export type TransitionDefinition = typeof TRANSITION_VOCABULARY[number];
|
|
210
|
+
export type TransitionType = TransitionDefinition['id'];
|
|
211
|
+
export type TransitionCategory = TransitionDefinition['category'];
|
|
212
|
+
export type TransitionPreviewKind = TransitionDefinition['previewKind'];
|
|
213
|
+
declare const unknownTransitionTypeBrand: unique symbol;
|
|
214
|
+
/** 読み取り側だけが保持する、schema より先行した未知種別。書き込み API には使わない。 */
|
|
215
|
+
export type UnknownTransitionType = string & {
|
|
216
|
+
readonly [unknownTransitionTypeBrand]: true;
|
|
217
|
+
};
|
|
218
|
+
export type ReadableTransitionType = TransitionType | UnknownTransitionType;
|
|
219
|
+
export declare const TRANSITION_TYPE_IDS: readonly TransitionType[];
|
|
220
|
+
export declare const TRANSITION_CATEGORIES: readonly TransitionCategory[];
|
|
221
|
+
export declare const TRANSITION_BY_ID: Readonly<Record<TransitionType, TransitionDefinition>>;
|
|
222
|
+
export declare function isTransitionType(value: unknown): value is TransitionType;
|
|
223
|
+
export {};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TRANSITION_BY_ID = exports.TRANSITION_CATEGORIES = exports.TRANSITION_TYPE_IDS = exports.TRANSITION_VOCABULARY = void 0;
|
|
4
|
+
exports.isTransitionType = isTransitionType;
|
|
5
|
+
/**
|
|
6
|
+
* edit.json の transition_out.type に使える正準語彙。
|
|
7
|
+
* JSON Schema はリテラル enum を保つため別置きだが、drift test でこの表と一致させる。
|
|
8
|
+
*/
|
|
9
|
+
exports.TRANSITION_VOCABULARY = [
|
|
10
|
+
{ id: 'dissolve', xfadeName: 'dissolve', labelJa: 'ディゾルブ', category: 'フェード', previewKind: 'dissolve', glyph: 'D' },
|
|
11
|
+
{ id: 'fade', xfadeName: 'fade', labelJa: 'クロスフェード', category: 'フェード', previewKind: 'fade', glyph: 'F' },
|
|
12
|
+
{ id: 'fade-black', xfadeName: 'fadeblack', labelJa: '黒フェード', category: 'フェード', previewKind: 'fade-black', glyph: 'B' },
|
|
13
|
+
{ id: 'fade-white', xfadeName: 'fadewhite', labelJa: '白フェード', category: 'フェード', previewKind: 'fade-white', glyph: 'W' },
|
|
14
|
+
{ id: 'fade-grays', xfadeName: 'fadegrays', labelJa: 'モノクロフェード', category: 'フェード', previewKind: 'fade-grays', glyph: 'G' },
|
|
15
|
+
{ id: 'wipe-left', xfadeName: 'wipeleft', labelJa: 'ワイプ(左へ)', category: 'ワイプ', previewKind: 'wipe-left', glyph: '←' },
|
|
16
|
+
{ id: 'wipe-right', xfadeName: 'wiperight', labelJa: 'ワイプ(右へ)', category: 'ワイプ', previewKind: 'wipe-right', glyph: '→' },
|
|
17
|
+
{ id: 'wipe-up', xfadeName: 'wipeup', labelJa: 'ワイプ(上へ)', category: 'ワイプ', previewKind: 'wipe-up', glyph: '↑' },
|
|
18
|
+
{ id: 'wipe-down', xfadeName: 'wipedown', labelJa: 'ワイプ(下へ)', category: 'ワイプ', previewKind: 'wipe-down', glyph: '↓' },
|
|
19
|
+
{ id: 'radial', xfadeName: 'radial', labelJa: '時計ワイプ', category: 'ワイプ', previewKind: 'radial', glyph: '◷' },
|
|
20
|
+
{ id: 'slide-left', xfadeName: 'slideleft', labelJa: 'スライド(左へ)', category: 'スライド', previewKind: 'slide-left', glyph: '←' },
|
|
21
|
+
{ id: 'slide-right', xfadeName: 'slideright', labelJa: 'スライド(右へ)', category: 'スライド', previewKind: 'slide-right', glyph: '→' },
|
|
22
|
+
{ id: 'slide-up', xfadeName: 'slideup', labelJa: 'スライド(上へ)', category: 'スライド', previewKind: 'slide-up', glyph: '↑' },
|
|
23
|
+
{ id: 'slide-down', xfadeName: 'slidedown', labelJa: 'スライド(下へ)', category: 'スライド', previewKind: 'slide-down', glyph: '↓' },
|
|
24
|
+
{ id: 'cover-left', xfadeName: 'coverleft', labelJa: 'カバー(左へ)', category: 'カバー', previewKind: 'cover-left', glyph: '←' },
|
|
25
|
+
{ id: 'cover-right', xfadeName: 'coverright', labelJa: 'カバー(右へ)', category: 'カバー', previewKind: 'cover-right', glyph: '→' },
|
|
26
|
+
{ id: 'cover-up', xfadeName: 'coverup', labelJa: 'カバー(上へ)', category: 'カバー', previewKind: 'cover-up', glyph: '↑' },
|
|
27
|
+
{ id: 'cover-down', xfadeName: 'coverdown', labelJa: 'カバー(下へ)', category: 'カバー', previewKind: 'cover-down', glyph: '↓' },
|
|
28
|
+
{ id: 'reveal-left', xfadeName: 'revealleft', labelJa: 'リビール(左へ)', category: 'リビール', previewKind: 'reveal-left', glyph: '←' },
|
|
29
|
+
{ id: 'reveal-right', xfadeName: 'revealright', labelJa: 'リビール(右へ)', category: 'リビール', previewKind: 'reveal-right', glyph: '→' },
|
|
30
|
+
{ id: 'reveal-down', xfadeName: 'revealdown', labelJa: '上からリビール', category: 'リビール', previewKind: 'reveal-down', glyph: '↓' },
|
|
31
|
+
{ id: 'reveal-up', xfadeName: 'revealup', labelJa: '下からリビール', category: 'リビール', previewKind: 'reveal-up', glyph: '↑' },
|
|
32
|
+
{ id: 'circle-open', xfadeName: 'circleopen', labelJa: 'サークル(開く)', category: '形状', previewKind: 'circle-open', glyph: '○' },
|
|
33
|
+
{ id: 'circle-close', xfadeName: 'circleclose', labelJa: 'サークル(閉じる)', category: '形状', previewKind: 'circle-close', glyph: '●' },
|
|
34
|
+
{ id: 'zoom-in', xfadeName: 'zoomin', labelJa: 'ズームイン', category: '変形', previewKind: 'zoom-in', glyph: '+' },
|
|
35
|
+
{ id: 'squeeze-h', xfadeName: 'squeezeh', labelJa: 'スクイーズ(縦つぶし)', category: '変形', previewKind: 'squeeze-h', glyph: '↕' },
|
|
36
|
+
{ id: 'squeeze-v', xfadeName: 'squeezev', labelJa: 'スクイーズ(横つぶし)', category: '変形', previewKind: 'squeeze-v', glyph: '↔' },
|
|
37
|
+
{ id: 'blur', xfadeName: 'hblur', labelJa: 'ブラー', category: '質感', previewKind: 'blur', glyph: 'B' },
|
|
38
|
+
{ id: 'pixelize', xfadeName: 'pixelize', labelJa: 'ピクセレート', category: '質感', previewKind: 'pixelize', glyph: 'P' }
|
|
39
|
+
];
|
|
40
|
+
exports.TRANSITION_TYPE_IDS = exports.TRANSITION_VOCABULARY.map(entry => entry.id);
|
|
41
|
+
exports.TRANSITION_CATEGORIES = [...new Set(exports.TRANSITION_VOCABULARY.map(entry => entry.category))];
|
|
42
|
+
exports.TRANSITION_BY_ID = Object.fromEntries(exports.TRANSITION_VOCABULARY.map(entry => [entry.id, entry]));
|
|
43
|
+
function isTransitionType(value) {
|
|
44
|
+
return typeof value === 'string' && Object.prototype.hasOwnProperty.call(exports.TRANSITION_BY_ID, value);
|
|
45
|
+
}
|
|
@@ -28,6 +28,42 @@ var AkariEditKernel = (() => {
|
|
|
28
28
|
transitionProgressAt: () => transitionProgressAt
|
|
29
29
|
});
|
|
30
30
|
|
|
31
|
+
// src/transition-vocabulary.ts
|
|
32
|
+
var TRANSITION_VOCABULARY = [
|
|
33
|
+
{ id: "dissolve", xfadeName: "dissolve", labelJa: "\u30C7\u30A3\u30BE\u30EB\u30D6", category: "\u30D5\u30A7\u30FC\u30C9", previewKind: "dissolve", glyph: "D" },
|
|
34
|
+
{ id: "fade", xfadeName: "fade", labelJa: "\u30AF\u30ED\u30B9\u30D5\u30A7\u30FC\u30C9", category: "\u30D5\u30A7\u30FC\u30C9", previewKind: "fade", glyph: "F" },
|
|
35
|
+
{ id: "fade-black", xfadeName: "fadeblack", labelJa: "\u9ED2\u30D5\u30A7\u30FC\u30C9", category: "\u30D5\u30A7\u30FC\u30C9", previewKind: "fade-black", glyph: "B" },
|
|
36
|
+
{ id: "fade-white", xfadeName: "fadewhite", labelJa: "\u767D\u30D5\u30A7\u30FC\u30C9", category: "\u30D5\u30A7\u30FC\u30C9", previewKind: "fade-white", glyph: "W" },
|
|
37
|
+
{ id: "fade-grays", xfadeName: "fadegrays", labelJa: "\u30E2\u30CE\u30AF\u30ED\u30D5\u30A7\u30FC\u30C9", category: "\u30D5\u30A7\u30FC\u30C9", previewKind: "fade-grays", glyph: "G" },
|
|
38
|
+
{ id: "wipe-left", xfadeName: "wipeleft", labelJa: "\u30EF\u30A4\u30D7\uFF08\u5DE6\u3078\uFF09", category: "\u30EF\u30A4\u30D7", previewKind: "wipe-left", glyph: "\u2190" },
|
|
39
|
+
{ id: "wipe-right", xfadeName: "wiperight", labelJa: "\u30EF\u30A4\u30D7\uFF08\u53F3\u3078\uFF09", category: "\u30EF\u30A4\u30D7", previewKind: "wipe-right", glyph: "\u2192" },
|
|
40
|
+
{ id: "wipe-up", xfadeName: "wipeup", labelJa: "\u30EF\u30A4\u30D7\uFF08\u4E0A\u3078\uFF09", category: "\u30EF\u30A4\u30D7", previewKind: "wipe-up", glyph: "\u2191" },
|
|
41
|
+
{ id: "wipe-down", xfadeName: "wipedown", labelJa: "\u30EF\u30A4\u30D7\uFF08\u4E0B\u3078\uFF09", category: "\u30EF\u30A4\u30D7", previewKind: "wipe-down", glyph: "\u2193" },
|
|
42
|
+
{ id: "radial", xfadeName: "radial", labelJa: "\u6642\u8A08\u30EF\u30A4\u30D7", category: "\u30EF\u30A4\u30D7", previewKind: "radial", glyph: "\u25F7" },
|
|
43
|
+
{ id: "slide-left", xfadeName: "slideleft", labelJa: "\u30B9\u30E9\u30A4\u30C9\uFF08\u5DE6\u3078\uFF09", category: "\u30B9\u30E9\u30A4\u30C9", previewKind: "slide-left", glyph: "\u2190" },
|
|
44
|
+
{ id: "slide-right", xfadeName: "slideright", labelJa: "\u30B9\u30E9\u30A4\u30C9\uFF08\u53F3\u3078\uFF09", category: "\u30B9\u30E9\u30A4\u30C9", previewKind: "slide-right", glyph: "\u2192" },
|
|
45
|
+
{ id: "slide-up", xfadeName: "slideup", labelJa: "\u30B9\u30E9\u30A4\u30C9\uFF08\u4E0A\u3078\uFF09", category: "\u30B9\u30E9\u30A4\u30C9", previewKind: "slide-up", glyph: "\u2191" },
|
|
46
|
+
{ id: "slide-down", xfadeName: "slidedown", labelJa: "\u30B9\u30E9\u30A4\u30C9\uFF08\u4E0B\u3078\uFF09", category: "\u30B9\u30E9\u30A4\u30C9", previewKind: "slide-down", glyph: "\u2193" },
|
|
47
|
+
{ id: "cover-left", xfadeName: "coverleft", labelJa: "\u30AB\u30D0\u30FC\uFF08\u5DE6\u3078\uFF09", category: "\u30AB\u30D0\u30FC", previewKind: "cover-left", glyph: "\u2190" },
|
|
48
|
+
{ id: "cover-right", xfadeName: "coverright", labelJa: "\u30AB\u30D0\u30FC\uFF08\u53F3\u3078\uFF09", category: "\u30AB\u30D0\u30FC", previewKind: "cover-right", glyph: "\u2192" },
|
|
49
|
+
{ id: "cover-up", xfadeName: "coverup", labelJa: "\u30AB\u30D0\u30FC\uFF08\u4E0A\u3078\uFF09", category: "\u30AB\u30D0\u30FC", previewKind: "cover-up", glyph: "\u2191" },
|
|
50
|
+
{ id: "cover-down", xfadeName: "coverdown", labelJa: "\u30AB\u30D0\u30FC\uFF08\u4E0B\u3078\uFF09", category: "\u30AB\u30D0\u30FC", previewKind: "cover-down", glyph: "\u2193" },
|
|
51
|
+
{ id: "reveal-left", xfadeName: "revealleft", labelJa: "\u30EA\u30D3\u30FC\u30EB\uFF08\u5DE6\u3078\uFF09", category: "\u30EA\u30D3\u30FC\u30EB", previewKind: "reveal-left", glyph: "\u2190" },
|
|
52
|
+
{ id: "reveal-right", xfadeName: "revealright", labelJa: "\u30EA\u30D3\u30FC\u30EB\uFF08\u53F3\u3078\uFF09", category: "\u30EA\u30D3\u30FC\u30EB", previewKind: "reveal-right", glyph: "\u2192" },
|
|
53
|
+
{ id: "reveal-down", xfadeName: "revealdown", labelJa: "\u4E0A\u304B\u3089\u30EA\u30D3\u30FC\u30EB", category: "\u30EA\u30D3\u30FC\u30EB", previewKind: "reveal-down", glyph: "\u2193" },
|
|
54
|
+
{ id: "reveal-up", xfadeName: "revealup", labelJa: "\u4E0B\u304B\u3089\u30EA\u30D3\u30FC\u30EB", category: "\u30EA\u30D3\u30FC\u30EB", previewKind: "reveal-up", glyph: "\u2191" },
|
|
55
|
+
{ id: "circle-open", xfadeName: "circleopen", labelJa: "\u30B5\u30FC\u30AF\u30EB\uFF08\u958B\u304F\uFF09", category: "\u5F62\u72B6", previewKind: "circle-open", glyph: "\u25CB" },
|
|
56
|
+
{ id: "circle-close", xfadeName: "circleclose", labelJa: "\u30B5\u30FC\u30AF\u30EB\uFF08\u9589\u3058\u308B\uFF09", category: "\u5F62\u72B6", previewKind: "circle-close", glyph: "\u25CF" },
|
|
57
|
+
{ id: "zoom-in", xfadeName: "zoomin", labelJa: "\u30BA\u30FC\u30E0\u30A4\u30F3", category: "\u5909\u5F62", previewKind: "zoom-in", glyph: "\uFF0B" },
|
|
58
|
+
{ id: "squeeze-h", xfadeName: "squeezeh", labelJa: "\u30B9\u30AF\u30A4\u30FC\u30BA\uFF08\u7E26\u3064\u3076\u3057\uFF09", category: "\u5909\u5F62", previewKind: "squeeze-h", glyph: "\u2195" },
|
|
59
|
+
{ id: "squeeze-v", xfadeName: "squeezev", labelJa: "\u30B9\u30AF\u30A4\u30FC\u30BA\uFF08\u6A2A\u3064\u3076\u3057\uFF09", category: "\u5909\u5F62", previewKind: "squeeze-v", glyph: "\u2194" },
|
|
60
|
+
{ id: "blur", xfadeName: "hblur", labelJa: "\u30D6\u30E9\u30FC", category: "\u8CEA\u611F", previewKind: "blur", glyph: "B" },
|
|
61
|
+
{ id: "pixelize", xfadeName: "pixelize", labelJa: "\u30D4\u30AF\u30BB\u30EC\u30FC\u30C8", category: "\u8CEA\u611F", previewKind: "pixelize", glyph: "P" }
|
|
62
|
+
];
|
|
63
|
+
var TRANSITION_TYPE_IDS = TRANSITION_VOCABULARY.map((entry) => entry.id);
|
|
64
|
+
var TRANSITION_CATEGORIES = [...new Set(TRANSITION_VOCABULARY.map((entry) => entry.category))];
|
|
65
|
+
var TRANSITION_BY_ID = Object.fromEntries(TRANSITION_VOCABULARY.map((entry) => [entry.id, entry]));
|
|
66
|
+
|
|
31
67
|
// src/edit-store.ts
|
|
32
68
|
function computeCutTrackSegments(cuts) {
|
|
33
69
|
const cursorByTrack = /* @__PURE__ */ new Map();
|
|
@@ -50,6 +86,32 @@ var AkariEditKernel = (() => {
|
|
|
50
86
|
return segments;
|
|
51
87
|
}
|
|
52
88
|
|
|
89
|
+
// src/cut-adjacency.ts
|
|
90
|
+
var DEFAULT_CUT_ADJACENCY_FPS = 30;
|
|
91
|
+
function effectiveCutFps(fps) {
|
|
92
|
+
return Number.isFinite(fps) && fps > 0 ? fps : DEFAULT_CUT_ADJACENCY_FPS;
|
|
93
|
+
}
|
|
94
|
+
function cutOverlapFrames(earlier, later, fps = DEFAULT_CUT_ADJACENCY_FPS) {
|
|
95
|
+
const resolvedFps = effectiveCutFps(fps);
|
|
96
|
+
return Math.round(earlier.tlEnd * resolvedFps) - Math.round(later.tlStart * resolvedFps);
|
|
97
|
+
}
|
|
98
|
+
var nonNegativeRoom = (value) => value === Number.POSITIVE_INFINITY ? value : Number.isFinite(value) && value > 0 ? value : 0;
|
|
99
|
+
function planTransitionHandleWindow(input) {
|
|
100
|
+
const declaredSeconds = Number.isFinite(input.declaredSeconds) && input.declaredSeconds > 0 ? input.declaredSeconds : 0;
|
|
101
|
+
const effectiveSeconds = Math.max(0, Math.min(
|
|
102
|
+
declaredSeconds,
|
|
103
|
+
2 * nonNegativeRoom(input.outgoingTailRoomSeconds),
|
|
104
|
+
2 * nonNegativeRoom(input.incomingHeadRoomSeconds),
|
|
105
|
+
2 * nonNegativeRoom(input.outgoingDurationSeconds),
|
|
106
|
+
2 * nonNegativeRoom(input.incomingDurationSeconds)
|
|
107
|
+
));
|
|
108
|
+
return {
|
|
109
|
+
effectiveSeconds,
|
|
110
|
+
halfSeconds: effectiveSeconds / 2,
|
|
111
|
+
outcome: effectiveSeconds <= 0 ? "none" : effectiveSeconds < declaredSeconds ? "clamped" : "full"
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
53
115
|
// src/timeline-map.ts
|
|
54
116
|
function transitionProgressAt(window, outputT) {
|
|
55
117
|
if (!(window.duration > 0)) return 0;
|
|
@@ -68,10 +130,46 @@ var AkariEditKernel = (() => {
|
|
|
68
130
|
const resolved = trackSegments.map((segment) => ({
|
|
69
131
|
start: segment.at,
|
|
70
132
|
end: segment.end,
|
|
133
|
+
baseStart: segment.at,
|
|
134
|
+
baseEnd: segment.end,
|
|
71
135
|
track: segment.track,
|
|
72
136
|
cut: usableCuts[segment.index],
|
|
73
137
|
cutIndex: usable[segment.index].index
|
|
74
138
|
}));
|
|
139
|
+
const fps = options?.fps ?? DEFAULT_CUT_ADJACENCY_FPS;
|
|
140
|
+
for (let outgoingIndex = 0; outgoingIndex < resolved.length; outgoingIndex++) {
|
|
141
|
+
const outgoing = resolved[outgoingIndex];
|
|
142
|
+
const transition = outgoing.cut.transitionOut;
|
|
143
|
+
if (!transition || !(typeof transition.duration === "number" && Number.isFinite(transition.duration) && transition.duration > 0)) continue;
|
|
144
|
+
const incoming = resolved.slice(outgoingIndex + 1).find((candidate) => candidate.track === outgoing.track);
|
|
145
|
+
if (!incoming || cutOverlapFrames(
|
|
146
|
+
{ tlEnd: outgoing.end },
|
|
147
|
+
{ tlStart: incoming.start },
|
|
148
|
+
fps
|
|
149
|
+
) !== 0) continue;
|
|
150
|
+
const outgoingRoom = options?.handleRoom?.(outgoing.cutIndex);
|
|
151
|
+
const incomingRoom = options?.handleRoom?.(incoming.cutIndex);
|
|
152
|
+
const incomingSpeed = typeof incoming.cut.speed === "number" && incoming.cut.speed > 0 ? incoming.cut.speed : 1;
|
|
153
|
+
const plan = planTransitionHandleWindow({
|
|
154
|
+
declaredSeconds: transition.duration,
|
|
155
|
+
outgoingTailRoomSeconds: outgoingRoom?.tailSeconds ?? Number.POSITIVE_INFINITY,
|
|
156
|
+
incomingHeadRoomSeconds: incomingRoom?.headSeconds ?? incoming.cut.in / incomingSpeed,
|
|
157
|
+
outgoingDurationSeconds: outgoing.baseEnd - outgoing.baseStart,
|
|
158
|
+
incomingDurationSeconds: incoming.baseEnd - incoming.baseStart
|
|
159
|
+
});
|
|
160
|
+
if (plan.effectiveSeconds <= 0) continue;
|
|
161
|
+
const cutPoint = outgoing.end;
|
|
162
|
+
outgoing.end = cutPoint + plan.halfSeconds;
|
|
163
|
+
outgoing.cut = {
|
|
164
|
+
...outgoing.cut,
|
|
165
|
+
transitionOut: { ...transition, duration: plan.effectiveSeconds }
|
|
166
|
+
};
|
|
167
|
+
incoming.start = cutPoint - plan.halfSeconds;
|
|
168
|
+
incoming.cut = {
|
|
169
|
+
...incoming.cut,
|
|
170
|
+
in: Math.max(0, incoming.cut.in - plan.halfSeconds * incomingSpeed)
|
|
171
|
+
};
|
|
172
|
+
}
|
|
75
173
|
const segmentSlice = (entry, start, end, transitionOut = null) => {
|
|
76
174
|
const cut = entry.cut;
|
|
77
175
|
const speed = typeof cut.speed === "number" && cut.speed > 0 ? cut.speed : 1;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@akari-video/matte-rvm",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"description": "Robust Video Matting model deployment and raw BGRA inference helper.",
|
|
7
|
+
"main": "src/index.mjs",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": "./src/index.mjs"
|
|
10
|
+
},
|
|
11
|
+
"scripts": {
|
|
12
|
+
"postinstall": "node scripts/fetch-models.mjs",
|
|
13
|
+
"fetch:resnet50": "node scripts/fetch-models.mjs --model resnet50",
|
|
14
|
+
"test": "node --test \"test/*.test.mjs\""
|
|
15
|
+
},
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"onnxruntime-node": "1.29.0"
|
|
18
|
+
},
|
|
19
|
+
"engines": {
|
|
20
|
+
"node": ">=20"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -6,6 +6,9 @@
|
|
|
6
6
|
import fs from "node:fs";
|
|
7
7
|
import path from "node:path";
|
|
8
8
|
import { fileURLToPath } from "node:url";
|
|
9
|
+
import { createRequire } from "node:module";
|
|
10
|
+
|
|
11
|
+
const { TRANSITION_TYPE_IDS } = createRequire(import.meta.url)("../../edit-store/lib/index.js");
|
|
9
12
|
|
|
10
13
|
const LAYER_KINDS = new Set(["baked", "video", "filter"]);
|
|
11
14
|
const LAYER_BLEND_MODES = new Set([
|
|
@@ -1063,8 +1066,8 @@ function validateTransitionOut(value, label) {
|
|
|
1063
1066
|
fail(`${label} は object である必要があります`);
|
|
1064
1067
|
return;
|
|
1065
1068
|
}
|
|
1066
|
-
if (!
|
|
1067
|
-
fail(`${label}.type は
|
|
1069
|
+
if (!TRANSITION_TYPE_IDS.includes(value.type)) {
|
|
1070
|
+
fail(`${label}.type は ${TRANSITION_TYPE_IDS.join("/")} のいずれかである必要があります`);
|
|
1068
1071
|
}
|
|
1069
1072
|
if (!isFiniteNumber(value.duration) || value.duration <= 0) {
|
|
1070
1073
|
fail(`${label}.duration は 0 より大きい有限数である必要があります`);
|
|
@@ -279,10 +279,10 @@
|
|
|
279
279
|
"additionalProperties": true,
|
|
280
280
|
"required": ["type", "duration"],
|
|
281
281
|
"properties": {
|
|
282
|
-
"type": { "enum": ["dissolve", "fade-black", "fade-white", "reveal-down", "reveal-up"] },
|
|
282
|
+
"type": { "enum": ["dissolve", "fade", "fade-black", "fade-white", "fade-grays", "wipe-left", "wipe-right", "wipe-up", "wipe-down", "radial", "slide-left", "slide-right", "slide-up", "slide-down", "cover-left", "cover-right", "cover-up", "cover-down", "reveal-left", "reveal-right", "reveal-down", "reveal-up", "circle-open", "circle-close", "zoom-in", "squeeze-h", "squeeze-v", "blur", "pixelize"] },
|
|
283
283
|
"duration": { "$ref": "#/$defs/positiveNumber" }
|
|
284
284
|
},
|
|
285
|
-
"$comment": "docs/contract-2026-07-22-render-basics.md #3。v0 は指定されたカット境界のみ xfade 経路(残裁定3)。
|
|
285
|
+
"$comment": "docs/contract-2026-07-22-render-basics.md #3。v0 は指定されたカット境界のみ xfade 経路(残裁定3)。enum は packages/edit-store の正準トランジション語彙と drift test で一致させる。"
|
|
286
286
|
},
|
|
287
287
|
"cutTransform": {
|
|
288
288
|
"type": "object",
|