Skip to content

Commit 5faeddb

Browse files
authored
Merge pull request #252 from Araxeus/fix-download-idtag-if-not-playing
Fix downloader metadata if not currently playing
2 parents 3fb08d2 + 1140c3e commit 5faeddb

File tree

9 files changed

+145
-56
lines changed

9 files changed

+145
-56
lines changed

config/defaults.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ const defaultConfig = {
4141
api_root: "http://ws.audioscrobbler.com/2.0/",
4242
api_key: "04d76faaac8726e60988e14c105d421a", // api key registered by @semvis123
4343
secret: "a5d2a36fdf64819290f6982481eaffa2",
44-
suffixesToRemove: [' - Topic', 'VEVO'] // removes suffixes of the artist name, for better recognition
4544
},
4645
discord: {
4746
enabled: false,

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
"YoutubeNonStop": "git://github.com/lawfx/YoutubeNonStop.git#v0.9.0",
7070
"async-mutex": "^0.3.1",
7171
"browser-id3-writer": "^4.4.0",
72+
"chokidar": "^3.5.1",
7273
"custom-electron-titlebar": "^3.2.6",
7374
"discord-rpc": "^3.2.0",
7475
"electron-debug": "^3.2.0",

plugins/downloader/back.js

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ const { dialog, ipcMain } = require("electron");
77
const getSongInfo = require("../../providers/song-info");
88
const { injectCSS, listenAction } = require("../utils");
99
const { ACTIONS, CHANNEL } = require("./actions.js");
10+
const { getImage } = require("../../providers/song-info");
1011

1112
const sendError = (win, err) => {
1213
const dialogOpts = {
@@ -41,23 +42,29 @@ function handle(win) {
4142
}
4243
});
4344

44-
ipcMain.on("add-metadata", (event, filePath, songBuffer, currentMetadata) => {
45+
ipcMain.on("add-metadata", async (event, filePath, songBuffer, currentMetadata) => {
4546
let fileBuffer = songBuffer;
4647
const songMetadata = { ...metadata, ...currentMetadata };
4748

49+
if (!songMetadata.image && songMetadata.imageSrc) {
50+
songMetadata.image = await getImage(songMetadata.imageSrc);
51+
}
52+
4853
try {
49-
const coverBuffer = songMetadata.image.toPNG();
54+
const coverBuffer = songMetadata.image ? songMetadata.image.toPNG() : null;
5055
const writer = new ID3Writer(songBuffer);
5156

5257
// Create the metadata tags
5358
writer
5459
.setFrame("TIT2", songMetadata.title)
55-
.setFrame("TPE1", [songMetadata.artist])
56-
.setFrame("APIC", {
60+
.setFrame("TPE1", [songMetadata.artist]);
61+
if (coverBuffer) {
62+
writer.setFrame("APIC", {
5763
type: 3,
5864
data: coverBuffer,
5965
description: "",
6066
});
67+
}
6168
writer.addTag();
6269
fileBuffer = Buffer.from(writer.arrayBuffer);
6370
} catch (error) {

plugins/downloader/front.js

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,18 @@ const baseUrl = defaultConfig.url;
3838
// contextBridge.exposeInMainWorld("downloader", {
3939
// download: () => {
4040
global.download = () => {
41+
let metadata;
4142
let videoUrl = getSongMenu()
4243
.querySelector("ytmusic-menu-navigation-item-renderer")
4344
.querySelector("#navigation-endpoint")
4445
.getAttribute("href");
45-
videoUrl = !videoUrl
46-
? global.songInfo.url || window.location.href
47-
: baseUrl + "/" + videoUrl;
46+
if (videoUrl) {
47+
videoUrl = baseUrl + "/" + videoUrl;
48+
metadata = null;
49+
} else {
50+
metadata = global.songInfo;
51+
videoUrl = metadata.url || window.location.href;
52+
}
4853

4954
downloadVideoToMP3(
5055
videoUrl,
@@ -61,7 +66,7 @@ global.download = () => {
6166
},
6267
reinit,
6368
pluginOptions,
64-
global.songInfo
69+
metadata
6570
);
6671
};
6772
// });

plugins/downloader/menu.js

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const { URL } = require("url");
55
const { dialog, ipcMain } = require("electron");
66
const is = require("electron-is");
77
const ytpl = require("ytpl");
8+
const chokidar = require('chokidar');
89

910
const { setOptions } = require("../../config/plugins");
1011
const getSongInfo = require("../../providers/song-info");
@@ -15,7 +16,7 @@ let downloadLabel = defaultMenuDownloadLabel;
1516
let metadataURL = undefined;
1617
let callbackIsRegistered = false;
1718

18-
module.exports = (win, options, refreshMenu) => {
19+
module.exports = (win, options) => {
1920
if (!callbackIsRegistered) {
2021
const registerCallback = getSongInfo(win);
2122
registerCallback((info) => {
@@ -35,7 +36,10 @@ module.exports = (win, options, refreshMenu) => {
3536
return;
3637
}
3738

38-
const playlist = await ytpl(playlistID);
39+
console.log("trying to get playlist ID" +playlistID);
40+
const playlist = await ytpl(playlistID,
41+
{ limit: options.playlistMaxItems || Infinity }
42+
);
3943
const playlistTitle = playlist.title;
4044

4145
const folder = getFolder(options.downloadFolder);
@@ -49,24 +53,40 @@ module.exports = (win, options, refreshMenu) => {
4953
}
5054
mkdirSync(playlistFolder, { recursive: true });
5155

52-
ipcMain.on("downloader-feedback", (_, feedback) => {
53-
downloadLabel = feedback;
54-
refreshMenu();
56+
dialog.showMessageBox({
57+
type: "info",
58+
buttons: ["OK"],
59+
title: "Started Download",
60+
message: `Downloading Playlist "${playlistTitle}"`,
61+
detail: `(${playlist.items.length} songs)`,
5562
});
5663

57-
downloadLabel = `Downloading "${playlistTitle}"`;
58-
refreshMenu();
59-
6064
if (is.dev()) {
6165
console.log(
6266
`Downloading playlist "${playlistTitle}" (${playlist.items.length} songs)`
6367
);
6468
}
6569

66-
playlist.items.slice(0, options.playlistMaxItems).forEach((song) => {
70+
const steps = 1 / playlist.items.length;
71+
let progress = 0;
72+
73+
win.setProgressBar(2); // starts with indefinite bar
74+
75+
let dirWatcher = chokidar.watch(playlistFolder);
76+
dirWatcher.on('add', () => {
77+
progress += steps;
78+
if (progress >= 0.9999) {
79+
win.setProgressBar(-1); // close progress bar
80+
dirWatcher.close().then(() => dirWatcher = null);
81+
} else {
82+
win.setProgressBar(progress);
83+
}
84+
});
85+
86+
playlist.items.forEach((song) => {
6787
win.webContents.send(
6888
"downloader-download-playlist",
69-
song,
89+
song.url,
7090
playlistTitle,
7191
options
7292
);

plugins/downloader/youtube-dl.js

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ const ytdl = require("ytdl-core");
1414

1515
const { triggerAction, triggerActionSync } = require("../utils");
1616
const { ACTIONS, CHANNEL } = require("./actions.js");
17-
const { defaultMenuDownloadLabel, getFolder } = require("./utils");
17+
const { getFolder } = require("./utils");
18+
const { cleanupArtistName } = require("../../providers/song-info");
1819

1920
const { createFFmpeg } = FFmpeg;
2021
const ffmpeg = createFFmpeg({
@@ -24,7 +25,7 @@ const ffmpeg = createFFmpeg({
2425
});
2526
const ffmpegMutex = new Mutex();
2627

27-
const downloadVideoToMP3 = (
28+
const downloadVideoToMP3 = async (
2829
videoUrl,
2930
sendFeedback,
3031
sendError,
@@ -35,6 +36,16 @@ const downloadVideoToMP3 = (
3536
) => {
3637
sendFeedback("Downloading…");
3738

39+
if (metadata === null) {
40+
const info = await ytdl.getInfo(videoUrl);
41+
const thumbnails = info.videoDetails?.author?.thumbnails;
42+
metadata = {
43+
artist: info.videoDetails?.media?.artist || cleanupArtistName(info.videoDetails?.author?.name) || "",
44+
title: info.videoDetails?.media?.song || info.videoDetails?.title || "",
45+
imageSrc: thumbnails ? thumbnails[thumbnails.length - 1].url : ""
46+
}
47+
}
48+
3849
let videoName = "YouTube Music - Unknown title";
3950
let videoReadableStream;
4051
try {
@@ -135,6 +146,7 @@ const toMP3 = async (
135146
ipcRenderer.send("add-metadata", filePath, fileBuffer, {
136147
artist: metadata.artist,
137148
title: metadata.title,
149+
imageSrc: metadata.imageSrc
138150
});
139151
ipcRenderer.once("add-metadata-done", reinit);
140152
} catch (e) {
@@ -165,22 +177,16 @@ module.exports = {
165177

166178
ipcRenderer.on(
167179
"downloader-download-playlist",
168-
(_, songMetadata, playlistFolder, options) => {
169-
const reinit = () =>
170-
ipcRenderer.send("downloader-feedback", defaultMenuDownloadLabel);
171-
180+
(_, url, playlistFolder, options) => {
172181
downloadVideoToMP3(
173-
songMetadata.url,
174-
(feedback) => {
175-
ipcRenderer.send("downloader-feedback", feedback);
176-
},
182+
url,
183+
() => {},
177184
(error) => {
178185
triggerAction(CHANNEL, ACTIONS.ERROR, error);
179-
reinit();
180186
},
181-
reinit,
187+
() => {},
182188
options,
183-
songMetadata,
189+
null,
184190
playlistFolder
185191
);
186192
}

plugins/last-fm/back.js

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,10 @@ const { setOptions } = require('../../config/plugins');
55
const getSongInfo = require('../../providers/song-info');
66
const defaultConfig = require('../../config/defaults');
77

8-
const cleanupArtistName = (config, artist) => {
9-
// removes the suffixes of the artist name for more recognition by last.fm
10-
const { suffixesToRemove } = config;
11-
if (suffixesToRemove === undefined) return artist;
12-
13-
for (suffix of suffixesToRemove) {
14-
artist = artist.replace(suffix, '');
15-
}
16-
return artist;
17-
}
18-
198
const createFormData = params => {
209
// creates the body for in the post request
2110
const formData = new URLSearchParams();
22-
for (key in params) {
11+
for (const key in params) {
2312
formData.append(key, params[key]);
2413
}
2514
return formData;
@@ -28,7 +17,7 @@ const createQueryString = (params, api_sig) => {
2817
// creates a querystring
2918
const queryData = [];
3019
params.api_sig = api_sig;
31-
for (key in params) {
20+
for (const key in params) {
3221
queryData.push(`${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`);
3322
}
3423
return '?'+queryData.join('&');
@@ -37,12 +26,12 @@ const createQueryString = (params, api_sig) => {
3726
const createApiSig = (params, secret) => {
3827
// this function creates the api signature, see: https://www.last.fm/api/authspec
3928
const keys = [];
40-
for (key in params) {
29+
for (const key in params) {
4130
keys.push(key);
4231
}
4332
keys.sort();
4433
let sig = '';
45-
for (key of keys) {
34+
for (const key of keys) {
4635
if (String(key) === 'format')
4736
continue
4837
sig += `${key}${params[key]}`;
@@ -157,8 +146,6 @@ const lastfm = async (win, config) => {
157146
registerCallback( songInfo => {
158147
// set remove the old scrobble timer
159148
clearTimeout(scrobbleTimer);
160-
// make the artist name a bit cleaner
161-
songInfo.artist = cleanupArtistName(config, songInfo.artist);
162149
if (!songInfo.isPaused) {
163150
setNowPlaying(songInfo, config);
164151
// scrobble when the song is half way through, or has passed the 4 minute mark

providers/song-info.js

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ const getArtist = async (win) => {
3838
artistName.textContent;
3939
}
4040
`
41-
)
41+
);
4242
}
4343

4444
// Fill songInfo with empty values
@@ -57,8 +57,8 @@ const songInfo = {
5757

5858
const handleData = async (responseText, win) => {
5959
let data = JSON.parse(responseText);
60-
songInfo.title = data?.videoDetails?.title;
61-
songInfo.artist = await getArtist(win) || data?.videoDetails?.author;
60+
songInfo.title = data.videoDetails?.media?.song || data?.videoDetails?.title;
61+
songInfo.artist = data.videoDetails?.media?.artist || await getArtist(win) || cleanupArtistName(data?.videoDetails?.author);
6262
songInfo.views = data?.videoDetails?.viewCount;
6363
songInfo.imageSrc = data?.videoDetails?.thumbnail?.thumbnails?.pop()?.url;
6464
songInfo.songDuration = data?.videoDetails?.lengthSeconds;
@@ -102,5 +102,20 @@ const registerProvider = (win) => {
102102
return registerCallback;
103103
};
104104

105+
const suffixesToRemove = [' - Topic', 'VEVO'];
106+
function cleanupArtistName(artist) {
107+
if (!artist) {
108+
return artist;
109+
}
110+
for (const suffix of suffixesToRemove) {
111+
if (artist.endsWith(suffix)) {
112+
return artist.slice(0, -suffix.length);
113+
}
114+
}
115+
return artist;
116+
}
117+
105118
module.exports = registerProvider;
106119
module.exports.getImage = getImage;
120+
module.exports.cleanupArtistName = cleanupArtistName;
121+

0 commit comments

Comments
 (0)