yt-grabber 1.8.4 → 1.8.6

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.
Files changed (63) hide show
  1. package/README.md +18 -1
  2. package/e2e/main.spec.ts +46 -46
  3. package/jest.config.ts +3 -0
  4. package/package.json +194 -188
  5. package/public/screenshots/main-downloading.png +0 -0
  6. package/public/screenshots/main-info.png +0 -0
  7. package/public/screenshots/main.png +0 -0
  8. package/public/screenshots/settings.png +0 -0
  9. package/src/App.tsx +4 -6
  10. package/src/automations/Helpers.ts +3 -3
  11. package/src/automations/Selectors.ts +2 -2
  12. package/src/automations/Youtube.ts +6 -11
  13. package/src/automations/YoutubeAlbums.ts +3 -8
  14. package/src/automations/YoutubeArtists.ts +4 -9
  15. package/src/automations/YoutubeTracks.ts +4 -9
  16. package/src/common/FileSystem.ts +4 -4
  17. package/src/common/Formatters.ts +8 -12
  18. package/src/common/Helpers.ts +7 -13
  19. package/src/common/Media.ts +2 -0
  20. package/src/common/Promise.ts +2 -2
  21. package/src/common/PuppeteerOptions.ts +11 -11
  22. package/src/common/Reporter.ts +3 -5
  23. package/src/common/Store.ts +9 -4
  24. package/src/common/YtdplUtils.ts +37 -35
  25. package/src/components/fileField/FileField.tsx +6 -6
  26. package/src/components/modals/detailsModal/DetailsModal.tsx +2 -2
  27. package/src/components/modals/failuresModal/FailuresModal.tsx +3 -4
  28. package/src/components/modals/imageModal/ImageModal.tsx +0 -1
  29. package/src/components/modals/selectArtistModal/SelectArtistModal.tsx +4 -5
  30. package/src/components/numberField/NumberField.tsx +18 -19
  31. package/src/components/themePicker/ThemePicker.tsx +2 -2
  32. package/src/components/youtube/formatSelector/FormatSelector.styl +35 -0
  33. package/src/components/youtube/formatSelector/FormatSelector.tsx +66 -34
  34. package/src/components/youtube/infoBar/InfoBar.tsx +13 -18
  35. package/src/components/youtube/inputModePicker/InputModePicker.tsx +5 -6
  36. package/src/components/youtube/inputPanel/InputPanel.tsx +19 -26
  37. package/src/components/youtube/logMenu/LogMenu.tsx +5 -6
  38. package/src/components/youtube/mediaInfoPanel/MediaInfoPanel.tsx +8 -12
  39. package/src/components/youtube/playlistTabs/PlaylistTabs.tsx +36 -45
  40. package/src/components/youtube/trackList/TrackList.tsx +50 -66
  41. package/src/hooks/useCancellablePromises.ts +1 -1
  42. package/src/hooks/useClickCounter.ts +3 -3
  43. package/src/hooks/useHelp.ts +3 -4
  44. package/src/hooks/useWindowUpdater.ts +2 -2
  45. package/src/i18next.ts +2 -2
  46. package/src/index.ts +1 -1
  47. package/src/messaging/MessageChannel.ts +2 -2
  48. package/src/messaging/MultiMessageChannel.ts +4 -6
  49. package/src/react/contexts/AppThemeContext.tsx +2 -2
  50. package/src/react/reducers/Reducer.ts +2 -2
  51. package/src/react/states/State.ts +4 -4
  52. package/src/resources/bin/yt-dlp.exe +0 -0
  53. package/src/resources/locales/de-DE/help.json +12 -1
  54. package/src/resources/locales/de-DE/translation.json +4 -0
  55. package/src/resources/locales/en-GB/help.json +12 -1
  56. package/src/resources/locales/en-GB/translation.json +4 -0
  57. package/src/resources/locales/pl-PL/help.json +12 -1
  58. package/src/resources/locales/pl-PL/translation.json +4 -0
  59. package/src/views/development/DevelopmentView.tsx +1 -1
  60. package/src/views/home/HomeView.tsx +91 -109
  61. package/src/views/settings/SettingsView.tsx +40 -30
  62. package/tests/TestPlaylistMock.ts +1 -1
  63. package/tsconfig.json +1 -1
