Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DEPRECATED] Chromecast Plugin #296

Draft
wants to merge 17 commits into
base: master
Choose a base branch
from
Draft
6 changes: 6 additions & 0 deletions config/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,12 @@ const defaultConfig = {
},
savedVolume: undefined //plugin save volume between session here
},
chromecast: {
enabled: false,
syncVolume: true,
syncStartTime: true,
syncSeek: true
},
sponsorblock: {
enabled: false,
apiURL: "https://sponsor.ajay.app",
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
"async-mutex": "^0.3.1",
"browser-id3-writer": "^4.4.0",
"chokidar": "^3.5.1",
"chromecast-api": "^0.3.4",
"custom-electron-titlebar": "^3.2.6",
"discord-rpc": "^3.2.0",
"electron-debug": "^3.2.0",
Expand Down Expand Up @@ -97,7 +98,8 @@
"xo": "^0.38.2"
},
"resolutions": {
"yargs-parser": "18.1.3"
"yargs-parser": "18.1.3",
"dns-packet": "5.2.4"
},
"xo": {
"envs": [
Expand Down
105 changes: 105 additions & 0 deletions plugins/chromecast/back.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
const registerCallback = require("../../providers/song-info");
const getSongControls = require('../../providers/song-controls');

const { ipcMain } = require('electron')
const ChromecastAPI = require('chromecast-api');
const { setOptions } = require("../../config/plugins");

let client;
let deviceList = [];

let controls;

let options;

module.exports = (win, initialOptions) => {
const { play, pause } = getSongControls(win);
controls = { play, pause };

options = initialOptions;

client = new ChromecastAPI();

client.on('device', (device) => {
if (!deviceList.includes(device.name)) {
registerDevice(device);
}
});
ipcMain.on('volume-change', (_, v) => setVolume(v));
ipcMain.on('seeked-to', (_, s) => seekTo(s + 1.5));
};

function setVolume(volume) {
if (options.syncVolume) {
for (const device of client.devices) {
device.setVolume(volume);
}
}
}

function seekTo(seconds) {
if (options.syncSeek) {
for (const device of client.devices) {
device.seekTo(seconds);
}
}
}

function registerDevice(device) {
deviceList.push(device.name);
let currentStatus;
device.on('status', async (status) => {
currentStatus = status.playerState;
if (options.syncStartTime) {
currentStatus === "PLAYING" ? controls.play() : controls.pause();
}
})

let currentUrl;
let isPaused;
registerCallback(songInfo => {
if (!songInfo?.title) {
return;
}
if (currentUrl !== songInfo.url) { //new song
currentUrl = songInfo.url;
if (options.syncStartTime) {
isPaused = true;
controls.pause();
} else {
isPaused = songInfo.isPaused;
}
device.play(transformURL(songInfo.url));

} else if (isPaused !== songInfo.isPaused) { //paused status changed
isPaused = songInfo.isPaused;
if (isPaused && currentStatus === "PLAYING") {
device.pause();
} else {
device.resume();
}
}
});
}

// will not be needed after https://github.com/alxhotel/chromecast-api/pull/69 (chromecastAPI v0.3.5)
function transformURL(url) {
const videoId = url.match(/(?:http(?:s?):\/\/)?(?:www\.)?(?:music\.)?youtu(?:be\.com\/watch\?v=|\.be\/)([\w\-\_]*)/);
// videoId[1] should always be valid since regex should always be valid - rickroll video should never happen :)
return "https://youtube.com/watch?v=" + (videoId.length > 1 ? videoId[1] : "dQw4w9WgXcQ");
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice Rickroll 🙃 Maybe worth a comment in code, I wondered why this hardcoded ID was there at first 😅

Copy link
Collaborator Author

@Araxeus Araxeus Jun 19, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a comment already 😅
I thought the module would update its version faster - and this method will be unneeded
but sadly it hasn't happened yet - the fix pr was merged but the npm version wasn't updated yet

(method should only be called when songInfo is updated which means the url regex is valid - which means you should never get rick-rolled 😝)

}

module.exports.setOption = (value, ...keys) => {
for (const key of keys) {
options[key] = value;
}
setOptions("chromecast", options);
};

module.exports.menuCheck = (options_) => {
if (!options) options = options_;
}

module.exports.refreshChromecast = () => {
if (client) client.update();
}
20 changes: 20 additions & 0 deletions plugins/chromecast/front.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
const { ipcRenderer } = require('electron');

const sendVolume = (volume) => ipcRenderer.send('volume-change', volume);
const sendTime = (seconds) => ipcRenderer.send('seeked-to', seconds);

module.exports = function checkVideoLoaded() {
const video = document.querySelector("video");
video ? setup(video) : setTimeout(checkVideoLoaded, 500);
}

function setup(video) {

sendVolume(video.volume);

video.addEventListener('volumechange', e =>
sendVolume(e.target.muted ? 0 : e.target.volume));

video.addEventListener('seeking', e =>
sendTime(e.target.currentTime));
}
30 changes: 30 additions & 0 deletions plugins/chromecast/menu.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
const { menuCheck, setOption, refreshChromecast } = require("./back");

module.exports = (_win, options) => {
menuCheck(options);
return [
{
label: "Sync Volume",
type: "checkbox",
checked: !!options.syncVolume,
click: (item) => setOption(item.checked, "syncVolume")
},
{
label: "Sync Start Time",
type: "checkbox",
checked: !!options.syncStartTime,
click: (item) => setOption(item.checked, "syncStartTime")
},
{
label: "Sync Seek",
type: "checkbox",
checked: !!options.syncSeek,
click: (item) => setOption(item.checked, "syncSeek")
},
{ type: "separator" },
{
label: "Refresh Device List",
click: refreshChromecast
}
]
};
6 changes: 3 additions & 3 deletions plugins/taskbar-mediacontrol/back.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,16 @@ function setThumbar(win, songInfo) {
{
tooltip: 'Previous',
icon: get('backward.png'),
click() { controls.previous(win.webContents); }
click() { controls.previous(); }
}, {
tooltip: 'Play/Pause',
// Update icon based on play state
icon: songInfo.isPaused ? get('play.png') : get('pause.png'),
click() { controls.playPause(win.webContents); }
click() { controls.playPause(); }
}, {
tooltip: 'Next',
icon: get('forward.png'),
click() { controls.next(win.webContents); }
click() { controls.next(); }
}
]);
}
Expand Down
47 changes: 33 additions & 14 deletions providers/song-controls-front.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,37 @@
const { ipcRenderer } = require("electron");

