diff options
author | RaindropsSys <raindrops@equestria.dev> | 2024-06-13 15:46:03 +0200 |
---|---|---|
committer | RaindropsSys <raindrops@equestria.dev> | 2024-06-13 15:46:03 +0200 |
commit | e44e2fe070484e06d384a31ef2699c3a2d5d474e (patch) | |
tree | 2d5eb5d1b01646270d18cf1f2d94519966d6e7de /intimate/src | |
download | faunerie-e44e2fe070484e06d384a31ef2699c3a2d5d474e.tar.gz faunerie-e44e2fe070484e06d384a31ef2699c3a2d5d474e.tar.bz2 faunerie-e44e2fe070484e06d384a31ef2699c3a2d5d474e.zip |
GitHub migration
Diffstat (limited to 'intimate/src')
-rwxr-xr-x | intimate/src/actions.js | 17 | ||||
-rwxr-xr-x | intimate/src/display.js | 153 | ||||
-rwxr-xr-x | intimate/src/fetcher.js | 183 | ||||
-rwxr-xr-x | intimate/src/keyboard.js | 11 |
4 files changed, 364 insertions, 0 deletions
diff --git a/intimate/src/actions.js b/intimate/src/actions.js new file mode 100755 index 0000000..2590656 --- /dev/null +++ b/intimate/src/actions.js @@ -0,0 +1,17 @@ +async function displayPause() { + if (!window.displayIsReady) return; + window.paused = true; + + try { + await require('electron/renderer').ipcRenderer.invoke("pauseDialog", window.currentImage); + } catch (e) { + console.error(e); + } + + window.paused = false; +} + +async function skipImage() { + if (!window.displayIsReady) return; + await doLoadNext(); +} diff --git a/intimate/src/display.js b/intimate/src/display.js new file mode 100755 index 0000000..921cdee --- /dev/null +++ b/intimate/src/display.js @@ -0,0 +1,153 @@ +function initiateLoad() { + document.getElementById("corner-loader").style.opacity = "1"; +} + +function completeLoad() { + document.getElementById("corner-loader").style.opacity = "0"; +} + +window.isInDisplay = false; + +function loadSlot(url, isVideo) { + return new Promise((res) => { + let slot; + let video; + + if (document.getElementById("display-slot-a").style.opacity === "0") { + slot = document.getElementById("display-slot-a"); + video = document.getElementById("display-slot-a-video"); + } else { + slot = document.getElementById("display-slot-b"); + video = document.getElementById("display-slot-b-video"); + } + + setTimeout(() => { + if (isVideo) { + slot.style.backgroundImage = ""; + video.src = url; + video.style.display = ""; + video.oncanplaythrough = () => res(); + } else { + let img = document.createElement("img"); + img.src = url; + img.onload = () => { + slot.style.backgroundImage = `url("${url.replaceAll('"', '\\"')}")`; + video.src = ""; + video.style.display = "none"; + res(); + } + } + }, 500); + }); +} + +function switchSlot() { + let oldSlot; + let newSlot; + + if (document.getElementById("display-slot-a").style.opacity === "0") { + newSlot = document.getElementById("display-slot-a"); + oldSlot = document.getElementById("display-slot-b"); + } else { + oldSlot = document.getElementById("display-slot-a"); + newSlot = document.getElementById("display-slot-b"); + } + + oldSlot.style.opacity = "0"; + newSlot.style.opacity = "1"; +} + +function openDisplay() { + window.isInDisplay = true; + document.getElementById("display").classList.add("show"); +} + +function closeDisplay() { + window.isInDisplay = false; + document.getElementById("display").classList.remove("show"); + window.close(); +} + +window.displayQueue = []; +window.lastNextDuration = null; +window.paused = false; +window.nextImageTimeout = null; +window.nextImage = null; + +async function startSlideshow(loopVideos = true, refreshFunction = () => {}) { + window.currentImage = null; + window.nextImage = window.displayQueue.shift(); + window.updating = false; + + window.doLoadNext = async () => { + if (window.paused) { + scheduleNext(lastNextDuration); + return; + } + + await loadNextImage(); + + if (window.displayQueue.length < 20) { + if (window.updating) return; + window.updating = true; + await refreshFunction(); + window.updating = false; + } + } + + function scheduleNext(nextDuration) { + window.lastNextDuration = nextDuration; + + window.nextImageTimeout = setTimeout(async () => { + await doLoadNext(); + }, nextDuration); + } + + window.loadNextImage = async () => { + clearTimeout(window.nextImageTimeout); + + switchSlot(); + openDisplay(); + await loadSlot(window.nextImage['view_url'], window.nextImage['mime_type'].startsWith("video/")); + window.currentImage = window.nextImage; + + await new Promise((res) => { + let s = setInterval(() => { + clearInterval(s); + window.nextImage = window.displayQueue.shift(); + + if (!!window.nextImage) { + clearInterval(s); + res(); + } + }); + }) + + let video; + + if (document.getElementById("display-slot-a").style.opacity !== "0") { + video = document.getElementById("display-slot-a-video"); + } else { + video = document.getElementById("display-slot-b-video"); + } + + let nextDuration = window.nextImage['mime_type'].startsWith("video/") ? video.duration * 1000 : 7000; + + if (window.nextImage['mime_type'].startsWith("video/")) { + video.play(); + + if (loopVideos) { + while (nextDuration <= 7000) { + nextDuration += video.duration * 1000; + } + } + } + + scheduleNext(nextDuration); + } + + await loadSlot(window.nextImage['view_url'], window.nextImage['mime_type'].startsWith("video/")); + window.currentImage = window.nextImage; + window.nextImage = window.displayQueue.shift(); + await loadNextImage(); +} diff --git a/intimate/src/fetcher.js b/intimate/src/fetcher.js new file mode 100755 index 0000000..bf80203 --- /dev/null +++ b/intimate/src/fetcher.js @@ -0,0 +1,183 @@ +window.displayIsReady = false; + +let testMode = false; + +const path = require('path'); + +// Source: https://stackoverflow.com/a/45130990 +const { promisify } = require('util'); +const { resolve } = require('path'); +const fs = require('fs'); +const readdir = promisify(fs.readdir); +const stat = promisify(fs.stat); + +async function getFiles(dir) { + const subdirs = await readdir(dir); + const files = await Promise.all(subdirs.map(async (subdir) => { + const res = resolve(dir, subdir); + return (await stat(res)).isDirectory() ? getFiles(res) : res; + })); + return files.reduce((a, f) => a.concat(f), []); +} + +function extToMime(ext) { + if (ext.startsWith(".")) ext = ext.substring(1); + + switch (ext) { + case "jpg": + case "jpe": + case "jpeg": + case "jfif": + case "jif": + case "jfi": + return "image/jpeg"; + + case "png": + return "image/png"; + + case "webp": + return "image/webp"; + + case "gif": + return "image/gif"; + + case "swf": + return "application/x-shockwave-flash"; + + case "webm": + return "video/webm"; + + case "mp4": + case "m4v": + case "mp4v": + return "video/mp4"; + } +} +// ------------------------------------ + +async function slideshowMain() { + console.log("Slideshow: Flushing queue"); + window.displayQueue = []; + + console.log("Slideshow: Displaying loader"); + initiateLoad(); + + let pageDerpibooru = 1; + let pageE621 = 1; + let pageLocal = 1; + let seed = Buffer.from(crypto.getRandomValues(new Uint8Array(32))).toString("hex"); + + console.log(config); + + async function loadNextPageE621() { + if (!config.modules.e621 || !config._show.e621) return; + console.log("Slideshow: Loading page " + pageE621 + " from e621"); + + let res = await fetch("https://e621.net/posts.json?limit=" + Math.round(50 * config.modules.e621.proportion) + "&tags=order:random%20fav:" + encodeURIComponent(config.modules.e621.source) + "%20rating:" + (testMode ? "q" : "e") + "%20randseed:" + seed + "&page=" + pageE621); + let json = await res.json(); + + if (json["posts"].length === 0 && pageE621 !== 1) { + console.log("Slideshow: Reached the end of e621, wrapping around"); + pageE621 = 1; + await loadNextPageE621(); + return; + } + + json["posts"] = json["posts"].map(i => { + i["view_url"] = i["file"]["url"]; + i["mime_type"] = extToMime(i["file"]["ext"]); + i["_source"] = "https://e621.net/posts/" + i["id"]; + return i; + }); + + window.displayQueue.push(...json["posts"]); + window.displayQueue = window.displayQueue.sort(() => Math.random() - Math.random()); + pageE621++; + } + + async function loadNextPageDerpibooru() { + if (!config.modules.derpibooru || !config._show.derpibooru) return; + console.log("Slideshow: Loading page " + pageDerpibooru + " from Derpibooru"); + + let res = await fetch("https://derpibooru.org/api/v1/json/search/images/?q=" + (testMode ? "suggestive" : "explicit") + ",%20faved_by:" + encodeURIComponent(config.modules.derpibooru.source) + "&per_page=" + Math.round(50 * config.modules.derpibooru.proportion) + "&filter_id=56027&sf=random:" + seed + "&page=" + pageDerpibooru); + let json = await res.json(); + + if (json["images"].length === 0 && pageDerpibooru !== 1) { + console.log("Slideshow: Reached the end of Derpibooru, wrapping around"); + pageDerpibooru = 1; + await loadNextPageDerpibooru(); + return; + } + + json["images"] = json["images"].map(i => { + i["_source"] = "https://derpibooru.org/images/" + i["id"]; + return i; + }); + + window.displayQueue.push(...json["images"]); + window.displayQueue = window.displayQueue.sort(() => Math.random() - Math.random()); + pageDerpibooru++; + } + + let fullList; + + if (config.modules.local && config._show.local) { + fullList = (await getFiles(config.modules.local.source)).filter(i => extToMime(path.extname(i))); + } + + async function loadNextPageLocal() { + if (!config.modules.local || !config._show.local) return; + console.log("Slideshow: Loading page " + pageLocal + " from local filesystem"); + + let res = fullList.slice((pageLocal - 1) * Math.round(50 * config.modules.derpibooru.proportion), pageLocal * Math.round(50 * config.modules.derpibooru.proportion)); + let json = { + images: res.map(i => { + return { + _file: i, + id: path.basename(i), + mime_type: extToMime(path.extname(i)), + view_url: "file://" + encodeURI(i.replaceAll("\\", "/")) + } + }) + }; + + if (json["images"].length === 0 && pageLocal !== 1) { + console.log("Slideshow: Reached the end of local filesystem, wrapping around"); + pageLocal = 1; + await loadNextPageLocal(); + return; + } + + json["images"] = json["images"].map(i => { + i["_source"] = i["_file"]; + return i; + }); + + window.displayQueue.push(...json["images"]); + window.displayQueue = window.displayQueue.sort(() => Math.random() - Math.random()); + pageLocal++; + } + + console.log("Slideshow: Loading first page"); + await loadNextPageDerpibooru(); + await loadNextPageE621(); + await loadNextPageLocal(); + + console.log("Slideshow: Starting slideshow"); + await startSlideshow(false, async () => { + await loadNextPageDerpibooru(); + await loadNextPageE621(); + await loadNextPageLocal(); + }); + + console.log("Slideshow: Removing loader"); + completeLoad(); + window.displayIsReady = true; +} + +window.addEventListener("load", () => { + setTimeout(async () => { + window.config = await require('electron/renderer').ipcRenderer.invoke("getConfig"); + await slideshowMain(); + }, 1000); +}); diff --git a/intimate/src/keyboard.js b/intimate/src/keyboard.js new file mode 100755 index 0000000..26916bb --- /dev/null +++ b/intimate/src/keyboard.js @@ -0,0 +1,11 @@ +window.onkeydown = (e) => { + console.log(e); + if (e.code === "Space") displayPause(); + if (e.code === "Enter") displayPause(); + if (e.code === "NumPadEnter") displayPause(); + + if (e.code === "ArrowLeft") skipImage(); + if (e.code === "ArrowRight") skipImage(); + if (e.code === "ArrowUp") skipImage(); + if (e.code === "ArrowDown") skipImage(); +} |