file.js 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. const fs = require('fs');
  2. const path = require('path');
  3. const paths = require('../data/paths.json'); // get our paths
  4. const settings = require('../data/settings.json'); // get the settings
  5. const { log, toTitleCase } = require('./utils');
  6. const db = require('./db');
  7. const handbrake = require('./handbrake');
  8. const tvdb = require('./tvdb');
  9. const dirs = Object.keys(paths); // get a list of the paths from the keys
  10. const defaults = settings.defaults || {}; // load the defaults
  11. const exists = async file => fs.existsSync(file);
  12. const stat = async file => fs.statSync(file);
  13. const mkdir = async file => fs.mkdirSync(file, { recursive: true });
  14. const remove = async file => fs.unlinkSync(file);
  15. const fileSize = async file => {
  16. let stats = (await exists(file)) ? await stat(file) : false;
  17. let bytes = stats ? stats['size'] : 0;
  18. return bytes && bytes > 0 ? bytes / 1024 : 0;
  19. };
  20. const cleanFileName = async (output='', clean = {}) => {
  21. // clean the output name
  22. let cleanKeys = Object.keys(clean);
  23. for (let c in cleanKeys) {
  24. let key = cleanKeys[c];
  25. let val = clean[key];
  26. let re = new RegExp(key, 'gi');
  27. output = output.replace(re, val);
  28. }
  29. // baseline the output name
  30. output = output.trim(); // trim the whitespace
  31. output = output.replace(/(\d{4})$/, `($1)`); // if there is a date at the end wrap it
  32. return output;
  33. };
  34. const process = async file => {
  35. let database,
  36. options,
  37. type = 'movie',
  38. preset = 'Fast 1080p30',
  39. clean = {
  40. '.': ' ',
  41. },
  42. ext = 'm4v',
  43. filename = path.parse(file).name,
  44. input = file,
  45. output = filename,
  46. titlecase = false,
  47. folder = false,
  48. genre = false,
  49. destination = '';
  50. // determine the presets and clean object
  51. for (let i = 0, l = dirs.length; i < l; i++) {
  52. let dir = dirs[i]; // pointer to the dir
  53. if (file && dir && file.indexOf(dir) > -1) {
  54. // is this in this path?
  55. options = Object.assign({}, defaults, paths[dir]); // baseline the options
  56. database = db.connect(dir, options); // init the db connection
  57. let found = db.find(database, file); // does it already exist?
  58. if (found && found.status && found.status === 'success') {
  59. // was it already processed?
  60. return false; // break this loop
  61. } else if (!found) {
  62. // was it found? .. nope
  63. log(` -> "${path.basename(file)}" [processing]`);
  64. db.set(database, {
  65. input: file,
  66. output: '',
  67. status: '',
  68. date: new Date(),
  69. }); // push onto the list an entry
  70. } else {
  71. log(` -> "${path.basename(file)}" [re-processing]`);
  72. db.set(database, file, {
  73. status: '',
  74. date: new Date(),
  75. }); // set the status to blank
  76. }
  77. type = options.type; // apply the specifics ...
  78. preset = options.preset; // apply the specifics ...
  79. clean = options.clean; // ...
  80. ext = options.ext; // ...
  81. titlecase = !!options.titlecase; // ...
  82. folder = !!options.folder; // ...
  83. genre = !!options.genre; // ...
  84. destination = options.destination; // ...
  85. break; // break the loop
  86. }
  87. }
  88. // clean the output name
  89. output = await cleanFileName(output,clean);
  90. // do we have a sub folder option?
  91. if (folder) {
  92. let match = output.match(/^(.*?)?\./gm); // get the name for the file before the first .
  93. folder = match && match.length > 0 ? match[0].slice(0, -1) : false; // get just the stuff before the dot ... or false
  94. }
  95. // do we want to use the genre for a parent folder?
  96. if (genre) {
  97. folder = await tvdb.genre(output, type); // lookup the genre name
  98. }
  99. // do we have title case enabled?
  100. if (options.titlecase) output = toTitleCase(output); // titlecase that string
  101. // baseline the target
  102. let target = destination + (folder ? '/' + folder : ''); // setup the target location
  103. output = target + '/' + output + '.' + ext; // update the new name
  104. // do we already have an existing output that matches?
  105. if (exists(output)) {
  106. let outputSize = await fileSize(output); //get output filesize
  107. if (outputSize > 100) {
  108. //make sure its bigger than 100k
  109. log(` -> "${path.basename(file)}" [skipping] (already processed)\n`);
  110. db.set(database, file, {
  111. output: output,
  112. status: 'success',
  113. date: new Date(),
  114. }); // update database with status
  115. return false;
  116. }
  117. }
  118. //process.stdout.write('\n'); // send a new line
  119. // update database with output name
  120. db.set(database, file, {
  121. output: output,
  122. });
  123. // create parent if required
  124. if (!exists(target)) {
  125. log(` -> "${path.basename(file)}" [creating parent directory] ("${target}")`);
  126. await mkdir(target);
  127. }
  128. // spawn handbrake
  129. if (!test) {
  130. try {
  131. await handbrake.process(input, output, preset);
  132. db.set(database, file, {
  133. status: 'success',
  134. date: new Date(),
  135. }); // update database with status
  136. } catch (err) {
  137. if (exists(output)) remove(output); // remove file on failure
  138. db.set(database, file, {
  139. status: 'failure',
  140. date: new Date(),
  141. }); // update database with status
  142. }
  143. }
  144. return true; // when complete return true
  145. };
  146. /*
  147. * cleanup removes from the db
  148. *
  149. * @async
  150. * @param Array dirs list of dirs.
  151. * @param String file file name to find/match.
  152. *
  153. * @return Boolean did we clean it up
  154. */
  155. const cleanup = async (dirs, file) => {
  156. let database, result;
  157. // loop the dirs for matches
  158. for (let i = 0, l = dirs.length; i < l; i++) {
  159. let dir = dirs[i]; // pointer to the dir
  160. // is this in this path?
  161. if (file && dir && file.indexOf(dir) > -1) {
  162. database = await db.connect(dir); // init the database connection
  163. result = await db.remove(database, file); // remove file form database
  164. break;
  165. }
  166. }
  167. return result;
  168. };
  169. module.exports = {
  170. cleanup,
  171. exists,
  172. fileSize,
  173. mkdir,
  174. process,
  175. stat
  176. };