aboutsummaryrefslogtreecommitdiff
path: root/intimate/src
diff options
context:
space:
mode:
Diffstat (limited to 'intimate/src')
-rwxr-xr-xintimate/src/actions.js17
-rwxr-xr-xintimate/src/display.js153
-rwxr-xr-xintimate/src/fetcher.js183
-rwxr-xr-xintimate/src/keyboard.js11
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();
+}