Merge branch 'master' into dependencies-update

This commit is contained in:
th-ch
2021-04-29 23:10:52 +02:00
committed by GitHub
9 changed files with 266 additions and 37 deletions

View File

@ -15,10 +15,13 @@ module.exports = (win, {activityTimoutEnabled, activityTimoutTime}) => {
const registerCallback = getSongInfo(win);
// If the page is ready, register the callback
win.on("ready-to-show", () => {
rpc.on("ready", () => {
win.once("ready-to-show", () => {
rpc.once("ready", () => {
// Register the callback
registerCallback((songInfo) => {
if (songInfo.title.length === 0 && songInfo.artist.length === 0) {
return;
}
// Song information changed, so lets update the rich presence
const activityInfo = {
details: songInfo.title,
@ -36,8 +39,7 @@ module.exports = (win, {activityTimoutEnabled, activityTimoutTime}) => {
activityInfo.smallImageText = "idle/paused";
// Set start the timer so the activity gets cleared after a while if enabled
if (activityTimoutEnabled)
clearActivity = setTimeout(()=>rpc.clearActivity(), activityTimoutTime||10,000);
clearActivity = setTimeout(()=>rpc.clearActivity(), activityTimoutTime||10000);
} else {
// stop the clear activity timout
clearTimeout(clearActivity);
@ -53,9 +55,6 @@ module.exports = (win, {activityTimoutEnabled, activityTimoutTime}) => {
});
// Startup the rpc client
rpc.login({
clientId,
})
.catch(console.error);
rpc.login({ clientId }).catch(console.error);
});
};

View File

@ -1,6 +1,8 @@
const { writeFileSync } = require("fs");
const { join } = require("path");
const { dialog } = require("electron");
const ID3Writer = require("browser-id3-writer");
const { dialog, ipcMain } = require("electron");
const getSongInfo = require("../../providers/song-info");
const { injectCSS, listenAction } = require("../utils");
@ -38,6 +40,34 @@ function handle(win) {
console.log("Unknown action: " + action);
}
});
ipcMain.on("add-metadata", (event, filePath, songBuffer, currentMetadata) => {
let fileBuffer = songBuffer;
const songMetadata = { ...metadata, ...currentMetadata };
try {
const coverBuffer = songMetadata.image.toPNG();
const writer = new ID3Writer(songBuffer);
// Create the metadata tags
writer
.setFrame("TIT2", songMetadata.title)
.setFrame("TPE1", [songMetadata.artist])
.setFrame("APIC", {
type: 3,
data: coverBuffer,
description: "",
});
writer.addTag();
fileBuffer = Buffer.from(writer.arrayBuffer);
} catch (error) {
sendError(win, error);
}
writeFileSync(filePath, fileBuffer);
// Notify the youtube-dl file
event.reply("add-metadata-done");
});
}
module.exports = handle;

View File

@ -44,7 +44,7 @@ global.download = () => {
.getAttribute("href");
videoUrl = !videoUrl
? global.songInfo.url || window.location.href
: baseUrl + videoUrl;
: baseUrl + "/" + videoUrl;
downloadVideoToMP3(
videoUrl,

View File

@ -1,9 +1,7 @@
const { randomBytes } = require("crypto");
const { writeFileSync } = require("fs");
const { join } = require("path");
const Mutex = require("async-mutex").Mutex;
const ID3Writer = require("browser-id3-writer");
const { ipcRenderer } = require("electron");
const is = require("electron-is");
const filenamify = require("filenamify");
@ -126,35 +124,19 @@ const toMP3 = async (
: videoName;
const filename = filenamify(name + "." + extension, {
replacement: "_",
maxLength: 255,
});
const filePath = join(folder, subfolder, filename);
const fileBuffer = ffmpeg.FS("readFile", safeVideoName + "." + extension);
// Add the metadata
try {
const writer = new ID3Writer(fileBuffer);
if (metadata.image) {
const coverBuffer = metadata.image.toPNG();
// Create the metadata tags
writer
.setFrame("TIT2", metadata.title)
.setFrame("TPE1", [metadata.artist])
.setFrame("APIC", {
type: 3,
data: coverBuffer,
description: "",
});
writer.addTag();
}
writeFileSync(filePath, Buffer.from(writer.arrayBuffer));
} catch (error) {
sendError(error);
} finally {
reinit();
}
sendFeedback("Adding metadata…");
ipcRenderer.send("add-metadata", filePath, fileBuffer, {
artist: metadata.artist,
title: metadata.title,
});
ipcRenderer.once("add-metadata-done", reinit);
} catch (e) {
sendError(e);
} finally {

175
plugins/last-fm/back.js Normal file
View File

@ -0,0 +1,175 @@
const fetch = require('node-fetch');
const md5 = require('md5');
const open = require("open");
const { setOptions } = require('../../config/plugins');
const getSongInfo = require('../../providers/song-info');
const defaultConfig = require('../../config/defaults');
const cleanupArtistName = (config, artist) => {
// removes the suffixes of the artist name for more recognition by last.fm
const { suffixesToRemove } = config;
if (suffixesToRemove === undefined) return artist;
for (suffix of suffixesToRemove) {
artist = artist.replace(suffix, '');
}
return artist;
}
const createFormData = params => {
// creates the body for in the post request
const formData = new URLSearchParams();
for (key in params) {
formData.append(key, params[key]);
}
return formData;
}
const createQueryString = (params, api_sig) => {
// creates a querystring
const queryData = [];
params.api_sig = api_sig;
for (key in params) {
queryData.push(`${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`);
}
return '?'+queryData.join('&');
}
const createApiSig = (params, secret) => {
// this function creates the api signature, see: https://www.last.fm/api/authspec
const keys = [];
for (key in params) {
keys.push(key);
}
keys.sort();
let sig = '';
for (key of keys) {
if (String(key) === 'format')
continue
sig += `${key}${params[key]}`;
}
sig += secret;
sig = md5(sig);
return sig;
}
const createToken = async ({ api_key, api_root, secret }) => {
// creates and stores the auth token
const data = {
method: 'auth.gettoken',
api_key: api_key,
format: 'json'
};
const api_sig = createApiSig(data, secret);
let response = await fetch(`${api_root}${createQueryString(data, api_sig)}`);
response = await response.json();
return response?.token;
}
const authenticate = async config => {
// asks the user for authentication
config.token = await createToken(config);
setOptions('last-fm', config);
open(`https://www.last.fm/api/auth/?api_key=${config.api_key}&token=${config.token}`);
return config;
}
const getAndSetSessionKey = async config => {
// get and store the session key
const data = {
api_key: config.api_key,
format: 'json',
method: 'auth.getsession',
token: config.token,
};
const api_sig = createApiSig(data, config.secret);
let res = await fetch(`${config.api_root}${createQueryString(data, api_sig)}`);
res = await res.json();
if (res.error)
await authenticate(config);
config.session_key = res?.session?.key;
setOptions('last-fm', config);
return config;
}
const postSongDataToAPI = async (songInfo, config, data) => {
// this sends a post request to the api, and adds the common data
if (!config.session_key)
await getAndSetSessionKey(config);
const postData = {
track: songInfo.title,
duration: songInfo.songDuration,
artist: songInfo.artist,
api_key: config.api_key,
sk: config.session_key,
format: 'json',
...data,
};
postData.api_sig = createApiSig(postData, config.secret);
fetch('https://ws.audioscrobbler.com/2.0/', {method: 'POST', body: createFormData(postData)})
.catch(res => {
if (res.response.data.error == 9) {
// session key is invalid, so remove it from the config and reauthenticate
config.session_key = undefined;
setOptions('last-fm', config);
authenticate(config);
}
});
}
const addScrobble = (songInfo, config) => {
// this adds one scrobbled song to last.fm
const data = {
method: 'track.scrobble',
timestamp: ~~((Date.now() - songInfo.elapsedSeconds) / 1000),
};
postSongDataToAPI(songInfo, config, data);
}
const setNowPlaying = (songInfo, config) => {
// this sets the now playing status in last.fm
const data = {
method: 'track.updateNowPlaying',
};
postSongDataToAPI(songInfo, config, data);
}
// this will store the timeout that will trigger addScrobble
let scrobbleTimer = undefined;
const lastfm = async (win, config) => {
const registerCallback = getSongInfo(win);
if (!config.api_root || !config.suffixesToRemove) {
// settings are not present, creating them with the default values
config = defaultConfig.plugins['last-fm'];
config.enabled = true;
setOptions('last-fm', config);
}
if (!config.session_key) {
// not authenticated
config = await getAndSetSessionKey(config);
}
registerCallback( songInfo => {
// set remove the old scrobble timer
clearTimeout(scrobbleTimer);
// make the artist name a bit cleaner
songInfo.artist = cleanupArtistName(config, songInfo.artist);
if (!songInfo.isPaused) {
setNowPlaying(songInfo, config);
// scrobble when the song is half way through, or has passed the 4 minute mark
const scrobbleTime = Math.min(Math.ceil(songInfo.songDuration / 2), 4 * 60);
if (scrobbleTime > songInfo.elapsedSeconds) {
// scrobble still needs to happen
const timeToWait = (scrobbleTime - songInfo.elapsedSeconds) * 1000;
scrobbleTimer = setTimeout(addScrobble, timeToWait, songInfo, config);
}
}
});
}
module.exports = lastfm;