| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423 |
- class Media {
- #date = 0;
- md5sum = "";
- fixedSum = "";
- path = "";
- fileName = "";
- meta = {};
- fixedTags = [];
- version = 0;
- tags = [];
- writeAccess = false;
- thumbnail = "";
- original = "";
- ui = null;
- constructor(data) {
- this.#date = data.date;
- this.md5sum = data.md5sum;
- this.fixedSum = data.fixedSum;
- this.path = data.path;
- this.fileName = data.fileName;
- this.meta = data.meta || {};
- this.fixedTags = [];
- this.version = data.version;
- this.tags = [];
- this.writeAccess = data.writeAccess !== undefined ? data.writeAccess : (data.accessType === 2);
- this.thumbnail = `/api/media/thumbnail/${data.fixedSum}.jpg`;
- this.original = `/api/media/original/${data.fixedSum}`;
- this.ui = null;
- this.setTags(data.fixedTags || [], data.tags || []);
- for (let i in this.meta) {
- if (this.meta[i].type === 'date')
- this.meta[i].value = new Date(parseInt(this.meta[i].value));
- else if (this.meta[i].type === 'number' || this.meta[i].type === 'octet')
- this.meta[i].value = parseInt(this.meta[i].value);
- else if (this.meta[i].type === 'string')
- this.meta[i].value = '' + this.meta[i].value;
- }
- }
- getDateTs() {
- return this.#date;
- }
- getDate() {
- return new Date(this.#date);
- }
- resize(maxWidth, maxHeight) {
- let ratio = Math.min(1, Math.max(
- maxWidth / (this.meta?.width?.value || maxWidth),
- maxHeight / (this.meta?.height?.value || maxHeight)));
- let result = {
- width: Math.floor(this.meta.width?.value *ratio),
- height: Math.floor(this.meta.height?.value *ratio),
- };
- if (isNaN(result.width) || isNaN(result.height) || !result.height || !result.width) {
- console.error("Failed to resize image ", this);
- return null;
- }
- return result;
- }
- setTags(fixedTags, tags) {
- this.tags = tags.reduce((acc, tag) => { acc.add(tag.replaceAll(/\/\/+/gi, '/')); return acc; }, new Set());
- this.fixedTags = fixedTags.reduce((acc, tag) => { acc.add(tag.replaceAll(/\/\/+/gi, '/')); return acc; }, new Set());
- }
- allTags() {
- return Array.from(new Set([...this.fixedTags, ...this.tags])).sort();
- }
- }
- function tryLoadMedia(md5sum) {
- return new Promise((ok, ko) => {
- $.get("/api/media/" +md5sum, data => {
- let item = new Media(data);
- MediaStorage.Instance.pushAll([item], true);
- ok(item);
- }).fail(err => {
- console.error("Trying to get media with md5sum " +md5sum +" failed:", err.responseText);
- ok(null);
- });
- });
- }
- class MediaStorage extends EventTarget
- {
- allMeta = {};
- allMetaTypes = {};
- allTags = new Set();
- medias = [];
- oldest = null;
- newest = null;
- #dbVersion = 0;
- loadingVersion = 0;
- constructor() {
- super();
- this.#reset();
- }
- #reset() {
- this.allMeta = {};
- this.allMetaTypes = {};
- this.allTags = new Set();
- this.medias = [];
- this.oldest = null;
- this.newest = null;
- this.#dbVersion = 0;
- this.loadingVersion = 0;
- }
- async rebuildMetaList() {
- this.#reset();
- this.dispatchEvent(new CustomEvent("rebuildMedia"));
- window.chronology.reset();
- this.downloadMetaList();
- }
- update() {
- this.downloadMetaList(true);
- }
- async restoreMetaList() {
- if (this.isLoading())
- return;
- this.#reset();
- this.#isLoading = true;
- document.getElementById("pch-infiniteScrollLoading").classList.remove("hidden");
- let hasData = false;
- try {
- await LoadingTasks.push(async () => {
- let data = await window.indexedData.listMedias();
- this.pushAll(data.medias.map(x => new Media(x)));
- this.#updateDbVersion(data.version);
- this.#isLoading = false;
- hasData = !!(data.medias?.length);
- });
- } catch (err) {
- console.error(err);
- }
- this.downloadMetaList(hasData);
- }
- #doDownloadMetaList(isUpdate) {
- this.#isLoading = true;
- document.getElementById("pch-infiniteScrollLoading").classList.remove("hidden");
- LoadingTasks.push(() => {
- return new Promise(ok => {
- let chronology = window.chronology.isInitialized() ? "" : "&chronology"
- let oldest = isUpdate !== true ? (this.oldest?.getDateTs() || 0) : 0;
- let oldestArg = oldest ? `&from=${oldest}` : "";
- let requestCount = 300;
- $.get(`/api/media/list?count=${requestCount}${chronology}${oldestArg}&version=${this.#dbVersion}`, async data => {
- this.pushAll(data.data.map(i => new Media(i)));
- if (data.first || data.last)
- window.chronology.rebuildRange(data.first, data.last);
- if (data.maxVersion)
- this.loadingVersion = parseInt(data.maxVersion);
- if ((data.data?.length || 0) < requestCount) {
- this.#isLoading = false;
- this.#updateDbVersion(this.loadingVersion);
- window.ReloadFilters(MediaStorage.Instance);
- this.dispatchEvent(new CustomEvent("doneLoading"));
- document.getElementById("pch-infiniteScrollLoading").classList.add("hidden");
- } else {
- this.#doDownloadMetaList(false);
- }
- ok();
- });
- });
- });
- }
- downloadMetaList(isUpdate) {
- if (this.isLoading())
- return;
- this.#doDownloadMetaList(isUpdate);
- }
- getDbVersion() { return this.#dbVersion; }
- #updateDbVersion(version) {
- this.#dbVersion = Math.max(this.#dbVersion, version);
- }
- #pushMeta(metaKey, metaVal) {
- if (metaKey === 'dateTime')
- return;
- if (!this.allMeta[metaKey])
- this.allMeta[metaKey] = new Set();
- this.allMeta[metaKey].add(metaVal.value);
- if (!this.allMetaTypes[metaKey])
- this.allMetaTypes[metaKey] = { type: metaVal.type, canBeEmpty: !!this.medias.length, canWrite: metaVal.canWrite };
- }
- #pushTag(tag, first) {
- while (tag.length && tag.endsWith('/'))
- tag = tag.substr(0, tag.length -1);
- this.allTags.add(tag);
- let index = tag.lastIndexOf('/');
- if (index >= 0)
- this.#pushTag(tag.substr(0, index));
- }
- #pushUnique(media) {
- for (let i of media.tags)
- this.#pushTag(i, true);
- for (let i of media.fixedTags)
- this.#pushTag(i, true);
- for (let key in media.meta)
- this.#pushMeta(key, media.meta[key]);
- for (let key in this.allMetaTypes)
- if (!media.meta[key])
- this.allMetaTypes[key].canBeEmpty = true;
- this.medias.push(media);
- }
- #isLoading = false;
- isLoading() { return this.#isLoading; }
- pushAll(arr, partialLoad) {
- let reorder = false;
- let newItems = [];
- for (let i of arr) {
- this.loadingVersion = Math.max(this.loadingVersion, i.version);
- if (partialLoad !== true) {
- this.oldest = !this.oldest || this.oldest.getDateTs() > i.getDateTs() ? i : this.oldest;
- this.newest = !this.newest || this.newest.getDateTs() < i.getDateTs() ? i : this.newest;
- }
- if (this.medias.length && this.medias[this.medias.length -1].getDateTs() < i.getDateTs())
- reorder = true;
- let previous = this.medias.find(x => x.fixedSum === i.fixedSum);
- if (previous) {
- this.medias = this.medias.filter(x => x.fixedSum !== i.fixedSum);
- i.ui = previous.ui;
- } else {
- newItems.push(i);
- }
- this.#pushUnique(i);
- }
- for (let i of newItems)
- this.dispatchEvent(new CustomEvent("newMedia", { detail: i }));
- if (reorder) {
- this.medias.sort((a, b) => b.getDateTs() - a.getDateTs());
- this.dispatchEvent(new CustomEvent("rebuildMedia"));
- }
- }
- onFilterUpdated() {
- this.dispatchEvent(new CustomEvent("rebuildMedia"));
- }
- getMediaIndex(media) {
- return this.medias.indexOf(media);
- }
- nextMedia(current) {
- return this.medias[this.getMediaIndex(current) +1];
- }
- previousMedia(current) {
- return this.medias[this.getMediaIndex(current) -1];
- }
- getMediaBetweenIndexes(a, b) {
- if (a > b)
- return this.getMediaBetweenIndexes(b, a);
- return this.medias.slice(a, b +1);
- }
- getMediaBetween(a, b) {
- let aIndex = this.medias.indexOf(a);
- let bIndex = this.medias.indexOf(b);
- if (aIndex < 0 || bIndex < 0 || aIndex === bIndex)
- return [];
- return this.getMediaBetweenIndexes(aIndex, bIndex);
- }
- removeMedia(md5sums) {
- let medias = this.medias.filter(x => md5sums.indexOf(x.fixedSum) >= 0);
- this.medias = this.medias.filter(x => md5sums.indexOf(x.fixedSum) === -1);
- for (let i of medias) {
- i.ui && i.ui.root.remove();
- this.dispatchEvent(new CustomEvent("mediaRemoved", { detail: i }));
- }
- }
- getMediaLocal(md5sum) {
- if (!md5sum)
- return null;
- return this.medias.find(x => x.fixedSum === md5sum);
- }
- async remoteRemove(md5Sums) {
- for (let md5sum of md5Sums) {
- await new Promise(ok => {
- $.ajax({
- url: `/api/media/${encodeURIComponent(md5sum)}`,
- type: "DELETE",
- success: ok,
- error: err => { console.error(err); ok() },
- });
- });
- }
- await indexedData.removeItems(md5Sums);
- this.removeMedia(md5Sums);
- }
- async getMedia(md5sum) {
- if (!md5sum)
- return null;
- let media = this.medias.find(x => x.fixedSum === md5sum);
- if (media)
- return media;
- return await tryLoadMedia(md5sum);
- }
- setMetaValue(md5sum, key, value) {
- let md5arr = undefined;
- if (Array.isArray(md5sum) && md5sum.length === 1)
- md5sum = md5sum[0];
- if (Array.isArray(md5sum)) {
- md5arr = md5sum;
- md5sum = "list";
- }
- return LoadingTasks.push(() => {
- return new Promise(ok => {
- let mediaCount = (md5arr || [ md5sum ]).map(checksum => this.medias.find(x => x.fixedSum === checksum)).filter(x => x.writeAccess).length;
- if (mediaCount != (md5arr || [ md5sum ]).length)
- return ok(false);
- $.ajax({
- url: `/api/media/${encodeURIComponent(md5sum)}/meta/${encodeURIComponent(key)}`,
- type: "PATCH",
- data: { value: value, list: md5arr },
- success: allData => {
- allData.forEach(data => {
- this.medias = this.medias.filter(x => x.fixedSum !== data.fixedSum);
- this.#pushUnique(new Media(data));
- });
- window.ReloadFilters(this);
- ok(true);
- },
- error: err => ok(false),
- });
- });
- });
- }
- removeTag(md5sum, tagName) {
- let md5arr = undefined;
- if (Array.isArray(md5sum)) {
- md5arr = md5sum;
- md5sum = "list";
- }
- return LoadingTasks.push(() => {
- return new Promise(ok => {
- let mediaCount = (md5arr || [ md5sum ]).map(checksum => this.medias.find(x => x.fixedSum === checksum)).filter(x => x.writeAccess).length;
- if (mediaCount != (md5arr || [ md5sum ]).length)
- return ok(false);
- $.ajax({
- url: `/api/media/${encodeURIComponent(md5sum)}/tag/del/${encodeURIComponent(tagName)}`,
- type: "POST",
- data: { list: md5arr || [0] },
- success: allData => {
- allData.forEach(data => {
- let media = this.medias.find(x => x.fixedSum === data.fixedSum);
- media.setTags(data.fixedTags, data.tags);
- for (let i of data.tags)
- this.#pushTag(i, true);
- for (let i of data.fixedTags)
- this.#pushTag(i, true);
- });
- ok(true);
- },
- error: err => ok(false),
- });
- });
- });
- }
- addTag(md5sum, tagName) {
- let md5arr = undefined;
- if (Array.isArray(md5sum)) {
- md5arr = md5sum;
- md5sum = "list";
- }
- return LoadingTasks.push(() => {
- return new Promise(ok => {
- let mediaCount = (md5arr || [ md5sum ]).map(checksum => this.medias.find(x => x.fixedSum === checksum)).filter(x => x.writeAccess).length;
- if (mediaCount != (md5arr || [ md5sum ]).length)
- return ok(false);
- $.ajax({
- url: `/api/media/${encodeURIComponent(md5sum)}/tag`,
- type: "PUT",
- data: { tag: tagName, list: md5arr },
- success: allData => {
- allData.forEach(data => {
- let media = this.medias.find(x => x.fixedSum === data.fixedSum);
- media.setTags(data.fixedTags, data.tags);
- for (let i of data.tags)
- this.#pushTag(i, true);
- for (let i of data.fixedTags)
- this.#pushTag(i, true);
- });
- ok(true);
- },
- error: err => ok(false),
- });
- });
- });
- }
- }
- MediaStorage.Instance = new MediaStorage();
- setInterval(MediaStorage.Instance.update.bind(MediaStorage.Instance), 60000);
|