Globalized the songinfo and song controls, and changed the pause/play button.

Globalized the songinfo and song controls, and changed the pause/play button. The songInfo file should eventually get another location, because it isn't really a plugin.
This commit is contained in:
Sem Vissscher
2020-12-21 18:18:34 +01:00
parent f71e0e9da9
commit 9be3e1afe9
2 changed files with 184 additions and 89 deletions

120
plugins/songInfo/back.js Normal file
View File

@ -0,0 +1,120 @@
const {nativeImage} = require('electron');
const fetch = require('node-fetch');
// This selects the song title
const titleSelector = '.title.style-scope.ytmusic-player-bar';
// This selects the song image
const imageSelector = '#layout > ytmusic-player-bar > div.middle-controls.style-scope.ytmusic-player-bar > img';
// This selects the song subinfo, this includes artist, views, likes
const subInfoSelector = '#layout > ytmusic-player-bar > div.middle-controls.style-scope.ytmusic-player-bar > div.content-info-wrapper.style-scope.ytmusic-player-bar > span';
// This is used for to control the songs
const presskey = (window, key) => {
window.webContents.sendInputEvent({
type: 'keydown',
keyCode: key
});
};
// Grab the title using the selector
const getTitle = win => {
return win.webContents.executeJavaScript(
'document.querySelector(\'' + titleSelector + '\').innerText'
).catch(error => {
console.log(error);
});
};
// Grab the image src using the selector
const getImageSrc = win => {
return win.webContents.executeJavaScript(
'document.querySelector(\'' + imageSelector + '\').src'
).catch(error => {
console.log(error);
});
};
// Grab the subinfo using the selector
const getSubInfo = async win => {
// Get innerText of subinfo element
const subInfoString = await win.webContents.executeJavaScript(
'document.querySelector("' + subInfoSelector + '").innerText');
// Split and clean the string
const splittedSubInfo = subInfoString.replaceAll('\n', '').split(' • ');
// Make sure we always return 3 elements in the aray
const subInfo = [];
for (let i = 0; i < 3; i++) {
// Fill array with empty string if not defined
subInfo.push(splittedSubInfo[i] || '');
}
return subInfo;
};
// Grab the native image using the src
const getImage = async src => {
const result = await fetch(src);
const buffer = await result.buffer();
return nativeImage.createFromBuffer(buffer);
};
const getPausedStatus = async win => {
const title = await win.webContents.executeJavaScript('document.title');
return !title.includes('-');
};
// This variable will be filled with the callbacks once they register
const callbacks = [];
module.exports = async win => {
// Fill songInfo with empty values
global.songInfo = {
title: '',
artist: '',
views: '',
likes: '',
imageSrc: '',
image: null,
isPaused: true
};
// The song control funcions
global.songControls = {
previous: () => presskey(win, 'k'),
next: () => presskey(win, 'j'),
pause: () => presskey(win, 'space'),
like: () => presskey(win, '_'),
dislike: () => presskey(win, '+')
};
// This function will allow plugins to register callback that will be triggered when data changes
global.songInfo.onNewData = callback => {
callbacks.push(callback);
};
win.on('page-title-updated', async () => {
// Save the old title temporarily
const oldTitle = global.songInfo.title;
// Get and set the new data
global.songInfo.title = await getTitle(win);
global.songInfo.isPaused = await getPausedStatus(win);
// If title changed then we do need to update other info
if (oldTitle !== global.songInfo.title) {
const subInfo = await getSubInfo(win);
global.songInfo.artist = subInfo[0];
global.songInfo.views = subInfo[1];
global.songInfo.likes = subInfo[2];
global.songInfo.imageSrc = await getImageSrc(win);
global.songInfo.image = await getImage(global.songInfo.imageSrc);
}
// Trigger the callbacks
callbacks.forEach(c => {
c(global.songInfo);
});
});
};

View File

