yt-grabber 1.5.0 → 1.6.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/package.json +1 -1
- package/public/screenshots/settings.png +0 -0
- package/src/automations/Helpers.ts +19 -0
- package/src/automations/Selectors.ts +22 -2
- package/src/automations/Youtube.ts +65 -77
- package/src/automations/YoutubeAlbums.ts +58 -72
- package/src/automations/YoutubeArtists.ts +147 -65
- package/src/automations/YoutubeTracks.ts +61 -75
- package/src/common/Media.ts +6 -1
- package/src/common/Messaging.ts +2 -0
- package/src/common/PuppeteerOptions.ts +4 -2
- package/src/common/Store.ts +22 -1
- package/src/common/Youtube.ts +6 -0
- package/src/common/YtdplUtils.ts +4 -3
- package/src/components/fileField/FileField.tsx +5 -5
- package/src/components/modals/detailsModal/DetailsModal.tsx +8 -1
- package/src/components/modals/selectArtistModal/SelectArtistModal.styl +65 -0
- package/src/components/modals/selectArtistModal/SelectArtistModal.tsx +99 -0
- package/src/components/styledTextField/StyledTextField.tsx +28 -0
- package/src/components/youtube/formatSelector/FormatSelector.tsx +1 -1
- package/src/components/youtube/mediaInfoPanel/MediaInfoPanel.tsx +5 -5
- package/src/components/youtube/playlistTabs/PlaylistTabs.tsx +35 -42
- package/src/messaging/MessageChannel.ts +15 -1
- package/src/messaging/Messages.ts +2 -0
- package/src/messaging/MultiMessageChannel.ts +2 -1
- package/src/messaging/channels/YoutubeArtistsMessageChannel.ts +3 -1
- package/src/react/contexts/DataContext.tsx +1 -1
- package/src/resources/bin/ffmpeg.exe +0 -0
- package/src/resources/bin/yt-dlp.exe +0 -0
- package/src/resources/locales/de-DE/help.json +16 -2
- package/src/resources/locales/de-DE/translation.json +9 -1
- package/src/resources/locales/en-GB/help.json +17 -3
- package/src/resources/locales/en-GB/translation.json +9 -1
- package/src/resources/locales/pl-PL/help.json +16 -2
- package/src/resources/locales/pl-PL/translation.json +9 -1
- package/src/views/home/HomeView.tsx +94 -45
- package/src/views/settings/SettingsView.tsx +70 -4
package/package.json
CHANGED
|
Binary file
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
+
import fs from "fs-extra";
|
|
2
|
+
import _isEmpty from "lodash/isEmpty";
|
|
1
3
|
import {ElementHandle, Page} from "puppeteer";
|
|
2
4
|
|
|
5
|
+
import {getProfilePath} from "../common/FileSystem";
|
|
6
|
+
import {waitFor} from "../common/Helpers";
|
|
3
7
|
import puppeteerOptions from "../common/PuppeteerOptions";
|
|
4
8
|
|
|
5
9
|
export const navigateToPage = async (url: string, page: Page, timeout = puppeteerOptions.timeout) => {
|
|
@@ -13,3 +17,18 @@ export const clearInput = async (input: ElementHandle<Element>, page: Page) => {
|
|
|
13
17
|
await input.click({clickCount: 3});
|
|
14
18
|
await page.keyboard.press("Backspace");
|
|
15
19
|
};
|
|
20
|
+
|
|
21
|
+
export const setCookies = async (page: Page) => {
|
|
22
|
+
const cachedCookies = fs.readJSONSync(getProfilePath() + "/cookies.json", {throws: false});
|
|
23
|
+
|
|
24
|
+
if (_isEmpty(cachedCookies)) {
|
|
25
|
+
await waitFor(3000);
|
|
26
|
+
const pageCookies = await page.cookies();
|
|
27
|
+
|
|
28
|
+
fs.writeJSONSync(getProfilePath() + "/cookies.json", pageCookies, {spaces: 2});
|
|
29
|
+
|
|
30
|
+
await page.setCookie(...pageCookies);
|
|
31
|
+
} else {
|
|
32
|
+
await page.setCookie(...cachedCookies);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
@@ -1,11 +1,21 @@
|
|
|
1
|
+
import _toLower from "lodash/toLower";
|
|
2
|
+
|
|
1
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')]";
|
|
2
4
|
|
|
3
|
-
export const
|
|
5
|
+
export const SinglesHrefSelector = "//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(), 'Single')]";
|
|
6
|
+
|
|
7
|
+
export const AlbumFilterSelector = "//ytmusic-app-layout//div[@id='content']/ytmusic-browse-response//ytmusic-section-list-renderer/div[@id='header']//iron-selector//ytmusic-chip-cloud-chip-renderer[not(@is-selected)]//a[contains(@title, 'Album')]";
|
|
8
|
+
|
|
9
|
+
export const SingleFilterSelector = "//ytmusic-app-layout//div[@id='content']/ytmusic-browse-response//ytmusic-section-list-renderer/div[@id='header']//iron-selector//ytmusic-chip-cloud-chip-renderer[not(@is-selected)]//a[contains(@title, 'Single')]";
|
|
4
10
|
|
|
5
11
|
export const AlbumLinkSelector = "//ytmusic-app-layout//div[@id='content']/ytmusic-browse-response//ytmusic-section-list-renderer/div[@id='contents']/ytmusic-grid-renderer/div[@id='items']/*[contains(@class, 'ytmusic-grid-renderer')]//a[contains(@class, 'yt-simple-endpoint') and contains(@class, 'yt-formatted-string')]";
|
|
6
12
|
|
|
13
|
+
export const SingleLinkSelector = "//ytmusic-app-layout//div[@id='content']/ytmusic-browse-response//ytmusic-section-list-renderer/div[@id='contents']/ytmusic-grid-renderer/div[@id='items']/*[contains(@class, 'ytmusic-grid-renderer')]//a[contains(@class, 'yt-simple-endpoint') and contains(@class, 'yt-formatted-string')]";
|
|
14
|
+
|
|
7
15
|
export const AlbumsDirectLinkSelector = "//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')]//a[contains(@class, 'yt-simple-endpoint') and contains(@class, 'yt-formatted-string')]";
|
|
8
16
|
|
|
17
|
+
export const SinglesDirectLinkSelector = "//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(), 'Single')]//ancestor::div[contains(@class, 'ytmusic-shelf')]//div[@id='items-wrapper']/ul/*[contains(@class, 'ytmusic-carousel')]//a[contains(@class, 'yt-simple-endpoint') and contains(@class, 'yt-formatted-string')]";
|
|
18
|
+
|
|
9
19
|
export const YtMusicSearchInputSelector = "//ytmusic-app-layout//ytmusic-search-box//input[@id='input']";
|
|
10
20
|
|
|
11
21
|
export const YtMusicArtistsChipSelector = "//ytmusic-app-layout//ytmusic-search-page//div[contains(@class, 'content')]//ytmusic-section-list-renderer//ytmusic-chip-cloud-renderer//iron-selector[@id='chips']//a[contains(@class, 'ytmusic-chip-cloud-chip-renderer')]//yt-formatted-string[contains(text(), 'Wykonawcy') or contains(text(), 'Artists')]//ancestor::a";
|
|
@@ -16,4 +26,14 @@ export const YtMusicAlbumsChipSelector = "//ytmusic-app-layout//ytmusic-search-p
|
|
|
16
26
|
|
|
17
27
|
export const YtMusicSongsChipSelector = "//ytmusic-app-layout//ytmusic-search-page//div[contains(@class, 'content')]//ytmusic-section-list-renderer//ytmusic-chip-cloud-renderer//iron-selector[@id='chips']//a[contains(@class, 'ytmusic-chip-cloud-chip-renderer')]//yt-formatted-string[contains(text(), 'Utwory') or contains(text(), 'Songs')]//ancestor::a";
|
|
18
28
|
|
|
19
|
-
export const
|
|
29
|
+
export const YtMusicSearchResultsArtistsSelector = "//ytmusic-app-layout//div[@id='content']//ytmusic-search-page//div[@id='contents']//ytmusic-shelf-renderer//div[@id='contents']//ytmusic-responsive-list-item-renderer";
|
|
30
|
+
|
|
31
|
+
export const YtMusicSearchResultsArtistsLinkSelector = "//ytmusic-app-layout//div[@id='content']//ytmusic-search-page//div[@id='contents']//ytmusic-shelf-renderer//div[@id='contents']//ytmusic-responsive-list-item-renderer/a";
|
|
32
|
+
|
|
33
|
+
export const YtMusicArtistRelativeThumbnailSelector = ".//ytmusic-thumbnail-renderer//img";
|
|
34
|
+
|
|
35
|
+
export const YtMusicArtistRelativeNameSelector = ".//div[contains(@class, 'title-column')]//yt-formatted-string";
|
|
36
|
+
|
|
37
|
+
export const YtMusicArtistRelativeLinkSelector = ".//a";
|
|
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`;
|
|
@@ -1,21 +1,19 @@
|
|
|
1
|
-
import
|
|
1
|
+
import {i18n as i18next} from "i18next";
|
|
2
2
|
import _forEach from "lodash/forEach";
|
|
3
3
|
import _includes from "lodash/includes";
|
|
4
4
|
import _isEmpty from "lodash/isEmpty";
|
|
5
5
|
import _map from "lodash/map";
|
|
6
6
|
import _merge from "lodash/merge";
|
|
7
7
|
import _replace from "lodash/replace";
|
|
8
|
-
import {Browser, Page, TimeoutError} from "puppeteer";
|
|
8
|
+
import {Browser, LaunchOptions, Page, TimeoutError} from "puppeteer";
|
|
9
9
|
import puppeteer from "puppeteer-extra";
|
|
10
10
|
import StealthPlugin from "puppeteer-extra-plugin-stealth";
|
|
11
11
|
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import puppeteerOptions from "../common/PuppeteerOptions";
|
|
16
|
-
import {IReporter, Reporter} from "../common/Reporter";
|
|
12
|
+
import {GetYoutubeParams, GetYoutubeResult} from "../common/Messaging";
|
|
13
|
+
import puppeteerOptions, {UserAgent} from "../common/PuppeteerOptions";
|
|
14
|
+
import {IReporter, ProgressInfo, Reporter} from "../common/Reporter";
|
|
17
15
|
import {MessageHandlerParams} from "../messaging/MessageChannel";
|
|
18
|
-
import {navigateToPage} from "./Helpers";
|
|
16
|
+
import {navigateToPage, setCookies} from "./Helpers";
|
|
19
17
|
import {
|
|
20
18
|
AlbumFilterSelector, AlbumLinkSelector, AlbumsDirectLinkSelector, AlbumsHrefSelector
|
|
21
19
|
} from "./Selectors";
|
|
@@ -33,79 +31,13 @@ export const execute = async (parameters: MessageHandlerParams) => {
|
|
|
33
31
|
});
|
|
34
32
|
|
|
35
33
|
try {
|
|
36
|
-
|
|
37
|
-
const userAgent = "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Mobile Safari/537.3";
|
|
34
|
+
|
|
38
35
|
await Promise.race([
|
|
39
|
-
(
|
|
40
|
-
await i18n.changeLanguage(params.lang);
|
|
41
|
-
|
|
42
|
-
reporter = new Reporter(onUpdate);
|
|
43
|
-
reporter.start(i18n.t("starting"));
|
|
44
|
-
browser = await puppeteer.launch(_merge(puppeteerOptions, options));
|
|
45
|
-
[page] = await browser.pages();
|
|
46
|
-
|
|
47
|
-
await page.setUserAgent(userAgent);
|
|
48
|
-
|
|
49
|
-
const cachedCookies = fs.readJSONSync(getProfilePath() + "/cookies.json", {throws: false});
|
|
50
|
-
|
|
51
|
-
if (_isEmpty(cachedCookies)) {
|
|
52
|
-
await waitFor(3000);
|
|
53
|
-
const pageCookies = await page.cookies();
|
|
54
|
-
|
|
55
|
-
fs.writeJSONSync(getProfilePath() + "/cookies.json", pageCookies, {spaces: 2});
|
|
56
|
-
|
|
57
|
-
await page.setCookie(...pageCookies);
|
|
58
|
-
} else {
|
|
59
|
-
await page.setCookie(...cachedCookies);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
await navigateToPage(params.url, page);
|
|
63
|
-
|
|
64
|
-
const process = async (urlToProcess: string) => {
|
|
65
|
-
const results: string[] = [];
|
|
66
|
-
|
|
67
|
-
try {
|
|
68
|
-
await navigateToPage(urlToProcess, page);
|
|
69
|
-
|
|
70
|
-
const element = await page.waitForSelector(`::-p-xpath(${AlbumsHrefSelector})`, {timeout: 1000});
|
|
71
|
-
const albumsUrl = await element.evaluate((el) => el.getAttribute("href"));
|
|
72
|
-
|
|
73
|
-
await navigateToPage(`${params.url}/${albumsUrl}`, page);
|
|
74
|
-
const albumFilterButton = await page.waitForSelector(`::-p-xpath(${AlbumFilterSelector})`, {timeout: 1000});
|
|
75
|
-
|
|
76
|
-
albumFilterButton.click();
|
|
77
|
-
await page.waitForNetworkIdle();
|
|
78
|
-
|
|
79
|
-
const items = await page.$$eval(`xpath/${AlbumLinkSelector}`, (elements) => elements.map((el) => el.getAttribute("href")));
|
|
80
|
-
|
|
81
|
-
for (const item of items) {
|
|
82
|
-
results.push(`${params.url}/${item}`);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
return results;
|
|
86
|
-
} catch (error) {
|
|
87
|
-
const items = await page.$$eval(`xpath/${AlbumsDirectLinkSelector}`, (elements) => elements.map((el) => el.getAttribute("href")));
|
|
88
|
-
|
|
89
|
-
for (const item of items) {
|
|
90
|
-
results.push(`${params.url}/${item}`);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
return results;
|
|
94
|
-
}
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
for (const u of params.values) {
|
|
98
|
-
const data = await process(u);
|
|
99
|
-
|
|
100
|
-
result.values.push(...data);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
reporter.finish("done", result);
|
|
104
|
-
})(),
|
|
36
|
+
run(params, options, i18n, onUpdate),
|
|
105
37
|
abortPromise,
|
|
106
38
|
]);
|
|
107
39
|
} catch (error: any) {
|
|
108
|
-
const result: GetYoutubeResult = {errors: []};
|
|
40
|
+
const result: GetYoutubeResult = {errors: [], sources: params.values};
|
|
109
41
|
if (error.message === "aborted") {
|
|
110
42
|
throw error;
|
|
111
43
|
}
|
|
@@ -126,6 +58,62 @@ export const execute = async (parameters: MessageHandlerParams) => {
|
|
|
126
58
|
}
|
|
127
59
|
};
|
|
128
60
|
|
|
61
|
+
const run = async (params: GetYoutubeParams, options: LaunchOptions, i18n: i18next, onUpdate: (data: ProgressInfo<GetYoutubeResult>) => void) => {
|
|
62
|
+
const result: GetYoutubeResult = {warnings: [], errors: [], values: [], sources: params.values};
|
|
63
|
+
|
|
64
|
+
await i18n.changeLanguage(params.lang);
|
|
65
|
+
|
|
66
|
+
reporter = new Reporter(onUpdate);
|
|
67
|
+
reporter.start(i18n.t("starting"));
|
|
68
|
+
browser = await puppeteer.launch(_merge(puppeteerOptions, options));
|
|
69
|
+
[page] = await browser.pages();
|
|
70
|
+
|
|
71
|
+
await page.setUserAgent(UserAgent);
|
|
72
|
+
await setCookies(page);
|
|
73
|
+
await navigateToPage(params.url, page);
|
|
74
|
+
|
|
75
|
+
const process = async (urlToProcess: string) => {
|
|
76
|
+
const results: string[] = [];
|
|
77
|
+
|
|
78
|
+
try {
|
|
79
|
+
await navigateToPage(urlToProcess, page);
|
|
80
|
+
|
|
81
|
+
const element = await page.waitForSelector(`::-p-xpath(${AlbumsHrefSelector})`, {timeout: 1000});
|
|
82
|
+
const albumsUrl = await element.evaluate((el) => el.getAttribute("href"));
|
|
83
|
+
|
|
84
|
+
await navigateToPage(`${params.url}/${albumsUrl}`, page);
|
|
85
|
+
const albumFilterButton = await page.waitForSelector(`::-p-xpath(${AlbumFilterSelector})`, {timeout: 1000});
|
|
86
|
+
|
|
87
|
+
albumFilterButton.click();
|
|
88
|
+
await page.waitForNetworkIdle();
|
|
89
|
+
|
|
90
|
+
const items = await page.$$eval(`xpath/${AlbumLinkSelector}`, (elements) => elements.map((el) => el.getAttribute("href")));
|
|
91
|
+
|
|
92
|
+
for (const item of items) {
|
|
93
|
+
results.push(`${params.url}/${item}`);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return results;
|
|
97
|
+
} catch (error) {
|
|
98
|
+
const items = await page.$$eval(`xpath/${AlbumsDirectLinkSelector}`, (elements) => elements.map((el) => el.getAttribute("href")));
|
|
99
|
+
|
|
100
|
+
for (const item of items) {
|
|
101
|
+
results.push(`${params.url}/${item}`);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return results;
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
for (const u of params.values) {
|
|
109
|
+
const data = await process(u);
|
|
110
|
+
|
|
111
|
+
result.values.push(...data);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
reporter.finish("done", result);
|
|
115
|
+
};
|
|
116
|
+
|
|
129
117
|
const closeResources = async () => {
|
|
130
118
|
if (page) await page.close();
|
|
131
119
|
if (browser) await browser.close();
|
|
@@ -1,23 +1,21 @@
|
|
|
1
|
-
import
|
|
1
|
+
import {i18n as i18next} from "i18next";
|
|
2
2
|
import _forEach from "lodash/forEach";
|
|
3
3
|
import _includes from "lodash/includes";
|
|
4
4
|
import _isEmpty from "lodash/isEmpty";
|
|
5
5
|
import _map from "lodash/map";
|
|
6
6
|
import _merge from "lodash/merge";
|
|
7
7
|
import _replace from "lodash/replace";
|
|
8
|
-
import {Browser, Page, TimeoutError} from "puppeteer";
|
|
8
|
+
import {Browser, LaunchOptions, Page, TimeoutError} from "puppeteer";
|
|
9
9
|
import puppeteer from "puppeteer-extra";
|
|
10
10
|
import StealthPlugin from "puppeteer-extra-plugin-stealth";
|
|
11
11
|
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import puppeteerOptions from "../common/PuppeteerOptions";
|
|
16
|
-
import {IReporter, Reporter} from "../common/Reporter";
|
|
12
|
+
import {GetYoutubeParams, GetYoutubeResult} from "../common/Messaging";
|
|
13
|
+
import puppeteerOptions, {UserAgent} from "../common/PuppeteerOptions";
|
|
14
|
+
import {IReporter, ProgressInfo, Reporter} from "../common/Reporter";
|
|
17
15
|
import {MessageHandlerParams} from "../messaging/MessageChannel";
|
|
18
|
-
import {clearInput, navigateToPage} from "./Helpers";
|
|
16
|
+
import {clearInput, navigateToPage, setCookies} from "./Helpers";
|
|
19
17
|
import {
|
|
20
|
-
YtMusicAlbumsChipSelector, YtMusicSearchInputSelector,
|
|
18
|
+
YtMusicAlbumsChipSelector, YtMusicSearchInputSelector, YtMusicSearchResultsArtistsLinkSelector
|
|
21
19
|
} from "./Selectors";
|
|
22
20
|
|
|
23
21
|
let page: Page;
|
|
@@ -34,72 +32,11 @@ export const execute = async (parameters: MessageHandlerParams) => {
|
|
|
34
32
|
|
|
35
33
|
try {
|
|
36
34
|
await Promise.race([
|
|
37
|
-
(
|
|
38
|
-
const result: GetYoutubeResult = {warnings: [], errors: [], values: []};
|
|
39
|
-
const userAgent = "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Mobile Safari/537.3";
|
|
40
|
-
|
|
41
|
-
reporter = new Reporter(onUpdate);
|
|
42
|
-
reporter.start(i18n.t("starting"));
|
|
43
|
-
browser = await puppeteer.launch(_merge(puppeteerOptions, options));
|
|
44
|
-
[page] = await browser.pages();
|
|
45
|
-
|
|
46
|
-
await page.setUserAgent(userAgent);
|
|
47
|
-
|
|
48
|
-
const cachedCookies = fs.readJSONSync(getProfilePath() + "/cookies.json", {throws: false});
|
|
49
|
-
|
|
50
|
-
if (_isEmpty(cachedCookies)) {
|
|
51
|
-
await waitFor(3000);
|
|
52
|
-
const pageCookies = await page.cookies();
|
|
53
|
-
|
|
54
|
-
fs.writeJSONSync(getProfilePath() + "/cookies.json", pageCookies, {spaces: 2});
|
|
55
|
-
|
|
56
|
-
await page.setCookie(...pageCookies);
|
|
57
|
-
} else {
|
|
58
|
-
await page.setCookie(...cachedCookies);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
await navigateToPage(params.url, page);
|
|
62
|
-
|
|
63
|
-
const process = async (album: string) => {
|
|
64
|
-
const results: string[] = [];
|
|
65
|
-
|
|
66
|
-
try {
|
|
67
|
-
const searchInput = await page.waitForSelector(`::-p-xpath(${YtMusicSearchInputSelector})`, {timeout: 1000});
|
|
68
|
-
await clearInput(searchInput, page);
|
|
69
|
-
await searchInput.type(album);
|
|
70
|
-
page.keyboard.press("Enter");
|
|
71
|
-
await page.waitForNetworkIdle();
|
|
72
|
-
|
|
73
|
-
const albumsChip = await page.waitForSelector(`::-p-xpath(${YtMusicAlbumsChipSelector})`, {timeout: 1000});
|
|
74
|
-
|
|
75
|
-
albumsChip.click();
|
|
76
|
-
await page.waitForNetworkIdle();
|
|
77
|
-
await page.waitForSelector(`::-p-xpath(${YtMusicSearchResultsSelector})`, {timeout: 1000});
|
|
78
|
-
|
|
79
|
-
const albumsElements = await page.$$(`::-p-xpath(${YtMusicSearchResultsSelector})`);
|
|
80
|
-
const albumEl = albumsElements[0];
|
|
81
|
-
const albumUrl = await albumEl.evaluate((el) => el.getAttribute("href"));
|
|
82
|
-
|
|
83
|
-
results.push(`${params.url}/${albumUrl}`);
|
|
84
|
-
|
|
85
|
-
return results;
|
|
86
|
-
} catch (error) {
|
|
87
|
-
return results;
|
|
88
|
-
}
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
for (const a of params.values) {
|
|
92
|
-
const data = await process(a);
|
|
93
|
-
|
|
94
|
-
result.values.push(...data);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
reporter.finish("done", result);
|
|
98
|
-
})(),
|
|
35
|
+
run(params, options, i18n, onUpdate),
|
|
99
36
|
abortPromise,
|
|
100
37
|
]);
|
|
101
38
|
} catch (error: any) {
|
|
102
|
-
const result: GetYoutubeResult = {errors: []};
|
|
39
|
+
const result: GetYoutubeResult = {errors: [], sources: params.values};
|
|
103
40
|
if (error.message === "aborted") {
|
|
104
41
|
throw error;
|
|
105
42
|
}
|
|
@@ -120,6 +57,55 @@ export const execute = async (parameters: MessageHandlerParams) => {
|
|
|
120
57
|
}
|
|
121
58
|
};
|
|
122
59
|
|
|
60
|
+
const run = async (params: GetYoutubeParams, options: LaunchOptions, i18n: i18next, onUpdate: (data: ProgressInfo<GetYoutubeResult>) => void) => {
|
|
61
|
+
const result: GetYoutubeResult = {warnings: [], errors: [], values: [], sources: params.values};
|
|
62
|
+
|
|
63
|
+
reporter = new Reporter(onUpdate);
|
|
64
|
+
reporter.start(i18n.t("starting"));
|
|
65
|
+
browser = await puppeteer.launch(_merge(puppeteerOptions, options));
|
|
66
|
+
[page] = await browser.pages();
|
|
67
|
+
|
|
68
|
+
await page.setUserAgent(UserAgent);
|
|
69
|
+
await setCookies(page);
|
|
70
|
+
await navigateToPage(params.url, page);
|
|
71
|
+
|
|
72
|
+
const process = async (album: string) => {
|
|
73
|
+
const results: string[] = [];
|
|
74
|
+
|
|
75
|
+
try {
|
|
76
|
+
const searchInput = await page.waitForSelector(`::-p-xpath(${YtMusicSearchInputSelector})`, {timeout: 1000});
|
|
77
|
+
await clearInput(searchInput, page);
|
|
78
|
+
await searchInput.type(album);
|
|
79
|
+
page.keyboard.press("Enter");
|
|
80
|
+
await page.waitForNetworkIdle();
|
|
81
|
+
|
|
82
|
+
const albumsChip = await page.waitForSelector(`::-p-xpath(${YtMusicAlbumsChipSelector})`, {timeout: 1000});
|
|
83
|
+
|
|
84
|
+
albumsChip.click();
|
|
85
|
+
await page.waitForNetworkIdle();
|
|
86
|
+
await page.waitForSelector(`::-p-xpath(${YtMusicSearchResultsArtistsLinkSelector})`, {timeout: 1000});
|
|
87
|
+
|
|
88
|
+
const albumsElements = await page.$$(`::-p-xpath(${YtMusicSearchResultsArtistsLinkSelector})`);
|
|
89
|
+
const albumEl = albumsElements[0];
|
|
90
|
+
const albumUrl = await albumEl.evaluate((el) => el.getAttribute("href"));
|
|
91
|
+
|
|
92
|
+
results.push(`${params.url}/${albumUrl}`);
|
|
93
|
+
|
|
94
|
+
return results;
|
|
95
|
+
} catch (error) {
|
|
96
|
+
return results;
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
for (const a of params.values) {
|
|
101
|
+
const data = await process(a);
|
|
102
|
+
|
|
103
|
+
result.values.push(...data);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
reporter.finish("done", result);
|
|
107
|
+
};
|
|
108
|
+
|
|
123
109
|
const closeResources = async () => {
|
|
124
110
|
if (page) await page.close();
|
|
125
111
|
if (browser) await browser.close();
|