run method
Future<AutoEditReport>
run(
- EditPlan plan, {
- ValueChanged<
String> ? onStatus, - String captionLanguage = 'en',
- bool useSmartAi = true,
useSmartAi lets the on-device model pick highlights and write the
post (when it is downloaded); otherwise the offline rules do it.
Implementation
Future<AutoEditReport> run(
EditPlan plan, {
ValueChanged<String>? onStatus,
String captionLanguage = 'en',
bool useSmartAi = true,
}) async {
final report = AutoEditReport();
if (!controller.hasProject) {
throw StateError('Add clips before running an AI edit.');
}
// Removals first, so "remove the filters and add vintage" ends vintage.
_removals(plan, report);
if (plan.ratio != null && plan.ratio != controller.aspectRatio) {
controller.setAspectRatio(plan.ratio!);
report.done.add('Ratio set to ${plan.ratio!.label}');
}
if (plan.fillMode != null) {
controller.setFillModeForAll(plan.fillMode!);
report.done.add('Every clip set to ${plan.fillMode!.label.toLowerCase()}');
}
if (plan.highlightLength != null) {
await _highlights(plan.highlightLength!, captionLanguage, report, onStatus, useSmartAi);
}
if (plan.reframe) {
onStatus?.call('Finding faces to crop around…');
await _reframe(report);
}
if (plan.removeSilence) {
onStatus?.call('Finding the talking parts…');
await _removeSilence(report);
}
if (plan.speed != null) {
final speed = plan.speed!;
controller.applyClipEdits((c) {
for (var i = 0; i < c.clips.length; i++) {
if (c.clips[i].isVideo) c.updateClipSpeed(i, speed);
}
});
report.done.add('Speed set to ${speed}x');
}
if (plan.targetLength != null) {
final removed = controller.trimProjectTo(plan.targetLength!);
if (removed > Duration.zero) {
report.done.add('Shortened to ${plan.targetLength!.inSeconds}s');
} else {
report.skipped.add('The video is already ${plan.targetLength!.inSeconds}s or shorter');
}
}
if (plan.clipVolume != null) {
final volume = plan.clipVolume!;
controller.applyClipEdits((c) {
for (var i = 0; i < c.clips.length; i++) {
if (c.clips[i].isVideo && c.clips[i].hasAudio) c.updateClipVolume(i, volume);
}
});
report.done.add(volume == 0 ? 'Original sound muted' : 'Original sound on');
}
if (plan.musicPrompt != null && !plan.noMusic && controller.audioTracks.isEmpty) {
onStatus?.call('Composing music…');
await _addMusic(plan.musicPrompt!, hasVoice: _hasVoice(), report: report);
}
if (plan.musicVolume != null) {
if (controller.audioTracks.isEmpty) {
report.skipped.add('There is no music to make louder or quieter');
} else {
controller.scaleAudioTrackVolumes(plan.musicVolume!);
report.done.add(plan.musicVolume! > 1 ? 'Music louder' : 'Music quieter');
}
}
if (plan.beatEvery != null) {
onStatus?.call('Cutting on the beat…');
await _beatSync(plan.beatEvery!, report);
}
if (plan.effect != null || plan.zoomCuts || plan.transition != null || plan.transitionMix != null) {
onStatus?.call('Styling clips…');
_style(plan);
if (plan.effect != null) report.done.add('${plan.effect!.label} look applied');
if (plan.zoomCuts) report.done.add('Zoom cuts added');
if (plan.transition != null && plan.transition != ClipTransition.none) {
report.done.add('${plan.transition!.label} transitions added');
}
if (plan.transition == ClipTransition.none) report.done.add('Transitions removed');
if (plan.transitionMix != null) report.done.add('A different transition on every cut');
}
for (final (effect, span) in plan.timedEffects) {
_addTimedEffect(effect, span);
report.done.add('${effect.label} effect added ${span.label}');
}
if (plan.audioCleanup != null) {
final n = controller.setAudioCleanupForAll(plan.audioCleanup!);
report.done.add(n == 0
? 'No clip has sound to clean up'
: '${plan.audioCleanup!.label} on $n clip${n == 1 ? '' : 's'}');
}
if (plan.captions) {
onStatus?.call('Writing captions…');
await _captions(plan, captionLanguage, report, onStatus);
}
if (plan.hookText != null) {
_hook(plan.hookText!);
report.done.add('Title "${plan.hookText}" added');
}
if (plan.stickers.isNotEmpty) {
_addStickers(plan.stickers);
report.done.add('${plan.stickers.join(' ')} added');
}
// A post is only worth writing for a finished short / styled edit.
if (plan.template != null || plan.highlightLength != null || plan.captions || plan.hookText != null) {
onStatus?.call('Writing your post caption…');
report.postText = await agent.writePost(
title: plan.hookText,
transcript: _transcriptText(),
template: plan.template,
useSmartAi: useSmartAi,
);
}
return report;
}