mirror of
https://github.com/MasterAcnolo/Freedom-Loader.git
synced 2026-07-29 18:25:47 +02:00
feat: support Linux. Refactor resources architecture. Create build script
Currently, themes are disabled and firefox notification too.
This commit is contained in:
26
.gitignore
vendored
26
.gitignore
vendored
@@ -1,15 +1,23 @@
|
|||||||
|
# Dependencies
|
||||||
/node_modules
|
/node_modules
|
||||||
/downloads
|
|
||||||
|
# Build Output Folder
|
||||||
/dist
|
/dist
|
||||||
/out
|
/out
|
||||||
|
|
||||||
/ressources/ffmpeg.exe
|
# Config JSON File. Create it and fill it if you want to have dev only settings
|
||||||
/ressources/ffprobe.exe
|
|
||||||
/ressources/deno.exe
|
|
||||||
|
|
||||||
/theme/**
|
|
||||||
|
|
||||||
config/config.dev.json
|
config/config.dev.json
|
||||||
|
|
||||||
logs/*.json
|
# IDE
|
||||||
logs/*.log
|
/.idea
|
||||||
|
/.vscode
|
||||||
|
|
||||||
|
# Linux executable
|
||||||
|
ffmpeg
|
||||||
|
ffprobe
|
||||||
|
deno
|
||||||
|
|
||||||
|
# Windows Executable
|
||||||
|
ffmpeg.exe
|
||||||
|
ffprobe.exe
|
||||||
|
deno.exe
|
||||||
@@ -275,7 +275,7 @@ Freedom-Loader/
|
|||||||
│ ├── settingsPanel.css
|
│ ├── settingsPanel.css
|
||||||
│ ├── topbar.css
|
│ ├── topbar.css
|
||||||
│ └── videoinfo.css
|
│ └── videoinfo.css
|
||||||
├── ressources/ # Internal resources (binaries)
|
├── resources/ # Internal resources (binaries)
|
||||||
├── server/ # Express backend server
|
├── server/ # Express backend server
|
||||||
│ ├── logger.js
|
│ ├── logger.js
|
||||||
│ ├── server.js
|
│ ├── server.js
|
||||||
@@ -386,7 +386,7 @@ npm run build
|
|||||||
This project relies on several external binaries.
|
This project relies on several external binaries.
|
||||||
Some of them are **not included** in the public repository and must be added manually.
|
Some of them are **not included** in the public repository and must be added manually.
|
||||||
|
|
||||||
You must download the required binaries and place them in the `ressources` folder, using the exact filenames listed below.
|
You must download the required binaries and place them in the `resources` folder, using the exact filenames listed below.
|
||||||
|
|
||||||
#### Required binaries
|
#### Required binaries
|
||||||
|
|
||||||
@@ -407,7 +407,7 @@ You must download the required binaries and place them in the `ressources` folde
|
|||||||
Final folder structure:
|
Final folder structure:
|
||||||
|
|
||||||
```
|
```
|
||||||
ressources/
|
resources/
|
||||||
├── deno.exe
|
├── deno.exe
|
||||||
├── ffmpeg.exe
|
├── ffmpeg.exe
|
||||||
├── ffprobe.exe
|
├── ffprobe.exe
|
||||||
|
|||||||
@@ -25,15 +25,19 @@ function checkNativeDependencies() {
|
|||||||
*/
|
*/
|
||||||
const { binaryPaths } = require("../server/helpers/path.helpers");
|
const { binaryPaths } = require("../server/helpers/path.helpers");
|
||||||
|
|
||||||
|
const isWindows = process.platform === "win32";
|
||||||
|
|
||||||
|
const addExe = isWindows ? ".exe" : "";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of required external executables used by the application runtime.
|
* List of required external executables used by the application runtime.
|
||||||
* Each entry defines a binary name and its resolved absolute path.
|
* Each entry defines a binary name and its resolved absolute path.
|
||||||
*/
|
*/
|
||||||
const deps = [
|
const deps = [
|
||||||
{ name: "yt-dlp.exe", path: binaryPaths.ytDlp },
|
{ name: `yt-dlp${addExe}`, path: binaryPaths.ytDlp },
|
||||||
{ name: "ffmpeg.exe", path: binaryPaths.ffmpeg },
|
{ name: `ffmpeg${addExe}`, path: binaryPaths.ffmpeg },
|
||||||
{ name: "ffprobe.exe", path: binaryPaths.ffprobe },
|
{ name: `ffprobe${addExe}`, path: binaryPaths.ffprobe },
|
||||||
{ name: "deno.exe", path: binaryPaths.deno },
|
{ name: `deno${addExe}`, path: binaryPaths.deno },
|
||||||
];
|
];
|
||||||
|
|
||||||
const missing = deps.filter(d => !fs.existsSync(d.path));
|
const missing = deps.filter(d => !fs.existsSync(d.path));
|
||||||
@@ -52,7 +56,7 @@ function checkNativeDependencies() {
|
|||||||
app.whenReady().then(() => {
|
app.whenReady().then(() => {
|
||||||
dialog.showErrorBox(
|
dialog.showErrorBox(
|
||||||
"Missing dependencies",
|
"Missing dependencies",
|
||||||
`The following files are missing in the 'ressources' folder:\n${list}\n\nThe application will now exit. Try to reinstall.`
|
`The following files are missing in the 'resources' folder:\n${list}\n\nThe application will now exit. Try to reinstall.`
|
||||||
);
|
);
|
||||||
app.quit();
|
app.quit();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -62,10 +62,19 @@ function validateDownloadPath(userPath) {
|
|||||||
* Required to prevent path traversal or alias bypass.
|
* Required to prevent path traversal or alias bypass.
|
||||||
*/
|
*/
|
||||||
const resolved = fs.realpathSync(path.resolve(userPath));
|
const resolved = fs.realpathSync(path.resolve(userPath));
|
||||||
|
|
||||||
|
if (!fs.existsSync(resolved)) {
|
||||||
|
fs.mkdirSync(resolved, { recursive: true });
|
||||||
|
logger.info(`Download folder created: ${resolved}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const real = fs.realpathSync(resolved);
|
||||||
|
|
||||||
if (!isSafePath(resolved)) {
|
if (!isSafePath(resolved)) {
|
||||||
throw new Error("Path not allowed: system folders are blocked!");
|
throw new Error("Path not allowed: system folders are blocked!");
|
||||||
}
|
}
|
||||||
return resolved;
|
|
||||||
|
return real;
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
|
|
||||||
|
|||||||
BIN
build/app-icon.png
Executable file
BIN
build/app-icon.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 107 KiB |
13
main.js
13
main.js
@@ -79,7 +79,7 @@ const { updateYtDlp } = require("./app/ytDlpUpdater");
|
|||||||
const { createMainWindow, getMainWindow } = require("./app/windowManager");
|
const { createMainWindow, getMainWindow } = require("./app/windowManager");
|
||||||
const { registerIpcHandlers } = require("./app/ipcHandlers");
|
const { registerIpcHandlers } = require("./app/ipcHandlers");
|
||||||
const { createSplashWindow, closeSplashWindow, setSplashProgress } = require("./app/splashManager");
|
const { createSplashWindow, closeSplashWindow, setSplashProgress } = require("./app/splashManager");
|
||||||
const { userThemesPath, initUserThemes } = require("./server/helpers/path.helpers");
|
const { userThemesPath, initUserThemes, isWindows } = require("./server/helpers/path.helpers");
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -116,8 +116,15 @@ app.whenReady().then(async () => {
|
|||||||
registerIpcHandlers(getMainWindow);
|
registerIpcHandlers(getMainWindow);
|
||||||
|
|
||||||
setSplashProgress(2); // Loading themes
|
setSplashProgress(2); // Loading themes
|
||||||
initUserThemes();
|
|
||||||
await initThemes(userThemesPath);
|
// TODO: Patch this, i disable this features for the moment
|
||||||
|
// Themes are currently disabled if we are not on Windows
|
||||||
|
if(isWindows){
|
||||||
|
initUserThemes();
|
||||||
|
await initThemes(userThemesPath);
|
||||||
|
} else {
|
||||||
|
logger.info(`OS is ${process.platform}, Skipping themes loading`)
|
||||||
|
}
|
||||||
|
|
||||||
setSplashProgress(3); // Almost ready
|
setSplashProgress(3); // Almost ready
|
||||||
await createMainWindow();
|
await createMainWindow();
|
||||||
|
|||||||
85
package.json
85
package.json
@@ -1,13 +1,17 @@
|
|||||||
{
|
{
|
||||||
"name": "freedom-loader",
|
"name": "freedom-loader",
|
||||||
"productName": "Freedom Loader",
|
"productName": "Freedom Loader",
|
||||||
"version": "1.5.1",
|
"version": "1.6.0",
|
||||||
"author": "MasterAcnolo",
|
"author": "MasterAcnolo <MasterAcnolo@users.noreply.github.com>",
|
||||||
"description": "Freedom Loader",
|
"description": "Freedom Loader",
|
||||||
"main": "main.js",
|
"main": "main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "electron --trace-warnings .",
|
"start": "electron --trace-warnings .",
|
||||||
|
"postinstall": "chmod +x resources/binaries/linux/* 2>/dev/null || true",
|
||||||
"build": "electron-builder",
|
"build": "electron-builder",
|
||||||
|
"build:win": "electron-builder --win",
|
||||||
|
"build:linux": "electron-builder --linux",
|
||||||
|
"build:all": "electron-builder -wl",
|
||||||
"update": "npm update"
|
"update": "npm update"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -26,7 +30,6 @@
|
|||||||
"electron": "^41.2.0",
|
"electron": "^41.2.0",
|
||||||
"electron-builder": "^26.8.1",
|
"electron-builder": "^26.8.1",
|
||||||
"@electron/devtron": "^2.1.1"
|
"@electron/devtron": "^2.1.1"
|
||||||
|
|
||||||
},
|
},
|
||||||
"build": {
|
"build": {
|
||||||
"appId": "com.masteracnolo.freedomloader",
|
"appId": "com.masteracnolo.freedomloader",
|
||||||
@@ -49,54 +52,23 @@
|
|||||||
},
|
},
|
||||||
"asar": true,
|
"asar": true,
|
||||||
"extraResources": [
|
"extraResources": [
|
||||||
{
|
{ "from": "theme", "to": "theme" },
|
||||||
"from": "theme",
|
{ "from": "build/confirm-icon.png", "to": "confirm-icon.png" },
|
||||||
"to": "theme"
|
{ "from": "build/banner.bmp", "to": "banner.bmp" },
|
||||||
},
|
{ "from": "build/banner.png", "to": "banner.png" },
|
||||||
{
|
{ "from": "build/error.png", "to": "error.png" },
|
||||||
"from": "build/confirm-icon.png",
|
{ "from": "config/config.default.json", "to": "config/config.default.json" }
|
||||||
"to": "confirm-icon.png"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"from": "build/app-icon.ico",
|
|
||||||
"to": "app-icon.ico"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"from": "build/banner.bmp",
|
|
||||||
"to": "banner.bmp"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"from": "build/banner.png",
|
|
||||||
"to": "banner.png"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"from": "build/error.png",
|
|
||||||
"to": "error.png"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"from": "ressources/yt-dlp.exe",
|
|
||||||
"to": "binaries/yt-dlp.exe"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"from": "ressources/ffmpeg.exe",
|
|
||||||
"to": "binaries/ffmpeg.exe"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"from": "ressources/ffprobe.exe",
|
|
||||||
"to": "binaries/ffprobe.exe"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"from": "ressources/deno.exe",
|
|
||||||
"to": "binaries/deno.exe"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"from": "config/config.default.json",
|
|
||||||
"to": "config/config.default.json"
|
|
||||||
}
|
|
||||||
],
|
],
|
||||||
"win": {
|
"win": {
|
||||||
"target": "nsis",
|
"target": "nsis",
|
||||||
"icon": "build/app-icon.ico"
|
"icon": "build/app-icon.ico",
|
||||||
|
"artifactName": "${productName}-Setup-${version}.${ext}",
|
||||||
|
"extraResources": [
|
||||||
|
{ "from": "resources/binaries/win-32/yt-dlp.exe", "to": "binaries/yt-dlp.exe" },
|
||||||
|
{ "from": "resources/binaries/win-32/ffmpeg.exe", "to": "binaries/ffmpeg.exe" },
|
||||||
|
{ "from": "resources/binaries/win-32/ffprobe.exe", "to": "binaries/ffprobe.exe" },
|
||||||
|
{ "from": "resources/binaries/win-32/deno.exe", "to": "binaries/deno.exe" }
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"nsis": {
|
"nsis": {
|
||||||
"oneClick": false,
|
"oneClick": false,
|
||||||
@@ -108,6 +80,23 @@
|
|||||||
"createDesktopShortcut": true,
|
"createDesktopShortcut": true,
|
||||||
"createStartMenuShortcut": true,
|
"createStartMenuShortcut": true,
|
||||||
"shortcutName": "Freedom Loader"
|
"shortcutName": "Freedom Loader"
|
||||||
|
},
|
||||||
|
"linux": {
|
||||||
|
"target": ["AppImage", "deb","rpm"],
|
||||||
|
"category": "Utility",
|
||||||
|
"icon": "build/app-icon.png",
|
||||||
|
"extraResources": [
|
||||||
|
{ "from": "resources/binaries/linux/yt-dlp", "to": "binaries/yt-dlp" },
|
||||||
|
{ "from": "resources/binaries/linux/ffmpeg", "to": "binaries/ffmpeg" },
|
||||||
|
{ "from": "resources/binaries/linux/ffprobe", "to": "binaries/ffprobe" },
|
||||||
|
{ "from": "resources/binaries/linux/deno", "to": "binaries/deno" }
|
||||||
|
],
|
||||||
|
"extraFiles": [
|
||||||
|
{
|
||||||
|
"from": "package/com.masteracnolo.freedomloader.metainfo.xml",
|
||||||
|
"to": "usr/share/metainfo/com.masteracnolo.freedomloader.metainfo.xml"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -9,6 +9,11 @@ const { contextBridge, ipcRenderer } = require("electron");
|
|||||||
*/
|
*/
|
||||||
contextBridge.exposeInMainWorld("electronAPI", {
|
contextBridge.exposeInMainWorld("electronAPI", {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return process.platform to renderer
|
||||||
|
*/
|
||||||
|
getProcessPlatform: () => process.platform,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the default system download directory.
|
* Returns the default system download directory.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -290,6 +290,7 @@
|
|||||||
|
|
||||||
<!-- Scripts -->
|
<!-- Scripts -->
|
||||||
<script src="script/toast.js"></script>
|
<script src="script/toast.js"></script>
|
||||||
|
<script src="script/customConfiguration.js"></script>
|
||||||
<script src="script/clipboardPaste.js"></script>
|
<script src="script/clipboardPaste.js"></script>
|
||||||
<script src="script/custompath.js"></script>
|
<script src="script/custompath.js"></script>
|
||||||
<script src="script/settingsPanel.js"></script>
|
<script src="script/settingsPanel.js"></script>
|
||||||
|
|||||||
30
public/script/customConfiguration.js
Normal file
30
public/script/customConfiguration.js
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
async function initPlatformRestrictions() {
|
||||||
|
const platform = await window.electronAPI.getProcessPlatform();
|
||||||
|
|
||||||
|
const themeSelect = document.getElementById("themeSelect");
|
||||||
|
const refreshBtn = document.getElementById("refresh-themes-btn");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is the OS windows
|
||||||
|
* @type {boolean}
|
||||||
|
*/
|
||||||
|
const isWindows = platform === "win32";
|
||||||
|
|
||||||
|
console.log(isWindows);
|
||||||
|
|
||||||
|
// If it's windows, the button is not disabled.
|
||||||
|
themeSelect.disabled = !isWindows;
|
||||||
|
refreshBtn.disabled = !isWindows;
|
||||||
|
|
||||||
|
themeSelect.classList.toggle("disabled", !isWindows);
|
||||||
|
refreshBtn.classList.toggle("disabled", !isWindows);
|
||||||
|
|
||||||
|
if (!isWindows) {
|
||||||
|
themeSelect.title = "Themes are only available on Windows";
|
||||||
|
refreshBtn.title = "Themes are only available on Windows";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', function(){
|
||||||
|
initPlatformRestrictions();
|
||||||
|
})
|
||||||
@@ -16,6 +16,14 @@
|
|||||||
@import url('https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap');
|
@import url('https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap');
|
||||||
|
|
||||||
|
|
||||||
|
/* Mark element as disabled */
|
||||||
|
.disabled {
|
||||||
|
pointer-events: none;
|
||||||
|
opacity: 0.5;
|
||||||
|
filter: grayscale(40%);
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
/* Because why not */
|
/* Because why not */
|
||||||
#reference{
|
#reference{
|
||||||
position: fixed;
|
position: fixed;
|
||||||
|
|||||||
BIN
resources/binaries/linux/yt-dlp
Executable file
BIN
resources/binaries/linux/yt-dlp
Executable file
Binary file not shown.
@@ -22,7 +22,8 @@ const { logger } = require("../logger");
|
|||||||
* @returns {string} The detected browser identifier.
|
* @returns {string} The detected browser identifier.
|
||||||
*/
|
*/
|
||||||
function getUserBrowser() {
|
function getUserBrowser() {
|
||||||
const userProfile = os.homedir();
|
//const userProfile = os.homedir();
|
||||||
|
const firefoxPath = getFirefoxProfilePath();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Browsers supported by yt-dlp cookie extraction.
|
* Browsers supported by yt-dlp cookie extraction.
|
||||||
@@ -32,7 +33,7 @@ function getUserBrowser() {
|
|||||||
* once compatibility has been verified.
|
* once compatibility has been verified.
|
||||||
*/
|
*/
|
||||||
const browsers = [
|
const browsers = [
|
||||||
{ name: "firefox", path: path.join(userProfile, "AppData", "Roaming", "Mozilla", "Firefox", "Profiles") },
|
{ name: "firefox", path: firefoxPath },
|
||||||
// { name: "chrome", path: path.join(userProfile, "AppData", "Local", "Google", "Chrome", "User Data", "Default") },
|
// { name: "chrome", path: path.join(userProfile, "AppData", "Local", "Google", "Chrome", "User Data", "Default") },
|
||||||
// { name: "brave", path: path.join(userProfile, "AppData", "Local", "BraveSoftware", "Brave-Browser", "User Data", "Default") },
|
// { name: "brave", path: path.join(userProfile, "AppData", "Local", "BraveSoftware", "Brave-Browser", "User Data", "Default") },
|
||||||
// { name: "edge", path: path.join(userProfile, "AppData", "Local", "Microsoft", "Edge", "User Data", "Default") },
|
// { name: "edge", path: path.join(userProfile, "AppData", "Local", "Microsoft", "Edge", "User Data", "Default") },
|
||||||
@@ -44,7 +45,7 @@ function getUserBrowser() {
|
|||||||
|
|
||||||
// Search for the first available browser profile.
|
// Search for the first available browser profile.
|
||||||
for (const browser of browsers) {
|
for (const browser of browsers) {
|
||||||
if (fs.existsSync(browser.path)) {
|
if (browser.path && fs.existsSync(browser.path)) {
|
||||||
logger.info(`Browser found: ${browser.name}`);
|
logger.info(`Browser found: ${browser.name}`);
|
||||||
return browser.name;
|
return browser.name;
|
||||||
}
|
}
|
||||||
@@ -52,11 +53,50 @@ function getUserBrowser() {
|
|||||||
|
|
||||||
// No supported browser found => Notify User
|
// No supported browser found => Notify User
|
||||||
logger.warn("No supported browser found on the system");
|
logger.warn("No supported browser found on the system");
|
||||||
notify.notifyFirefoxBrowserMissing();
|
|
||||||
|
// If you somehow managed to live without Firefox and need help installing it. - Don't applied to my Linux chad
|
||||||
|
if (process.platform === "win32") {
|
||||||
|
notify.notifyFirefoxBrowserMissing();
|
||||||
|
}
|
||||||
|
|
||||||
// Fallback to Firefox and let yt-dlp handle the error gracefully.
|
// Fallback to Firefox and let yt-dlp handle the error gracefully.
|
||||||
// This prevents the application from crashing
|
// This prevents the application from crashing
|
||||||
return "firefox";
|
return "firefox";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getFirefoxProfilePath() {
|
||||||
|
const home = os.homedir();
|
||||||
|
|
||||||
|
if (process.platform === "win32") {
|
||||||
|
return path.join(
|
||||||
|
home,
|
||||||
|
"AppData",
|
||||||
|
"Roaming",
|
||||||
|
"Mozilla",
|
||||||
|
"Firefox",
|
||||||
|
"Profiles"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.platform === "linux") {
|
||||||
|
return path.join(
|
||||||
|
"usr",
|
||||||
|
"bin",
|
||||||
|
"firefox"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.platform === "darwin") {
|
||||||
|
return path.join(
|
||||||
|
home,
|
||||||
|
"Library",
|
||||||
|
"Application Support",
|
||||||
|
"Firefox",
|
||||||
|
"Profiles"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = getUserBrowser
|
module.exports = getUserBrowser
|
||||||
@@ -1,18 +1,49 @@
|
|||||||
const path = require("path");
|
const path = require("path");
|
||||||
const fs = require("fs");
|
const fs = require("fs");
|
||||||
const os = require("os");
|
|
||||||
const { app } = require("electron");
|
const { app } = require("electron");
|
||||||
const config = require("../../config.js");
|
const config = require("../../config.js");
|
||||||
|
|
||||||
const { logger } = require("../logger.js");
|
const { logger } = require("../logger.js");
|
||||||
|
|
||||||
// Centralized resource paths
|
/**
|
||||||
|
* Is the OS windows
|
||||||
|
*/
|
||||||
|
const isWindows = process.platform === 'win32'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On windows the binaries are name.exe on Linux/macOS no
|
||||||
|
*/
|
||||||
|
const ext = isWindows ? '.exe' : ''
|
||||||
|
|
||||||
|
/** Centralized resource paths */
|
||||||
const resourcesPath = config.devMode
|
const resourcesPath = config.devMode
|
||||||
? path.join(__dirname, "../../ressources")
|
? path.join(__dirname, "../../resources")
|
||||||
: process.resourcesPath;
|
: process.resourcesPath;
|
||||||
|
|
||||||
// Default download folder (centralized)
|
/** Default download folder (centralized) */
|
||||||
const defaultDownloadFolder = path.join(os.homedir(), "Downloads", "Freedom Loader");
|
const defaultDownloadFolder = path.join(app.getPath("downloads"), "Freedom Loader");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current platform:
|
||||||
|
* win32 | linux | darwin
|
||||||
|
*/
|
||||||
|
const platform = process.platform;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dev source tree uses platform-named subfolders (matches resources/binaries/{win-32,linux,darwin}).
|
||||||
|
* Packaged builds flatten everything directly under binaries/, since each
|
||||||
|
* platform build only ships its own binaries — no subfolder needed.
|
||||||
|
*/
|
||||||
|
const devPlatformFolder = {
|
||||||
|
win32: "win-32",
|
||||||
|
linux: "linux",
|
||||||
|
darwin: "darwin"
|
||||||
|
}[platform];
|
||||||
|
|
||||||
|
const binariesPath = config.devMode
|
||||||
|
? path.join(resourcesPath, "binaries", devPlatformFolder)
|
||||||
|
: path.join(resourcesPath, "binaries");
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Runtime-resolved binary paths.
|
* Runtime-resolved binary paths.
|
||||||
@@ -24,7 +55,10 @@ let ffmpegPath;
|
|||||||
let denoPath;
|
let denoPath;
|
||||||
|
|
||||||
/** Source binary bundled inside application resources (used for first-time copy) */
|
/** Source binary bundled inside application resources (used for first-time copy) */
|
||||||
const sourceYtDlp = path.join(resourcesPath, "binaries","yt-dlp.exe");
|
const sourceYtDlp = path.join(
|
||||||
|
binariesPath,
|
||||||
|
`yt-dlp${ext}`
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolve binary locations depending on environment:
|
* Resolve binary locations depending on environment:
|
||||||
@@ -32,16 +66,47 @@ const sourceYtDlp = path.join(resourcesPath, "binaries","yt-dlp.exe");
|
|||||||
* - prod: extracted userData + packaged resources
|
* - prod: extracted userData + packaged resources
|
||||||
*/
|
*/
|
||||||
if (config.devMode) {
|
if (config.devMode) {
|
||||||
userYtDlp = path.join(__dirname, "../../ressources/yt-dlp.exe");
|
userYtDlp = path.join(
|
||||||
ffmpegPath = path.join(__dirname, "../../ressources/"); // <- has ffmpeg.exe and ffprobe.exe
|
binariesPath,
|
||||||
denoPath = path.join(__dirname, "../../ressources/deno.exe");
|
`yt-dlp${ext}`
|
||||||
} else {
|
);
|
||||||
userYtDlp = path.join(app.getPath("userData"),"yt-dlp.exe");
|
|
||||||
ffmpegPath = path.join(resourcesPath, "binaries","ffmpeg.exe");
|
|
||||||
denoPath = path.join(resourcesPath, "binaries","deno.exe");
|
|
||||||
|
|
||||||
if (!fs.existsSync(userYtDlp)) {
|
ffmpegPath = path.join(
|
||||||
|
binariesPath,
|
||||||
|
`ffmpeg${ext}`
|
||||||
|
);
|
||||||
|
|
||||||
|
denoPath = path.join(
|
||||||
|
binariesPath,
|
||||||
|
`deno${ext}`
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
userYtDlp = path.join(
|
||||||
|
app.getPath("userData"),
|
||||||
|
`yt-dlp${ext}`
|
||||||
|
);
|
||||||
|
|
||||||
|
ffmpegPath = path.join(
|
||||||
|
binariesPath,
|
||||||
|
`ffmpeg${ext}`
|
||||||
|
);
|
||||||
|
|
||||||
|
denoPath = path.join(
|
||||||
|
binariesPath,
|
||||||
|
`deno${ext}`
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!fs.existsSync(userYtDlp) && fs.existsSync(sourceYtDlp)) {
|
||||||
fs.copyFileSync(sourceYtDlp, userYtDlp);
|
fs.copyFileSync(sourceYtDlp, userYtDlp);
|
||||||
|
|
||||||
|
// Linux/macOS executable permission
|
||||||
|
if (!isWindows) {
|
||||||
|
try {
|
||||||
|
fs.chmodSync(userYtDlp, 0o755);
|
||||||
|
} catch (err) {
|
||||||
|
logger.warn(`Failed to chmod yt-dlp: ${err.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,10 +125,25 @@ const iconPaths = {
|
|||||||
* Used mainly for verification and diagnostics.
|
* Used mainly for verification and diagnostics.
|
||||||
*/
|
*/
|
||||||
const binaryPaths = {
|
const binaryPaths = {
|
||||||
ytDlp: path.join(resourcesPath, "binaries", "yt-dlp.exe"),
|
ytDlp: path.join(
|
||||||
ffmpeg: path.join(resourcesPath, "binaries", "ffmpeg.exe"),
|
binariesPath,
|
||||||
ffprobe: path.join(resourcesPath, "binaries", "ffprobe.exe"),
|
`yt-dlp${ext}`
|
||||||
deno: path.join(resourcesPath, "binaries", "deno.exe")
|
),
|
||||||
|
|
||||||
|
ffmpeg: path.join(
|
||||||
|
binariesPath,
|
||||||
|
`ffmpeg${ext}`
|
||||||
|
),
|
||||||
|
|
||||||
|
ffprobe: path.join(
|
||||||
|
binariesPath,
|
||||||
|
`ffprobe${ext}`
|
||||||
|
),
|
||||||
|
|
||||||
|
deno: path.join(
|
||||||
|
binariesPath,
|
||||||
|
`deno${ext}`
|
||||||
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -96,25 +176,31 @@ function initUserThemes() {
|
|||||||
for (const theme of defaultThemes) {
|
for (const theme of defaultThemes) {
|
||||||
const srcPath = path.join(defaultThemesSourcePath, theme);
|
const srcPath = path.join(defaultThemesSourcePath, theme);
|
||||||
const destPath = path.join(userThemesPath, theme);
|
const destPath = path.join(userThemesPath, theme);
|
||||||
|
|
||||||
if (!fs.existsSync(destPath)) {
|
if (!fs.existsSync(destPath)) {
|
||||||
try {
|
try {
|
||||||
copyDirectory(srcPath, destPath);
|
copyDirectory(srcPath, destPath);
|
||||||
fs.chmodSync(destPath, 0o777);
|
fs.chmodSync(destPath, 0o777);
|
||||||
logger.info(`Copied default theme: ${theme}`);
|
logger.info(`Copied default theme: ${theme}`);
|
||||||
} catch (copyErr) {
|
} catch (copyErr) {
|
||||||
logger.warn(`Failed to copy theme ${theme}: ${copyErr.message}. Theme folder will be created, add theme files manually.`);
|
logger.warn(
|
||||||
|
`Failed to copy theme ${theme}: ${copyErr.message}. Theme folder will be created, add theme files manually.`
|
||||||
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
fs.mkdirSync(destPath, { recursive: true });
|
fs.mkdirSync(destPath, { recursive: true });
|
||||||
fs.chmodSync(destPath, 0o777);
|
fs.chmodSync(destPath, 0o777);
|
||||||
} catch (mkdirErr) {
|
} catch (mkdirErr) {
|
||||||
logger.warn(`Could not create theme directory ${theme}: ${mkdirErr.message}`);
|
logger.warn(
|
||||||
|
`Could not create theme directory ${theme}: ${mkdirErr.message}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
logger.warn(`Default themes source path not found: ${defaultThemesSourcePath}`);
|
logger.warn(
|
||||||
|
`Default themes source path not found: ${defaultThemesSourcePath}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error(`Failed to initialize user themes: ${err.message}`);
|
logger.error(`Failed to initialize user themes: ${err.message}`);
|
||||||
@@ -131,10 +217,14 @@ function initUserThemes() {
|
|||||||
function copyDirectory(src, dest) {
|
function copyDirectory(src, dest) {
|
||||||
try {
|
try {
|
||||||
if (!fs.existsSync(dest)) {
|
if (!fs.existsSync(dest)) {
|
||||||
fs.mkdirSync(dest, { recursive: true, mode: 0o777 });
|
fs.mkdirSync(dest, {
|
||||||
|
recursive: true,
|
||||||
|
mode: 0o777
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const files = fs.readdirSync(src);
|
const files = fs.readdirSync(src);
|
||||||
|
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
const srcFile = path.join(src, file);
|
const srcFile = path.join(src, file);
|
||||||
const destFile = path.join(dest, file);
|
const destFile = path.join(dest, file);
|
||||||
@@ -151,8 +241,16 @@ function copyDirectory(src, dest) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Runtime validation of critical binaries
|
// Runtime validation of critical binaries
|
||||||
if (!userYtDlp){ logger.error("Missing yt-dlp binary")}
|
if (!userYtDlp) {
|
||||||
if (!ffmpegPath){ logger.error("Missing ffmpeg binary")}
|
logger.error("Missing yt-dlp binary");
|
||||||
if (!denoPath){ logger.error("Missing deno binary")}
|
}
|
||||||
|
|
||||||
module.exports = { userYtDlp, ffmpegPath, denoPath, iconPaths, binaryPaths, resourcesPath, defaultDownloadFolder, userThemesPath, initUserThemes };
|
if (!ffmpegPath) {
|
||||||
|
logger.error("Missing ffmpeg binary");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!denoPath) {
|
||||||
|
logger.error("Missing deno binary");
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { userYtDlp, ffmpegPath, denoPath, iconPaths, binaryPaths, resourcesPath, defaultDownloadFolder, userThemesPath, initUserThemes, isWindows };
|
||||||
@@ -4,9 +4,10 @@ const fs = require("fs");
|
|||||||
const path = require("path");
|
const path = require("path");
|
||||||
const os = require("os");
|
const os = require("os");
|
||||||
const config = require("../config");
|
const config = require("../config");
|
||||||
|
const {isWindows} = require("./helpers/path.helpers");
|
||||||
|
|
||||||
// Logs folder in Windows
|
// Logs folder in Windows
|
||||||
const logDir = path.join(os.homedir(), "AppData", "Local", "FreedomLoader", "logs");
|
const logDir = isWindows ? path.join(os.homedir(), "AppData", "Local", "FreedomLoader", "logs") : path.join(process.env.XDG_DATA_HOME || path.join(os.homedir(), ".local", "share"), "FreedomLoader", "logs");
|
||||||
|
|
||||||
// Create "Logs" folder if needed
|
// Create "Logs" folder if needed
|
||||||
try {
|
try {
|
||||||
|
|||||||
Reference in New Issue
Block a user