1
0

medias.js 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. class Media {
  2. constructor(data) {
  3. this.date = new Date(data.date);
  4. this.md5sum = data.md5sum;
  5. this.path = data.path;
  6. this.fileName = data.fileName;
  7. this.meta = data.meta || {};
  8. this.fixedTags = [];
  9. this.tags = [];
  10. this.writeAccess = data.accessType === 2;
  11. this.thumbnail = `/api/media/thumbnail/${data.md5sum}.jpg`;
  12. this.original = `/api/media/original/${data.md5sum}`;
  13. this.ui = null;
  14. this.setTags(data.fixedTags || [], data.tags || []);
  15. for (let i in this.meta) {
  16. if (this.meta[i].type === 'date')
  17. this.meta[i].value = new Date(parseInt(this.meta[i].value));
  18. else if (this.meta[i].type === 'number' || this.meta[i].type === 'octet')
  19. this.meta[i].value = parseInt(this.meta[i].value);
  20. else if (this.meta[i].type === 'string')
  21. this.meta[i].value = '' + this.meta[i].value;
  22. }
  23. }
  24. resize(maxWidth, maxHeight) {
  25. let ratio = Math.min(1, Math.max(
  26. maxWidth / (this.meta?.width || maxWidth),
  27. maxHeight / (this.meta?.height || maxHeight)));
  28. return {
  29. width: Math.floor(this.meta.width *ratio),
  30. height: Math.floor(this.meta.height *ratio),
  31. };
  32. }
  33. setTags(fixedTags, tags) {
  34. this.tags = tags.reduce((acc, tag) => { acc.add(tag.replaceAll(/\/\/+/gi, '/')); return acc; }, new Set());
  35. this.fixedTags = fixedTags.reduce((acc, tag) => { acc.add(tag.replaceAll(/\/\/+/gi, '/')); return acc; }, new Set());
  36. }
  37. allTags() {
  38. return Array.from(new Set([...this.fixedTags, ...this.tags])).sort();
  39. }
  40. }
  41. function tryLoadMedia(md5sum) {
  42. return new Promise((ok, ko) => {
  43. $.get("/api/media/" +md5sum, data => {
  44. let item = new Media(data);
  45. MediaStorage.Instance.pushAll([item], true);
  46. ok(item);
  47. }).fail(err => {
  48. console.error("Trying to get media with md5sum " +md5sum +" failed:", err.responseText);
  49. ok(null);
  50. });
  51. });
  52. }
  53. class MediaStorage extends EventTarget
  54. {
  55. constructor() {
  56. super();
  57. this.allMeta = {};
  58. this.allMetaTypes = {};
  59. this.allTags = new Set();
  60. this.medias = [];
  61. this.oldest = null;
  62. this.newest = null;
  63. }
  64. #pushMeta(metaKey, metaVal) {
  65. if (metaKey === 'dateTime')
  66. return;
  67. if (!this.allMeta[metaKey])
  68. this.allMeta[metaKey] = new Set();
  69. this.allMeta[metaKey].add(metaVal.value);
  70. if (!this.allMetaTypes[metaKey])
  71. this.allMetaTypes[metaKey] = { type: metaVal.type, canBeEmpty: !!this.medias.length, canWrite: metaVal.canWrite };
  72. }
  73. #pushTag(tag, first) {
  74. while (tag.length && tag.endsWith('/'))
  75. tag = tag.substr(0, tag.length -1);
  76. this.allTags.add(tag);
  77. let index = tag.lastIndexOf('/');
  78. if (index >= 0)
  79. this.#pushTag(tag.substr(0, index));
  80. }
  81. #pushUnique(media) {
  82. for (let i of media.tags)
  83. this.#pushTag(i, true);
  84. for (let i of media.fixedTags)
  85. this.#pushTag(i, true);
  86. for (let key in media.meta)
  87. this.#pushMeta(key, media.meta[key]);
  88. for (let key in this.allMetaTypes)
  89. if (!media.meta[key])
  90. this.allMetaTypes[key].canBeEmpty = true;
  91. this.medias.push(media);
  92. media.md5sum === 'b1bc7614d67333cacb60af149ed5ee1f' && console.log(this);
  93. }
  94. pushAll(arr, partialLoad) {
  95. let result = [];
  96. let reorder = false;
  97. for (let i of arr) {
  98. if (partialLoad !== true) {
  99. this.oldest = !this.oldest || this.oldest.date.getTime() > i.date.getTime() ? i : this.oldest;
  100. this.newest = !this.newest || this.newest.date.getTime() < i.date.getTime() ? i : this.newest;
  101. }
  102. if (this.medias.length && this.medias[this.medias.length -1].date.getTime() < i.date.getTime())
  103. reorder = true;
  104. if (this.medias.find(x => x.md5sum === i.md5sum))
  105. continue;
  106. this.#pushUnique(i);
  107. result.push(i);
  108. }
  109. for (let i of result)
  110. this.dispatchEvent(new CustomEvent("newMedia", { detail: i }));
  111. if (reorder) {
  112. this.medias.sort((a, b) => b.date.getTime() - a.date.getTime());
  113. this.dispatchEvent(new CustomEvent("rebuildMedia"));
  114. }
  115. }
  116. onFilterUpdated() {
  117. this.dispatchEvent(new CustomEvent("rebuildMedia"));
  118. }
  119. getMediaIndex(media) {
  120. return this.medias.indexOf(media);
  121. }
  122. nextMedia(current) {
  123. return this.medias[this.getMediaIndex(current) +1];
  124. }
  125. previousMedia(current) {
  126. return this.medias[this.getMediaIndex(current) -1];
  127. }
  128. getMediaBetweenIndexes(a, b) {
  129. if (a > b)
  130. return this.getMediaBetweenIndexes(b, a);
  131. return this.medias.slice(a, b +1);
  132. }
  133. getMediaBetween(a, b) {
  134. let aIndex = this.medias.indexOf(a);
  135. let bIndex = this.medias.indexOf(b);
  136. if (aIndex < 0 || bIndex < 0 || aIndex === bIndex)
  137. return [];
  138. return this.getMediaBetweenIndexes(aIndex, bIndex);
  139. }
  140. getMediaLocal(md5sum) {
  141. return this.medias.find(x => x.md5sum === md5sum);
  142. }
  143. async getMedia(md5sum) {
  144. let media = this.medias.find(x => x.md5sum === md5sum);
  145. if (media)
  146. return media;
  147. return await tryLoadMedia(md5sum);
  148. }
  149. setMetaValue(md5sum, key, value) {
  150. return LoadingTasks.push(() => {
  151. return new Promise(ok => {
  152. let media = this.medias.find(x => x.md5sum === md5sum);
  153. if (!media || !media.writeAccess)
  154. return ok(false);
  155. $.ajax({
  156. url: `/api/media/${encodeURIComponent(md5sum)}/meta/${encodeURIComponent(key)}`,
  157. type: "PATCH",
  158. data: { value },
  159. success: (media) => {
  160. let meta = media.meta[key] || { type: 'string', value: value, canWrite: true };
  161. meta.value = value;
  162. this.#pushMeta(key, meta);
  163. media.meta[key] = meta;
  164. window.ReloadFilters(this);
  165. ok(true);
  166. },
  167. error: err => ok(false),
  168. });
  169. });
  170. });
  171. }
  172. removeTag(md5sum, tagName) {
  173. return LoadingTasks.push(() => {
  174. return new Promise(ok => {
  175. let media = this.medias.find(x => x.md5sum === md5sum);
  176. if (!media || !media.writeAccess)
  177. return ok(false);
  178. $.ajax({
  179. url: `/api/media/${encodeURIComponent(md5sum)}/tag/${encodeURIComponent(tagName)}`,
  180. type: "DELETE",
  181. success: data => {
  182. media.setTags(data.fixedTags, data.tags);
  183. for (let i of data.tags)
  184. this.#pushTag(i, true);
  185. for (let i of data.fixedTags)
  186. this.#pushTag(i, true);
  187. ok(true);
  188. },
  189. error: err => ok(false),
  190. });
  191. });
  192. });
  193. }
  194. addTag(md5sum, tagName) {
  195. return LoadingTasks.push(() => {
  196. return new Promise(ok => {
  197. let media = this.medias.find(x => x.md5sum === md5sum);
  198. if (!media || !media.writeAccess)
  199. return ok(false);
  200. $.ajax({
  201. url: `/api/media/${encodeURIComponent(md5sum)}/tag`,
  202. type: "PUT",
  203. data: { tag: tagName },
  204. success: data => {
  205. media.setTags(data.fixedTags, data.tags);
  206. for (let i of data.tags)
  207. this.#pushTag(i, true);
  208. for (let i of data.fixedTags)
  209. this.#pushTag(i, true);
  210. ok(true);
  211. },
  212. error: err => ok(false),
  213. });
  214. });
  215. });
  216. }
  217. }
  218. MediaStorage.Instance = new MediaStorage();