From e44e2fe070484e06d384a31ef2699c3a2d5d474e Mon Sep 17 00:00:00 2001 From: RaindropsSys Date: Thu, 13 Jun 2024 15:46:03 +0200 Subject: GitHub migration --- src/PrisbeamAppDisplay.ts | 455 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 455 insertions(+) create mode 100755 src/PrisbeamAppDisplay.ts (limited to 'src/PrisbeamAppDisplay.ts') diff --git a/src/PrisbeamAppDisplay.ts b/src/PrisbeamAppDisplay.ts new file mode 100755 index 0000000..e3a9ee6 --- /dev/null +++ b/src/PrisbeamAppDisplay.ts @@ -0,0 +1,455 @@ +import {PrisbeamApp} from "./PrisbeamApp"; +import {PrisbeamImageType} from "libprisbeam"; +import * as fs from "node:fs"; +import {PrisbeamUtilities} from "./PrisbeamUtilities"; +import {shell} from "@electron/remote"; + +export class PrisbeamAppDisplay { + private instance: PrisbeamApp; + + constructor(app: PrisbeamApp) { + this.instance = app; + } + + runUserSort(a: any, b: any) { + if ((document.getElementById("sorting") as HTMLInputElement).value === "new") { + return new Date(b.first_seen_at).getTime() - new Date(a.first_seen_at).getTime(); + } else if ((document.getElementById("sorting") as HTMLInputElement).value === "id") { + return b.id - a.id; + } else if ((document.getElementById("sorting") as HTMLInputElement).value === "popular") { + return b.score - a.score; + } else if ((document.getElementById("sorting") as HTMLInputElement).value === "size") { + return b.size - a.size; + } else if ((document.getElementById("sorting") as HTMLInputElement).value === "duration") { + return b.duration - a.duration; + } else if ((document.getElementById("sorting") as HTMLInputElement).value === "resolution") { + return (b.height * b.width) - (a.height * a.width); + } else if ((document.getElementById("sorting") as HTMLInputElement).value === "relevant") { + return b.wilson_score - a.wilson_score; + } + } + + displayNoResults() { + document.getElementById("search-warning").style.display = ""; + document.getElementById("pages").style.display = "none"; + document.getElementById("search-warning").innerHTML = "There are no images matching your request. Make sure the selected rating, filters and search query are correct and try again."; + } + + getThumbnailHTML(i: any) { + if (i['representations']['thumb'].endsWith(".webm")) i['representations']['thumb'] = i['representations']['thumb'].substring(0, i['representations']['thumb'].length - 5) + ".gif"; + let thumbnail = this.instance.dataStore.database.frontend.getImageFile(i, PrisbeamImageType.ThumbnailURL); + + // noinspection CssUnknownTarget + return ` +
+ `; + } + + filterEnabledRatings(i: any) { + return (((document.getElementById("rating-safe") as HTMLInputElement).checked && i.tags.includes("safe")) || + ((document.getElementById("rating-suggestive") as HTMLInputElement).checked && i.tags.includes("suggestive")) || + ((document.getElementById("rating-questionable") as HTMLInputElement).checked && i.tags.includes("questionable")) || + ((document.getElementById("rating-explicit") as HTMLInputElement).checked && i.tags.includes("explicit")) || + ((document.getElementById("rating-grimdark") as HTMLInputElement).checked && i.tags.includes("grimdark"))) + } + + getPageNumbers() { + let _dataStore = this.instance.dataStore; + let items = _dataStore.currentViewItems; + let totalPages = Math.ceil(items.length / 50) + 1; + let pageNumbers = []; + + if (_dataStore.page - 4 > 0) { + pageNumbers.push(_dataStore.page - 4); + pageNumbers.push(_dataStore.page - 3); + pageNumbers.push(_dataStore.page - 2); + pageNumbers.push(_dataStore.page - 1); + } else if (_dataStore.page - 3 > 0) { + pageNumbers.push(_dataStore.page - 3); + pageNumbers.push(_dataStore.page - 2); + pageNumbers.push(_dataStore.page - 1); + } else if (_dataStore.page - 2 > 0) { + pageNumbers.push(_dataStore.page - 2); + pageNumbers.push(_dataStore.page - 1); + } else if (_dataStore.page - 1 > 0) { + pageNumbers.push(_dataStore.page - 1); + } + + pageNumbers.push(_dataStore.page); + let i = 0; + + while (pageNumbers.length < 9) { + if (_dataStore.page + i + 1 <= totalPages) { + pageNumbers.push(_dataStore.page + i + 1); + } else { + break; + } + + i++; + } + + return pageNumbers; + } + + getPageHTML(page: number) { + let length = (Math.ceil(this.instance.dataStore.currentViewItems.length / 50) + 1).toString().length; + return "" + ("0".repeat(length).substring(0, length - page.toString().length) + page.toString()) + ""; + } + + getPageListHTML(pageNumbers: number[]) { + let _dataStore = this.instance.dataStore; + let items = _dataStore.currentViewItems; + let totalPages = Math.ceil(items.length / 50) + 1; + + return ` +
+ « + +
+
+ ${totalPages > 5 ? pageNumbers.map(i => ` + ${this.getPageHTML(i)} + `).join("") : Array(totalPages).fill(null).map((_, i) => i + 1).map(i => ` + ${this.getPageHTML(i)} + `).join("")} +
+
+ + » +
+ `; + } + + updateDisplay() { + let _dataStore = this.instance.dataStore; + document.getElementById("search-warning").style.display = "none"; + + if (document.getElementById("search-error").style.display === "none") { + document.getElementById("pages").style.display = ""; + } else { + document.getElementById("pages").style.display = "none"; + } + + let items = _dataStore.currentViewItems = [..._dataStore.currentView] + .sort(this.runUserSort) + .filter(this.filterEnabledRatings) + .map((i) => this.getThumbnailHTML(i)); + + if (items.length === 0 && document.getElementById("search-error").style.display === "none") this.displayNoResults(); + + if ((document.getElementById("order") as HTMLInputElement).value === "up") { + items = items.reverse(); + } + + document.getElementById("images").innerHTML = items.splice((_dataStore.page - 1) * 50, 50).join(""); + document.getElementById("pages").innerHTML = this.getPageListHTML(this.getPageNumbers()); + + this.updateTitle(); + this.instance.bootstrapTooltips(); + } + + updateTitle() { + let _dataStore = this.instance.dataStore; + let totalPages = Math.ceil(_dataStore.currentViewItems.length / 50) + 1; + + if (_dataStore.searching) { + if (_dataStore.loadedFromCache) { + document.title = "Searching for " + (document.getElementById("search") as HTMLInputElement).value.trim() + " (page " + _dataStore.page + "/" + totalPages + ") — Prisbeam (Cached)"; + } else { + document.title = "Searching for " + (document.getElementById("search") as HTMLInputElement).value.trim() + " (page " + _dataStore.page + "/" + totalPages + ") — Prisbeam"; + } + } else { + if (_dataStore.loadedFromCache) { + document.title = "All images (page " + _dataStore.page + "/" + totalPages + ") — Prisbeam (Cached)"; + } else { + document.title = "All images (page " + _dataStore.page + "/" + totalPages + ") — Prisbeam"; + } + } + } + + setRating(rating: string) { + Array.from(document.querySelectorAll(".tooltip.bs-tooltip-auto")).map(i => i.outerHTML = ""); + + (document.getElementById("rating-safe") as HTMLInputElement).checked = false; + (document.getElementById("rating-questionable") as HTMLInputElement).checked = false; + (document.getElementById("rating-suggestive") as HTMLInputElement).checked = false; + (document.getElementById("rating-explicit") as HTMLInputElement).checked = false; + (document.getElementById("rating-grimdark") as HTMLInputElement).checked = false; + + let code = 0; + if (rating === "safe") code = 1; + if (rating === "questionable") code = 2; + if (rating === "suggestive") code = 3; + if (rating === "explicit") code = 4; + if (rating === "grimdark") code = 5; + + if (code >= 1) (document.getElementById("rating-safe") as HTMLInputElement).checked = true; + if (code >= 2) (document.getElementById("rating-questionable") as HTMLInputElement).checked = true; + if (code >= 3) (document.getElementById("rating-suggestive") as HTMLInputElement).checked = true; + if (code >= 4) (document.getElementById("rating-explicit") as HTMLInputElement).checked = true; + if (code >= 5) (document.getElementById("rating-grimdark") as HTMLInputElement).checked = true; + } + + highlightZone(id: string, state: boolean) { + if (state) { + document.getElementById("preview-zone-" + id).classList.add("hover"); + document.getElementById("preview-tag-zone-" + id).classList.add("hover"); + } else { + document.getElementById("preview-zone-" + id).classList.remove("hover"); + document.getElementById("preview-tag-zone-" + id).classList.remove("hover"); + } + } + + buildImageTitle() { + let _dataStore = this.instance.dataStore; + + return "Image #" + + (_dataStore.currentImage.source_id ?? _dataStore.currentImage.id) + + (_dataStore.currentImage.source_name + ? " (" + _dataStore.currentImage.source_name + ")" + : "") + + "" + + PrisbeamUtilities.getMimeBadge(_dataStore.currentImage.mime_type) + } + + displayImageSize() { + let _dataStore = this.instance.dataStore; + let size = 0; + let sizeExplanation = []; + + let file = _dataStore.database.frontend.getImageFile(_dataStore.currentImage, PrisbeamImageType.ViewFile); + let thumb = _dataStore.database.frontend.getImageFile(_dataStore.currentImage, PrisbeamImageType.ThumbnailFile); + + if (file) { + let cSize = fs.lstatSync(file).size; + size += cSize; + sizeExplanation.push("Image: " + PrisbeamUtilities.formatSize(cSize)); + } + + if (thumb) { + let cSize = fs.lstatSync(thumb).size; + size += cSize; + sizeExplanation.push("Thumbnail: " + PrisbeamUtilities.formatSize(cSize)); + } + + let cSize = JSON.stringify(_dataStore.currentImage).length; + size += cSize; + sizeExplanation.push("Metadata: " + PrisbeamUtilities.formatSize(cSize)); + + document.getElementById("preview-size").innerHTML = + "" + + PrisbeamUtilities.formatSize(size) + ""; + } + + categorySortingNumber(cat: string) { + if (cat === "error") return 0; + if (cat === "rating") return 1; + if (cat === "origin") return 2; + if (cat === "character") return 3; + if (cat === "oc") return 4; + if (cat === "species") return 5; + if (cat === "body-type") return 6; + if (cat === "content-official") return 7; + if (cat === "content-fanmade") return 8; + if (cat === "spoiler") return 9; + return 999; + } + + async getImpliedTags() { + let _dataStore = this.instance.dataStore; + + let categories = {}; + let implied = []; + let impliedNames = []; + + for (let id of _dataStore.currentImage.tag_ids) { + implied.push(...(await _dataStore.database.frontend.getImpliedTagIdsFromId(id)).filter(i => !_dataStore.currentImage.tag_ids.includes(i))); + impliedNames.push(...(await _dataStore.database.frontend.getImpliedTagNamesFromId(id)).filter(i => !_dataStore.currentImage.tags.includes(i))); + } + + for (let id of implied) { + categories[id] = (await _dataStore.database._sql("SELECT category FROM tags WHERE id=" + id))[0]['category']; + } + + implied = [...new Set(implied)]; + impliedNames = [...new Set(impliedNames)]; + + let impliedTags = impliedNames.map((i: string, j: number) => [i, implied[j], categories[implied[j]]]); + + return impliedTags.sort((a, b) => { + return a[0].localeCompare(b[0]); + }).sort((a, b) => { + return this.categorySortingNumber(a[2]) - this.categorySortingNumber(b[2]); + }); + } + + async getTags() { + let _dataStore = this.instance.dataStore; + let categories = {}; + + for (let id of _dataStore.currentImage.tag_ids) { + categories[id] = (await _dataStore.database._sql("SELECT category FROM tags WHERE id=" + id))[0]['category']; + } + + let tags = _dataStore.currentImage.tags.map((i: string, j: number) => [i, _dataStore.currentImage.tag_ids[j], categories[_dataStore.currentImage.tag_ids[j]]]); + + return tags.sort((a: string, b: string) => { + return a[0].localeCompare(b[0]); + }).sort((a: string, b: string) => { + return this.categorySortingNumber(a[2]) - this.categorySortingNumber(b[2]); + }); + } + + async displayTags() { + let tags = await this.getTags(); + let impliedTags = await this.getImpliedTags(); + + document.getElementById("preview-tags").innerHTML = tags.map((i: any[]) => + "" + + i[0] + "" + ).join("") + + impliedTags.map(i => + "" + + i[0] + "" + ).join(""); + } + + async initializeImageUI(id: string) { + let _dataStore = this.instance.dataStore; + + _dataStore.modal.show(); + _dataStore.currentImage = await _dataStore.database.frontend.getImage(id); + _dataStore.currentImageClasses = []; + document.getElementById("preview-zones").innerHTML = ""; + + document.getElementById("preview-parts-loader").style.display = ""; + document.getElementById("preview-parts-none").style.display = "none"; + document.getElementById("preview-parts-unsupported").style.display = "none"; + document.getElementById("preview-parts-list").style.display = "none"; + document.title = "Viewing image #" + (_dataStore.currentImage.source_id ?? id) + " — " + document.title; + document.getElementById("preview-title").innerHTML = this.buildImageTitle(); + document.getElementById("preview-date").innerHTML = "Uploaded " + PrisbeamUtilities.timeAgo(_dataStore.currentImage.created_at * 1000) + ""; + document.getElementById("preview-resolution").innerText = _dataStore.currentImage.width + " × " + _dataStore.currentImage.height; + document.getElementById("preview-source-cta").innerText = "View on " + _dataStore.currentImage.source_name ?? "Derpibooru"; + } + + getCreditDescriptors() { + let descriptors = { + artists: [], editors: [], generators: [], photographers: [], prompters: [], colorists: [] + }; + let finalDescriptors = []; + + for (let tag of this.instance.dataStore.currentImage.tags) { + if (tag.startsWith("artist:")) descriptors.artists.push(tag.substring(7)); + if (tag.startsWith("editor:")) descriptors.editors.push(tag.substring(7)); + if (tag.startsWith("generator:")) descriptors.generators.push(tag.substring(10)); + if (tag.startsWith("photographer:")) descriptors.photographers.push(tag.substring(13)); + if (tag.startsWith("prompter:")) descriptors.prompters.push(tag.substring(9)); + if (tag.startsWith("colorist:")) descriptors.colorists.push(tag.substring(9)); + } + + if (descriptors.artists.length > 0) finalDescriptors.push("artist" + (descriptors.artists.length > 1 ? "s" : "") + ": " + descriptors.artists.join("")); + if (descriptors.editors.length > 0) finalDescriptors.push("editor" + (descriptors.editors.length > 1 ? "s" : "") + ": " + descriptors.editors.join("")); + if (descriptors.generators.length > 0) finalDescriptors.push("generator" + (descriptors.generators.length > 1 ? "s" : "") + ": " + descriptors.generators.join("")); + if (descriptors.photographers.length > 0) finalDescriptors.push("photographer" + (descriptors.photographers.length > 1 ? "s" : "") + ": " + descriptors.photographers.join("")); + if (descriptors.prompters.length > 0) finalDescriptors.push("prompter" + (descriptors.prompters.length > 1 ? "s" : "") + ": " + descriptors.prompters.join("")); + if (descriptors.colorists.length > 0) finalDescriptors.push("colorist" + (descriptors.colorists.length > 1 ? "s" : "") + ": " + descriptors.colorists.join("")); + + if (finalDescriptors[0]) finalDescriptors[0] = finalDescriptors[0].substring(0, 1).toUpperCase() + finalDescriptors[0].substring(1); + + return finalDescriptors; + } + + displayCredits() { + let _dataStore = this.instance.dataStore; + let artist = "Anonymous artist"; + let finalDescriptors = this.getCreditDescriptors(); + if (finalDescriptors.length > 0) artist = finalDescriptors.join("; "); + + if (_dataStore.currentImage.tags.includes("official")) { + if (finalDescriptors.length > 0) { + artist = finalDescriptors.join("; ") + " (official content)"; + } else { + artist = "Official Hasbro content"; + } + } + + if (_dataStore.currentImage.tags.includes("artist needed")) { + artist = "Unknown artist"; + } + + document.getElementById("preview-artist").innerText = artist; + } + + displayScore() { + let _dataStore = this.instance.dataStore; + + if (_dataStore.currentImage.score !== 0) { + try { document.getElementById("preview-score").outerHTML = ""; } catch (e) {} + document.getElementById("preview-statistics").insertAdjacentHTML("afterbegin", `
`); + + if (_dataStore.currentImage.faves > 0) { + document.getElementById("preview-score").innerText = _dataStore.currentImage.faves + " favorites · " + _dataStore.currentImage.score + " points (" + _dataStore.currentImage.upvotes + " up, " + _dataStore.currentImage.downvotes + " down)"; + } else { + document.getElementById("preview-score").innerText = _dataStore.currentImage.score + " points (" + _dataStore.currentImage.upvotes + " up, " + _dataStore.currentImage.downvotes + " down)"; + } + } else { + try { document.getElementById("preview-score").outerHTML = ""; } catch (e) {} + } + } + + displayViewer() { + let _dataStore = this.instance.dataStore; + let url = _dataStore.database.frontend.getImageFile(_dataStore.currentImage, PrisbeamImageType.ViewURL) + + if (_dataStore.currentImage.mime_type.startsWith("video/")) { + document.getElementById("preview-content").innerHTML = ` + + `; + } else { + document.getElementById("preview-content").innerHTML = ` + + `; + } + } + + async openImage(id: string) { + await this.initializeImageUI(id); + this.displayImageSize(); + await this.displayTags(); + this.displayCredits(); + this.displayScore(); + this.displayViewer(); + this.instance.ai.displayClasses(id); + this.instance.bootstrapTooltips(); + } + + openImageOnSource() { + let image = this.instance.dataStore.currentImage; + shell.openExternal(image['source'].replace("%s", image['source_id'])); + } +} -- cgit