mirror of
https://github.com/5rahim/seanime
synced 2026-04-18 22:24:55 +02:00
feat: denshi settings
feat: use typescript-go
This commit is contained in:
@@ -1430,6 +1430,10 @@ func (ad *AutoDownloader) isSeasonAndEpisodeMatch(
|
||||
if rule.CustomEpisodeNumberAbsoluteOffset > 0 {
|
||||
episode = episode - rule.CustomEpisodeNumberAbsoluteOffset
|
||||
hasAbsoluteEpisode = true
|
||||
if episode < 1 {
|
||||
episode = 1
|
||||
hasAbsoluteEpisode = false
|
||||
}
|
||||
} else {
|
||||
// Handle absolute episode numbers from metadata
|
||||
if listEntry.GetMedia().GetCurrentEpisodeCount() != -1 && episode > listEntry.GetMedia().GetCurrentEpisodeCount() {
|
||||
|
||||
BIN
seanime-denshi/assets/iconTemplate.png
Normal file
BIN
seanime-denshi/assets/iconTemplate.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
@@ -1,4 +1,4 @@
|
||||
const { app, BrowserWindow, Menu, Tray, ipcMain, shell, dialog, remote, net, protocol, contextBridge } = require("electron")
|
||||
const { app, BrowserWindow, Menu, Tray, ipcMain, shell, dialog, remote, net, protocol, nativeImage } = require("electron")
|
||||
const path = require("path")
|
||||
const { spawn } = require("child_process")
|
||||
const fs = require("fs")
|
||||
@@ -11,6 +11,44 @@ const { autoUpdater } = require("electron-updater")
|
||||
const log = require("electron-log/main")
|
||||
log.initialize()
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Settings
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
const DENSHI_SETTINGS_DEFAULTS = {
|
||||
minimizeToTray: true,
|
||||
openInBackground: false,
|
||||
openAtLaunch: false,
|
||||
}
|
||||
|
||||
function getDenshiSettingsPath() {
|
||||
return path.join(app.getPath("userData"), "denshi-settings.json")
|
||||
}
|
||||
|
||||
function loadDenshiSettings() {
|
||||
try {
|
||||
const settingsPath = getDenshiSettingsPath()
|
||||
if (fs.existsSync(settingsPath)) {
|
||||
const data = JSON.parse(fs.readFileSync(settingsPath, "utf-8"))
|
||||
return { ...DENSHI_SETTINGS_DEFAULTS, ...data }
|
||||
}
|
||||
} catch (err) {
|
||||
log.error("[Denshi] Failed to load settings:", err)
|
||||
}
|
||||
return { ...DENSHI_SETTINGS_DEFAULTS }
|
||||
}
|
||||
|
||||
function saveDenshiSettings(settings) {
|
||||
try {
|
||||
const settingsPath = getDenshiSettingsPath()
|
||||
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2), "utf-8")
|
||||
} catch (err) {
|
||||
log.error("[Denshi] Failed to save settings:", err)
|
||||
}
|
||||
}
|
||||
|
||||
let denshiSettings = DENSHI_SETTINGS_DEFAULTS
|
||||
|
||||
const _isRsbuildFrontend = true
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -452,14 +490,17 @@ if (!gotTheLock) {
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
function createTray() {
|
||||
let iconPath = path.join(__dirname, "../assets/icon.png")
|
||||
if (process.platform === "darwin") {
|
||||
iconPath = path.join(__dirname, "../assets/18x18.png")
|
||||
const iconName = process.platform === "darwin" ? "18x18.png" : "icon.png"
|
||||
|
||||
let iconPath = path.join(__dirname, "../assets", iconName)
|
||||
if (_development) {
|
||||
iconPath = path.join(app.getAppPath(), "assets", iconName)
|
||||
}
|
||||
tray = new Tray(iconPath)
|
||||
const icon = nativeImage.createFromPath(iconPath)
|
||||
tray = new Tray(icon)
|
||||
|
||||
const contextMenu = Menu.buildFromTemplate([{
|
||||
id: "toggle_visibility", label: "Toggle visibility", click: () => {
|
||||
id: "toggle_visibility", label: "Toggle Visibility", click: () => {
|
||||
if (mainWindow.isVisible()) {
|
||||
mainWindow.hide()
|
||||
if (process.platform === "darwin") {
|
||||
@@ -474,7 +515,7 @@ function createTray() {
|
||||
}
|
||||
}
|
||||
}, ...(process.platform === "darwin" ? [{
|
||||
id: "accessory_mode", label: "Remove from dock", click: () => {
|
||||
id: "accessory_mode", label: "Remove from Dock", click: () => {
|
||||
app.dock.hide()
|
||||
}
|
||||
}
|
||||
@@ -529,7 +570,7 @@ async function launchSeanimeServer(isRestart) {
|
||||
splashScreen.close()
|
||||
splashScreen = null
|
||||
}
|
||||
if (mainWindow && !mainWindow.isDestroyed()) {
|
||||
if (mainWindow && !mainWindow.isDestroyed() && !denshiSettings.openInBackground) {
|
||||
mainWindow.maximize()
|
||||
mainWindow.show()
|
||||
}
|
||||
@@ -626,8 +667,14 @@ async function launchSeanimeServer(isRestart) {
|
||||
console.log("[Main] Server started close splash screen")
|
||||
setTimeout(() => {
|
||||
if (mainWindow && !mainWindow.isDestroyed()) {
|
||||
mainWindow.maximize()
|
||||
mainWindow.show()
|
||||
if (denshiSettings.openInBackground) {
|
||||
// Keep window hidden, just maximize it for when user shows it
|
||||
mainWindow.maximize()
|
||||
log.info("[Denshi] Opened in background")
|
||||
} else {
|
||||
mainWindow.maximize()
|
||||
mainWindow.show()
|
||||
}
|
||||
}
|
||||
}, 1000)
|
||||
resolve()
|
||||
@@ -772,10 +819,15 @@ function createMainWindow() {
|
||||
|
||||
mainWindow.on("close", (event) => {
|
||||
if (!isShutdown) {
|
||||
event.preventDefault()
|
||||
mainWindow.hide()
|
||||
if (process.platform === "darwin") {
|
||||
app.dock.hide()
|
||||
if (denshiSettings.minimizeToTray) {
|
||||
event.preventDefault()
|
||||
mainWindow.hide()
|
||||
if (process.platform === "darwin") {
|
||||
app.dock.hide()
|
||||
}
|
||||
} else {
|
||||
// Close the app completely
|
||||
cleanupAndExit()
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -788,7 +840,7 @@ function createMainWindow() {
|
||||
function createSplashScreen() {
|
||||
logStartupEvent("Creating splash screen")
|
||||
splashScreen = new BrowserWindow({
|
||||
width: 800, height: 600, frame: false, resizable: false, backgroundColor: "#0c0c0c", webPreferences: {
|
||||
width: 800, height: 600, frame: false, resizable: false, show: !denshiSettings.openInBackground, backgroundColor: "#0c0c0c", webPreferences: {
|
||||
nodeIntegration: false, contextIsolation: true, preload: path.join(__dirname, "preload.js")
|
||||
}
|
||||
})
|
||||
@@ -886,6 +938,20 @@ function cleanupAndExit() {
|
||||
app.whenReady().then(async () => {
|
||||
logStartupEvent("App ready")
|
||||
|
||||
// Load denshi settings early
|
||||
denshiSettings = loadDenshiSettings()
|
||||
if (_development) {
|
||||
denshiSettings.openInBackground = false
|
||||
}
|
||||
log.info("[Denshi] Loaded settings:", JSON.stringify(denshiSettings))
|
||||
|
||||
// Apply openAtLaunch setting (only supported on macOS and Windows)
|
||||
if (!_development && (process.platform === "darwin" || process.platform === "win32")) {
|
||||
app.setLoginItemSettings({
|
||||
openAtLogin: denshiSettings.openAtLaunch,
|
||||
})
|
||||
}
|
||||
|
||||
// Set up Chromium flags for better video playback
|
||||
setupChromiumFlags()
|
||||
|
||||
@@ -1138,6 +1204,26 @@ app.whenReady().then(async () => {
|
||||
|
||||
ipcMain.handle("get-local-server-port", () => localServerPort)
|
||||
|
||||
// Denshi settings IPC handlers
|
||||
ipcMain.handle("denshi:getSettings", () => {
|
||||
return { ...denshiSettings }
|
||||
})
|
||||
|
||||
ipcMain.handle("denshi:setSettings", (_, newSettings) => {
|
||||
denshiSettings = { ...DENSHI_SETTINGS_DEFAULTS, ...newSettings }
|
||||
saveDenshiSettings(denshiSettings)
|
||||
log.info("[Denshi] Settings updated:", JSON.stringify(denshiSettings))
|
||||
|
||||
// Apply openAtLaunch immediately (only supported on macOS and Windows)
|
||||
if (!_development && (process.platform === "darwin" || process.platform === "win32")) {
|
||||
app.setLoginItemSettings({
|
||||
openAtLogin: denshiSettings.openAtLaunch,
|
||||
})
|
||||
}
|
||||
|
||||
return { ...denshiSettings }
|
||||
})
|
||||
|
||||
app.on("window-all-closed", () => {
|
||||
if (process.platform !== "darwin") {
|
||||
app.quit()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const {contextBridge, ipcRenderer, ipcMain, shell, BrowserWindow} = require('electron');
|
||||
const { contextBridge, ipcRenderer, ipcMain, shell, BrowserWindow } = require("electron")
|
||||
|
||||
// Expose protected methods that allow the renderer process to use
|
||||
// the ipcRenderer without exposing the entire object
|
||||
@@ -6,22 +6,22 @@ contextBridge.exposeInMainWorld(
|
||||
'electron', {
|
||||
// Window Controls
|
||||
window: {
|
||||
minimize: () => ipcRenderer.send('window:minimize'),
|
||||
maximize: () => ipcRenderer.send('window:maximize'),
|
||||
close: () => ipcRenderer.send('window:close'),
|
||||
isMaximized: () => ipcRenderer.invoke('window:isMaximized'),
|
||||
isMinimizable: () => ipcRenderer.invoke('window:isMinimizable'),
|
||||
isMaximizable: () => ipcRenderer.invoke('window:isMaximizable'),
|
||||
isClosable: () => ipcRenderer.invoke('window:isClosable'),
|
||||
isFullscreen: () => ipcRenderer.invoke('window:isFullscreen'),
|
||||
setFullscreen: (fullscreen) => ipcRenderer.send('window:setFullscreen', fullscreen),
|
||||
toggleMaximize: () => ipcRenderer.send('window:toggleMaximize'),
|
||||
hide: () => ipcRenderer.send('window:hide'),
|
||||
show: () => ipcRenderer.send('window:show'),
|
||||
isVisible: () => ipcRenderer.invoke('window:isVisible'),
|
||||
setTitleBarStyle: (style) => ipcRenderer.send('window:setTitleBarStyle', style),
|
||||
getCurrentWindow: () => ipcRenderer.invoke('window:getCurrentWindow'),
|
||||
isMainWindow: () => ipcRenderer.send('window:isMainWindow'),
|
||||
minimize: () => ipcRenderer.send("window:minimize"),
|
||||
maximize: () => ipcRenderer.send("window:maximize"),
|
||||
close: () => ipcRenderer.send("window:close"),
|
||||
isMaximized: () => ipcRenderer.invoke("window:isMaximized"),
|
||||
isMinimizable: () => ipcRenderer.invoke("window:isMinimizable"),
|
||||
isMaximizable: () => ipcRenderer.invoke("window:isMaximizable"),
|
||||
isClosable: () => ipcRenderer.invoke("window:isClosable"),
|
||||
isFullscreen: () => ipcRenderer.invoke("window:isFullscreen"),
|
||||
setFullscreen: (fullscreen) => ipcRenderer.send("window:setFullscreen", fullscreen),
|
||||
toggleMaximize: () => ipcRenderer.send("window:toggleMaximize"),
|
||||
hide: () => ipcRenderer.send("window:hide"),
|
||||
show: () => ipcRenderer.send("window:show"),
|
||||
isVisible: () => ipcRenderer.invoke("window:isVisible"),
|
||||
setTitleBarStyle: (style) => ipcRenderer.send("window:setTitleBarStyle", style),
|
||||
getCurrentWindow: () => ipcRenderer.invoke("window:getCurrentWindow"),
|
||||
isMainWindow: () => ipcRenderer.send("window:isMainWindow"),
|
||||
},
|
||||
|
||||
localServer: {
|
||||
@@ -32,28 +32,28 @@ contextBridge.exposeInMainWorld(
|
||||
on: (channel, callback) => {
|
||||
// Whitelist channels
|
||||
const validChannels = [
|
||||
'message',
|
||||
'crash',
|
||||
'window:maximized',
|
||||
'window:unmaximized',
|
||||
'window:fullscreen',
|
||||
'update-downloaded',
|
||||
'update-error',
|
||||
'update-available',
|
||||
'download-progress',
|
||||
'window:currentWindow',
|
||||
'window:isMainWindow',
|
||||
];
|
||||
"message",
|
||||
"crash",
|
||||
"window:maximized",
|
||||
"window:unmaximized",
|
||||
"window:fullscreen",
|
||||
"update-downloaded",
|
||||
"update-error",
|
||||
"update-available",
|
||||
"download-progress",
|
||||
"window:currentWindow",
|
||||
"window:isMainWindow",
|
||||
]
|
||||
if (validChannels.includes(channel)) {
|
||||
// Remove the event listener to avoid memory leaks
|
||||
ipcRenderer.removeAllListeners(channel);
|
||||
ipcRenderer.removeAllListeners(channel)
|
||||
// Add the event listener
|
||||
ipcRenderer.on(channel, (_, ...args) => callback(...args));
|
||||
ipcRenderer.on(channel, (_, ...args) => callback(...args))
|
||||
|
||||
// Return a function to remove the listener
|
||||
return () => {
|
||||
ipcRenderer.removeAllListeners(channel);
|
||||
};
|
||||
ipcRenderer.removeAllListeners(channel)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -61,13 +61,13 @@ contextBridge.exposeInMainWorld(
|
||||
emit: (channel, data) => {
|
||||
// Whitelist channels
|
||||
const validChannels = [
|
||||
'restart-server',
|
||||
'kill-server',
|
||||
'macos-activation-policy-accessory',
|
||||
'macos-activation-policy-regular'
|
||||
];
|
||||
"restart-server",
|
||||
"kill-server",
|
||||
"macos-activation-policy-accessory",
|
||||
"macos-activation-policy-regular"
|
||||
]
|
||||
if (validChannels.includes(channel)) {
|
||||
ipcRenderer.send(channel, data);
|
||||
ipcRenderer.send(channel, data)
|
||||
}
|
||||
},
|
||||
|
||||
@@ -75,11 +75,11 @@ contextBridge.exposeInMainWorld(
|
||||
send: (channel, ...args) => {
|
||||
// Whitelist channels
|
||||
const validChannels = [
|
||||
'restart-app',
|
||||
'quit-app'
|
||||
];
|
||||
"restart-app",
|
||||
"quit-app"
|
||||
]
|
||||
if (validChannels.includes(channel)) {
|
||||
ipcRenderer.send(channel, ...args);
|
||||
ipcRenderer.send(channel, ...args)
|
||||
}
|
||||
},
|
||||
|
||||
@@ -93,13 +93,19 @@ contextBridge.exposeInMainWorld(
|
||||
|
||||
// Clipboard
|
||||
clipboard: {
|
||||
writeText: (text) => ipcRenderer.invoke('clipboard:writeText', text)
|
||||
writeText: (text) => ipcRenderer.invoke("clipboard:writeText", text)
|
||||
},
|
||||
|
||||
// Update functions
|
||||
checkForUpdates: () => ipcRenderer.invoke('check-for-updates'),
|
||||
installUpdate: () => ipcRenderer.invoke('install-update'),
|
||||
killServer: () => ipcRenderer.invoke('kill-server')
|
||||
checkForUpdates: () => ipcRenderer.invoke("check-for-updates"),
|
||||
installUpdate: () => ipcRenderer.invoke("install-update"),
|
||||
killServer: () => ipcRenderer.invoke("kill-server"),
|
||||
|
||||
// Denshi Settings
|
||||
denshiSettings: {
|
||||
get: () => ipcRenderer.invoke("denshi:getSettings"),
|
||||
set: (settings) => ipcRenderer.invoke("denshi:setSettings", settings),
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
118
seanime-web/package-lock.json
generated
118
seanime-web/package-lock.json
generated
@@ -148,6 +148,7 @@
|
||||
"@types/react": "^18.3.23",
|
||||
"@types/react-dom": "^18.3.7",
|
||||
"@types/react-window": "^1.8.8",
|
||||
"@typescript/native-preview": "^7.0.0-dev.20260221.1",
|
||||
"autoprefixer": "^10",
|
||||
"babel-plugin-react-compiler": "^19.0.0-beta-e993439-20250405",
|
||||
"encoding": "^0.1.13",
|
||||
@@ -6073,6 +6074,123 @@
|
||||
"integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@typescript/native-preview": {
|
||||
"version": "7.0.0-dev.20260221.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/native-preview/-/native-preview-7.0.0-dev.20260221.1.tgz",
|
||||
"integrity": "sha512-tEUzcnj6pD+z1vANchRzhpPl+3RMD+xQRvIN//0+qjtP5zyYB5T+MIaAWycpKDwlHP9C13JnQgcgYnC+LlNkrg==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"tsgo": "bin/tsgo.js"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@typescript/native-preview-darwin-arm64": "7.0.0-dev.20260221.1",
|
||||
"@typescript/native-preview-darwin-x64": "7.0.0-dev.20260221.1",
|
||||
"@typescript/native-preview-linux-arm": "7.0.0-dev.20260221.1",
|
||||
"@typescript/native-preview-linux-arm64": "7.0.0-dev.20260221.1",
|
||||
"@typescript/native-preview-linux-x64": "7.0.0-dev.20260221.1",
|
||||
"@typescript/native-preview-win32-arm64": "7.0.0-dev.20260221.1",
|
||||
"@typescript/native-preview-win32-x64": "7.0.0-dev.20260221.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript/native-preview-darwin-arm64": {
|
||||
"version": "7.0.0-dev.20260221.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/native-preview-darwin-arm64/-/native-preview-darwin-arm64-7.0.0-dev.20260221.1.tgz",
|
||||
"integrity": "sha512-m3ttEpK+eXV7P06RVZZuSuUvNDj8psXODrMJRRQWpTNsk3qITbIdBSgOx2Q/M3tbQ9Mo2IBHt6jUjqOdRW9oZQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
]
|
||||
},
|
||||
"node_modules/@typescript/native-preview-darwin-x64": {
|
||||
"version": "7.0.0-dev.20260221.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/native-preview-darwin-x64/-/native-preview-darwin-x64-7.0.0-dev.20260221.1.tgz",
|
||||
"integrity": "sha512-BNaNe3rox2rpkh5sWcnZZob6sDA/at9KK55/WSRAH4W+9dFReOLFAR9YXhKxrLGZ1QpleuIBahKbV8o037S+pA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
]
|
||||
},
|
||||
"node_modules/@typescript/native-preview-linux-arm": {
|
||||
"version": "7.0.0-dev.20260221.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/native-preview-linux-arm/-/native-preview-linux-arm-7.0.0-dev.20260221.1.tgz",
|
||||
"integrity": "sha512-+/uyIw7vg4FyAnNpsCJHmSOhMiR2m56lqaEo1J5pMAstJmfLTTKQdJ1muIWCDCqc24k2U30IStHOaCqUerp/nQ==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@typescript/native-preview-linux-arm64": {
|
||||
"version": "7.0.0-dev.20260221.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/native-preview-linux-arm64/-/native-preview-linux-arm64-7.0.0-dev.20260221.1.tgz",
|
||||
"integrity": "sha512-Y4jsvwDq86LXq63UYRLqCAd+nD1r6C2NVaGNR39H+c6D8SgOBkPLJa8quTH0Ir8E5bsR8vTN4E6xHY9jD4J2PA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@typescript/native-preview-linux-x64": {
|
||||
"version": "7.0.0-dev.20260221.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/native-preview-linux-x64/-/native-preview-linux-x64-7.0.0-dev.20260221.1.tgz",
|
||||
"integrity": "sha512-7agd5FtVLPp+gRMvsecSDmdQ/XM80q/uaQ6+Kahan9uNrCuPJIyMiAtJvCoYYgT1nXX2AjwZk39DH63fRaw/Mg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@typescript/native-preview-win32-arm64": {
|
||||
"version": "7.0.0-dev.20260221.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/native-preview-win32-arm64/-/native-preview-win32-arm64-7.0.0-dev.20260221.1.tgz",
|
||||
"integrity": "sha512-lXbsy5vDzS//oE0evX+QwZBwpKselXTd8H18lT42CBQo2hL2r0+w9YBguaYXrnGkAoHjDXEfKA2xii8yVZKVUg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
]
|
||||
},
|
||||
"node_modules/@typescript/native-preview-win32-x64": {
|
||||
"version": "7.0.0-dev.20260221.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/native-preview-win32-x64/-/native-preview-win32-x64-7.0.0-dev.20260221.1.tgz",
|
||||
"integrity": "sha512-O02pfQlVlRTsBmp0hODs/bOHm2ic2kXZpIchBP5Qm0wKCp1Ytz/7i3SNT1gN47I+KC4axn/AHhFmkWQyIu9kRQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
]
|
||||
},
|
||||
"node_modules/@uidotdev/usehooks": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@uidotdev/usehooks/-/usehooks-2.4.1.tgz",
|
||||
|
||||
@@ -8,11 +8,11 @@
|
||||
"dev:mobile": "env-cmd -f .env.mobile rsbuild dev",
|
||||
"dev:desktop": "env-cmd -f .env.desktop rsbuild dev",
|
||||
"dev:denshi": "env-cmd -f .env.denshi rsbuild dev",
|
||||
"build": "tsc && rsbuild build",
|
||||
"build": "tsgo && rsbuild build",
|
||||
"build:rsdoctor": "RSDOCTOR=true rsbuild build",
|
||||
"build:desktop": "tsc && env-cmd -f .env.desktop rsbuild build",
|
||||
"build:denshi": "tsc && env-cmd -f .env.denshi rsbuild build",
|
||||
"build:development:desktop": "tsc && env-cmd -f .env.development.desktop rsbuild build",
|
||||
"build:desktop": "tsgo && env-cmd -f .env.desktop rsbuild build",
|
||||
"build:denshi": "tsgo && env-cmd -f .env.denshi rsbuild build",
|
||||
"build:development:desktop": "tsgo && env-cmd -f .env.development.desktop rsbuild build",
|
||||
"preview": "rsbuild preview"
|
||||
},
|
||||
"dependencies": {
|
||||
@@ -156,6 +156,7 @@
|
||||
"@types/react": "^18.3.23",
|
||||
"@types/react-dom": "^18.3.7",
|
||||
"@types/react-window": "^1.8.8",
|
||||
"@typescript/native-preview": "^7.0.0-dev.20260221.1",
|
||||
"autoprefixer": "^10",
|
||||
"babel-plugin-react-compiler": "^19.0.0-beta-e993439-20250405",
|
||||
"encoding": "^0.1.13",
|
||||
@@ -164,4 +165,4 @@
|
||||
"typescript": "^5",
|
||||
"workbox-webpack-plugin": "^7.4.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
import { SettingsCard } from "@/app/(main)/settings/_components/settings-card"
|
||||
import { Switch } from "@/components/ui/switch"
|
||||
import React from "react"
|
||||
import { RiSettings3Fill } from "react-icons/ri"
|
||||
|
||||
export function DenshiSettings() {
|
||||
|
||||
const [settings, setSettings] = React.useState<DenshiSettings | null>(null)
|
||||
const settingsRef = React.useRef<DenshiSettings | null>(null)
|
||||
const [loading, setLoading] = React.useState(true)
|
||||
|
||||
React.useEffect(() => {
|
||||
if (window.electron?.denshiSettings) {
|
||||
window.electron.denshiSettings.get().then((s) => {
|
||||
setSettings(s)
|
||||
settingsRef.current = s
|
||||
setLoading(false)
|
||||
})
|
||||
}
|
||||
}, [])
|
||||
|
||||
function updateSetting(key: keyof DenshiSettings, value: boolean) {
|
||||
if (!settingsRef.current || !window.electron?.denshiSettings) return
|
||||
|
||||
const newSettings = { ...settingsRef.current, [key]: value }
|
||||
settingsRef.current = newSettings
|
||||
setSettings(newSettings)
|
||||
window.electron.denshiSettings.set(newSettings)
|
||||
}
|
||||
|
||||
if (loading || !settings) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<SettingsCard title="Window">
|
||||
<Switch
|
||||
side="right"
|
||||
value={settings.minimizeToTray}
|
||||
onValueChange={(v) => updateSetting("minimizeToTray", v)}
|
||||
label="Minimize to tray on close"
|
||||
help="When enabled, closing the window will minimize the app to the system tray instead of quitting."
|
||||
/>
|
||||
<Switch
|
||||
side="right"
|
||||
value={settings.openInBackground}
|
||||
onValueChange={(v) => updateSetting("openInBackground", v)}
|
||||
label="Open in background"
|
||||
help="When enabled, the app will start hidden. You can show it from the system tray."
|
||||
/>
|
||||
</SettingsCard>
|
||||
|
||||
<SettingsCard title="System">
|
||||
<Switch
|
||||
side="right"
|
||||
value={settings.openAtLaunch}
|
||||
onValueChange={(v) => updateSetting("openAtLaunch", v)}
|
||||
label="Open at launch"
|
||||
help={window.electron?.platform === "linux"
|
||||
? "This feature is not supported on Linux."
|
||||
: "When enabled, the app will start automatically when you log in to your computer."}
|
||||
disabled={window.electron?.platform === "linux"}
|
||||
/>
|
||||
</SettingsCard>
|
||||
|
||||
<div className="flex items-center gap-2 text-sm text-gray-500 bg-gray-50 dark:bg-gray-900/30 rounded-lg p-3 border border-gray-200 dark:border-gray-800 border-dashed">
|
||||
<RiSettings3Fill className="text-base" />
|
||||
<span>Settings are saved automatically and applied after a restart</span>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -46,6 +46,7 @@ import {
|
||||
LuCirclePlay,
|
||||
LuFileSearch,
|
||||
LuLibrary,
|
||||
LuMonitor,
|
||||
LuMonitorPlay,
|
||||
LuPalette,
|
||||
LuTabletSmartphone,
|
||||
@@ -57,6 +58,7 @@ import { SiBittorrent, SiQbittorrent, SiTransmission } from "react-icons/si"
|
||||
import { TbDatabaseExclamation } from "react-icons/tb"
|
||||
import { VscDebugAlt } from "react-icons/vsc"
|
||||
import { SettingsCard, SettingsNavCard, SettingsPageHeader } from "./_components/settings-card"
|
||||
import { DenshiSettings } from "./_containers/denshi-settings"
|
||||
import { DiscordRichPresenceSettings } from "./_containers/discord-rich-presence-settings"
|
||||
import { LocalSettings } from "./_containers/local-settings"
|
||||
import { NakamaSettings } from "./_containers/nakama-settings"
|
||||
@@ -259,6 +261,12 @@ export default function Page() {
|
||||
{/*</div>*/}
|
||||
|
||||
<Card className="lg:p-2 contents lg:block border-0 bg-transparent lg:border lg:bg-gray-950/80">
|
||||
{__isElectronDesktop__ && (
|
||||
<TabsTrigger
|
||||
value="denshi"
|
||||
className="group"
|
||||
><LuMonitor className="text-xl mr-3 transition-transform duration-200" /> Denshi</TabsTrigger>
|
||||
)}
|
||||
<TabsTrigger
|
||||
value="ui"
|
||||
className="group"
|
||||
@@ -410,7 +418,7 @@ export default function Page() {
|
||||
mediaPlayerHost: status?.settings?.mediaPlayer?.host,
|
||||
torrentProvider: status?.settings?.library?.torrentProvider || DEFAULT_TORRENT_PROVIDER, // (Backwards compatibility)
|
||||
autoSelectTorrentProvider: status?.settings?.library?.autoSelectTorrentProvider || DEFAULT_TORRENT_PROVIDER, // (Backwards
|
||||
// compatibility)
|
||||
// compatibility)
|
||||
autoScan: status?.settings?.library?.autoScan,
|
||||
defaultPlayer: status?.settings?.mediaPlayer?.defaultPlayer,
|
||||
vlcPort: status?.settings?.mediaPlayer?.vlcPort,
|
||||
@@ -869,6 +877,20 @@ export default function Page() {
|
||||
|
||||
</TabsContent>
|
||||
|
||||
{__isElectronDesktop__ && (
|
||||
<TabsContent value="denshi" className={tabContentClass}>
|
||||
|
||||
<SettingsPageHeader
|
||||
title="Denshi"
|
||||
description="Desktop client settings"
|
||||
icon={LuMonitor}
|
||||
/>
|
||||
|
||||
<DenshiSettings />
|
||||
|
||||
</TabsContent>
|
||||
)}
|
||||
|
||||
|
||||
{/*<TabsContent value="data" className="space-y-4">*/}
|
||||
|
||||
|
||||
10
seanime-web/src/types/index.d.ts
vendored
10
seanime-web/src/types/index.d.ts
vendored
@@ -67,8 +67,18 @@ declare global {
|
||||
checkForUpdates: () => Promise<any>;
|
||||
installUpdate: () => Promise<any>;
|
||||
killServer: () => Promise<any>;
|
||||
denshiSettings: {
|
||||
get: () => Promise<DenshiSettings>;
|
||||
set: (settings: DenshiSettings) => Promise<DenshiSettings>;
|
||||
};
|
||||
};
|
||||
|
||||
__isElectronDesktop__?: boolean;
|
||||
}
|
||||
|
||||
interface DenshiSettings {
|
||||
minimizeToTray: boolean;
|
||||
openInBackground: boolean;
|
||||
openAtLaunch: boolean;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"compilerOptions": {
|
||||
"types": [
|
||||
"node",
|
||||
"@rsbuild/core/types"
|
||||
"@rsbuild/core/types"
|
||||
],
|
||||
"target": "ES2020",
|
||||
"useDefineForClassFields": true,
|
||||
@@ -26,8 +26,10 @@
|
||||
"noUnusedLocals": false,
|
||||
"noUnusedParameters": false,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"*": [
|
||||
"./*"
|
||||
],
|
||||
"@/*": [
|
||||
"./src/*"
|
||||
]
|
||||
@@ -45,4 +47,4 @@
|
||||
"path": "./tsconfig.node.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user