From f778c4f44bcb98c1276d16c818700e17bc54dff2 Mon Sep 17 00:00:00 2001 From: alikia2x Date: Wed, 1 Jan 2025 04:46:57 +0800 Subject: [PATCH] feature: a usable timeline --- bun.lockb | Bin 383018 -> 383753 bytes package.json | 168 +++++++++++------------ pages/rewind/index.tsx | 215 +++++++++++++++++++++++++++++- src/electron/index.ts | 13 ++ src/electron/preload/rewind.cjs | 19 +-- src/electron/preload/settings.cjs | 4 + src/electron/server/index.ts | 5 +- src/electron/utils/video/index.ts | 2 +- src/renderer/app.tsx | 30 +++++ src/renderer/state/apiInfo.ts | 6 + 10 files changed, 356 insertions(+), 106 deletions(-) create mode 100644 src/renderer/state/apiInfo.ts diff --git a/bun.lockb b/bun.lockb index 578b8cf9c268f42715675dd1cad754ed8e5db117..c2529d1cc3094bc74628a3496de1f61ec7fd14a0 100755 GIT binary patch delta 60325 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 WDkR#5k4Wm=TDx(<+4|Z;4*efH9Nc#R delta 60027 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 diff --git a/package.json b/package.json index 111e920..405a3c4 100644 --- a/package.json +++ b/package.json @@ -1,85 +1,87 @@ { - "name": "openrewind", - "version": "0.6.0", - "type": "module", - "description": "Your second brain, superpowered.", - "main": "dist/electron/index.js", - "scripts": { - "dev": "cross-env NODE_ENV=dev bun run dev:all", - "dev:all": "concurrently -n=react,electron -c='#ff3e00',blue \"bun run dev:react\" \"bun run dev:electron\"", - "dev:react": "vite dev", - "dev:electron": "bunx gulp build && electron dist/electron/index.js", - "build:react": "vite build", - "build:app": "bunx gulp build", - "build:electron": "electron-builder", - "format": "bunx prettier --write ." - }, - "keywords": [], - "author": "", - "license": "MIT", - "dependencies": { - "@alikia/random-key": "npm:@jsr/alikia__random-key", - "@electron/remote": "^2.1.2", - "@hono/node-server": "^1.13.7", - "@unly/universal-language-detector": "^2.0.3", - "better-sqlite3": "^11.6.0", - "detect-port": "^2.1.0", - "electron-context-menu": "^4.0.4", - "electron-reloader": "^1.2.3", - "electron-screencapture": "^1.1.0", - "electron-serve": "^2.1.1", - "electron-store": "^10.0.0", - "electron-window-state": "^5.0.3", - "execa": "^9.5.1", - "hono": "^4.6.15", - "i18next": "^24.0.2", - "i18next-browser-languagedetector": "^8.0.0", - "i18next-electron-fs-backend": "^3.0.2", - "i18next-fs-backend": "^2.6.0", - "i18next-icu": "^2.3.0", - "image-size": "^1.1.1", - "memory-cache": "^0.2.0", - "react": "^18.3.1", - "react-dom": "^18.3.1", - "react-i18next": "^15.1.2", - "react-router": "^7.0.1", - "react-router-dom": "^7.0.1", - "screenshot-desktop": "^1.15.0", - "sqlite3": "^5.1.7", - "sqlstring": "^2.3.3", - "vite-tsconfig-paths": "^5.1.3" - }, - "devDependencies": { - "@electron/rebuild": "^3.7.1", - "@eslint/js": "^9.13.0", - "@iconify-icon/react": "^2.1.0", - "@types/better-sqlite3": "^7.6.12", - "@types/gulp": "^4.0.17", - "@types/memory-cache": "^0.2.6", - "@types/react": "^18.3.12", - "@types/react-dom": "^18.3.1", - "@types/screenshot-desktop": "^1.12.3", - "@types/sqlstring": "^2.3.2", - "@vitejs/plugin-react": "^4.3.3", - "autoprefixer": "^10.4.19", - "concurrently": "^9.0.1", - "cross-env": "^7.0.3", - "del": "^8.0.0", - "electron": "^33.2.0", - "electron-build": "^0.0.3", - "electron-builder": "^25.1.8", - "eslint": "^9.13.0", - "eslint-plugin-react-hooks": "^5.0.0", - "eslint-plugin-react-refresh": "^0.4.14", - "globals": "^15.11.0", - "gulp": "^5.0.0", - "gulp-clean": "^0.4.0", - "gulp-typescript": "6.0.0-alpha.1", - "postcss": "^8.4.38", - "tailwindcss": "^3.4.15", - "typescript": "~5.6.2", - "typescript-eslint": "^8.11.0", - "vite": "^5.4.10", - "vite-plugin-chunk-split": "^0.5.0" - } + "name": "openrewind", + "version": "0.6.0", + "type": "module", + "description": "Your second brain, superpowered.", + "main": "dist/electron/index.js", + "scripts": { + "dev": "cross-env NODE_ENV=dev bun run dev:all", + "dev:all": "concurrently -n=react,electron -c='#ff3e00',blue \"bun run dev:react\" \"bun run dev:electron\"", + "dev:react": "vite dev", + "dev:electron": "bunx gulp build && electron dist/electron/index.js", + "build:react": "vite build", + "build:app": "bunx gulp build", + "build:electron": "electron-builder", + "format": "bunx prettier --write ." + }, + "keywords": [], + "author": "", + "license": "MIT", + "dependencies": { + "@alikia/random-key": "npm:@jsr/alikia__random-key", + "@electron/remote": "^2.1.2", + "@hono/node-server": "^1.13.7", + "@unly/universal-language-detector": "^2.0.3", + "better-sqlite3": "^11.6.0", + "dayjs": "^1.11.13", + "detect-port": "^2.1.0", + "electron-context-menu": "^4.0.4", + "electron-reloader": "^1.2.3", + "electron-screencapture": "^1.1.0", + "electron-serve": "^2.1.1", + "electron-store": "^10.0.0", + "electron-window-state": "^5.0.3", + "execa": "^9.5.1", + "hono": "^4.6.15", + "i18next": "^24.0.2", + "i18next-browser-languagedetector": "^8.0.0", + "i18next-electron-fs-backend": "^3.0.2", + "i18next-fs-backend": "^2.6.0", + "i18next-icu": "^2.3.0", + "image-size": "^1.1.1", + "jotai": "^2.11.0", + "memory-cache": "^0.2.0", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-i18next": "^15.1.2", + "react-router": "^7.0.1", + "react-router-dom": "^7.0.1", + "screenshot-desktop": "^1.15.0", + "sqlite3": "^5.1.7", + "sqlstring": "^2.3.3", + "vite-tsconfig-paths": "^5.1.3" + }, + "devDependencies": { + "@electron/rebuild": "^3.7.1", + "@eslint/js": "^9.13.0", + "@iconify-icon/react": "^2.1.0", + "@types/better-sqlite3": "^7.6.12", + "@types/gulp": "^4.0.17", + "@types/memory-cache": "^0.2.6", + "@types/react": "^18.3.12", + "@types/react-dom": "^18.3.1", + "@types/screenshot-desktop": "^1.12.3", + "@types/sqlstring": "^2.3.2", + "@vitejs/plugin-react": "^4.3.3", + "autoprefixer": "^10.4.19", + "concurrently": "^9.0.1", + "cross-env": "^7.0.3", + "del": "^8.0.0", + "electron": "^33.2.0", + "electron-build": "^0.0.3", + "electron-builder": "^25.1.8", + "eslint": "^9.13.0", + "eslint-plugin-react-hooks": "^5.0.0", + "eslint-plugin-react-refresh": "^0.4.14", + "globals": "^15.11.0", + "gulp": "^5.0.0", + "gulp-clean": "^0.4.0", + "gulp-typescript": "6.0.0-alpha.1", + "postcss": "^8.4.38", + "tailwindcss": "^3.4.15", + "typescript": "~5.6.2", + "typescript-eslint": "^8.11.0", + "vite": "^5.4.10", + "vite-plugin-chunk-split": "^0.5.0" + } } diff --git a/pages/rewind/index.tsx b/pages/rewind/index.tsx index 3d35de0..30e6406 100644 --- a/pages/rewind/index.tsx +++ b/pages/rewind/index.tsx @@ -1,9 +1,216 @@ import "./index.css"; +import { useCallback, useEffect, useRef, useState } from "react"; +import type { Frame } from "../../src/electron/backend/schema.d.ts"; +import dayjs from "dayjs"; +import relativeTime from "dayjs/plugin/relativeTime"; +import localizedFormat from "dayjs/plugin/localizedFormat"; +import updateLocale from "dayjs/plugin/updateLocale"; +import { useAtomValue } from "jotai"; +import { apiInfoAtom } from "src/renderer/state/apiInfo.ts"; -export default function RewindPage() { +dayjs.extend(relativeTime); +dayjs.extend(localizedFormat); +dayjs.extend(updateLocale); +dayjs.updateLocale("en", { + relativeTime: { + future: "in %s", + past: "%s ago", + s: "%d seconds", + m: "1 minute", + mm: "%d minutes", + h: "1 hour", + hh: "%d hours", + d: "1 day", + dd: "%d days", + M: "1 month", + MM: "%d months", + y: "1 year", + yy: "%d years" + } +}); + +function Image({ src }: { src: string }) { return ( - <> -
- + Current frame + ); +} + +export default function RewindPage() { + const { port, apiKey } = useAtomValue(apiInfoAtom); + const [timeline, setTimeline] = useState([]); + const [currentFrameId, setCurrentFrameId] = useState(null); + const [images, setImages] = useState>({}); + const [isLoadingMore, setIsLoadingMore] = useState(false); + const lastRequestTime = useRef(Date.now()); + const containerRef = useRef(null); + const lastAvaliableFrameId = useRef(null); + + useEffect(() => { + if (currentFrameId && images[currentFrameId]) { + lastAvaliableFrameId.current = currentFrameId; + } + }, [images, currentFrameId]); + + // Fetch timeline data + const fetchTimeline = useCallback( + async (untilID?: number) => { + if (!port) return; + try { + const url = new URL(`http://localhost:${port}/timeline`); + if (untilID) { + url.searchParams.set("untilID", untilID.toString()); + } + + const response = await fetch(url.toString(), { + headers: { + "x-api-key": apiKey + } + }); + const data = await response.json(); + setTimeline((prev) => (untilID ? [...prev, ...data] : data)); + } catch (error) { + console.error(error); + } + }, + [port, apiKey] + ); + + useEffect(() => { + fetchTimeline(); + }, [fetchTimeline]); + + const loadImage = useCallback( + (frameId: number) => { + if (!port) return; + // Rate limit to at most 1 request every 200ms + const now = Date.now(); + if (images[frameId]) return; + + lastRequestTime.current = now; + fetch(`http://localhost:${port}/frame/${frameId}`, { + headers: { + "x-api-key": apiKey + } + }) + .then((res) => res.blob()) + .then((blob) => { + const url = URL.createObjectURL(blob); + setImages((prev) => ({ ...prev, [frameId]: url })); + }) + .catch(console.error); + }, + [apiKey, images, port] + ); + + // Load initial images + useEffect(() => { + if (timeline.length > 0 && !currentFrameId) { + setCurrentFrameId(timeline[0].id); + loadImage(timeline[0].id); + if (timeline.length > 1) { + loadImage(timeline[1].id); + } + } + }, [timeline, currentFrameId, loadImage]); + + const lastScrollTime = useRef(Date.now()); + + const handleScroll = (e: React.WheelEvent) => { + if (!containerRef.current || !currentFrameId) return; + + // Only allow scroll changes every 80ms + const now = Date.now(); + if (now - lastScrollTime.current < 80) return; + lastScrollTime.current = now; + + const delta = Math.sign(e.deltaY); + const currentIndex = timeline.findIndex((frame) => frame.id === currentFrameId); + if (currentIndex === -1) return; + + const newIndex = Math.min(Math.max(currentIndex - delta, 0), timeline.length - 1); + const newFrameId = timeline[newIndex].id; + + if (newFrameId !== currentFrameId) { + setCurrentFrameId(newFrameId); + // Preload adjacent images + if (newIndex > 0) loadImage(timeline[newIndex - 1].id); + if (newIndex < timeline.length - 1) loadImage(timeline[newIndex + 1].id); + + // Load more timeline data when we're near the end + if (newIndex > timeline.length - 10 && !isLoadingMore) { + setIsLoadingMore(true); + const lastID = timeline[timeline.length - 1].id; + fetchTimeline(lastID).finally(() => setIsLoadingMore(false)); + } + } + }; + + function displayTime(time: number) { + // if diff < 1h, fromNow() + // else use localized format + const diff = dayjs().diff(dayjs.unix(time), "second"); + if (diff < 3600) { + return dayjs.unix(time).fromNow(); + } else { + return dayjs.unix(time).format("llll"); + } + } + + return ( +
+ {/* Current image */} + + + {/* Time capsule */} +
+ {currentFrameId + ? displayTime( + timeline.find((frame) => frame.id === currentFrameId)!.createdAt + ) || "Loading..." + : "Loading..."} +
+ + {/* Timeline */} +
+ {timeline + .filter((frame) => frame.id <= currentFrameId!) + .map((frame) => ( +
{ + setCurrentFrameId(frame.id); + loadImage(frame.id); + }} + > + #{frame.id} + + {dayjs().diff(dayjs.unix(frame.createdAt), "second")} sec ago + +
+ ))} +
+
); } diff --git a/src/electron/index.ts b/src/electron/index.ts index 57b9d37..b0bb404 100644 --- a/src/electron/index.ts +++ b/src/electron/index.ts @@ -99,6 +99,12 @@ app.on("ready", () => { cache.put("server:APIKey", key); } serve({ fetch: honoApp.fetch, port: port }); + + // Send API info to renderer + settingsWindow?.webContents.send("api-info", { + port, + apiKey: key + }); console.log(`App server running on port ${port}`); }); }); @@ -126,3 +132,10 @@ app.on("will-quit", () => { ipcMain.on("close-settings", () => { settingsWindow?.hide(); }); + +ipcMain.handle("request-api-info", () => { + return { + port: cache.get("server:port"), + apiKey: cache.get("server:APIKey") + }; +}); diff --git a/src/electron/preload/rewind.cjs b/src/electron/preload/rewind.cjs index ea28cf5..52a5241 100644 --- a/src/electron/preload/rewind.cjs +++ b/src/electron/preload/rewind.cjs @@ -1,20 +1,5 @@ const { contextBridge, ipcRenderer } = require("electron"); -// Expose protected methods that allow the renderer process to use -// the ipcRenderer without exposing the entire object -contextBridge.exposeInMainWorld("api", { - send: (channel, data) => { - // whitelist channels - let validChannels = ["toMain"]; - if (validChannels.includes(channel)) { - ipcRenderer.send(channel, data); - } - }, - receive: (channel, func) => { - let validChannels = ["fromMain"]; - if (validChannels.includes(channel)) { - // Deliberately strip event as it includes `sender` - ipcRenderer.on(channel, (event, ...args) => func(...args)); - } - } +contextBridge.exposeInMainWorld("appGlobal", { + requestApiInfo: () => ipcRenderer.invoke("request-api-info") }); diff --git a/src/electron/preload/settings.cjs b/src/electron/preload/settings.cjs index 4c1cede..0004681 100644 --- a/src/electron/preload/settings.cjs +++ b/src/electron/preload/settings.cjs @@ -17,3 +17,7 @@ contextBridge.exposeInMainWorld("settingsWindow", { ipcRenderer.send("close-settings", {}); } }); + +contextBridge.exposeInMainWorld("appGlobal", { + requestApiInfo: () => ipcRenderer.invoke("request-api-info") +}); diff --git a/src/electron/server/index.ts b/src/electron/server/index.ts index 092c7dc..877dea8 100644 --- a/src/electron/server/index.ts +++ b/src/electron/server/index.ts @@ -1,4 +1,5 @@ import { Hono } from "hono"; +import { cors } from "hono/cors"; import cache from "memory-cache"; import { join } from "path"; import fs from "fs"; @@ -15,6 +16,8 @@ import { existsSync } from "fs"; const app = new Hono(); +app.use("*", cors()); + app.use(async (c, next) => { const key = cache.get("server:APIKey"); if (key && c.req.header("x-api-key") !== key) { @@ -45,7 +48,7 @@ function getFramesUntilID(db: Database, untilID: number, limit = 50): Frame[] { ` SELECT id, createdAt, imgFilename, videoPath, videoFrameIndex FROM frame - WHERE id <= ? + WHERE id < ? ORDER BY createdAt DESC LIMIT ? ` diff --git a/src/electron/utils/video/index.ts b/src/electron/utils/video/index.ts index a48d9a8..c483d2e 100644 --- a/src/electron/utils/video/index.ts +++ b/src/electron/utils/video/index.ts @@ -36,7 +36,7 @@ export function immediatelyExtractFrameFromVideo( const outputFilename = `${bareVideoFilename}_${frameIndex.toString().padStart(4, "0")}.bmp`; const outputPathArg = join(outputPath, outputFilename); const args = [ - "-ss", + "-ss", `${formatTime(frameIndex / ENCODING_FRAME_RATE)}`, "-i", `${fullVideoPath}`, diff --git a/src/renderer/app.tsx b/src/renderer/app.tsx index c2bfc36..38dc59f 100644 --- a/src/renderer/app.tsx +++ b/src/renderer/app.tsx @@ -3,8 +3,38 @@ import SettingsPage from "pages/settings"; import "./i18n.ts"; import RewindPage from "pages/rewind"; import "./app.css"; +import { useEffect } from "react"; +import { useAtom } from "jotai"; +import { apiInfoAtom } from "./state/apiInfo.ts"; + +declare global { + interface Window { + appGlobal: { + requestApiInfo: () => Promise<{ port: number; apiKey: string }>; + }; + } +} export function App() { + const [apiInfo, setApiInfo] = useAtom(apiInfoAtom); + + useEffect(() => { + const fetchApiInfo = async () => { + try { + const info = await window.appGlobal.requestApiInfo(); + setApiInfo(info); + } catch (error) { + console.error("Failed to fetch API info:", error); + } + }; + + fetchApiInfo(); + }, [setApiInfo]); + + if (!apiInfo) { + return null; + } + return (
diff --git a/src/renderer/state/apiInfo.ts b/src/renderer/state/apiInfo.ts new file mode 100644 index 0000000..066be24 --- /dev/null +++ b/src/renderer/state/apiInfo.ts @@ -0,0 +1,6 @@ +import { atom } from 'jotai' + +export const apiInfoAtom = atom({ + port: -1, + apiKey: '' +}) \ No newline at end of file