library.js 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. const fs = require('fs');
  2. const path = require('path');
  3. const mime = require("mime-types");
  4. const md5Stats = require('craftlabhttpserver/src/md5sum').stats;
  5. const md5String = require('craftlabhttpserver/src/md5sum.js').string;
  6. const FileTypeManager = require('./fileTypeManager.js');
  7. const { ACCESS_TO, AccessModel } = require("../model/access.js");
  8. const MediaFileModel = require("../model/mediaItem.js").MediaFileModel;
  9. const MediaFileMetaModel = require("../model/mediaItemMeta.js").MediaFileMetaModel;
  10. const MediaFileTagModel = require("../model/mediaItemTag.js").MediaFileTagModel;
  11. const MANAGED_FILES = [ /* ".cr2" */ ]; // TODO
  12. const BUFFER_MAXSIZE = 100;
  13. function File(fullPath, name) {
  14. this.name = name;
  15. this.path = fullPath;
  16. this.fixedSum = null;
  17. this.checksum = null;
  18. this.mimeType = null;
  19. this.isMedia = null;
  20. this.meta = {};
  21. this.dbItem = null;
  22. this.tags = [];
  23. }
  24. File.prototype.getIsMedia = function() {
  25. if (this.isMedia !== null)
  26. return this.isMedia;
  27. if (!this.mimeType)
  28. this.mimeType = mime.lookup(this.name) || "";
  29. this.isMedia = this.mimeType.startsWith("image/") || /*this.mimeType.startsWith("video/") || */!!MANAGED_FILES.find(i => this.name.toLowerCase().endsWith(i));
  30. return this.isMedia;
  31. }
  32. File.prototype.computeChecksum = async function() {
  33. if (!this.checksum && this.getIsMedia())
  34. this.checksum = await md5Stats(this.path);
  35. if (!this.fixedSum)
  36. this.fixedSum = this.checksum;
  37. return this.checksum;
  38. }
  39. File.prototype.enrich = async function() {
  40. this.mimeType = mime.lookup(this.name) || "";
  41. const lowerName = this.name.toLowerCase();
  42. this.meta = null;
  43. await this.computeChecksum();
  44. if (this.getIsMedia()) {
  45. this.meta = await FileTypeManager.createMeta(this);
  46. this.tags = this.meta.tags || [];
  47. this.meta.tags = undefined;
  48. }
  49. }
  50. File.prototype.saveDb = async function(db, libraryHash) {
  51. if (this.dbItem) {
  52. this.fixedSum = this.dbItem.fixedSum;
  53. await db.remove(MediaFileModel, { path: this.dbItem.path, fixedSum: this.dbItem.fixedSum });
  54. await db.remove(MediaFileMetaModel, { md5sum: this.dbItem.fixedSum, fromFile: true });
  55. await db.remove(MediaFileTagModel, { md5sum: this.dbItem.fixedSum, fromMeta: true });
  56. }
  57. let entity = new MediaFileModel();
  58. let _this = this;
  59. entity.path = this.path;
  60. entity.md5sum = await this.computeChecksum();
  61. entity.fixedSum = this.fixedSum;
  62. entity.date = this.meta.dateTime || new Date(fs.statSync(this.path)?.birthtimeMs || Date.now());
  63. await db.insertOne(entity);
  64. this.meta.photochamberImport = new Date();
  65. this.meta.libraryPath = libraryHash;
  66. let metaEntities = Object.keys(this.meta).filter(i => i !== 'tags').map(i => new MediaFileMetaModel(_this.fixedSum, i, _this.meta[i], true));
  67. await db.insertMultipleSameTable(metaEntities);
  68. if (this.tags.length) {
  69. let tagsEntities = this.tags.map(i => new MediaFileTagModel(_this.fixedSum, i, true));
  70. await db.insertMultipleSameTable(tagsEntities);
  71. }
  72. this.dbItem = null;
  73. }
  74. File.prototype.createThumbnail = async function(w, h, quality) {
  75. return await FileTypeManager.createThumbnail(this, w, h, quality);
  76. }
  77. async function enrichAll(lib) {
  78. for (let i =0; i < lib.buff.length; i += 5)
  79. await Promise.allSettled(lib.buff.slice(i, i+5).map(i => i.enrich()));
  80. }
  81. async function Library_doUpdate(dbHelper, lib) {
  82. lib.buff = lib.buff.filter(i => !!i.checksum);
  83. if (lib.buff.length === 0)
  84. return;
  85. const dbItems = (await dbHelper.fetchRaw(["path", "md5sum", "fixedSum"], MediaFileModel.prototype.getTableName.call(null), { "path": lib.buff.map(i => i.path) }));
  86. lib.buff.forEach(i => i.dbItem = dbItems.find(x => x.path == i.path));
  87. lib.buff = lib.buff.filter(i => !i.dbItem || i.dbItem.md5sum != i.checksum);
  88. await enrichAll(lib);
  89. (await Promise.allSettled(lib.buff.map(i => i.saveDb(dbHelper, lib.dbHash)))).forEach(x => { if (x.status === 'rejected') { console.log(`Cannot update item: `, x.reason); }});
  90. lib.foundMedias = lib.foundMedias.concat(lib.buff);
  91. lib.buff.length && console.log(`Updated ${lib.buff.length} media items`);
  92. lib.buff = [];
  93. }
  94. async function Library_depthupdate(dbHelper, library, dir) {
  95. for (let o of fs.readdirSync(dir, { withFileTypes: true })) {
  96. const fullPath = path.join(dir, o.name);
  97. if (o.isDirectory())
  98. await Library_depthupdate(dbHelper, library, fullPath);
  99. else if (o.isFile()) {
  100. let f = new File(fullPath, o.name);
  101. if (!f.getIsMedia())
  102. continue;
  103. await f.computeChecksum();
  104. library.buff.push(f);
  105. if (library.buff.length >= BUFFER_MAXSIZE)
  106. await Library_doUpdate(dbHelper, library);
  107. }
  108. }
  109. }
  110. function Library_isValid(_this) {
  111. if (fs.existsSync(_this.path))
  112. return true;
  113. console.error(`Library folder not found (${_this.path})`);
  114. }
  115. function Library(p) {
  116. this.path = path.normalize(p);
  117. this.dbHash = md5String(this.path);
  118. }
  119. Library.prototype.updateLibrary = async function(dbHelper) {
  120. const startTime = Date.now();
  121. console.log(`Starting update of library ${this.path}`);
  122. this.foundMedias = [];
  123. this.buff = [];
  124. this.errors = {};
  125. try {
  126. if (Library_isValid(this))
  127. await Library_depthupdate(dbHelper, this, this.path);
  128. await Library_doUpdate(dbHelper, this)
  129. } catch (err) {
  130. console.err(`Cannot update Library ${this.path}:`, err);
  131. }
  132. let timeDiff = (Date.now() - startTime) / 1000;
  133. const timeDiffMin = Math.floor(timeDiff / 60);
  134. timeDiff -= timeDiffMin*60;
  135. console.log(`Done updating library ${this.path}. Managed ${this.foundMedias.length} new media files. in ${timeDiffMin}m ${timeDiff}s`);
  136. }
  137. Library.prototype.findMedia = async function(p) {
  138. if (!p.startsWith(this.path))
  139. return null;
  140. let result = new File(p, path.basename(p));
  141. await result.enrich();
  142. return result;
  143. }
  144. module.exports.Library = Library;