Rework to use the melt binary

This commit is contained in:
Kuba Orlik 2021-10-23 20:37:41 +02:00
parent 3deb39fabb
commit f205deb5e4
5 changed files with 37 additions and 143 deletions

View File

@ -1,13 +0,0 @@
const audio_props = {
"stream.handler_name.markup": "SoundHandler",
"stream.type": "audio",
"codec.sample_fmt": (metadata) => {
const type = metadata.data.Format_Settings_Sign == "Signed" ? "s" : "u";
const bit_depth = metadata.data.BitDepth;
return `${type}${bit_depth}`;
},
"codec.channels": (metadata) => metadata.data.Channels,
"codec.sample_rate": (metadata) => metadata.data.SamplingRate,
};
export default audio_props;

View File

@ -3,21 +3,26 @@
import { formatDuration } from "./util.mjs"; import { formatDuration } from "./util.mjs";
import Producer from "./producer.mjs"; import Producer from "./producer.mjs";
export default async function kdenliveProject( export default class Project {
source_files, constructor(fps) {
project_settings, // const project_settings = { fps: 30 }; this.producers = [];
clips this.clips = [];
) { this.fps = fps;
const producers = await Promise.all(source_files.map(Producer.fromFile)); }
addProducer(file) {
this.producers.push(new Producer(file));
return this;
}
async toXML() {
return `<?xml version='1.0' encoding='utf-8'?> return `<?xml version='1.0' encoding='utf-8'?>
<mlt LC_NUMERIC="C" producer="main_bin" version="7.0.0" root="/home/kuba/Downloads"> <mlt LC_NUMERIC="C" producer="main_bin" version="7.0.0" root="/home/kuba/Downloads">
<profile frame_rate_num="${ <profile frame_rate_num="${
project_settings.fps this.fps
}" sample_aspect_num="1" display_aspect_den="9" colorspace="601" progressive="1" description="1920x1080 29.90fps" display_aspect_num="16" frame_rate_den="1" width="1920" height="1080" sample_aspect_den="1"/> }" sample_aspect_num="1" display_aspect_den="9" colorspace="601" progressive="1" description="1920x1080 29.90fps" display_aspect_num="16" frame_rate_den="1" width="1920" height="1080" sample_aspect_den="1"/>
${( ${(
await Promise.all( await Promise.all(this.producers.map((producer) => producer.toXML(this.fps)))
producers.map((producer) => producer.toXML(project_settings))
)
).join("\n")} ).join("\n")}
<playlist id="main_bin"> <playlist id="main_bin">
<property name="kdenlive:docproperties.activeTrack">2</property> <property name="kdenlive:docproperties.activeTrack">2</property>
@ -29,15 +34,12 @@ ${(
] ]
</property> </property>
<property name="kdenlive:docproperties.kdenliveversion">21.08.1</property> <property name="kdenlive:docproperties.kdenliveversion">21.08.1</property>
<property name="kdenlive:docproperties.version">1.02</property> g <property name="kdenlive:docproperties.version">1.02</property>
<property name="kdenlive:expandedFolders"/> <property name="kdenlive:expandedFolders"/>
<property name="kdenlive:documentnotes"/> <property name="kdenlive:documentnotes"/>
<property name="xml_retain">1</property> <property name="xml_retain">1</property>
${producers ${this.producers
.map( .map((producer) => `<entry producer="producer${producer.index}"/>`)
(producer, index) =>
`<entry producer="producer${index}" in="00:00:00.000" out="${producer.getDuration()}"/>`
)
.join("\n")} .join("\n")}
</playlist> </playlist>
<producer id="black_track" in="00:00:00.000" out="00:08:20.000"> <producer id="black_track" in="00:00:00.000" out="00:08:20.000">
@ -51,6 +53,8 @@ ${producers
</producer> </producer>
<playlist id="playlist0"> <playlist id="playlist0">
<property name="kdenlive:audio_track">1</property> <property name="kdenlive:audio_track">1</property>
<blank length="1000"/>
<entry producer="producer1"/>
</playlist> </playlist>
<playlist id="playlist1"> <playlist id="playlist1">
<property name="kdenlive:audio_track">1</property> <property name="kdenlive:audio_track">1</property>
@ -93,12 +97,12 @@ ${producers
</tractor> </tractor>
</mlt> </mlt>
`; `;
}
} }
const project_content = await kdenliveProject( const project = new Project(30);
["/home/kuba/Videos/5min.mp4", "/home/kuba/Videos/5min.wav"],
{ project.addProducer("/home/kuba/Videos/5min.mp4");
fps: 30, project.addProducer("/home/kuba/Videos/5min.wav");
}
); await $`echo ${await project.toXML()} > project-generated.kdenlive`;
await $`echo ${project_content} > project-generated.kdenlive`;

View File

@ -6,107 +6,23 @@ import {
formatDuration, formatDuration,
} from "./util.mjs"; } from "./util.mjs";
import { makeIDGen } from "./util.mjs"; import { makeIDGen } from "./util.mjs";
import video_props from "./video_props.mjs";
import audio_props from "./audio_props.mjs";
const makeId = makeIDGen(1); const makeId = makeIDGen(1);
const producerIndexGen = makeIDGen(0); const producerIndexGen = makeIDGen(0);
export default class Producer { export default class Producer {
constructor(metadata) { constructor(path) {
this.metadata = metadata; this.path = path;
this.index = producerIndexGen.next().value; this.index = producerIndexGen.next().value;
} }
static prop_types = { async toXML(fps) {
length: (metadata, project_settings) => { const xml = (
const duration = getStream(metadata.track, "General").Duration; await $`melt ${
return `${duration * project_settings.fps}`; this.path
}, } -consumer xml ${`frame_rate_num=${fps}`} | htmlq producer`
eof: "pause", ).stdout;
resource: (metadata) => metadata["@ref"],
audio_index: (metadata) => getStreamIndex(metadata.track, "Audio"),
video_index: (metadata) => getStreamIndex(metadata.track, "Video"),
mute_on_pause: "0",
mlt_service: "avformat-novalidate",
seekable: "1",
aspect_ratio: "1",
"kdenlive:clipname": "",
"kdenlive:folderid": "-1",
"kdenlive:audio_max0": "208",
"kdenlive:id": () => makeId.next().value,
"kdenlive:file_size": async (metadata) =>
(
await $`du --bytes ${metadata["@ref"]} | awk '{print $1}'`
).stdout.replace("\n", ""),
"kdenlive:file_hash": async (metadata) =>
(
await $`head -c 1000000 ${metadata["@ref"]} && tail -c 1000000 ${metadata["@ref"]}`.pipe(
$`md5sum | awk '{print $1}'`
)
).stdout.replace("\n", ""),
"meta.media.nb_streams": (metadata) => metadata.track.length - 1,
$$$video: async (metadata, project_settings) => {
const video_index = indexOf(metadata.track, (e) => e["@type"] == "Video");
if (video_index == -1) {
return null;
}
return {
$replace: await renderAllProps(
video_props,
{
index: video_index,
path: metadata["@ref"],
data: metadata.track[video_index],
},
project_settings,
`meta.media.${getStreamIndex(metadata.track, "Video")}.`
),
};
},
$$$audio: async (metadata, project_settings) => {
const audio_index = indexOf(metadata.track, (e) => e["@type"] == "Audio");
if (audio_index == -1) {
return null;
}
return {
$replace: await renderAllProps(
audio_props,
{
index: audio_index,
path: metadata["@ref"],
data: metadata.track[audio_index],
},
project_settings,
`meta.media.${getStreamIndex(metadata.track, "Audio")}.`
),
};
},
};
static async fromFile(file_path) { return xml.replace("producer0", `producer${this.index}`);
const metadata = JSON.parse(
(await $`mediainfo --Output=JSON ${file_path}`).stdout
).media;
return new Producer(metadata);
}
getDuration() {
return formatDuration(
parseFloat(getStream(this.metadata.track, "General").Duration)
);
}
async toXML(project_settings) {
return `<producer id="producer${
this.index
}" in="00:00:00.000" out="${this.getDuration()}">
${await renderAllProps(
Producer.prop_types,
this.metadata,
project_settings
)}
</producer>
`;
} }
} }

View File

@ -20,7 +20,6 @@ export async function renderProperty(
} else { } else {
value = await fn(metadata, project_settings); value = await fn(metadata, project_settings);
} }
console.log(name, value);
if (value === null) { if (value === null) {
return ""; return "";
} else if (value && value.$replace) { } else if (value && value.$replace) {

View File

@ -1,12 +0,0 @@
const video_props = {
"stream.type": "video",
"stream.frame_rate": (metadata) => metadata.data.FrameRate,
"stream.sample_aspect_ratio": "1",
"codec.width": (metadata) => metadata.data.Width,
"codec.height": (metadata) => metadata.data.Height,
"codec.rotate": "0",
"codec.frame_rate": (metadata) => metadata.data.FrameRate,
"stream.handler_name.markup": "VideoHandle",
};
export default video_props