giovani-rubim

Video cut script

Apr 17th, 2021 (edited)
428
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. const crypto = require('crypto');
  2. const fs = require('fs');
  3. const path = require('path');
  4. const {execSync} = require('child_process');
  5.  
  6. const shortHash = (str) => crypto.createHash('sha256').update(str).digest('hex').substr(0, 16);
  7. const run = (cmd, output) => {
  8.     if (fs.existsSync(output)) {
  9.         return;
  10.     }
  11.     cmd = `ffmpeg ${cmd.trim().replace(/\s*\n\s*/g, ' ')} ${output}`;
  12.     console.log(cmd);
  13.     execSync(cmd, {'stdio': 'pipe'});
  14. };
  15.  
  16. const configPathname = path.join(__dirname, 'config.json');
  17. const defaultConfig = {
  18.     source: '',
  19.     destination: '',
  20.     format: '',
  21.     ranges: [
  22.         '00:01:00.000 --> 00:02:00.000'
  23.     ],
  24. };
  25.  
  26. const cacheSrc = path.join(__dirname, 'cache');
  27. if (!fs.existsSync(cacheSrc)) {
  28.     fs.mkdirSync(cacheSrc);
  29. }
  30.  
  31. const loadConfig = () => {
  32.     if (!fs.existsSync(configPathname)) {
  33.         const json = JSON.stringify(defaultConfig, 'null', '\t');
  34.         fs.writeFileSync(configPathname, json);
  35.         process.exit(0);
  36.     }
  37.     const json = fs.readFileSync(configPathname)
  38.         .toString()
  39.         .replace(/("([^\\"]|\\.)*")|(\/\/[^\n]*)|\/\*.*?\*\//g, '$1');
  40.     return JSON.parse(json);
  41. };
  42.  
  43. const {
  44.     source,
  45.     destination,
  46.     format,
  47.     ranges
  48. } = loadConfig();
  49.  
  50. const parseTime = (time) => {
  51.     let scales = [1, 60, 60*60];
  52.     return time
  53.         .replace(/,/g, '.')
  54.         .split(':')
  55.         .map(Number)
  56.         .reverse()
  57.         .map((x,i) => x*scales[i])
  58.         .reduce((a,b) => a+b)*1000;
  59. };
  60.  
  61. const stringifyTime = (time) => {
  62.     let scales = [60000, 60, 100];
  63.     let pads = [5, 2, 2];
  64.     return scales
  65.     .map(s => {
  66.         let res = time%s;
  67.         time = Math.round((time - res)/s);
  68.         return res;
  69.     })
  70.     .map((x,i) => (x+'').padStart(pads[i], '0'))
  71.         .reverse()
  72.         .join(':')
  73.         .replace(/(\d{3})$/, '.$1');
  74. };
  75.  
  76. const parseRange = (range) => range
  77.     .replace(/\s/g, '')
  78.     .split('-->')
  79.     .map(parseTime);
  80.  
  81. const hashRange = (range) => range
  82.     .map(x => x.toString().padStart(8, '0'))
  83.     .join('');
  84.  
  85. const targets = ranges
  86.     .map((item) => hashRange(parseRange(item)));
  87.  
  88. const checksum = shortHash(targets.join('-'));
  89. const mergedPathname = path.join(cacheSrc, `merged-${checksum}.${format}`);
  90. const fastPathname = path.join(cacheSrc, `fast-${checksum}.${format}`);
  91.  
  92. const parseHash = (hash) => hash
  93.     .match(/(\d{8})(\d{8})/)
  94.     .slice(1)
  95.     .map((x) => parseInt(x));
  96.  
  97. const hashToCutName = (hash) => `${hash}.${format}`;
  98. const isCutName = (name) => /^\d{16}\.\w+$/.test(name);
  99. const cutNameToHash = (name) => name.replace(/^(\d+).*$/, '$1');
  100.  
  101. const exist = fs
  102.     .readdirSync(destination)
  103.     .filter(isCutName)
  104.     .map(cutNameToHash);
  105.  
  106. const hashToPathname = (hash) => path.join(cacheSrc, hashToCutName(hash));
  107.  
  108. // exist.forEach((hash) => {
  109. //  if (targets.includes(hash)) {
  110. //      return
  111. //  }
  112. //  console.log(`deleting ${hash}`);
  113. //  fs.unlinkSync(hashToPathname(hash));
  114. // });
  115.  
  116. targets.forEach((hash) => {
  117.     let range = parseHash(hash);
  118.     let [a, b] = range;
  119.     let start = stringifyTime(a);
  120.     let duration = stringifyTime(b - a);
  121.     let output = hashToPathname(hash);
  122.     run(`
  123.         -ss ${start}
  124.         -t ${duration}
  125.         -i ${source}
  126.         -vf scale=640:272
  127.     `, output);
  128. });
  129.  
  130. const remove = (pathname) => {
  131.     if (fs.existsSync(pathname)) {
  132.         fs.unlinkSync(pathname);
  133.     }
  134. };
  135.  
  136. fs.writeFileSync('./merged.txt', targets.map((hash) => 'file ' + hashToPathname(hash)).join('\n') + '\n');
  137.  
  138. run(`
  139.     -f concat
  140.     -safe 0
  141.     -i ./merged.txt
  142.     -c copy
  143.     -an
  144. `, mergedPathname);
  145.  
  146. run(`
  147.     -itsscale 0.5
  148.     -i ${mergedPathname}
  149.     -c copy
  150. `, fastPathname);
  151.  
RAW Paste Data