package/README.md CHANGED
@@ -27,6 +27,7 @@ Each download can be customized to your needs for easy workflow automation.
27
27
  - [Development](#development)
28
28
  - [Running](#running)
29
29
  - [Packaging](#packaging)
30
+ - [Testing](#testing)
30
31
  - [License](#license)
31
32
  - [Legal Disclaimer](#legal-disclaimer)
32
33
 
@@ -35,7 +36,7 @@ Each download can be customized to your needs for easy workflow automation.
35
36
 
36
37
  * Download video, audio, playlists, songs, albums and complete artist discographies
37
38
 
38
- * Multiple output formats (mp3, m4u, flac, wav, mp4, mkv)
39
+ * Multiple output formats (mp3, m4a, flac, wav, mp4, mkv, mov, avi, mpeg, gif)
39
40
 
40
41
  * Customizable audio and video quality
41
42
 
@@ -43,8 +44,14 @@ Each download can be customized to your needs for easy workflow automation.
43
44
 
44
45
  * Batch multimedia download
45
46
 
47
+ * Autodetecting download type
48
+
46
49
  * Metadata (tags) embedding
47
50
 
51
+ * Filterable artist discography downloads by release type (album, single, ep) or release year
52
+
53
+ * Generating animated GIF's from videos (with embedding customizable top and/or bottom text)
54
+
48
55
  * Configurable output
49
56
 
50
57
  * Multiple language support (English, German, Polish out of the box)
@@ -94,6 +101,16 @@ This will start webpack development server that will watch for changes to source
94
101
 
95
102
  To prepare release application package run `npm dist` or `yarn dist` command.
96
103
 
104
+ ## Testing
105
+
106
+ There are two types of tests avalable:
107
+ - *unit tests* (using Jest)
108
+ - *end2end tests* (using Playwright)
109
+
110
+ To execute unit tests run `npm test` or `yarn test` command (unit tests are also run when packaging the applciation).
111
+
112
+ To execute end2end tests run `npm playwright test` or `yarn playwright test` command.
113
+
97
114
  ## License
98
115
 
99
116
  This project is licensed under the [MIT License](LICENSE).
package/e2e/main.spec.ts CHANGED
@@ -1,46 +1,46 @@
1
- import {findLatestBuild, parseElectronApp} from "electron-playwright-helpers";
2
- import path from "path";
3
- import {_electron as electron, ElectronApplication, Page} from "playwright";
4
-
5
- import {expect, test} from "@playwright/test";
6
-
7
- let electronApp: ElectronApplication;
8
- let page: Page
9
-
10
- test.beforeAll(async () => {
11
- const latestBuild = findLatestBuild("release");
12
- const appInfo = parseElectronApp(latestBuild);
13
-
14
- process.env.CI = "e2e";
15
- electronApp = await electron.launch({
16
- args: [appInfo.main],
17
- executablePath: path.resolve(__dirname, "..", "release", "win-unpacked", "yt-grabber.exe"),
18
- });
19
- electronApp.on("window", async (page) => {
20
- const filename = page.url()?.split("/").pop();
21
-
22
- console.log(`"Window opened: ${filename}`);
23
- page.on("pageerror", (error) => {
24
- console.error(error);
25
- });
26
- page.on("console", (msg) => {
27
- console.log(msg.text());
28
- });
29
- });
30
- });
31
-
32
- test.afterAll(async () => {
33
- await electronApp.close();
34
- });
35
-
36
- test("renders main page", async () => {
37
- page = await electronApp.firstWindow();
38
- await page.waitForSelector("#root");
39
- await page.waitForSelector("h6");
40
-
41
- const text = await page.$eval("h6", (el) => el.textContent);
42
- expect(text).toBe("YT GRABBER");
43
-
44
- const title = await page.title();
45
- expect(title).toBe("Youtube Grabber");
46
- });
1
+ import {findLatestBuild, parseElectronApp} from "electron-playwright-helpers";
2
+ import path from "path";
3
+ import {_electron as electron, ElectronApplication, Page} from "playwright";
4
+
5
+ import {expect, test} from "@playwright/test";
6
+
7
+ let electronApp: ElectronApplication;
8
+ let page: Page;
9
+
10
+ test.beforeAll(async () => {
11
+ const latestBuild = findLatestBuild("release");
12
+ const appInfo = parseElectronApp(latestBuild);
13
+
14
+ process.env.CI = "e2e";
15
+ electronApp = await electron.launch({
16
+ args: [appInfo.main],
17
+ executablePath: path.resolve(__dirname, "..", "release", "win-unpacked", "yt-grabber.exe"),
18
+ });
19
+ electronApp.on("window", async (page) => {
20
+ const filename = page.url()?.split("/").pop();
21
+
22
+ console.log(`"Window opened: ${filename}`);
23
+ page.on("pageerror", (error) => {
24
+ console.error(error);
25
+ });
26
+ page.on("console", (msg) => {
27
+ console.log(msg.text());
28
+ });
29
+ });
30
+ });
31
+
32
+ test.afterAll(async () => {
33
+ await electronApp.close();
34
+ });
35
+
36
+ test("renders main page", async () => {
37
+ page = await electronApp.firstWindow();
38
+ await page.waitForSelector("#root");
39
+ await page.waitForSelector("h6");
40
+
41
+ const text = await page.$eval("h6", (el) => el.textContent);
42
+ expect(text).toBe("YT GRABBER");
43
+
44
+ const title = await page.title();
45
+ expect(title).toBe("Youtube Grabber");
46
+ });
package/jest.config.ts CHANGED
@@ -6,6 +6,9 @@ const config: Config = {
6
6
  transform: {
7
7
  "^.+\\.(t|j)sx?$": ["@swc/jest", {}],
8
8
  },
9
+ transformIgnorePatterns: [
10
+ "node_modules/(?!(lodash-es)/)",
11
+ ],
9
12
  moduleNameMapper: {
10
13
  "\\.(css|less|scss|sass|styl)$": "identity-obj-proxy",
11
14
  "\\.(jpg|jpeg|png|gif|svg)$": "<rootDir>/tests/FileMock.ts",
package/package.json CHANGED
@@ -1,188 +1,194 @@
1
- {
2
- "name": "yt-grabber",
3
- "version": "1.8.4",
4
- "description": "Youtube Grabber - robust desktop application designed to retrieve multimedia from YouTube and YouTube Music services",
5
- "keywords": [
6
- "youtube",
7
- "youtube music",
8
- "grabber",
9
- "downloader",
10
- "music",
11
- "albums",
12
- "artists",
13
- "album releases",
14
- "songs",
15
- "discography",
16
- "yt-dlp",
17
- "youtube playlist",
18
- "youtube channel",
19
- "youtube video",
20
- "youtube audio",
21
- "youtube mp3",
22
- "youtube mp4",
23
- "youtube mpeg",
24
- "youtube mkv",
25
- "youtube avi"
26
- ],
27
- "main": "./dist/index.js",
28
- "repository": {
29
- "url": "https://github.com/karenpommeroy/yt-grabber.git"
30
- },
31
- "scripts": {
32
- "clean": "rimraf dist",
33
- "start": "concurrently \"cross-env NODE_ENV=development yarn run watch\" \"yarn run electron\"",
34
- "start:debug": "concurrently \"cross-env NODE_ENV=development yarn run watch\" \"yarn run electron-debug\"",
35
- "build": "yarn clean && \"cross-env NODE_ENV=development webpack\" --config webpack.config.ts",
36
- "build:prod": "yarn clean && \"cross-env NODE_ENV=production webpack\" --config webpack.config.ts",
37
- "electron": "wait-on dist/index.js && ELECTRON_DISABLE_SECURITY_WARNINGS=true electron .",
38
- "electron-debug": "wait-on dist/index.js && ELECTRON_DISABLE_SECURITY_WARNINGS=true electron --inspect-brk=9229 .",
39
- "test": "jest",
40
- "watch": "webpack --watch --config webpack.config.ts",
41
- "pack": "yarn build:prod && electron-builder --dir",
42
- "dist": "yarn build:prod && yarn test && electron-builder"
43
- },
44
- "build": {
45
- "appId": "yt.grabber",
46
- "productName": "YT Grabber",
47
- "copyright": "© 2025 Marcin Karpiński",
48
- "files": [
49
- "dist/**/*",
50
- "package.json"
51
- ],
52
- "publish": null,
53
- "asar": true,
54
- "win": {
55
- "target": "nsis",
56
- "icon": "dist/resources/icons/logo.ico",
57
- "artifactName": "${name}-${version}-setup.${ext}"
58
- },
59
- "nsis": {
60
- "oneClick": false,
61
- "allowToChangeInstallationDirectory": true,
62
- "installerIcon": "dist/resources/icons/logo.ico",
63
- "uninstallerIcon": "dist/resources/icons/logo.ico",
64
- "installerHeaderIcon": "dist/resources/icons/logo.ico",
65
- "createDesktopShortcut": true,
66
- "createStartMenuShortcut": true,
67
- "shortcutName": "YT Grabber"
68
- },
69
- "directories": {
70
- "output": "release"
71
- },
72
- "extraResources": [
73
- {
74
- "from": "dist/resources/bin",
75
- "to": "bin",
76
- "filter": [
77
- "*.exe"
78
- ]
79
- },
80
- {
81
- "from": "dist/resources/locales",
82
- "to": "locales"
83
- },
84
- {
85
- "from": "public/profile",
86
- "to": "profile"
87
- }
88
- ]
89
- },
90
- "author": "Marcin Karpiński <mkarpins@gmail.com>",
91
- "license": "MIT",
92
- "dependencies": {
93
- "@emotion/react": "^11.14.0",
94
- "@emotion/styled": "^11.14.0",
95
- "@mui/icons-material": "^7.0.0",
96
- "@mui/lab": "^7.0.0-beta.9",
97
- "@mui/material": "^7.0.0",
98
- "axios": "^1.8.4",
99
- "classnames": "^2.5.1",
100
- "electron-devtools-installer": "^4.0.0",
101
- "electron-reload": "^2.0.0-alpha.1",
102
- "electron-store": "^8.2.0",
103
- "fs-extra": "^11.3.0",
104
- "i18next": "^24.2.2",
105
- "i18next-node-fs-backend": "^2.1.3",
106
- "jsonschema": "^1.5.0",
107
- "lodash": "^4.17.21",
108
- "mkdirp": "^3.0.1",
109
- "moment": "^2.30.1",
110
- "moment-duration-format": "^2.3.2",
111
- "puppeteer": "^24.4.0",
112
- "puppeteer-extra": "^3.3.6",
113
- "puppeteer-extra-plugin-devtools": "^2.4.6",
114
- "puppeteer-extra-plugin-stealth": "^2.11.2",
115
- "react": "^19.0.0",
116
- "react-dom": "^19.0.0",
117
- "react-i18next": "^15.4.1",
118
- "react-number-format": "^5.4.3",
119
- "react-router-dom": "^7.4.0",
120
- "usehooks-ts": "^3.1.1",
121
- "win-version-info": "^6.0.1",
122
- "yt-dlp-wrap": "^2.3.12"
123
- },
124
- "devDependencies": {
125
- "@playwright/test": "^1.53.1",
126
- "@swc/core": "^1.13.5",
127
- "@swc/jest": "^0.2.39",
128
- "@testing-library/dom": "^10.4.1",
129
- "@testing-library/jest-dom": "^6.9.1",
130
- "@testing-library/react": "^16.3.0",
131
- "@testing-library/user-event": "^14.6.1",
132
- "@types/eslint": "^9.6.1",
133
- "@types/fs-extra": "^11.0.4",
134
- "@types/i18next": "^13.0.0",
135
- "@types/i18next-node-fs-backend": "^2.1.5",
136
- "@types/jest": "^30.0.0",
137
- "@types/jsonschema": "^1.1.1",
138
- "@types/lodash": "^4.17.16",
139
- "@types/moment-duration-format": "^2.2.6",
140
- "@types/mui-image": "^1.0.5",
141
- "@types/node": "^22.13.14",
142
- "@types/prettier": "^3.0.0",
143
- "@types/react": "^19.0.12",
144
- "@types/react-dom": "^19.0.4",
145
- "@types/webpack-dev-server": "^4.7.2",
146
- "@types/webpack-env": "^1.18.8",
147
- "@types/win-version-info": "^3.1.3",
148
- "@typescript-eslint/eslint-plugin": "^8.28.0",
149
- "@typescript-eslint/parser": "^8.28.0",
150
- "concurrently": "^9.1.2",
151
- "copy-webpack-plugin": "^13.0.0",
152
- "cross-env": "^7.0.3",
153
- "css-loader": "^7.1.2",
154
- "electron": "^36.5.0",
155
- "electron-builder": "26.0.16",
156
- "electron-playwright-helpers": "^1.7.1",
157
- "eslint": "^9.23.0",
158
- "eslint-config-prettier": "^10.1.1",
159
- "eslint-plugin-css": "^0.11.0",
160
- "eslint-plugin-react": "^7.37.4",
161
- "eslint-webpack-plugin": "^5.0.0",
162
- "globals": "^16.0.0",
163
- "html-webpack-plugin": "^5.6.3",
164
- "i18next-scanner-webpack": "^0.9.1",
165
- "identity-obj-proxy": "^3.0.0",
166
- "jest": "^30.2.0",
167
- "jest-environment-jsdom": "^30.2.0",
168
- "jiti": "^2.4.2",
169
- "mini-css-extract-plugin": "^2.9.2",
170
- "prettier": "^3.5.3",
171
- "raw-loader": "^4.0.2",
172
- "rimraf": "^6.0.1",
173
- "source-map-loader": "^5.0.0",
174
- "stylus": "^0.64.0",
175
- "stylus-loader": "^8.1.1",
176
- "ts-loader": "^9.5.2",
177
- "ts-node": "^10.9.2",
178
- "ts-node-dev": "^2.0.0",
179
- "typescript": "^5.8.2",
180
- "util": "^0.12.5",
181
- "wait-on": "^8.0.2",
182
- "webpack": "^5.98.0",
183
- "webpack-cli": "^6.0.1",
184
- "webpack-dev-server": "^5.2.1",
185
- "webpack-node-externals": "^3.0.0"
186
- },
187
- "packageManager": "yarn@4.4.1"
188
- }
1
+ {
2
+ "name": "yt-grabber",
3
+ "version": "1.8.6",
4
+ "description": "Youtube Grabber - robust desktop application designed to retrieve multimedia from YouTube and YouTube Music services",
5
+ "keywords": [
6
+ "youtube",
7
+ "youtube music",
8
+ "grabber",
9
+ "downloader",
10
+ "music",
11
+ "albums",
12
+ "artists",
13
+ "album releases",
14
+ "songs",
15
+ "discography",
16
+ "yt-dlp",
17
+ "youtube playlist",
18
+ "youtube channel",
19
+ "youtube video",
20
+ "youtube audio",
21
+ "youtube mp3",
22
+ "youtube wav",
23
+ "youtube mp4",
24
+ "youtube mpeg",
25
+ "youtube mkv",
26
+ "youtube avi",
27
+ "youtube gif"
28
+ ],
29
+ "main": "./dist/index.js",
30
+ "repository": {
31
+ "url": "https://github.com/karenpommeroy/yt-grabber.git"
32
+ },
33
+ "scripts": {
34
+ "clean": "rimraf dist",
35
+ "start": "concurrently \"cross-env NODE_ENV=development yarn webpack-tsx --watch\" \"yarn run electron\"",
36
+ "start:debug": "concurrently \"cross-env NODE_ENV=development yarn webpack-tsx --watch\" \"yarn run electron\"",
37
+ "webpack-tsx": "tsx node_modules/webpack/bin/webpack.js --config webpack.config.ts",
38
+ "build": "yarn clean && \"cross-env NODE_ENV=development yarn webpack-tsx\"",
39
+ "build:prod": "yarn clean && \"cross-env NODE_ENV=production yarn webpack-tsx\"",
40
+ "electron": "wait-on dist/index.js && ELECTRON_DISABLE_SECURITY_WARNINGS=true electron .",
41
+ "electron-debug": "wait-on dist/index.js && ELECTRON_DISABLE_SECURITY_WARNINGS=true electron --inspect-brk=9229 .",
42
+ "test": "jest",
43
+ "pack": "yarn build:prod && electron-builder --dir",
44
+ "dist": "yarn build:prod && yarn test && electron-builder"
45
+ },
46
+ "build": {
47
+ "appId": "yt.grabber",
48
+ "productName": "YT Grabber",
49
+ "copyright": "© 2025 Marcin Karpiński",
50
+ "files": [
51
+ "dist/**/*",
52
+ "package.json"
53
+ ],
54
+ "publish": null,
55
+ "asar": true,
56
+ "win": {
57
+ "target": "nsis",
58
+ "icon": "dist/resources/icons/logo.ico",
59
+ "artifactName": "${name}-${version}-setup.${ext}"
60
+ },
61
+ "nsis": {
62
+ "oneClick": false,
63
+ "allowToChangeInstallationDirectory": true,
64
+ "installerIcon": "dist/resources/icons/logo.ico",
65
+ "uninstallerIcon": "dist/resources/icons/logo.ico",
66
+ "installerHeaderIcon": "dist/resources/icons/logo.ico",
67
+ "createDesktopShortcut": true,
68
+ "createStartMenuShortcut": true,
69
+ "shortcutName": "YT Grabber"
70
+ },
71
+ "directories": {
72
+ "output": "release"
73
+ },
74
+ "extraResources": [
75
+ {
76
+ "from": "dist/resources/bin",
77
+ "to": "bin",
78
+ "filter": [
79
+ "*.exe"
80
+ ]
81
+ },
82
+ {
83
+ "from": "dist/resources/locales",
84
+ "to": "locales"
85
+ },
86
+ {
87
+ "from": "public/profile",
88
+ "to": "profile"
89
+ }
90
+ ]
91
+ },
92
+ "author": "Marcin Karpiński <mkarpins@gmail.com>",
93
+ "license": "MIT",
94
+ "dependencies": {
95
+ "@emotion/react": "^11.14.0",
96
+ "@emotion/styled": "^11.14.1",
97
+ "@mui/icons-material": "^7.3.4",
98
+ "@mui/lab": "^7.0.1-beta.18",
99
+ "@mui/material": "^7.3.4",
100
+ "axios": "^1.12.2",
101
+ "classnames": "^2.5.1",
102
+ "electron-devtools-installer": "^4.0.0",
103
+ "electron-reload": "^2.0.0-alpha.1",
104
+ "electron-store": "^8.2.0",
105
+ "fs-extra": "^11.3.2",
106
+ "i18next": "^25.6.0",
107
+ "i18next-node-fs-backend": "^2.1.3",
108
+ "jsonschema": "^1.5.0",
109
+ "lodash-es": "^4.17.21",
110
+ "mkdirp": "^3.0.1",
111
+ "moment": "^2.30.1",
112
+ "moment-duration-format": "^2.3.2",
113
+ "puppeteer": "^24.26.0",
114
+ "puppeteer-extra": "^3.3.6",
115
+ "puppeteer-extra-plugin-devtools": "^2.4.6",
116
+ "puppeteer-extra-plugin-stealth": "^2.11.2",
117
+ "react": "^19.2.0",
118
+ "react-dom": "^19.2.0",
119
+ "react-i18next": "^16.1.5",
120
+ "react-number-format": "^5.4.4",
121
+ "react-router-dom": "^7.9.4",
122
+ "usehooks-ts": "^3.1.1",
123
+ "win-version-info": "^6.0.1",
124
+ "yt-dlp-wrap": "^2.3.12"
125
+ },
126
+ "devDependencies": {
127
+ "@playwright/test": "^1.56.1",
128
+ "@swc/core": "^1.13.5",
129
+ "@swc/jest": "^0.2.39",
130
+ "@testing-library/dom": "^10.4.1",
131
+ "@testing-library/jest-dom": "^6.9.1",
132
+ "@testing-library/react": "^16.3.0",
133
+ "@testing-library/user-event": "^14.6.1",
134
+ "@types/eslint": "^9.6.1",
135
+ "@types/fs-extra": "^11.0.4",
136
+ "@types/html-webpack-plugin": "^3.2.9",
137
+ "@types/i18next": "^13.0.0",
138
+ "@types/i18next-node-fs-backend": "^2.1.5",
139
+ "@types/jest": "^30.0.0",
140
+ "@types/jsonschema": "^1.1.1",
141
+ "@types/lodash-es": "^4.17.12",
142
+ "@types/mini-css-extract-plugin": "^2.5.1",
143
+ "@types/moment-duration-format": "^2.2.6",
144
+ "@types/mui-image": "^1.0.5",
145
+ "@types/node": "^24.9.1",
146
+ "@types/prettier": "^3.0.0",
147
+ "@types/puppeteer": "^7.0.4",
148
+ "@types/react": "^19.2.2",
149
+ "@types/react-dom": "^19.2.2",
150
+ "@types/webpack-dev-server": "^4.7.2",
151
+ "@types/webpack-env": "^1.18.8",
152
+ "@types/win-version-info": "^3.1.3",
153
+ "@typescript-eslint/eslint-plugin": "^8.46.2",
154
+ "@typescript-eslint/parser": "^8.46.2",
155
+ "concurrently": "^9.2.1",
156
+ "copy-webpack-plugin": "^13.0.1",
157
+ "cross-env": "^10.1.0",
158
+ "css-loader": "^7.1.2",
159
+ "electron": "^38.4.0",
160
+ "electron-builder": "^26.0.12",
161
+ "electron-playwright-helpers": "^1.8.2",
162
+ "eslint": "^9.38.0",
163
+ "eslint-config-prettier": "^10.1.8",
164
+ "eslint-plugin-css": "^0.11.1",
165
+ "eslint-plugin-react": "^7.37.5",
166
+ "eslint-webpack-plugin": "^5.0.2",
167
+ "globals": "^16.4.0",
168
+ "html-webpack-plugin": "^5.6.4",
169
+ "i18next-scanner-webpack": "^1.0.0",
170
+ "identity-obj-proxy": "^3.0.0",
171
+ "jest": "^30.2.0",
172
+ "jest-environment-jsdom": "^30.2.0",
173
+ "jiti": "^2.6.1",
174
+ "mini-css-extract-plugin": "^2.9.4",
175
+ "prettier": "^3.6.2",
176
+ "raw-loader": "^4.0.2",
177
+ "rimraf": "^6.0.1",
178
+ "source-map-loader": "^5.0.0",
179
+ "stylus": "^0.64.0",
180
+ "stylus-loader": "^8.1.2",
181
+ "ts-loader": "^9.5.4",
182
+ "ts-node": "^10.9.2",
183
+ "ts-node-dev": "^2.0.0",
184
+ "tsx": "^4.20.6",
185
+ "typescript": "^5.9.3",
186
+ "util": "^0.12.5",
187
+ "wait-on": "^9.0.1",
188
+ "webpack": "^5.102.1",
189
+ "webpack-cli": "^6.0.1",
190
+ "webpack-dev-server": "^5.2.2",
191
+ "webpack-node-externals": "^3.0.0"
192
+ },
193
+ "packageManager": "yarn@4.4.1"
194
+ }
Binary file
Binary file
Binary file
package/src/App.tsx CHANGED
@@ -1,7 +1,5 @@
1
1
  import classnames from "classnames";
2
- import _isArray from "lodash/isArray";
3
- import _map from "lodash/map";
4
- import _split from "lodash/split";
2
+ import {isArray, map, split} from "lodash-es";
5
3
  import React from "react";
6
4
  import {HashRouter, Route, Routes} from "react-router-dom";
7
5
 
@@ -20,13 +18,13 @@ export const App: React.FC = (props: Record<string, any>) => {
20
18
  const {anchorEl, help} = useHelp();
21
19
 
22
20
  const renderLineBreaks = (value: string) => {
23
- return _map(_split(value, "\n"), (v, k) => <React.Fragment key={k}>{v}<br /></React.Fragment>);
21
+ return map(split(value, "\n"), (v, k) => <React.Fragment key={k}>{v}<br /></React.Fragment>);
24
22
  };
25
23
 
26
24
  const renderText = (value: string | string[]) => {
27
- const valueArray = _isArray(value) ? value : [value];
25
+ const valueArray = isArray(value) ? value : [value];
28
26
 
29
- return _map(valueArray, (v, k) => <p key={k}>{renderLineBreaks(v)}</p>);
27
+ return map(valueArray, (v, k) => <p key={k}>{renderLineBreaks(v)}</p>);
30
28
  };
31
29
 
32
30
  return (
@@ -1,6 +1,6 @@
1
1
  import fs from "fs-extra";
2
- import _isEmpty from "lodash/isEmpty";
3
- import {ElementHandle, Page} from "puppeteer";
2
+ import {isEmpty} from "lodash-es";
3
+ import {ElementHandle, Page} from "puppeteer-core";
4
4
 
5
5
  import {getProfilePath} from "../common/FileSystem";
6
6
  import {waitFor} from "../common/Helpers";
@@ -21,7 +21,7 @@ export const clearInput = async (input: ElementHandle<Element>, page: Page) => {
21
21
  export const setCookies = async (page: Page) => {
22
22
  const cachedCookies = fs.readJSONSync(getProfilePath() + "/cookies.json", {throws: false});
23
23
 
24
- if (_isEmpty(cachedCookies)) {
24
+ if (isEmpty(cachedCookies)) {
25
25
  await waitFor(3000);
26
26
  const pageCookies = await page.cookies();
27
27
 
@@ -1,4 +1,4 @@
1
- import _toLower from "lodash/toLower";
1
+ import {toLower} from "lodash-es";
2
2
 
3
3
  export const AlbumsHrefSelector = "//ytmusic-app-layout//div[@id='content']/ytmusic-browse-response//ytmusic-section-list-renderer//div[@id='content-group']//div[contains(@class, 'header-renderer')]/yt-formatted-string/a[contains(text(), 'Album')]";
4
4
 
@@ -36,7 +36,7 @@ export const YtMusicArtistRelativeNameSelector = ".//div[contains(@class, 'title
36
36
 
37
37
  export const YtMusicArtistRelativeLinkSelector = ".//a";
38
38
 
39
- export const getYtMusicSearchResultsArtistsSelector = (artist: string) => `//ytmusic-app-layout//div[@id='content']//ytmusic-search-page//div[@id='contents']//ytmusic-shelf-renderer//div[@id='contents']//ytmusic-responsive-list-item-renderer//div[contains(@class, 'title-column')]/yt-formatted-string//text()[translate(., 'ABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÜÉÈÊÀÁÂÒÓÔÙÚÛÇÅÏÕÑŒĄĆĘŁŃÓŚŹŻ', 'abcdefghijklmnopqrstuvwxyzäöüéèêàáâòóôùúûçåïõñœąćęłńóśźż')='${_toLower(artist)}' or translate(., 'ABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÜÉÈÊÀÁÂÒÓÔÙÚÛÇÅÏÕÑŒĄĆĘŁŃÓŚŹŻ', 'abcdefghijklmnopqrstuvwxyzäöüéèêàáâòóôùúûçåïõñœąćęłńóśźż')='the ${_toLower(artist)}']//ancestor::ytmusic-responsive-list-item-renderer`;
39
+ export const getYtMusicSearchResultsArtistsSelector = (artist: string) => `//ytmusic-app-layout//div[@id='content']//ytmusic-search-page//div[@id='contents']//ytmusic-shelf-renderer//div[@id='contents']//ytmusic-responsive-list-item-renderer//div[contains(@class, 'title-column')]/yt-formatted-string//text()[translate(., 'ABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÜÉÈÊÀÁÂÒÓÔÙÚÛÇÅÏÕÑŒĄĆĘŁŃÓŚŹŻ', 'abcdefghijklmnopqrstuvwxyzäöüéèêàáâòóôùúûçåïõñœąćęłńóśźż')='${toLower(artist)}' or translate(., 'ABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÜÉÈÊÀÁÂÒÓÔÙÚÛÇÅÏÕÑŒĄĆĘŁŃÓŚŹŻ', 'abcdefghijklmnopqrstuvwxyzäöüéèêàáâòóôùúûçåïõñœąćęłńóśźż')='the ${toLower(artist)}']//ancestor::ytmusic-responsive-list-item-renderer`;
40
40
 
41
41
  export const getYtMusicAlbumsDirectLinkSelectorFilteredByDate = (fromYear = 0, untilYear = 9999) => `//ytmusic-app-layout//div[@id='content']/ytmusic-browse-response//ytmusic-section-list-renderer//div[@id='content-group']//div[contains(@class, 'header-renderer')]/yt-formatted-string[contains(text(), 'Album')]//ancestor::div[contains(@class, 'ytmusic-shelf')]//div[@id='items-wrapper']/ul/*[contains(@class, 'ytmusic-carousel')]//div[contains(@class, 'details')]/span/yt-formatted-string[number(text()) >= ${fromYear} and number(text()) <= ${untilYear}]//ancestor::div[contains(@class, 'details')]//a[contains(@class, 'yt-simple-endpoint') and contains(@class, 'yt-formatted-string')]`;
42
42