let videoStream = document.querySelector(".video-stream");
module.exports = () => {
ipcRenderer.on("playPause", () => {
if (!videoStream) {
videoStream = document.querySelector(".video-stream");
}
let video = document.querySelector("video");

if (videoStream.paused) {
videoStream.play();
} else {
videoStream.yns_pause ?
videoStream.yns_pause() :
videoStream.pause();
}
});
module.exports = () => {
ipcRenderer.on("playPause", (_e, toPlay) => playPause(toPlay));
};
module.exports.playPause = playPause;

function playPause(toPlay = undefined) {
if (!checkVideo()) return;

switch (toPlay) {
case true:
video.play();
break;
case false:
pause();
break;
default: //usually undefined
video.paused ? video.play() : pause();
}

function pause() {
video.yns_pause ?
video.yns_pause() :
video.pause();
}
}

function checkVideo() {
if (!video) {
video = document.querySelector("video");
}

return !!video;
}
3 changes: 2 additions & 1 deletion providers/song-controls.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ module.exports = (win) => {
previous: () => pressKey(win, "k"),
next: () => pressKey(win, "j"),
playPause: () => win.webContents.send("playPause"),
like: () => pressKey(win, "_"),
th-ch marked this conversation as resolved.
Show resolved Hide resolved
play: () => win.webContents.send("playPause", true),
pause: () => win.webContents.send("playPause", false),
dislike: () => pressKey(win, "+"),
go10sBack: () => pressKey(win, "h"),
go10sForward: () => pressKey(win, "l"),
Expand Down
Loading