Compare commits
	
		
			2 Commits
		
	
	
		
			eaec1a7f86
			...
			f205deb5e4
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					f205deb5e4 | ||
| 
						 | 
					3deb39fabb | 
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
/.log/
 | 
			
		||||
							
								
								
									
										108
									
								
								kdenlive.mjs
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										108
									
								
								kdenlive.mjs
									
									
									
									
									
										Executable file
									
								
							@ -0,0 +1,108 @@
 | 
			
		||||
#!/usr/bin/zx --quiet
 | 
			
		||||
 | 
			
		||||
import { formatDuration } from "./util.mjs";
 | 
			
		||||
import Producer from "./producer.mjs";
 | 
			
		||||
 | 
			
		||||
export default class Project {
 | 
			
		||||
  constructor(fps) {
 | 
			
		||||
    this.producers = [];
 | 
			
		||||
    this.clips = [];
 | 
			
		||||
    this.fps = fps;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  addProducer(file) {
 | 
			
		||||
    this.producers.push(new Producer(file));
 | 
			
		||||
    return this;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async toXML() {
 | 
			
		||||
    return `<?xml version='1.0' encoding='utf-8'?>
 | 
			
		||||
<mlt LC_NUMERIC="C" producer="main_bin" version="7.0.0" root="/home/kuba/Downloads">
 | 
			
		||||
 <profile frame_rate_num="${
 | 
			
		||||
   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"/>
 | 
			
		||||
${(
 | 
			
		||||
  await Promise.all(this.producers.map((producer) => producer.toXML(this.fps)))
 | 
			
		||||
).join("\n")}
 | 
			
		||||
 <playlist id="main_bin">
 | 
			
		||||
  <property name="kdenlive:docproperties.activeTrack">2</property>
 | 
			
		||||
  <property name="kdenlive:docproperties.audioChannels">2</property>
 | 
			
		||||
  <property name="kdenlive:docproperties.audioTarget">1</property>
 | 
			
		||||
  <property name="kdenlive:docproperties.disablepreview">0</property>
 | 
			
		||||
  <property name="kdenlive:docproperties.documentid">1633881496938</property>
 | 
			
		||||
  <property name="kdenlive:docproperties.groups">[
 | 
			
		||||
]
 | 
			
		||||
</property>
 | 
			
		||||
  <property name="kdenlive:docproperties.kdenliveversion">21.08.1</property>
 | 
			
		||||
g  <property name="kdenlive:docproperties.version">1.02</property>
 | 
			
		||||
  <property name="kdenlive:expandedFolders"/>
 | 
			
		||||
  <property name="kdenlive:documentnotes"/>
 | 
			
		||||
  <property name="xml_retain">1</property>
 | 
			
		||||
${this.producers
 | 
			
		||||
  .map((producer) => `<entry producer="producer${producer.index}"/>`)
 | 
			
		||||
  .join("\n")}  
 | 
			
		||||
 </playlist>
 | 
			
		||||
 <producer id="black_track" in="00:00:00.000" out="00:08:20.000">
 | 
			
		||||
  <property name="length">2147483647</property>
 | 
			
		||||
  <property name="eof">continue</property>
 | 
			
		||||
  <property name="resource">black</property>
 | 
			
		||||
  <property name="aspect_ratio">1</property>
 | 
			
		||||
  <property name="mlt_service">color</property>
 | 
			
		||||
  <property name="mlt_image_format">rgb24a</property>
 | 
			
		||||
  <property name="set.test_audio">0</property>
 | 
			
		||||
 </producer>
 | 
			
		||||
 <playlist id="playlist0">
 | 
			
		||||
  <property name="kdenlive:audio_track">1</property>
 | 
			
		||||
<blank length="1000"/>
 | 
			
		||||
<entry producer="producer1"/>
 | 
			
		||||
 </playlist>
 | 
			
		||||
 <playlist id="playlist1">
 | 
			
		||||
  <property name="kdenlive:audio_track">1</property>
 | 
			
		||||
 </playlist>
 | 
			
		||||
 <tractor id="tractor0" in="00:00:00.000">
 | 
			
		||||
  <property name="kdenlive:audio_track">1</property>
 | 
			
		||||
  <property name="kdenlive:timeline_active">1</property>
 | 
			
		||||
  <track hide="video" producer="playlist0"/>
 | 
			
		||||
  <track hide="video" producer="playlist1"/>
 | 
			
		||||
 </tractor>
 | 
			
		||||
 <playlist id="playlist2">
 | 
			
		||||
  <property name="kdenlive:audio_track">1</property>
 | 
			
		||||
 </playlist>
 | 
			
		||||
 <playlist id="playlist3">
 | 
			
		||||
  <property name="kdenlive:audio_track">1</property>
 | 
			
		||||
 </playlist>
 | 
			
		||||
 <tractor id="tractor1" in="00:00:00.000">
 | 
			
		||||
  <property name="kdenlive:audio_track">1</property>
 | 
			
		||||
  <track hide="video" producer="playlist2"/>
 | 
			
		||||
  <track hide="video" producer="playlist3"/>
 | 
			
		||||
 </tractor>
 | 
			
		||||
 <playlist id="playlist4"/>
 | 
			
		||||
 <playlist id="playlist5"/>
 | 
			
		||||
 <tractor id="tractor2" in="00:00:00.000">
 | 
			
		||||
  <track hide="audio" producer="playlist4"/>
 | 
			
		||||
  <track hide="audio" producer="playlist5"/>
 | 
			
		||||
 </tractor>
 | 
			
		||||
 <playlist id="playlist6"/>
 | 
			
		||||
 <playlist id="playlist7"/>
 | 
			
		||||
 <tractor id="tractor3" in="00:00:00.000">
 | 
			
		||||
  <track hide="audio" producer="playlist6"/>
 | 
			
		||||
  <track hide="audio" producer="playlist7"/>
 | 
			
		||||
 </tractor>
 | 
			
		||||
 <tractor id="tractor4" in="00:00:00.000" out="00:08:20.000">
 | 
			
		||||
  <track producer="black_track"/>
 | 
			
		||||
  <track producer="tractor0"/>
 | 
			
		||||
  <track producer="tractor1"/>
 | 
			
		||||
  <track producer="tractor2"/>
 | 
			
		||||
  <track producer="tractor3"/>
 | 
			
		||||
 </tractor>
 | 
			
		||||
</mlt>
 | 
			
		||||
`;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const project = new Project(30);
 | 
			
		||||
 | 
			
		||||
project.addProducer("/home/kuba/Videos/5min.mp4");
 | 
			
		||||
project.addProducer("/home/kuba/Videos/5min.wav");
 | 
			
		||||
 | 
			
		||||
await $`echo ${await project.toXML()} > project-generated.kdenlive`;
 | 
			
		||||
							
								
								
									
										28
									
								
								producer.mjs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								producer.mjs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,28 @@
 | 
			
		||||
import {
 | 
			
		||||
  getStream,
 | 
			
		||||
  indexOf,
 | 
			
		||||
  renderAllProps,
 | 
			
		||||
  getStreamIndex,
 | 
			
		||||
  formatDuration,
 | 
			
		||||
} from "./util.mjs";
 | 
			
		||||
import { makeIDGen } from "./util.mjs";
 | 
			
		||||
 | 
			
		||||
const makeId = makeIDGen(1);
 | 
			
		||||
const producerIndexGen = makeIDGen(0);
 | 
			
		||||
 | 
			
		||||
export default class Producer {
 | 
			
		||||
  constructor(path) {
 | 
			
		||||
    this.path = path;
 | 
			
		||||
    this.index = producerIndexGen.next().value;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async toXML(fps) {
 | 
			
		||||
    const xml = (
 | 
			
		||||
      await $`melt ${
 | 
			
		||||
        this.path
 | 
			
		||||
      } -consumer xml ${`frame_rate_num=${fps}`} | htmlq producer`
 | 
			
		||||
    ).stdout;
 | 
			
		||||
 | 
			
		||||
    return xml.replace("producer0", `producer${this.index}`);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										83
									
								
								util.mjs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								util.mjs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,83 @@
 | 
			
		||||
export function indexOf(array, predicate) {
 | 
			
		||||
  for (let i in array) {
 | 
			
		||||
    if (predicate(array[i])) {
 | 
			
		||||
      return i;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export async function renderProperty(
 | 
			
		||||
  name,
 | 
			
		||||
  fn,
 | 
			
		||||
  metadata,
 | 
			
		||||
  project_settings,
 | 
			
		||||
  prefix = ""
 | 
			
		||||
) {
 | 
			
		||||
  let value;
 | 
			
		||||
  if (typeof fn === "string") {
 | 
			
		||||
    value = fn;
 | 
			
		||||
  } else {
 | 
			
		||||
    value = await fn(metadata, project_settings);
 | 
			
		||||
  }
 | 
			
		||||
  if (value === null) {
 | 
			
		||||
    return "";
 | 
			
		||||
  } else if (value && value.$replace) {
 | 
			
		||||
    return value.$replace;
 | 
			
		||||
  } else {
 | 
			
		||||
    return `<property name="${prefix}${name}">${value}</property>`;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export async function renderAllProps(
 | 
			
		||||
  props,
 | 
			
		||||
  metadata,
 | 
			
		||||
  project_settings,
 | 
			
		||||
  prefix = ""
 | 
			
		||||
) {
 | 
			
		||||
  return (
 | 
			
		||||
    await Promise.all(
 | 
			
		||||
      Object.entries(props).map(([name, fn]) =>
 | 
			
		||||
        renderProperty(name, fn, metadata, project_settings, prefix)
 | 
			
		||||
      )
 | 
			
		||||
    )
 | 
			
		||||
  ).join("\n");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function getStream(tracks, type) {
 | 
			
		||||
  return tracks.find((track) => track["@type"] == type);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const HOUR = 60 * 60;
 | 
			
		||||
const MINUTE = 60;
 | 
			
		||||
 | 
			
		||||
export function formatDuration(float_s) {
 | 
			
		||||
  const hours = Math.floor(float_s / HOUR);
 | 
			
		||||
  float_s = float_s - hours * HOUR;
 | 
			
		||||
  const minutes = Math.floor(float_s / MINUTE);
 | 
			
		||||
  const seconds = float_s - minutes * MINUTE;
 | 
			
		||||
  return `${twoDigits(hours)}:${twoDigits(minutes)}:${twoDigits(seconds, 5)}`;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function twoDigits(number, decimal = 0) {
 | 
			
		||||
  let [int, dec] = number.toFixed(decimal).split(".");
 | 
			
		||||
  dec = dec || "";
 | 
			
		||||
  if (dec == "") {
 | 
			
		||||
    return int.padStart(2, "0");
 | 
			
		||||
  } else {
 | 
			
		||||
    return int.padStart(2, "0") + "." + dec;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function getStreamIndex(trackarray, type) {
 | 
			
		||||
  const array_index = indexOf(trackarray, (e) => e["@type"] == type);
 | 
			
		||||
  return array_index == -1 ? -1 : trackarray[array_index].StreamOrder || 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function* makeIDGen(first = 1) {
 | 
			
		||||
  let i = first;
 | 
			
		||||
  while (true) {
 | 
			
		||||
    yield i;
 | 
			
		||||
    i++;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user