diff --git a/simpliplay/src/constants.js b/simpliplay/src/constants.js deleted file mode 100644 index 5a83302..0000000 --- a/simpliplay/src/constants.js +++ /dev/null @@ -1,12 +0,0 @@ -// src/constants.js -const path = require('path'); -const os = require('os'); - -module.exports = { - APP_CONSTANTS: { - VERSION: '2.0.4.4', - GPU_ACCEL: 'enabled', - SNAPSHOTS_DIR: path.join(os.homedir(), 'simpliplay-snapshots'), - BAD_FILE_EXTENSIONS: ['.exe', '.bat', '.cmd', '.sh', '.msi', '.com', '.vbs', '.ps1', '.jar', '.scr'] - } -}; \ No newline at end of file diff --git a/simpliplay/src/fileHandler.js b/simpliplay/src/fileHandler.js deleted file mode 100644 index 825922c..0000000 --- a/simpliplay/src/fileHandler.js +++ /dev/null @@ -1,110 +0,0 @@ -// src/fileHandler.js -const { dialog, shell, BrowserWindow } = require('electron'); -const fs = require('fs'); -const path = require('path'); -const { pathToFileURL } = require('url'); -const { APP_CONSTANTS } = require('./constants'); - -let hasOpenedFile = false; - -const takeSnapshot = async () => { - const mainWindow = BrowserWindow.getFocusedWindow(); - if (!mainWindow) return; - - try { - const image = await mainWindow.webContents.capturePage(); - const png = image.toPNG(); - - fs.mkdirSync(APP_CONSTANTS.SNAPSHOTS_DIR, { recursive: true }); - const filePath = path.join(APP_CONSTANTS.SNAPSHOTS_DIR, `snapshot-${Date.now()}.png`); - fs.writeFileSync(filePath, png); - - const { response } = await dialog.showMessageBox(mainWindow, { - type: 'info', - title: 'Snapshot Saved', - message: `Snapshot saved to:\n${filePath}`, - buttons: ['OK', 'Open File'], - defaultId: 0, - }); - - if (response === 1) shell.openPath(filePath); - } catch (error) { - dialog.showErrorBox("Snapshot Error", `Failed to capture snapshot: ${error.message}`); - } -}; - -const openFile = (filePath) => { - const mainWindow = BrowserWindow.getFocusedWindow(); - if (!mainWindow) return; - - const fileURL = pathToFileURL(filePath).href; - - if (mainWindow.webContents.isLoading()) { - mainWindow.webContents.once("did-finish-load", () => { - mainWindow.webContents.send("play-media", fileURL); - }); - } else { - mainWindow.webContents.send("play-media", fileURL); - } -}; - -const openFileSafely = (filePath) => { - if (hasOpenedFile) return; - hasOpenedFile = true; - - const absPath = path.resolve(filePath); - if (isValidFileArg(absPath)) { - const winFileURL = pathToFileURL(absPath).href; - const mainWindow = BrowserWindow.getFocusedWindow(); - if (mainWindow?.webContents) { - mainWindow.webContents.send("play-media", winFileURL); - } - } - - setTimeout(() => { hasOpenedFile = false; }, 1000); -}; - -const isValidFileArg = (arg) => { - if (!arg || arg.startsWith('-') || arg.includes('electron')) return false; - - const resolvedPath = path.resolve(arg); - if (!fs.existsSync(resolvedPath)) return false; - - const ext = path.extname(resolvedPath).toLowerCase(); - return !APP_CONSTANTS.BAD_FILE_EXTENSIONS.includes(ext); -}; - -const handleFileOpen = () => { - const args = process.argv.slice(2); - const fileArg = args.find(isValidFileArg); - - if (fileArg) { - app.whenReady().then(() => { - openFileSafely(fileArg); - }); - } - - app.on('open-file', (event, filePath) => { - event.preventDefault(); - openFileSafely(filePath); - }); - - if (['win32', 'linux'].includes(process.platform)) { - if (!app.requestSingleInstanceLock()) { - app.quit(); - } else { - app.on('second-instance', (event, argv) => { - const fileArg = argv.find(isValidFileArg); - if (fileArg) openFileSafely(fileArg); - }); - } - } -}; - -module.exports = { - takeSnapshot, - openFile, - openFileSafely, - isValidFileArg, - handleFileOpen -}; \ No newline at end of file diff --git a/simpliplay/src/menuManager.js b/simpliplay/src/menuManager.js deleted file mode 100644 index 4716cf9..0000000 --- a/simpliplay/src/menuManager.js +++ /dev/null @@ -1,182 +0,0 @@ -// src/menuManager.js -const { Menu, MenuItem, shell, dialog } = require('electron'); -const path = require('path'); -const { pathToFileURL } = require('url'); -const { getMainWindow } = require('./windowManager'); -const { checkForUpdate } = require('./updateChecker'); -const { APP_CONSTANTS } = require('./constants'); -const { takeSnapshot } = require('./fileHandler'); - -const loadedAddons = new Map(); - -const setupMenu = () => { - const template = [ - { - label: 'File', - submenu: [ - { - label: 'Take a Snapshot', - accelerator: 'CommandOrControl+Shift+S', - click: takeSnapshot - } - ] - }, - { - label: 'Add-ons', - submenu: [ - { - label: 'Load Add-on', - accelerator: 'CommandOrControl+Shift+A', - click: handleLoadAddon - }, - { type: 'separator' } - ] - }, - { - label: 'Help', - submenu: [ - { - label: 'Source Code', - click: () => shell.openExternal('https://github.com/A-Star100/simpliplay-desktop') - }, - { - label: 'Website', - click: () => shell.openExternal('https://simpliplay.netlify.app') - }, - { - label: 'Help Center', - click: () => shell.openExternal('https://simpliplay.netlify.app/help') - }, - { type: 'separator' }, - { - label: 'Check for Updates', - accelerator: 'CommandOrControl+Shift+U', - click: () => checkForUpdate(APP_CONSTANTS.VERSION) - }, - { type: 'separator' }, - { - label: 'Quit', - click: () => app.quit() - } - ] - } - ]; - - if (process.platform === 'darwin') { - template.unshift({ - label: 'SimpliPlay', - submenu: [ - { - label: 'Check for Updates', - accelerator: 'CommandOrControl+Shift+U', - click: () => checkForUpdate(APP_CONSTANTS.VERSION) - } - ] - }); - } - - const menu = Menu.buildFromTemplate(template); - Menu.setApplicationMenu(menu); - return menu; -}; - -const setupContextMenu = (window) => { - const contextMenu = new Menu(); - contextMenu.append(new MenuItem({ - label: 'Take a Snapshot', - click: takeSnapshot - })); - contextMenu.append(new MenuItem({ type: 'separator' })); - contextMenu.append(new MenuItem({ - label: 'Inspect', - click: () => window.webContents.openDevTools() - })); - - window.webContents.on('context-menu', (event) => { - event.preventDefault(); - contextMenu.popup({ window }); - }); -}; - -const handleLoadAddon = async () => { - const mainWindow = getMainWindow(); - if (!mainWindow) return; - - const result = await dialog.showOpenDialog(mainWindow, { - title: 'Load Add-on', - filters: [{ name: 'JavaScript Files', extensions: ['simpliplay'] }], - properties: ['openFile'], - }); - - if (result.canceled || result.filePaths.length === 0) return; - - const filePath = result.filePaths[0]; - const fileName = path.basename(filePath); - const fileURL = pathToFileURL(filePath).href; - - if ([...loadedAddons.keys()].some(p => path.basename(p) === fileName)) { - await dialog.showMessageBox(mainWindow, { - type: 'error', - title: 'Could not load addon', - message: `An add-on named "${fileName}" has already been loaded.`, - buttons: ['OK'] - }); - return; - } - - if (!loadedAddons.has(filePath)) { - mainWindow.webContents.send('load-addon', fileURL); - addAddonToMenu(filePath, fileName, fileURL); - } -}; - -const addAddonToMenu = (filePath, fileName, fileURL) => { - const menu = Menu.getApplicationMenu(); - const addonsMenu = menu.items.find(item => item.label === 'Add-ons')?.submenu; - if (!addonsMenu) return; - - const addonItem = new MenuItem({ - label: fileName, - type: 'checkbox', - checked: true, - click: createAddonClickHandler(filePath, fileName, fileURL) - }); - - addonsMenu.append(addonItem); - loadedAddons.set(filePath, addonItem); -}; - -const createAddonClickHandler = (filePath, fileName, fileURL) => { - return async (menuItem) => { - const mainWindow = getMainWindow(); - if (!mainWindow) return; - - if (menuItem.checked) { - fs.access(filePath, (err) => { - if (err) { - handleAddonError(mainWindow, fileName); - menuItem.checked = false; - return; - } - mainWindow.webContents.send('load-addon', fileURL); - }); - } else { - mainWindow.webContents.send('unload-addon', fileURL); - } - }; -}; - -const handleAddonError = async (window, fileName) => { - await dialog.showMessageBox(window, { - type: 'error', - title: 'Could not load addon', - message: `The add-on "${fileName}" could not be found or doesn't exist anymore.`, - buttons: ['OK'] - }); -}; - -module.exports = { - setupMenu, - setupContextMenu, - loadedAddons -}; \ No newline at end of file diff --git a/simpliplay/src/shortcuts.js b/simpliplay/src/shortcuts.js deleted file mode 100644 index ccbbdf0..0000000 --- a/simpliplay/src/shortcuts.js +++ /dev/null @@ -1,46 +0,0 @@ -// src/shortcuts.js -const { globalShortcut, dialog, app } = require('electron'); -const { getMainWindow } = require('./windowManager'); -const { takeSnapshot } = './fileHandler'; - -let didRegisterShortcuts = false; - -const setupShortcuts = () => { - if (didRegisterShortcuts) return; - - // Quit confirmation - globalShortcut.register('CommandOrControl+Q', () => { - const window = getMainWindow(); - if (!window) return; - - dialog.showMessageBox(window, { - type: 'question', - buttons: ['Cancel', 'Quit'], - defaultId: 1, - title: 'Quit?', - message: 'Are you sure you want to quit SimpliPlay?', - }).then(({ response }) => { - if (response === 1) app.quit(); - }); - }); - - // Snapshot shortcuts - ['CommandOrControl+Shift+S', 'CommandOrControl+S'].forEach(accelerator => { - globalShortcut.register(accelerator, () => { - const window = getMainWindow(); - if (window) takeSnapshot(); - }); - }); - - didRegisterShortcuts = true; -}; - -const unregisterShortcuts = () => { - didRegisterShortcuts = false; - globalShortcut.unregisterAll(); -}; - -module.exports = { - setupShortcuts, - unregisterShortcuts -}; \ No newline at end of file diff --git a/simpliplay/src/updateChecker.js b/simpliplay/src/updateChecker.js deleted file mode 100644 index 5b6119c..0000000 --- a/simpliplay/src/updateChecker.js +++ /dev/null @@ -1,83 +0,0 @@ -const https = require('https'); -const { URL } = require('url'); -const { dialog, shell } = require('electron'); - -const latestReleaseUrl = 'https://github.com/A-Star100/simpliplay-desktop/releases/latest/'; - -function fetchRedirectLocation(url) { - return new Promise((resolve, reject) => { - https.get(url, (res) => { - if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) { - resolve(res.headers.location); - } else { - reject(new Error(`Expected redirect but got status code: ${res.statusCode}`)); - } - }).on('error', reject); - }); -} - -function normalizeVersion(version) { - return version.trim().replace(/[^\d\.]/g, ''); -} - -function compareVersions(v1, v2) { - const a = v1.split('.').map(Number); - const b = v2.split('.').map(Number); - const len = Math.max(a.length, b.length); - - for (let i = 0; i < len; i++) { - const num1 = a[i] || 0; - const num2 = b[i] || 0; - if (num1 > num2) return 1; - if (num1 < num2) return -1; - } - return 0; -} - -/** - * Checks for update and shows native dialog if update is available. - * @param {string} currentVersion - */ -async function checkForUpdate(currentVersion) { - try { - const redirectUrl = await fetchRedirectLocation(latestReleaseUrl); - - const urlObj = new URL(redirectUrl); - const parts = urlObj.pathname.split('/'); - - const releaseTag = parts[parts.length - 1]; - const versionMatch = releaseTag.match(/release-([\d\.]+)/); - if (!versionMatch) { - throw new Error(`Could not parse version from release tag: ${releaseTag}`); - } - const latestVersion = normalizeVersion(versionMatch[1]); - - const cmp = compareVersions(latestVersion, currentVersion); - - if (cmp > 0) { - const result = dialog.showMessageBoxSync({ - type: 'info', - buttons: ['Download', 'Later'], - defaultId: 0, - cancelId: 1, - title: 'Update Available', - message: `A new version (${latestVersion}) is available. Would you like to download it?`, - }); - - if (result === 0) { - shell.openExternal("https://simpliplay.netlify.app/#download-options"); - } - } else { - dialog.showMessageBoxSync({ - type: 'info', - buttons: ['OK'], - title: "You're up to date!", - message: `You are using the latest version (${currentVersion}).`, - }); - } - } catch (err) { - dialog.showErrorBox('Could not check for update.', err.message); - } -} - -module.exports = { checkForUpdate }; diff --git a/simpliplay/src/windowManager.js b/simpliplay/src/windowManager.js deleted file mode 100644 index ca079b8..0000000 --- a/simpliplay/src/windowManager.js +++ /dev/null @@ -1,63 +0,0 @@ -// src/windowManager.js -const { BrowserWindow, Menu, dialog } = require('electron'); -const path = require('path'); -const { setupContextMenu } = require('./menuManager'); -const { APP_CONSTANTS } = require('./constants'); - -let mainWindow; - -const createWindow = (onReadyCallback) => { - if (!app.isReady()) { - app.whenReady().then(() => createWindow(onReadyCallback)); - return; - } - - if (mainWindow) mainWindow.close(); - - mainWindow = new BrowserWindow({ - width: 1920, - height: 1080, - webPreferences: { - preload: path.join(__dirname, '../preload.js'), - contextIsolation: true, - enableRemoteModule: false, - nodeIntegration: false, - sandbox: true, - }, - }); - - mainWindow.loadFile('index.html'); - setupWindowHandlers(onReadyCallback); - return mainWindow; -}; - -const setupWindowHandlers = (onReadyCallback) => { - if (!mainWindow) return; - - mainWindow.once('ready-to-show', () => { - if (APP_CONSTANTS.GPU_ACCEL === 'disabled') { - showGpuWarning(); - } - if (onReadyCallback) onReadyCallback(); - }); - - setupContextMenu(mainWindow); -}; - -const showGpuWarning = () => { - dialog.showMessageBox(mainWindow, { - type: 'warning', - buttons: ['OK'], - defaultId: 0, - title: 'Warning!', - message: "Disabling GPU acceleration greatly decreases performance and is not recommended, but if you're curious, I don't wanna stop you.", - }); -}; - -const getMainWindow = () => mainWindow; - -module.exports = { - createWindow, - setupWindowHandlers, - getMainWindow -}; \ No newline at end of file