From 8d29cb7eb8d51eb1b4dc817f5c03ec19e0b37176 Mon Sep 17 00:00:00 2001 From: therealchadwilliamsza-hub Date: Fri, 19 Jun 2026 22:42:39 +0200 Subject: [PATCH 01/10] Fix Streambert review findings --- index.js | 1 + package-lock.json | 230 ++++++++++++++++----------------- package.json | 3 + src/App.jsx | 1 + src/components/UpdateModal.jsx | 26 +++- src/ipc/downloads.js | 6 +- src/ipc/player.js | 71 +++++++--- src/ipc/subtitles.js | 35 +++-- src/pages/HomePage.jsx | 2 +- src/pages/LibraryPage.jsx | 3 +- src/utils/updates.js | 17 ++- src/utils/useRatings.js | 5 +- 12 files changed, 244 insertions(+), 156 deletions(-) diff --git a/index.js b/index.js index 9304011..28aba19 100644 --- a/index.js +++ b/index.js @@ -230,6 +230,7 @@ function createWindow() { additionalArguments: ["--js-flags=--max-old-space-size=256 --expose-gc"], }, }); + playerIpc.registerWindowStateListeners(mainWindow); // Force long-lived disk caching for TMDB images in the default session. session.defaultSession.webRequest.onHeadersReceived( diff --git a/package-lock.json b/package-lock.json index 8527804..68bfc64 100644 --- a/package-lock.json +++ b/package-lock.json @@ -751,9 +751,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz", - "integrity": "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz", + "integrity": "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==", "cpu": [ "ppc64" ], @@ -768,9 +768,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.7.tgz", - "integrity": "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.1.tgz", + "integrity": "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==", "cpu": [ "arm" ], @@ -785,9 +785,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz", - "integrity": "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.1.tgz", + "integrity": "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==", "cpu": [ "arm64" ], @@ -802,9 +802,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.7.tgz", - "integrity": "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.1.tgz", + "integrity": "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==", "cpu": [ "x64" ], @@ -819,9 +819,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz", - "integrity": "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.1.tgz", + "integrity": "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==", "cpu": [ "arm64" ], @@ -836,9 +836,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz", - "integrity": "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.1.tgz", + "integrity": "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==", "cpu": [ "x64" ], @@ -853,9 +853,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz", - "integrity": "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.1.tgz", + "integrity": "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==", "cpu": [ "arm64" ], @@ -870,9 +870,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz", - "integrity": "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.1.tgz", + "integrity": "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==", "cpu": [ "x64" ], @@ -887,9 +887,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz", - "integrity": "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.1.tgz", + "integrity": "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==", "cpu": [ "arm" ], @@ -904,9 +904,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz", - "integrity": "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.1.tgz", + "integrity": "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==", "cpu": [ "arm64" ], @@ -921,9 +921,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz", - "integrity": "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.1.tgz", + "integrity": "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==", "cpu": [ "ia32" ], @@ -938,9 +938,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz", - "integrity": "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.1.tgz", + "integrity": "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==", "cpu": [ "loong64" ], @@ -955,9 +955,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz", - "integrity": "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.1.tgz", + "integrity": "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==", "cpu": [ "mips64el" ], @@ -972,9 +972,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz", - "integrity": "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.1.tgz", + "integrity": "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==", "cpu": [ "ppc64" ], @@ -989,9 +989,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz", - "integrity": "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.1.tgz", + "integrity": "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==", "cpu": [ "riscv64" ], @@ -1006,9 +1006,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz", - "integrity": "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.1.tgz", + "integrity": "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==", "cpu": [ "s390x" ], @@ -1023,9 +1023,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz", - "integrity": "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.1.tgz", + "integrity": "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==", "cpu": [ "x64" ], @@ -1040,9 +1040,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz", - "integrity": "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.1.tgz", + "integrity": "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==", "cpu": [ "arm64" ], @@ -1057,9 +1057,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz", - "integrity": "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.1.tgz", + "integrity": "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==", "cpu": [ "x64" ], @@ -1074,9 +1074,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz", - "integrity": "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.1.tgz", + "integrity": "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==", "cpu": [ "arm64" ], @@ -1091,9 +1091,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz", - "integrity": "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.1.tgz", + "integrity": "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==", "cpu": [ "x64" ], @@ -1108,9 +1108,9 @@ } }, "node_modules/@esbuild/openharmony-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz", - "integrity": "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.1.tgz", + "integrity": "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==", "cpu": [ "arm64" ], @@ -1125,9 +1125,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz", - "integrity": "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.1.tgz", + "integrity": "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==", "cpu": [ "x64" ], @@ -1142,9 +1142,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz", - "integrity": "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.1.tgz", + "integrity": "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==", "cpu": [ "arm64" ], @@ -1159,9 +1159,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz", - "integrity": "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.1.tgz", + "integrity": "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==", "cpu": [ "ia32" ], @@ -1176,9 +1176,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz", - "integrity": "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.1.tgz", + "integrity": "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==", "cpu": [ "x64" ], @@ -3406,9 +3406,9 @@ "optional": true }, "node_modules/esbuild": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz", - "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz", + "integrity": "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -3419,32 +3419,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.27.7", - "@esbuild/android-arm": "0.27.7", - "@esbuild/android-arm64": "0.27.7", - "@esbuild/android-x64": "0.27.7", - "@esbuild/darwin-arm64": "0.27.7", - "@esbuild/darwin-x64": "0.27.7", - "@esbuild/freebsd-arm64": "0.27.7", - "@esbuild/freebsd-x64": "0.27.7", - "@esbuild/linux-arm": "0.27.7", - "@esbuild/linux-arm64": "0.27.7", - "@esbuild/linux-ia32": "0.27.7", - "@esbuild/linux-loong64": "0.27.7", - "@esbuild/linux-mips64el": "0.27.7", - "@esbuild/linux-ppc64": "0.27.7", - "@esbuild/linux-riscv64": "0.27.7", - "@esbuild/linux-s390x": "0.27.7", - "@esbuild/linux-x64": "0.27.7", - "@esbuild/netbsd-arm64": "0.27.7", - "@esbuild/netbsd-x64": "0.27.7", - "@esbuild/openbsd-arm64": "0.27.7", - "@esbuild/openbsd-x64": "0.27.7", - "@esbuild/openharmony-arm64": "0.27.7", - "@esbuild/sunos-x64": "0.27.7", - "@esbuild/win32-arm64": "0.27.7", - "@esbuild/win32-ia32": "0.27.7", - "@esbuild/win32-x64": "0.27.7" + "@esbuild/aix-ppc64": "0.28.1", + "@esbuild/android-arm": "0.28.1", + "@esbuild/android-arm64": "0.28.1", + "@esbuild/android-x64": "0.28.1", + "@esbuild/darwin-arm64": "0.28.1", + "@esbuild/darwin-x64": "0.28.1", + "@esbuild/freebsd-arm64": "0.28.1", + "@esbuild/freebsd-x64": "0.28.1", + "@esbuild/linux-arm": "0.28.1", + "@esbuild/linux-arm64": "0.28.1", + "@esbuild/linux-ia32": "0.28.1", + "@esbuild/linux-loong64": "0.28.1", + "@esbuild/linux-mips64el": "0.28.1", + "@esbuild/linux-ppc64": "0.28.1", + "@esbuild/linux-riscv64": "0.28.1", + "@esbuild/linux-s390x": "0.28.1", + "@esbuild/linux-x64": "0.28.1", + "@esbuild/netbsd-arm64": "0.28.1", + "@esbuild/netbsd-x64": "0.28.1", + "@esbuild/openbsd-arm64": "0.28.1", + "@esbuild/openbsd-x64": "0.28.1", + "@esbuild/openharmony-arm64": "0.28.1", + "@esbuild/sunos-x64": "0.28.1", + "@esbuild/win32-arm64": "0.28.1", + "@esbuild/win32-ia32": "0.28.1", + "@esbuild/win32-x64": "0.28.1" } }, "node_modules/escalade": { @@ -3593,17 +3593,17 @@ } }, "node_modules/form-data": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", - "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz", + "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==", "dev": true, "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", - "hasown": "^2.0.2", - "mime-types": "^2.1.12" + "hasown": "^2.0.4", + "mime-types": "^2.1.35" }, "engines": { "node": ">= 6" @@ -5526,9 +5526,9 @@ } }, "node_modules/undici": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.26.0.tgz", - "integrity": "sha512-4yqz8a3n5HmGTlsbADNtr/dJlhkh/55Rq798G6ibiULcXbDtaLpTl1pvdqcbFfeoj3iSi52lePFM7h9H21cw/A==", + "version": "6.27.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.27.0.tgz", + "integrity": "sha512-YmfV3YnEDzXRC5lZ2jWtWWHKGUm1zIt8AhesR1tens+HTNv+YZlN/dp6G727LOvMJ8xjP9Be7Y2Sdr96LDm+pg==", "dev": true, "license": "MIT", "engines": { diff --git a/package.json b/package.json index de90d1a..032a660 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,9 @@ "terser": "^5.46.0", "vite": "^7.3.1" }, + "overrides": { + "esbuild": "^0.28.1" + }, "build": { "appId": "com.truelockmc.streambert", "productName": "Streambert", diff --git a/src/App.jsx b/src/App.jsx index d7cfead..82a3310 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1025,6 +1025,7 @@ export default function App() { onMarkWatched={markWatched} onMarkUnwatched={markUnwatched} onRemoveHistory={removeHistory} + apiKey={apiKey} /> )} {page === "settings" && ( diff --git a/src/components/UpdateModal.jsx b/src/components/UpdateModal.jsx index 55da2c5..ed3717a 100644 --- a/src/components/UpdateModal.jsx +++ b/src/components/UpdateModal.jsx @@ -446,9 +446,11 @@ export default function UpdateModal({ return () => window.electron.offUpdateProgress(handler); }, []); - const assetUrl = format && assets?.[format]; + const asset = format ? assets?.[format] : null; + const assetUrl = typeof asset === "string" ? asset : asset?.url; + const assetDigest = typeof asset === "string" ? null : asset?.digest; const canInstall = - format && assetUrl && activeDownloads === 0 && phase === "idle"; + format && assetUrl && assetDigest && activeDownloads === 0 && phase === "idle"; const handleInstall = async () => { if (!canInstall) return; @@ -461,6 +463,7 @@ export default function UpdateModal({ const result = await window.electron.downloadAndInstallUpdate({ url: assetUrl, format, + digest: assetDigest, }); if (cancelRef.current) return; if (!result.ok) throw new Error(result.error || "Update failed"); @@ -707,6 +710,25 @@ export default function UpdateModal({ )} + {format && assetUrl && !assetDigest && phase === "idle" && ( +
+ This release asset does not include a checksum. Use the{" "} + { + e.preventDefault(); + window.electron?.openExternal(url); + }} + style={{ color: "var(--red)", cursor: "pointer" }} + > + GitHub releases page + {" "} + to download manually. +
+ )} + {/* Progress bar */} {(phase === "downloading" || phase === "installing") && (
diff --git a/src/ipc/downloads.js b/src/ipc/downloads.js index 396b3d2..34d499b 100644 --- a/src/ipc/downloads.js +++ b/src/ipc/downloads.js @@ -719,7 +719,7 @@ function register(getMainWindow) { ipcMain.handle("get-downloads", () => downloads); - ipcMain.handle("delete-download", (_, { id, filePath }) => { + ipcMain.handle("delete-download", (_, { id }) => { try { const dlEntry = downloads.find((d) => d.id === id); if (activeProcs.has(id)) { @@ -728,9 +728,9 @@ function register(getMainWindow) { } catch {} activeProcs.delete(id); } - if (filePath) { + if (dlEntry?.filePath) { try { - if (fs.existsSync(filePath)) fs.unlinkSync(filePath); + if (fs.existsSync(dlEntry.filePath)) fs.unlinkSync(dlEntry.filePath); } catch {} } for (const sp of dlEntry?.subtitlePaths || []) { diff --git a/src/ipc/player.js b/src/ipc/player.js index 88c9616..42ab7a2 100644 --- a/src/ipc/player.js +++ b/src/ipc/player.js @@ -7,9 +7,40 @@ const fs = require("fs"); const https = require("https"); const http = require("http"); const os = require("os"); +const crypto = require("crypto"); let _updateAbortController = null; +function normaliseSha256Digest(digest) { + if (typeof digest !== "string") return null; + const trimmed = digest.trim().toLowerCase(); + const match = + trimmed.match(/^sha256:([a-f0-9]{64})$/) || + trimmed.match(/^([a-f0-9]{64})$/); + return match ? match[1] : null; +} + +function sha256File(filePath) { + return new Promise((resolve, reject) => { + const hash = crypto.createHash("sha256"); + const stream = fs.createReadStream(filePath); + stream.on("data", (chunk) => hash.update(chunk)); + stream.on("error", reject); + stream.on("end", () => resolve(hash.digest("hex"))); + }); +} + +function registerWindowStateListeners(win) { + if (!win || win.isDestroyed()) return; + const pushMaximized = (v) => { + if (!win.isDestroyed()) win.webContents.send("window-maximized", v); + }; + win.on("maximize", () => pushMaximized(true)); + win.on("unmaximize", () => pushMaximized(false)); + win.on("enter-full-screen", () => pushMaximized(true)); + win.on("leave-full-screen", () => pushMaximized(false)); +} + function register(getMainWindow, { writeSecretMigration }) { // ── Open file at specific timestamp in mpv / VLC ───────────────────────── ipcMain.handle( @@ -113,19 +144,6 @@ function register(getMainWindow, { writeSecretMigration }) { return mw ? mw.isMaximized() : false; }); - // Push maximize state to the renderer so WindowTitlebar doesn't need to poll - const pushMaximized = (v) => { - const mw = getMainWindow(); - if (mw && !mw.isDestroyed()) mw.webContents.send("window-maximized", v); - }; - const mwForEvents = getMainWindow(); - if (mwForEvents) { - mwForEvents.on("maximize", () => pushMaximized(true)); - mwForEvents.on("unmaximize", () => pushMaximized(false)); - mwForEvents.on("enter-full-screen", () => pushMaximized(true)); - mwForEvents.on("leave-full-screen", () => pushMaximized(false)); - } - ipcMain.handle("quit-app", () => { const mw = getMainWindow(); if (mw && !mw.isDestroyed()) mw.close(); @@ -208,7 +226,7 @@ function register(getMainWindow, { writeSecretMigration }) { return null; }); - ipcMain.handle("download-and-install-update", async (_, { url, format }) => { + ipcMain.handle("download-and-install-update", async (_, { url, format, digest }) => { try { const ALLOWED_FORMATS = ["exe", "deb", "pacman", "dmg", "dmg_arm64", "appimage"]; @@ -216,6 +234,11 @@ function register(getMainWindow, { writeSecretMigration }) { return { ok: false, error: "Invalid format" }; } + const expectedSha256 = normaliseSha256Digest(digest); + if (!expectedSha256) { + return { ok: false, error: "Missing or invalid update checksum" }; + } + const TRUSTED_ORIGIN = "https://github.com"; const TRUSTED_PATH = "/truelockmc/streambert/releases/download/"; // Domains that are allowed as redirect targets (GitHub CDN). @@ -340,6 +363,24 @@ function register(getMainWindow, { writeSecretMigration }) { if (signal.aborted) return { ok: false, error: "Cancelled" }; + const sendVerifying = () => { + const mw = getMainWindow(); + if (mw && !mw.isDestroyed()) { + mw.webContents.send("update-progress", { + percent: 100, + label: "Verifying…", + }); + } + }; + sendVerifying(); + const actualSha256 = await sha256File(destPath); + if (actualSha256 !== expectedSha256) { + try { + fs.unlinkSync(destPath); + } catch {} + return { ok: false, error: "Update checksum verification failed" }; + } + // ── Helper: send "Installing…" to renderer ────────────────────────────── const sendInstalling = () => { const mw = getMainWindow(); @@ -520,4 +561,4 @@ function register(getMainWindow, { writeSecretMigration }) { }); } -module.exports = { register }; +module.exports = { register, registerWindowStateListeners }; diff --git a/src/ipc/subtitles.js b/src/ipc/subtitles.js index f489dd3..69d41fd 100644 --- a/src/ipc/subtitles.js +++ b/src/ipc/subtitles.js @@ -507,18 +507,31 @@ function register({ getDownloads, saveDownloads }) { // ── Delete a single subtitle file ───────────────────────────────────────── ipcMain.handle("delete-subtitle-file", (_, { downloadId, subtitlePath }) => { try { - if (subtitlePath && fs.existsSync(subtitlePath)) - fs.unlinkSync(subtitlePath); - if (downloadId) { - const downloads = getDownloads(); - const idx = downloads.findIndex((d) => d.id === downloadId); - if (idx >= 0) { - downloads[idx].subtitlePaths = ( - downloads[idx].subtitlePaths || [] - ).filter((sp) => sp.path !== subtitlePath); - saveDownloads(); - } + if (!downloadId || !subtitlePath) { + return { ok: false, error: "Missing subtitle record" }; } + + const downloads = getDownloads(); + const idx = downloads.findIndex((d) => d.id === downloadId); + if (idx < 0) return { ok: false, error: "Download not found" }; + + const requestedPath = path.resolve(subtitlePath); + const subtitlePaths = downloads[idx].subtitlePaths || []; + const stored = subtitlePaths.find((sp) => { + const p = typeof sp === "string" ? sp : sp?.path; + return p && path.resolve(p) === requestedPath; + }); + if (!stored) return { ok: false, error: "Subtitle path not registered" }; + + const storedPath = typeof stored === "string" ? stored : stored.path; + if (storedPath && fs.existsSync(storedPath)) { + fs.unlinkSync(storedPath); + } + downloads[idx].subtitlePaths = subtitlePaths.filter((sp) => { + const p = typeof sp === "string" ? sp : sp?.path; + return p && path.resolve(p) !== requestedPath; + }); + saveDownloads(); return { ok: true }; } catch (e) { return { ok: false, error: e.message }; diff --git a/src/pages/HomePage.jsx b/src/pages/HomePage.jsx index 59b7584..c369608 100644 --- a/src/pages/HomePage.jsx +++ b/src/pages/HomePage.jsx @@ -70,7 +70,7 @@ export default function HomePage({ [inProgress, trending, trendingTV, recommendedItems, topRatedItems], ); - const { ratingsMap, ageLimitSetting } = useRatings(allItems); + const { ratingsMap, ageLimitSetting } = useRatings(allItems, apiKey); const getRating = useCallback( (item) => getRatingForItem(item, ratingsMap), diff --git a/src/pages/LibraryPage.jsx b/src/pages/LibraryPage.jsx index 7ddeb60..347c083 100644 --- a/src/pages/LibraryPage.jsx +++ b/src/pages/LibraryPage.jsx @@ -16,12 +16,13 @@ export default function LibraryPage({ onMarkWatched, onMarkUnwatched, onRemoveHistory, + apiKey, }) { const allItems = useMemo( () => [...inProgress, ...saved], [inProgress, saved], ); - const { ratingsMap, ageLimitSetting } = useRatings(allItems); + const { ratingsMap, ageLimitSetting } = useRatings(allItems, apiKey); const [sort, setSort] = useState( () => storage.get(STORAGE_KEYS.LIBRARY_SORT) || "manual", diff --git a/src/utils/updates.js b/src/utils/updates.js index fd78192..343568b 100644 --- a/src/utils/updates.js +++ b/src/utils/updates.js @@ -52,19 +52,26 @@ export async function checkForUpdates() { const url = data.html_url || `https://github.com/${GITHUB_REPO}/releases/latest`; + const toAssetInfo = (asset) => ({ + url: asset.browser_download_url, + digest: asset.digest || null, + name: asset.name, + size: asset.size || null, + }); + // Map release assets to install formats const assets = {}; for (const asset of data.assets || []) { const name = asset.name.toLowerCase(); if (name.endsWith(".appimage")) - assets.appimage = asset.browser_download_url; - else if (name.endsWith(".deb")) assets.deb = asset.browser_download_url; + assets.appimage = toAssetInfo(asset); + else if (name.endsWith(".deb")) assets.deb = toAssetInfo(asset); else if (name.endsWith(".exe")) - assets.exe = asset.browser_download_url; + assets.exe = toAssetInfo(asset); else if (name.endsWith(".pacman")) - assets.pacman = asset.browser_download_url; + assets.pacman = toAssetInfo(asset); else if (name.endsWith(".dmg")) - assets.dmg = asset.browser_download_url; + assets.dmg = toAssetInfo(asset); } return { diff --git a/src/utils/useRatings.js b/src/utils/useRatings.js index b928cf9..926cb75 100644 --- a/src/utils/useRatings.js +++ b/src/utils/useRatings.js @@ -5,7 +5,7 @@ import { getAgeLimitSetting, getRatingCountry, } from "./ageRating"; -import { storage, getApiKey } from "./storage"; +import { storage } from "./storage"; const CACHE_KEY = "ratingsCache"; const CACHE_TTL = 1000 * 60 * 60 * 24 * 7; // 7 days @@ -41,13 +41,12 @@ function evictStale(cache) { /** * Hook that fetches + caches age ratings for an array of items. */ -export function useRatings(items) { +export function useRatings(items, apiKey) { const [ratingsMap, setRatingsMap] = useState({}); // Read stable settings once — these only change when user visits Settings, // which unmounts/remounts affected pages anyway, so useState(init) is correct. const [ageLimitSetting] = useState(() => getAgeLimitSetting(storage)); const [ratingCountry] = useState(() => getRatingCountry(storage)); - const [apiKey] = useState(() => getApiKey()); const itemsKey = useMemo(() => { if (!items?.length) return ""; From 03391f9e8dd7432a5068ed3c972475d4e33bb73a Mon Sep 17 00:00:00 2001 From: therealchadwilliamsza-hub Date: Sat, 20 Jun 2026 01:05:36 +0200 Subject: [PATCH 02/10] Add web-accessible deployment mode --- .gitignore | 1 + package.json | 1 + src/components/DownloadModal.jsx | 11 +- src/components/SetupScreen.jsx | 4 + src/components/Sidebar.jsx | 21 ++- src/components/TrailerModal.jsx | 28 ++- src/pages/MoviePage.jsx | 262 +++++++++++++++------------- src/pages/TVPage.jsx | 286 +++++++++++++++++-------------- src/styles/global.css | 3 +- src/utils/storage.js | 17 +- vercel.json | 11 ++ 11 files changed, 382 insertions(+), 263 deletions(-) create mode 100644 vercel.json diff --git a/.gitignore b/.gitignore index 16f4faf..bd56d09 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ node_modules/ +.vercel/ temp/ download_config.ini dist/ diff --git a/package.json b/package.json index 032a660..bc688e8 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "private": true, "scripts": { "dev": "vite build --watch", + "build": "vite build", "start": "vite build && electron .", "dist": "cross-env ELECTRON_DIST=1 vite build && npm run dist:linux && npm run dist:win && npm run dist:mac", "dist:linux": "cross-env ELECTRON_DIST=1 vite build && electron-builder --linux deb rpm AppImage pacman --publish never", diff --git a/src/components/DownloadModal.jsx b/src/components/DownloadModal.jsx index 358fc84..1383345 100644 --- a/src/components/DownloadModal.jsx +++ b/src/components/DownloadModal.jsx @@ -498,10 +498,12 @@ export default function DownloadModal({ }, [m3u8Url, subEnabled, wyzieApiKey]); const pickBinaryFolder = async () => { + if (!isElectron) return; const folder = await window.electron.pickFolder(); if (folder) setDownloaderFolder(folder); }; const pickDownloadFolder = async () => { + if (!isElectron) return; const folder = await window.electron.pickFolder(); if (folder) { setDownloadPath(folder); @@ -511,6 +513,7 @@ export default function DownloadModal({ }; const handleDownload = async () => { + if (!isElectron) return; if (!downloader?.token || !downloadPath || !m3u8Url) return; setDownloadStatus("starting"); @@ -1060,11 +1063,13 @@ export default function DownloadModal({ Download the latest release from{" "} { + if (!isElectron) return; e.preventDefault(); - isElectron && - window.electron.openExternal(releaseUrl); + window.electron.openExternal(releaseUrl); }} > github.com/truelockmc/vid-dl-cli-only/releases/latest diff --git a/src/components/SetupScreen.jsx b/src/components/SetupScreen.jsx index 58f3132..d3a425e 100644 --- a/src/components/SetupScreen.jsx +++ b/src/components/SetupScreen.jsx @@ -72,11 +72,15 @@ function errorMessage(reason, status) { } function ExternalLink({ href, className, children }) { + const isDesktop = typeof window !== "undefined" && !!window.electron; return ( { + if (!isDesktop) return; e.preventDefault(); window.electron.openExternal(href); }} diff --git a/src/components/Sidebar.jsx b/src/components/Sidebar.jsx index 522530c..2b3fa32 100644 --- a/src/components/Sidebar.jsx +++ b/src/components/Sidebar.jsx @@ -1,5 +1,6 @@ import { useState, useRef, useEffect } from "react"; import { imgUrl } from "../utils/api"; +import { isElectron } from "../utils/storage"; import { useSeasonalEvent } from "../components/seasonalEvents.jsx"; import { StreambertLogo, @@ -228,15 +229,17 @@ export default function Sidebar({ icon={} label="Settings" /> - + {isElectron && ( + + )}
); diff --git a/src/components/TrailerModal.jsx b/src/components/TrailerModal.jsx index 94b76e0..c7fbfe9 100644 --- a/src/components/TrailerModal.jsx +++ b/src/components/TrailerModal.jsx @@ -1,6 +1,6 @@ import { useEffect, useRef, useState, useCallback } from "react"; import { CloseIcon, ExternalLinkIcon } from "./Icons"; -import { storage } from "../utils/storage"; +import { isElectron, storage } from "../utils/storage"; export const DEFAULT_INVIDIOUS_BASE = "https://inv.nadeko.net"; @@ -104,12 +104,14 @@ export default function TrailerModal({ trailerKey, title, onClose }) { const openInBrowser = () => { const preferred = getInvidiousBase(); const url = `${preferred}/watch?v=${trailerKey}`; - window.electron?.openExternal(url); + if (isElectron) window.electron?.openExternal(url); + else window.open(url, "_blank", "noreferrer"); }; useEffect(() => { const wv = webviewRef.current; if (!wv || !currentSrc) return; + if (!isElectron) return; const onLoad = () => { wv.executeJavaScript(DETECT_BOT_JS) @@ -231,7 +233,8 @@ export default function TrailerModal({ trailerKey, title, onClose }) { )} - {currentSrc && ( + {currentSrc && + (isElectron ? ( - )} + ) : ( +