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.
- package/README.md +18 -1
- package/e2e/main.spec.ts +46 -46
- package/jest.config.ts +3 -0
- package/package.json +194 -188
- package/public/screenshots/main-downloading.png +0 -0
- package/public/screenshots/main-info.png +0 -0
- package/public/screenshots/main.png +0 -0
- package/public/screenshots/settings.png +0 -0
- package/src/App.tsx +4 -6
- package/src/automations/Helpers.ts +3 -3
- package/src/automations/Selectors.ts +2 -2
- package/src/automations/Youtube.ts +6 -11
- package/src/automations/YoutubeAlbums.ts +3 -8
- package/src/automations/YoutubeArtists.ts +4 -9
- package/src/automations/YoutubeTracks.ts +4 -9
- package/src/common/FileSystem.ts +4 -4
- package/src/common/Formatters.ts +8 -12
- package/src/common/Helpers.ts +7 -13
- package/src/common/Media.ts +2 -0
- package/src/common/Promise.ts +2 -2
- package/src/common/PuppeteerOptions.ts +11 -11
- package/src/common/Reporter.ts +3 -5
- package/src/common/Store.ts +9 -4
- package/src/common/YtdplUtils.ts +37 -35
- package/src/components/fileField/FileField.tsx +6 -6
- package/src/components/modals/detailsModal/DetailsModal.tsx +2 -2
- package/src/components/modals/failuresModal/FailuresModal.tsx +3 -4
- package/src/components/modals/imageModal/ImageModal.tsx +0 -1
- package/src/components/modals/selectArtistModal/SelectArtistModal.tsx +4 -5
- package/src/components/numberField/NumberField.tsx +18 -19
- package/src/components/themePicker/ThemePicker.tsx +2 -2
- package/src/components/youtube/formatSelector/FormatSelector.styl +35 -0
- package/src/components/youtube/formatSelector/FormatSelector.tsx +66 -34
- package/src/components/youtube/infoBar/InfoBar.tsx +13 -18
- package/src/components/youtube/inputModePicker/InputModePicker.tsx +5 -6
- package/src/components/youtube/inputPanel/InputPanel.tsx +19 -26
- package/src/components/youtube/logMenu/LogMenu.tsx +5 -6
- package/src/components/youtube/mediaInfoPanel/MediaInfoPanel.tsx +8 -12
- package/src/components/youtube/playlistTabs/PlaylistTabs.tsx +36 -45
- package/src/components/youtube/trackList/TrackList.tsx +50 -66
- package/src/hooks/useCancellablePromises.ts +1 -1
- package/src/hooks/useClickCounter.ts +3 -3
- package/src/hooks/useHelp.ts +3 -4
- package/src/hooks/useWindowUpdater.ts +2 -2
- package/src/i18next.ts +2 -2
- package/src/index.ts +1 -1
- package/src/messaging/MessageChannel.ts +2 -2
- package/src/messaging/MultiMessageChannel.ts +4 -6
- package/src/react/contexts/AppThemeContext.tsx +2 -2
- package/src/react/reducers/Reducer.ts +2 -2
- package/src/react/states/State.ts +4 -4
- package/src/resources/bin/yt-dlp.exe +0 -0
- package/src/resources/locales/de-DE/help.json +12 -1
- package/src/resources/locales/de-DE/translation.json +4 -0
- package/src/resources/locales/en-GB/help.json +12 -1
- package/src/resources/locales/en-GB/translation.json +4 -0
- package/src/resources/locales/pl-PL/help.json +12 -1
- package/src/resources/locales/pl-PL/translation.json +4 -0
- package/src/views/development/DevelopmentView.tsx +1 -1
- package/src/views/home/HomeView.tsx +91 -109
- package/src/views/settings/SettingsView.tsx +40 -30
- package/tests/TestPlaylistMock.ts +1 -1
- 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,
|
|
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
|
-
"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
|
|
23
|
-
"youtube
|
|
24
|
-
"youtube
|
|
25
|
-
"youtube
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
"
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
"
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
"
|
|
63
|
-
"
|
|
64
|
-
"
|
|
65
|
-
"
|
|
66
|
-
"
|
|
67
|
-
"
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
"
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
"
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
"@
|
|
96
|
-
"@
|
|
97
|
-
"@mui/material": "^7.
|
|
98
|
-
"
|
|
99
|
-
"
|
|
100
|
-
"
|
|
101
|
-
"
|
|
102
|
-
"electron-
|
|
103
|
-
"
|
|
104
|
-
"
|
|
105
|
-
"
|
|
106
|
-
"
|
|
107
|
-
"
|
|
108
|
-
"
|
|
109
|
-
"
|
|
110
|
-
"
|
|
111
|
-
"
|
|
112
|
-
"
|
|
113
|
-
"puppeteer
|
|
114
|
-
"puppeteer-extra
|
|
115
|
-
"
|
|
116
|
-
"
|
|
117
|
-
"react
|
|
118
|
-
"react-
|
|
119
|
-
"react-
|
|
120
|
-
"
|
|
121
|
-
"
|
|
122
|
-
"
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
"@
|
|
128
|
-
"@
|
|
129
|
-
"@
|
|
130
|
-
"@testing-library/
|
|
131
|
-
"@testing-library/
|
|
132
|
-
"@
|
|
133
|
-
"@
|
|
134
|
-
"@types/
|
|
135
|
-
"@types/
|
|
136
|
-
"@types/
|
|
137
|
-
"@types/
|
|
138
|
-
"@types/
|
|
139
|
-
"@types/
|
|
140
|
-
"@types/
|
|
141
|
-
"@types/
|
|
142
|
-
"@types/
|
|
143
|
-
"@types/
|
|
144
|
-
"@types/
|
|
145
|
-
"@types/
|
|
146
|
-
"@types/
|
|
147
|
-
"@types/
|
|
148
|
-
"@
|
|
149
|
-
"@
|
|
150
|
-
"
|
|
151
|
-
"
|
|
152
|
-
"
|
|
153
|
-
"
|
|
154
|
-
"
|
|
155
|
-
"
|
|
156
|
-
"
|
|
157
|
-
"
|
|
158
|
-
"
|
|
159
|
-
"
|
|
160
|
-
"
|
|
161
|
-
"
|
|
162
|
-
"
|
|
163
|
-
"
|
|
164
|
-
"
|
|
165
|
-
"
|
|
166
|
-
"
|
|
167
|
-
"
|
|
168
|
-
"
|
|
169
|
-
"
|
|
170
|
-
"
|
|
171
|
-
"
|
|
172
|
-
"
|
|
173
|
-
"
|
|
174
|
-
"
|
|
175
|
-
"
|
|
176
|
-
"
|
|
177
|
-
"
|
|
178
|
-
"
|
|
179
|
-
"
|
|
180
|
-
"
|
|
181
|
-
"
|
|
182
|
-
"
|
|
183
|
-
"
|
|
184
|
-
"
|
|
185
|
-
"
|
|
186
|
-
|
|
187
|
-
|
|
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
|
|
Binary file
|
package/src/App.tsx
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import classnames from "classnames";
|
|
2
|
-
import
|
|
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
|
|
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 =
|
|
25
|
+
const valueArray = isArray(value) ? value : [value];
|
|
28
26
|
|
|
29
|
-
return
|
|
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
|
|
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 (
|
|
24
|
+
if (isEmpty(cachedCookies)) {
|
|
25
25
|
await waitFor(3000);
|
|
26
26
|
const pageCookies = await page.cookies();
|
|
27
27
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
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äöüéèêàáâòóôùúûçåïõñœąćęłńóśźż')='${
|
|
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
|
|