From 51364b63e7b7cb9cc2b33266bba9b2852b769dfe Mon Sep 17 00:00:00 2001 From: Araxeus <78568641+Araxeus@users.noreply.github.com> Date: Sat, 23 Oct 2021 16:13:06 +0300 Subject: [PATCH 1/6] get songInfo from youtube API --- providers/song-info-front.js | 42 ++++++++++++++++++++---------------- providers/song-info.js | 39 ++++++++++++--------------------- 2 files changed, 37 insertions(+), 44 deletions(-) diff --git a/providers/song-info-front.js b/providers/song-info-front.js index fccc54a8..25cea261 100644 --- a/providers/song-info-front.js +++ b/providers/song-info-front.js @@ -9,23 +9,27 @@ ipcRenderer.on("update-song-info", async (_, extractedSongInfo) => { global.songInfo.image = await getImage(global.songInfo.imageSrc); }); -const injectListener = () => { - const oldXHR = window.XMLHttpRequest; - function newXHR() { - const realXHR = new oldXHR(); - realXHR.addEventListener( - "readystatechange", - () => { - if (realXHR.readyState === 4 && realXHR.status === 200 - && realXHR.responseURL.includes("/player")) { - // if the request contains the song info, send the response to ipcMain - ipcRenderer.send("song-info-request", realXHR.responseText); - } - }, - false - ); - return realXHR; - } - window.XMLHttpRequest = newXHR; +function setup() { + if (document.querySelector('#movie_player')) { + injectListener(); + return; + } + + const observer = new MutationObserver(() => { + if (document.querySelector('#movie_player')) { + observer.disconnect(); + injectListener(); + } + }) + + observer.observe(document.documentElement, { childList: true, subtree: true }); +} + +function injectListener() { + document.querySelector('video').addEventListener('loadedmetadata', () => { + const data = document.querySelector('#movie_player').getPlayerResponse(); + ipcRenderer.send("song-info-request", JSON.stringify(data)); + }); }; -module.exports = injectListener; + +module.exports = setup; diff --git a/providers/song-info.js b/providers/song-info.js index 6e5a119c..d386e23a 100644 --- a/providers/song-info.js +++ b/providers/song-info.js @@ -2,15 +2,11 @@ const { ipcMain, nativeImage } = require("electron"); const fetch = require("node-fetch"); -// This selects the progress bar, used for current progress -const progressSelector = "#progress-bar"; - - // Grab the progress using the selector const getProgress = async (win) => { // Get current value of the progressbar element return win.webContents.executeJavaScript( - 'document.querySelector("' + progressSelector + '").value' + 'document.querySelector("#progress-bar").value' ); }; @@ -29,16 +25,10 @@ const getImage = async (src) => { // To find the paused status, we check if the title contains `-` const getPausedStatus = async (win) => { const title = await win.webContents.executeJavaScript("document.title"); + console.log('doc title = ',title) return !title.includes("-"); }; -const getArtist = async (win) => { - return win.webContents.executeJavaScript(` - document.querySelector(".subtitle.ytmusic-player-bar .yt-formatted-string") - ?.textContent - `); -} - // Fill songInfo with empty values /** * @typedef {songInfo} SongInfo @@ -59,14 +49,13 @@ const songInfo = { const handleData = async (responseText, win) => { let data = JSON.parse(responseText); songInfo.title = cleanupName(data?.videoDetails?.title); - songInfo.artist = - (await getArtist(win)) || cleanupName(data?.videoDetails?.author); + songInfo.artist =cleanupName(data?.videoDetails?.author); songInfo.views = data?.videoDetails?.viewCount; songInfo.imageSrc = data?.videoDetails?.thumbnail?.thumbnails?.pop()?.url; songInfo.songDuration = data?.videoDetails?.lengthSeconds; songInfo.image = await getImage(songInfo.imageSrc); songInfo.uploadDate = data?.microformat?.microformatDataRenderer?.uploadDate; - songInfo.url = data?.microformat?.microformatDataRenderer?.urlCanonical; + songInfo.url = data?.microformat?.microformatDataRenderer?.urlCanonical?.split("&")[0]; win.webContents.send("update-song-info", JSON.stringify(songInfo)); }; @@ -111,19 +100,19 @@ const registerProvider = (win) => { }; const suffixesToRemove = [ - " - Topic", - "VEVO", - " (Performance Video)", - " (Official Music Video)", - " (Official Video)", - " (Clip officiel)", + " - topic", + "vevo", + " (performance video)", + " (official music video)", + " (official video)", + " (clip officiel)", ]; + function cleanupName(artist) { - if (!artist) { - return artist; - } + if (!artist) return artist; + const lowerCaseArtist = artist.toLowerCase(); for (const suffix of suffixesToRemove) { - if (artist.endsWith(suffix)) { + if (lowerCaseArtist.endsWith(suffix)) { return artist.slice(0, -suffix.length); } } From 2224786478ce99b00473a52780aebc073e8fd463 Mon Sep 17 00:00:00 2001 From: Araxeus <78568641+Araxeus@users.noreply.github.com> Date: Sat, 23 Oct 2021 16:19:45 +0300 Subject: [PATCH 2/6] lint --- providers/song-info-front.js | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/providers/song-info-front.js b/providers/song-info-front.js index 25cea261..ccf0b764 100644 --- a/providers/song-info-front.js +++ b/providers/song-info-front.js @@ -4,30 +4,33 @@ const { getImage } = require("./song-info"); global.songInfo = {}; +let api = document.querySelector('#movie_player'); + ipcRenderer.on("update-song-info", async (_, extractedSongInfo) => { global.songInfo = JSON.parse(extractedSongInfo); global.songInfo.image = await getImage(global.songInfo.imageSrc); }); function setup() { - if (document.querySelector('#movie_player')) { - injectListener(); - return; - } + if (api) { + injectListener(); + return; + } - const observer = new MutationObserver(() => { - if (document.querySelector('#movie_player')) { - observer.disconnect(); - injectListener(); - } - }) + const observer = new MutationObserver(() => { + api = document.querySelector('#movie_player'); + if (api) { + observer.disconnect(); + injectListener(); + } + }) - observer.observe(document.documentElement, { childList: true, subtree: true }); + observer.observe(document.documentElement, { childList: true, subtree: true }); } function injectListener() { document.querySelector('video').addEventListener('loadedmetadata', () => { - const data = document.querySelector('#movie_player').getPlayerResponse(); + const data = api.getPlayerResponse(); ipcRenderer.send("song-info-request", JSON.stringify(data)); }); }; From 978aca1f9aac3148909d20f69d0e52ea11d6cb08 Mon Sep 17 00:00:00 2001 From: Araxeus <78568641+Araxeus@users.noreply.github.com> Date: Sat, 23 Oct 2021 16:21:42 +0300 Subject: [PATCH 3/6] use loadeddata instead of loadedmetadata send event closer to actual initial start time of video --- providers/song-info-front.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/providers/song-info-front.js b/providers/song-info-front.js index ccf0b764..674faeb7 100644 --- a/providers/song-info-front.js +++ b/providers/song-info-front.js @@ -29,7 +29,7 @@ function setup() { } function injectListener() { - document.querySelector('video').addEventListener('loadedmetadata', () => { + document.querySelector('video').addEventListener('loadeddata', () => { const data = api.getPlayerResponse(); ipcRenderer.send("song-info-request", JSON.stringify(data)); }); From 2d518abc1931ee06213cc59b78aa03fd1a3dc3fc Mon Sep 17 00:00:00 2001 From: Araxeus <78568641+Araxeus@users.noreply.github.com> Date: Sat, 23 Oct 2021 16:26:45 +0300 Subject: [PATCH 4/6] remove leftover console.log --- providers/song-info.js | 1 - 1 file changed, 1 deletion(-) diff --git a/providers/song-info.js b/providers/song-info.js index d386e23a..f88d4103 100644 --- a/providers/song-info.js +++ b/providers/song-info.js @@ -25,7 +25,6 @@ const getImage = async (src) => { // To find the paused status, we check if the title contains `-` const getPausedStatus = async (win) => { const title = await win.webContents.executeJavaScript("document.title"); - console.log('doc title = ',title) return !title.includes("-"); }; From 9b1a5b8d26f93c58cf78b95487ce3d0cf80be01f Mon Sep 17 00:00:00 2001 From: Araxeus <78568641+Araxeus@users.noreply.github.com> Date: Sat, 23 Oct 2021 17:18:58 +0300 Subject: [PATCH 5/6] clarify var names in cleanupName() --- providers/song-info.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/providers/song-info.js b/providers/song-info.js index f88d4103..3f4ad435 100644 --- a/providers/song-info.js +++ b/providers/song-info.js @@ -107,15 +107,15 @@ const suffixesToRemove = [ " (clip officiel)", ]; -function cleanupName(artist) { - if (!artist) return artist; - const lowerCaseArtist = artist.toLowerCase(); +function cleanupName(name) { + if (!name) return name; + const lowCaseName = name.toLowerCase(); for (const suffix of suffixesToRemove) { - if (lowerCaseArtist.endsWith(suffix)) { - return artist.slice(0, -suffix.length); + if (lowCaseName.endsWith(suffix)) { + return name.slice(0, -suffix.length); } } - return artist; + return name; } module.exports = registerCallback; From 79a95f133bde84f3fb26e7012ea598933608a36f Mon Sep 17 00:00:00 2001 From: Araxeus <78568641+Araxeus@users.noreply.github.com> Date: Sat, 23 Oct 2021 18:22:17 +0300 Subject: [PATCH 6/6] use apiLoad event --- preload.js | 27 +++++++++++++++++++++++++++ providers/song-info-front.js | 31 ++++++------------------------- 2 files changed, 33 insertions(+), 25 deletions(-) diff --git a/preload.js b/preload.js index 5a09c845..67ae8f7c 100644 --- a/preload.js +++ b/preload.js @@ -10,6 +10,8 @@ const setupSongInfo = require("./providers/song-info-front"); const plugins = config.plugins.getEnabled(); +let api; + plugins.forEach(([plugin, options]) => { const preloadPath = path.join(__dirname, "plugins", plugin, "preload.js"); fileExists(preloadPath, () => { @@ -38,6 +40,9 @@ document.addEventListener("DOMContentLoaded", () => { }); }); + // wait for complete load of youtube api + listenForApiLoad(); + // inject song-info provider setupSongInfo(); @@ -51,3 +56,25 @@ document.addEventListener("DOMContentLoaded", () => { global.reload = () => remote.getCurrentWindow().webContents.loadURL(config.get("url")); }); + +function listenForApiLoad() { + api = document.querySelector('#movie_player'); + if (api) { + onApiLoaded(); + return; + } + + const observer = new MutationObserver(() => { + api = document.querySelector('#movie_player'); + if (api) { + observer.disconnect(); + onApiLoaded(); + } + }) + + observer.observe(document.documentElement, { childList: true, subtree: true }); +} + +function onApiLoaded() { + document.dispatchEvent(new CustomEvent('apiLoaded', { detail: api })); +} diff --git a/providers/song-info-front.js b/providers/song-info-front.js index 674faeb7..80c2e990 100644 --- a/providers/song-info-front.js +++ b/providers/song-info-front.js @@ -4,35 +4,16 @@ const { getImage } = require("./song-info"); global.songInfo = {}; -let api = document.querySelector('#movie_player'); - ipcRenderer.on("update-song-info", async (_, extractedSongInfo) => { global.songInfo = JSON.parse(extractedSongInfo); global.songInfo.image = await getImage(global.songInfo.imageSrc); }); -function setup() { - if (api) { - injectListener(); - return; - } - - const observer = new MutationObserver(() => { - api = document.querySelector('#movie_player'); - if (api) { - observer.disconnect(); - injectListener(); - } +module.exports = () => { + document.addEventListener('apiLoaded', e => { + document.querySelector('video').addEventListener('loadeddata', () => { + const data = e.detail.getPlayerResponse(); + ipcRenderer.send("song-info-request", JSON.stringify(data)); + }); }) - - observer.observe(document.documentElement, { childList: true, subtree: true }); -} - -function injectListener() { - document.querySelector('video').addEventListener('loadeddata', () => { - const data = api.getPlayerResponse(); - ipcRenderer.send("song-info-request", JSON.stringify(data)); - }); }; - -module.exports = setup;