@ -1,6 +1,4 @@
const { const {TouchBar} = require('electron');
TouchBar, nativeImage
} = require('electron');
const { const {
TouchBarButton, TouchBarButton,
TouchBarLabel, TouchBarLabel,
@ -8,61 +6,28 @@ const {
TouchBarSegmentedControl, TouchBarSegmentedControl,
TouchBarScrubber TouchBarScrubber
} = TouchBar; } = TouchBar;
const fetch = require('node-fetch');
// This selects the song title // Songtitle label
const titleSelector = '.title.style-scope.ytmusic-player-bar'; const songTitle = new TouchBarLabel({
// This selects the song image
const imageSelector = '#layout > ytmusic-player-bar > div.middle-controls.style-scope.ytmusic-player-bar > img';
// These keys will be used to go backwards, pause, skip songs, like songs, dislike songs
const keys = ['k', 'space', 'j', '_', '+'];
const presskey = (window, key) => {
window.webContents.sendInputEvent({
type: 'keydown',
keyCode: key
});
};
// Grab the title using the selector
const getTitle = win => {
return win.webContents.executeJavaScript(
'document.querySelector(\'' + titleSelector + '\').innerText'
).catch(error => {
console.log(error);
});
};
// Grab the image src using the selector
const getImage = win => {
return win.webContents.executeJavaScript(
'document.querySelector(\'' + imageSelector + '\').src'
).catch(error => {
console.log(error);
});
};
module.exports = win => {
// Songtitle label
const songTitle = new TouchBarLabel({
label: '' label: ''
}); });
// This will store the song controls once available
let controls = [];
// This will store the song image once available // This will store the song image once available
const songImage = {}; const songImage = {};
// The song control buttons (keys to press are in the same order) // Pause/play button
const buttons = new TouchBarSegmentedControl({ const pausePlayButton = new TouchBarButton();
// The song control buttons (control functions are in the same order)
const buttons = new TouchBarSegmentedControl({
mode: 'buttons', mode: 'buttons',
segments: [ segments: [
new TouchBarButton({ new TouchBarButton({
label: '⏮' label: '⏮'
}), }),
new TouchBarButton({ pausePlayButton,
label: '⏯️'
}),
new TouchBarButton({ new TouchBarButton({
label: '⏭' label: '⏭'
}), }),
@ -73,11 +38,11 @@ module.exports = win => {
label: '👍' label: '👍'
}) })
], ],
change: i => presskey(win, keys[i]) change: i => controls[i]()
}); });
// This is the touchbar object, this combines everything with proper layout // This is the touchbar object, this combines everything with proper layout
const touchBar = new TouchBar({ const touchBar = new TouchBar({
items: [ items: [
new TouchBarScrubber({ new TouchBarScrubber({
items: [songImage, songTitle], items: [songImage, songTitle],
@ -88,23 +53,33 @@ module.exports = win => {
}), }),
buttons buttons
] ]
}); });
module.exports = win => {
// If the page is ready, register the callback
win.on('ready-to-show', () => {
controls = [
global.songControls.previous,
global.songControls.pause,
global.songControls.next,
global.songControls.like,
global.songControls.dislike
];
// Register the callback
global.songInfo.onNewData(songInfo => {
// Song information changed, so lets update the touchBar
// If the page title changes, update touchbar and song title
win.on('page-title-updated', async () => {
// Set the song title // Set the song title
songTitle.label = await getTitle(win); songTitle.label = songInfo.title;
// Changes the pause button if paused
pausePlayButton.label = songInfo.isPaused ? '▶️' : '⏸';
// Get image source // Get image source
const imageSrc = await getImage(win); songImage.icon = songInfo.image ? songInfo.image.resize({height: 23}) : null;
// Fetch and set song image
await fetch(imageSrc)
.then(response => response.buffer())
.then(data => {
songImage.icon = nativeImage.createFromBuffer(data).resize({height: 23});
});
win.setTouchBar(touchBar); win.setTouchBar(touchBar);
}); });
});
}; };