u2a 2.1.8 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -2
- package/package.json +26 -23
- package/src/commands/create.js +269 -11
- package/src/index.js +3 -0
- package/src/utils/favicon.js +51 -2
- package/src/utils/favicon256.ico +0 -0
- package/src/utils/logger.js +1 -1
package/README.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<div align="center">
|
|
2
|
-
<a href="
|
|
2
|
+
<a href="https://urltoapp.xyz" style="display: block; text-align: center;">
|
|
3
3
|
<img
|
|
4
4
|
alt="Image of this repo"
|
|
5
5
|
src="https://togp.xyz?owner=douxxtech&repo=urltoapp&theme=json-dark-all&cache=false"
|
|
@@ -86,8 +86,37 @@ This will:
|
|
|
86
86
|
2. Delete the application files
|
|
87
87
|
3. Remove the entry from the U2A database
|
|
88
88
|
|
|
89
|
-
## How It Works
|
|
90
89
|
|
|
90
|
+
### Creating an executable
|
|
91
|
+
|
|
92
|
+
To directly create a windows, macos or linux executable, you can use the `--executable [windows|darwin|linux] [--arch <architecture>]` argument with the create command.
|
|
93
|
+
|
|
94
|
+
This will:
|
|
95
|
+
1. Temporarily install the application
|
|
96
|
+
2. Create an executable and move it to your working directory
|
|
97
|
+
3. Delete the application
|
|
98
|
+
|
|
99
|
+
> [!WARNING]
|
|
100
|
+
> To launch an executable, you will need all the files that are created by U2A.
|
|
101
|
+
|
|
102
|
+
### Creating a setup
|
|
103
|
+
|
|
104
|
+
You can also directly create a setup file, so people can install it on their machine. Use the `--executable [...] --setup` to do so.
|
|
105
|
+
|
|
106
|
+
This will:
|
|
107
|
+
1. Temporarily install the application
|
|
108
|
+
2. Create an executable and move it to your working directory
|
|
109
|
+
3. Create a setup file and move it to your working directory
|
|
110
|
+
4. Delete the application
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
> [!WARNING]
|
|
114
|
+
> To use the setup, you will need all the files that are created by U2A.
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
## How It Works
|
|
118
|
+
|
|
119
|
+
`This dont apply for executables and setup`
|
|
91
120
|
U2A creates a minimal Electron application that loads the specified website URL. It:
|
|
92
121
|
|
|
93
122
|
1. Downloads the site's favicon to use as the application icon
|
package/package.json
CHANGED
|
@@ -1,39 +1,42 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "u2a",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"description": "URL to App - Turn any URL into a desktop application",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"bin": {
|
|
7
|
-
|
|
7
|
+
"u2a": "./src/index.js"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
|
-
|
|
10
|
+
"start": "node src/index.js"
|
|
11
11
|
},
|
|
12
12
|
"keywords": [
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
13
|
+
"cli",
|
|
14
|
+
"webapp",
|
|
15
|
+
"electron",
|
|
16
|
+
"url",
|
|
17
|
+
"desktop",
|
|
18
|
+
"build",
|
|
19
|
+
"application"
|
|
20
20
|
],
|
|
21
21
|
"author": "Douxx",
|
|
22
22
|
"license": "GPL-3.0-only",
|
|
23
23
|
"dependencies": {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
24
|
+
"axios": "^1.6.0",
|
|
25
|
+
"chalk": "^4.1.2",
|
|
26
|
+
"commander": "^10.0.0",
|
|
27
|
+
"electron": "^22.0.0",
|
|
28
|
+
"icojs": "^0.19.5",
|
|
29
|
+
"inquirer": "^8.2.5",
|
|
30
|
+
"open": "^8.4.0",
|
|
31
|
+
"png-to-ico": "^2.1.8",
|
|
32
|
+
"sharp": "^0.33.5"
|
|
30
33
|
},
|
|
31
34
|
"homepage": "https://urltoapp.xyz",
|
|
32
35
|
"repository": {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
"type": "git",
|
|
37
|
+
"url": "https://github.com/douxxtech/urltoapp"
|
|
38
|
+
},
|
|
39
|
+
"bugs": {
|
|
40
|
+
"url": "https://github.com/douxxtech/urltoapp/issues"
|
|
41
|
+
}
|
|
42
|
+
}
|
package/src/commands/create.js
CHANGED
|
@@ -2,7 +2,7 @@ const fs = require('fs');
|
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const { execSync } = require('child_process');
|
|
4
4
|
const { normalizeUrl, getDomainName } = require('../utils/url');
|
|
5
|
-
const { getFavicon } = require('../utils/favicon');
|
|
5
|
+
const { getFavicon, processFavicon } = require('../utils/favicon');
|
|
6
6
|
const { APPS_DIR, readDB, writeDB } = require('../utils/config');
|
|
7
7
|
const Logger = require('../utils/logger');
|
|
8
8
|
const os = require('os');
|
|
@@ -557,7 +557,10 @@ app.on('activate', () => {
|
|
|
557
557
|
`;
|
|
558
558
|
}
|
|
559
559
|
|
|
560
|
-
|
|
560
|
+
|
|
561
|
+
|
|
562
|
+
|
|
563
|
+
async function generatePackageJson(appName, iconPath, isExecutable = false, createSetup = false) {
|
|
561
564
|
const u2aPackagePath = path.resolve(__dirname, '../../package.json');
|
|
562
565
|
|
|
563
566
|
let u2aVersion = '1.0.0';
|
|
@@ -566,14 +569,19 @@ function generatePackageJson(appName, iconPath) {
|
|
|
566
569
|
const u2aPackage = JSON.parse(u2aPackageContent);
|
|
567
570
|
u2aVersion = u2aPackage.version || u2aVersion;
|
|
568
571
|
} catch (error) {
|
|
569
|
-
logger.error('Error while fetching u2a package.json', error)
|
|
572
|
+
logger.error('Error while fetching u2a package.json', error);
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
if (createSetup) {
|
|
576
|
+
iconPath = await processFavicon(iconPath);
|
|
570
577
|
}
|
|
571
578
|
|
|
572
|
-
|
|
579
|
+
const packageJson = {
|
|
573
580
|
name: `u2a-${appName.replace(/\s+/g, '-')}`,
|
|
574
581
|
version: u2aVersion,
|
|
575
582
|
description: `Web app for ${appName}`,
|
|
576
583
|
main: 'main.js',
|
|
584
|
+
author: `${appName}`,
|
|
577
585
|
scripts: {
|
|
578
586
|
start: 'electron .'
|
|
579
587
|
},
|
|
@@ -586,6 +594,201 @@ function generatePackageJson(appName, iconPath) {
|
|
|
586
594
|
icon: iconPath
|
|
587
595
|
}
|
|
588
596
|
};
|
|
597
|
+
|
|
598
|
+
if (isExecutable) {
|
|
599
|
+
packageJson.devDependencies = {
|
|
600
|
+
"electron-packager": "^17.1.1",
|
|
601
|
+
"electron-builder": "^24.6.3",
|
|
602
|
+
"electron": "^22.0.0"
|
|
603
|
+
};
|
|
604
|
+
|
|
605
|
+
packageJson.dependencies = {};
|
|
606
|
+
|
|
607
|
+
packageJson.scripts.package = "electron-packager . --overwrite --asar";
|
|
608
|
+
packageJson.scripts.setup = "electron-builder";
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
if (isExecutable && createSetup) {
|
|
612
|
+
packageJson.build = {
|
|
613
|
+
...packageJson.build,
|
|
614
|
+
appId: `com.u2a.${appName.replace(/\s+/g, '-')}`,
|
|
615
|
+
productName: appName,
|
|
616
|
+
directories: {
|
|
617
|
+
output: "installer"
|
|
618
|
+
},
|
|
619
|
+
files: [
|
|
620
|
+
"**/*",
|
|
621
|
+
"!**/node_modules/*/{CHANGELOG.md,README.md,README,readme.md,readme}",
|
|
622
|
+
"!**/node_modules/*/{test,__tests__,tests,powered-test,example,examples}",
|
|
623
|
+
"!**/node_modules/*.d.ts",
|
|
624
|
+
"!**/node_modules/.bin",
|
|
625
|
+
"!**/.{idea,git,cache,build,dist}",
|
|
626
|
+
"!dist/**/*",
|
|
627
|
+
"!installer/**/*"
|
|
628
|
+
],
|
|
629
|
+
win: {
|
|
630
|
+
target: "nsis",
|
|
631
|
+
icon: iconPath
|
|
632
|
+
},
|
|
633
|
+
mac: {
|
|
634
|
+
target: "dmg"
|
|
635
|
+
},
|
|
636
|
+
linux: {
|
|
637
|
+
target: "AppImage",
|
|
638
|
+
icon: iconPath
|
|
639
|
+
},
|
|
640
|
+
nsis: {
|
|
641
|
+
oneClick: false,
|
|
642
|
+
allowToChangeInstallationDirectory: true
|
|
643
|
+
}
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
return packageJson;
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
function copyFolderRecursiveSync(source, target) {
|
|
651
|
+
if (!fs.existsSync(target)) {
|
|
652
|
+
fs.mkdirSync(target, { recursive: true });
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
const files = fs.readdirSync(source);
|
|
656
|
+
|
|
657
|
+
files.forEach((file) => {
|
|
658
|
+
const sourcePath = path.join(source, file);
|
|
659
|
+
const targetPath = path.join(target, file);
|
|
660
|
+
|
|
661
|
+
if (fs.lstatSync(sourcePath).isDirectory()) {
|
|
662
|
+
copyFolderRecursiveSync(sourcePath, targetPath);
|
|
663
|
+
} else {
|
|
664
|
+
fs.copyFileSync(sourcePath, targetPath);
|
|
665
|
+
}
|
|
666
|
+
});
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
async function buildExecutable(appDir, appName, platform, iconPath, options) {
|
|
670
|
+
logger.info(`Building executable for ${platform}...`);
|
|
671
|
+
|
|
672
|
+
try {
|
|
673
|
+
const installOptions = {
|
|
674
|
+
cwd: appDir,
|
|
675
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
676
|
+
windowsHide: true
|
|
677
|
+
};
|
|
678
|
+
|
|
679
|
+
execSync('npm install --save-dev electron-packager electron', installOptions);
|
|
680
|
+
|
|
681
|
+
let platformFlag = '';
|
|
682
|
+
let archFlag = `--arch=${options.arch || 'x64'}`;
|
|
683
|
+
let iconOption = '';
|
|
684
|
+
|
|
685
|
+
switch(platform) {
|
|
686
|
+
case 'windows':
|
|
687
|
+
platformFlag = '--platform=win32';
|
|
688
|
+
iconOption = iconPath ? `--icon="${iconPath}"` : '';
|
|
689
|
+
break;
|
|
690
|
+
case 'darwin':
|
|
691
|
+
platformFlag = '--platform=darwin';
|
|
692
|
+
if (iconPath && !iconPath.endsWith('.icns')) {
|
|
693
|
+
logger.warn('MacOs Icons are not supported at this time.');
|
|
694
|
+
}
|
|
695
|
+
iconOption = iconPath ? `--icon="${iconPath}"` : '';
|
|
696
|
+
break;
|
|
697
|
+
case 'linux':
|
|
698
|
+
platformFlag = '--platform=linux';
|
|
699
|
+
iconOption = iconPath ? `--icon="${iconPath}"` : '';
|
|
700
|
+
break;
|
|
701
|
+
default:
|
|
702
|
+
platformFlag = `--platform=${process.platform}`;
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
const packageCommand = `npx electron-packager . "${appName}" ${platformFlag} ${archFlag} --out=dist --overwrite --asar ${iconOption}`;
|
|
706
|
+
|
|
707
|
+
logger.debug(`Executing: ${packageCommand}`);
|
|
708
|
+
|
|
709
|
+
execSync(packageCommand, installOptions);
|
|
710
|
+
|
|
711
|
+
let distPlatform = '';
|
|
712
|
+
switch(platform) {
|
|
713
|
+
case 'windows': distPlatform = 'win32'; break;
|
|
714
|
+
case 'darwin': distPlatform = 'darwin'; break;
|
|
715
|
+
case 'linux': distPlatform = 'linux'; break;
|
|
716
|
+
default: distPlatform = process.platform;
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
const outputPath = path.join(appDir, 'dist', `${appName}-${distPlatform}-x64`);
|
|
720
|
+
|
|
721
|
+
if (fs.existsSync(outputPath)) {
|
|
722
|
+
logger.debug(`Executable built successfully at: ${outputPath}`);
|
|
723
|
+
return outputPath;
|
|
724
|
+
} else {
|
|
725
|
+
logger.error(`Failed to find the built executable at: ${outputPath}`);
|
|
726
|
+
return null;
|
|
727
|
+
}
|
|
728
|
+
} catch (error) {
|
|
729
|
+
logger.error(`Error while building executable:`, error);
|
|
730
|
+
return null;
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
function remove(path) {
|
|
735
|
+
try {
|
|
736
|
+
if (fs.existsSync(path)) {
|
|
737
|
+
fs.rmSync(path, { recursive: true, force: true });
|
|
738
|
+
logger.debug(`Dir/file removed: ${path}`);
|
|
739
|
+
}
|
|
740
|
+
} catch (error) {
|
|
741
|
+
logger.error(`Error while removing dir/file ${path}`, error);
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
async function buildSetup(appDir, platform, arch) {
|
|
746
|
+
logger.info(`Building setup for ${platform}${arch ? ` (${arch})` : ''}...`);
|
|
747
|
+
|
|
748
|
+
try {
|
|
749
|
+
const installOptions = {
|
|
750
|
+
cwd: appDir,
|
|
751
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
752
|
+
windowsHide: true
|
|
753
|
+
};
|
|
754
|
+
|
|
755
|
+
execSync('npm install --save-dev electron-builder', installOptions);
|
|
756
|
+
|
|
757
|
+
let builderArgs = '';
|
|
758
|
+
switch(platform) {
|
|
759
|
+
case 'windows':
|
|
760
|
+
builderArgs = '--win';
|
|
761
|
+
break;
|
|
762
|
+
case 'darwin':
|
|
763
|
+
builderArgs = '--mac';
|
|
764
|
+
break;
|
|
765
|
+
case 'linux':
|
|
766
|
+
builderArgs = '--linux';
|
|
767
|
+
break;
|
|
768
|
+
default:
|
|
769
|
+
builderArgs = '';
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
if (arch) {
|
|
773
|
+
builderArgs += ` --${arch}`;
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
const builderCommand = `npx electron-builder ${builderArgs}`;
|
|
777
|
+
logger.debug(`Executing: ${builderCommand}`);
|
|
778
|
+
execSync(builderCommand, installOptions);
|
|
779
|
+
|
|
780
|
+
const installerPath = path.join(appDir, 'installer');
|
|
781
|
+
if (fs.existsSync(installerPath)) {
|
|
782
|
+
logger.debug(`Setup created at: ${installerPath}`);
|
|
783
|
+
return installerPath;
|
|
784
|
+
} else {
|
|
785
|
+
logger.error(`Failed to find the built installer at: ${installerPath}`);
|
|
786
|
+
return null;
|
|
787
|
+
}
|
|
788
|
+
} catch (error) {
|
|
789
|
+
logger.error(`Error while building setup:`, error);
|
|
790
|
+
return null;
|
|
791
|
+
}
|
|
589
792
|
}
|
|
590
793
|
|
|
591
794
|
async function createApp(url, options) {
|
|
@@ -615,8 +818,10 @@ async function createApp(url, options) {
|
|
|
615
818
|
fs.writeFileSync(mainJsPath, mainJsContent);
|
|
616
819
|
logger.debug(`main.js file created`);
|
|
617
820
|
|
|
821
|
+
const isExecutable = !!options.executable;
|
|
822
|
+
const createSetup = !!options.setup;
|
|
618
823
|
const packageJsonPath = path.join(appDir, 'package.json');
|
|
619
|
-
const packageJsonContent = generatePackageJson(appName, iconPath);
|
|
824
|
+
const packageJsonContent = await generatePackageJson(appName, iconPath, isExecutable, createSetup);
|
|
620
825
|
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJsonContent, null, 2));
|
|
621
826
|
logger.debug(`package.json file created`);
|
|
622
827
|
|
|
@@ -628,10 +833,59 @@ async function createApp(url, options) {
|
|
|
628
833
|
windowsHide: true
|
|
629
834
|
};
|
|
630
835
|
|
|
631
|
-
|
|
632
|
-
logger.debug(`npm install completed
|
|
633
|
-
|
|
634
|
-
|
|
836
|
+
execSync('npm install --only=prod', installOptions);
|
|
837
|
+
logger.debug(`npm install completed`);
|
|
838
|
+
|
|
839
|
+
let executablePath = null;
|
|
840
|
+
let desktopPath = null;
|
|
841
|
+
|
|
842
|
+
if (isExecutable) {
|
|
843
|
+
const targetPlatform = options.executable === true ? process.platform : options.executable;
|
|
844
|
+
executablePath = await buildExecutable(appDir, appName, targetPlatform, iconPath, options);
|
|
845
|
+
|
|
846
|
+
if (options.setup) {
|
|
847
|
+
const setupPath = await buildSetup(appDir, targetPlatform, options.arch);
|
|
848
|
+
if (setupPath) {
|
|
849
|
+
logger.debug(`Setup installer created at: ${setupPath}`);
|
|
850
|
+
|
|
851
|
+
const currentDir = process.cwd();
|
|
852
|
+
const setupTargetDir = path.join(currentDir, `${appName}-setup`);
|
|
853
|
+
|
|
854
|
+
if (!fs.existsSync(setupTargetDir)) {
|
|
855
|
+
fs.mkdirSync(setupTargetDir, { recursive: true });
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
copyFolderRecursiveSync(setupPath, setupTargetDir);
|
|
859
|
+
logger.success(`Setup installer created at: ${setupTargetDir}`);
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
if (executablePath) {
|
|
864
|
+
logger.debug(`Executable created at: ${executablePath}`);
|
|
865
|
+
|
|
866
|
+
const currentDir = process.cwd();
|
|
867
|
+
const targetDir = path.join(currentDir, `${appName}-executable`);
|
|
868
|
+
|
|
869
|
+
if (!fs.existsSync(targetDir)) {
|
|
870
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
copyFolderRecursiveSync(executablePath, targetDir);
|
|
874
|
+
|
|
875
|
+
logger.success(`Executable created at: ${targetDir}`);
|
|
876
|
+
|
|
877
|
+
executablePath = targetDir;
|
|
878
|
+
|
|
879
|
+
removeAppFromOS(appName);
|
|
880
|
+
remove(appDir);
|
|
881
|
+
remove(iconPath);
|
|
882
|
+
|
|
883
|
+
logger.debug(`Temporary application files removed after executable creation`);
|
|
884
|
+
return;
|
|
885
|
+
}
|
|
886
|
+
} else {
|
|
887
|
+
desktopPath = addAppToOS(appName, url, appDir, iconPath);
|
|
888
|
+
}
|
|
635
889
|
|
|
636
890
|
const appData = {
|
|
637
891
|
url,
|
|
@@ -639,6 +893,7 @@ async function createApp(url, options) {
|
|
|
639
893
|
path: appDir,
|
|
640
894
|
icon: iconPath,
|
|
641
895
|
desktopPath,
|
|
896
|
+
executablePath,
|
|
642
897
|
name: options.name,
|
|
643
898
|
width: options.width,
|
|
644
899
|
height: options.height
|
|
@@ -651,12 +906,15 @@ async function createApp(url, options) {
|
|
|
651
906
|
if (desktopPath) {
|
|
652
907
|
logger.info(`A shortcut has been created in your system's applications directory`);
|
|
653
908
|
}
|
|
909
|
+
if (executablePath) {
|
|
910
|
+
logger.info(`A standalone executable has been created at: ${executablePath}`);
|
|
911
|
+
}
|
|
654
912
|
} catch (error) {
|
|
655
|
-
logger.error(`Error while creating an application for ${url}
|
|
913
|
+
logger.error(`Error while creating an application for ${url}: ${error}`);
|
|
656
914
|
}
|
|
657
915
|
}
|
|
658
916
|
|
|
659
917
|
module.exports = {
|
|
660
918
|
createApp,
|
|
661
919
|
removeAppFromOS
|
|
662
|
-
};
|
|
920
|
+
};
|
package/src/index.js
CHANGED
|
@@ -20,6 +20,9 @@ program
|
|
|
20
20
|
.option('--name <name>', 'Specify the application name')
|
|
21
21
|
.option('--width <width>', 'Specify the window width', parseInt)
|
|
22
22
|
.option('--height <height>', 'Specify the window height', parseInt)
|
|
23
|
+
.option('--executable [windows|darwin|linux]', 'Create a single executable for the target system')
|
|
24
|
+
.option('--arch [x64|armv7l|arm64|universal]', 'Specify the target architecture for the executable')
|
|
25
|
+
.option('--setup', 'Creates a setup file for the executable')
|
|
23
26
|
.action((url, options) => {
|
|
24
27
|
createApp(url, options);
|
|
25
28
|
});
|
package/src/utils/favicon.js
CHANGED
|
@@ -4,6 +4,10 @@ const axios = require('axios');
|
|
|
4
4
|
const { APPS_DIR } = require('./config');
|
|
5
5
|
const { normalizeUrl, getDomainName } = require('./url');
|
|
6
6
|
const Logger = require('./logger');
|
|
7
|
+
const { parseICO} = require('icojs');
|
|
8
|
+
const sharp = require('sharp');
|
|
9
|
+
const pngToIco = require('png-to-ico');
|
|
10
|
+
|
|
7
11
|
|
|
8
12
|
const logger = new Logger('favicon');
|
|
9
13
|
|
|
@@ -19,7 +23,7 @@ async function getFavicon(url) {
|
|
|
19
23
|
const iconResponse = await axios.get(faviconUrl, { responseType: 'arraybuffer' });
|
|
20
24
|
|
|
21
25
|
const contentType = iconResponse.headers['content-type'];
|
|
22
|
-
let fileExtension = '.ico';
|
|
26
|
+
let fileExtension = '.ico';
|
|
23
27
|
if (contentType.includes('png')) {
|
|
24
28
|
fileExtension = '.png';
|
|
25
29
|
} else if (contentType.includes('jpeg') || contentType.includes('jpg')) {
|
|
@@ -44,6 +48,51 @@ async function getFavicon(url) {
|
|
|
44
48
|
}
|
|
45
49
|
}
|
|
46
50
|
|
|
51
|
+
async function processFavicon(iconPath) {
|
|
52
|
+
const dir = path.dirname(iconPath);
|
|
53
|
+
const ext = path.extname(iconPath);
|
|
54
|
+
const baseName = path.basename(iconPath, ext);
|
|
55
|
+
|
|
56
|
+
if (baseName === 'favicon' && ext === '.ico') {
|
|
57
|
+
const newPath = path.join(dir, 'favicon256.ico');
|
|
58
|
+
fs.copyFileSync(iconPath, newPath);
|
|
59
|
+
logger.debug("Default favicon.ico updated to favicon256.ico");
|
|
60
|
+
return newPath;
|
|
61
|
+
} else {
|
|
62
|
+
try {
|
|
63
|
+
const icoBuffer = fs.readFileSync(iconPath);
|
|
64
|
+
const images = await parseICO(icoBuffer, 'image/png');
|
|
65
|
+
|
|
66
|
+
if (images && images.length > 0) {
|
|
67
|
+
const pngBuffer = Buffer.from(images[0].buffer);
|
|
68
|
+
const tempPngPath = iconPath + '.png';
|
|
69
|
+
const resizedPngPath = iconPath + '_resized.png';
|
|
70
|
+
|
|
71
|
+
fs.writeFileSync(tempPngPath, pngBuffer);
|
|
72
|
+
|
|
73
|
+
await sharp(tempPngPath)
|
|
74
|
+
.resize(256, 256)
|
|
75
|
+
.toFile(resizedPngPath);
|
|
76
|
+
|
|
77
|
+
fs.renameSync(resizedPngPath, tempPngPath);
|
|
78
|
+
|
|
79
|
+
const newIcoBuffer = await pngToIco([tempPngPath]);
|
|
80
|
+
fs.writeFileSync(iconPath, newIcoBuffer);
|
|
81
|
+
fs.unlinkSync(tempPngPath);
|
|
82
|
+
|
|
83
|
+
logger.warn(`To proceed to setup, favicon has been resized to 256x256. Quality loss is possible.`);
|
|
84
|
+
return iconPath;
|
|
85
|
+
}
|
|
86
|
+
} catch (error) {
|
|
87
|
+
logger.error('Error processing ICO file:', error);
|
|
88
|
+
return iconPath;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
|
|
47
95
|
module.exports = {
|
|
48
|
-
getFavicon
|
|
96
|
+
getFavicon,
|
|
97
|
+
processFavicon
|
|
49
98
|
};
|
|
Binary file
|
package/src/utils/logger.js
CHANGED
|
@@ -41,7 +41,7 @@ class Logger {
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
error(message, error = null) {
|
|
44
|
-
const formattedMessage = this._format('ERROR', message);
|
|
44
|
+
const formattedMessage = this._format('ERROR', `${message} ${error}`);
|
|
45
45
|
console.log(chalk.red(formattedMessage));
|
|
46
46
|
this._writeToFile(formattedMessage);
|
|
47
47
|
|