Skip to content

Commit

Permalink
1.0.0: update info instantly for bangs!
Browse files Browse the repository at this point in the history
previously there would be a delay between passing a bang
and info updating, if the tab was not active.
now we've fixed this (and no longer need WNPCListener.js)
by automatically updating info when we fire bangs.
idk why I didn't think of it sooner.
also a major refactor of the main script and youtube scripts.
  • Loading branch information
aminomancer committed Aug 4, 2022
1 parent 71440f7 commit 8d7ca6a
Show file tree
Hide file tree
Showing 8 changed files with 350 additions and 418 deletions.
60 changes: 37 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,23 +1,37 @@
# Web Now Playing Companion Extension - Unofficial Updated Fork
[Source code](https://github.com/shmediaproductions/WebNowPlaying-Companion-Personal-Edit/releases)
The extension to go along with the WebNowPlaying plugin for Rainmeter
Source code and downloads for the Rainmeter plugin can be found [here](https://github.com/tjhrulz/WebNowPlaying-BrowserExtension)

The original extension can be found in both the [Chrome Web Store](https://chrome.google.com/webstore/detail/webnowplaying-companion/jfakgfcdgpghbbefmdfjkbdlibjgnbli) and the [Firefox Addons Store](https://addons.mozilla.org/en-US/firefox/addon/webnowplaying-companion/).

#### If you would like to support this extension please check out the original author's [patreon](https://www.patreon.com/tjhrulz)

### List of supported sites:
- Youtube (Both new and old layouts)
- Embedded Youtube videos/playlists on any webpage
- Soundcloud
- Google Play Music
- Amazon Music
- Pandora
- Spotify
- Tidal
- Deezer
- Plex (Music)
- PocketCasts
- Generic site support that can be turned on in the settings (works with Netflix)

# Web Now Playing Companion Extension - Unofficial Updated Fork
[Source code](https://github.com/shmediaproductions/WebNowPlaying-Companion-Personal-Edit/releases)
The browser extension required to use the [WebNowPlaying plugin](https://github.com/tjhrulz/WebNowPlaying-BrowserExtension) for [Rainmeter](https://www.rainmeter.net/). This is an unnoficial fork of the original companion extension, which has been modified to enhance the experience on YouTube and Netflix and to update the site scripts more regularly.

I've also configured the extension so that it automatically sends info updates to Rainmeter when the user successfully activates a command. Normally, after executing a bang through your skin, you'd have to wait for the extension's info watcher to pick up on changes to the site. That's not a big deal when you have the tab open, since it checks for changes every 50ms. But timeouts and intervals in background tabs are throttled, so the update rate is drastically reduced (e.g., once per second in Firefox).

For example, after passing the Pause bang to a background tab, it would take up to 1 second for your skin's play/pause button to update to reflect the fact that the video was paused. But we can just send updated info as part of executing the Bang. So the extension receives the Pause bang and goes about pausing the video through JavaScript APIs, and then immediately sends Rainmeter new information indicating that the video is now paused. That means Rainmeter's state will update instantly. And then, how soon it visually updates just depends on your skin's Update properties.

The YouTube-specific changes mostly include improved logic for bangs, but also for some info. For example, YouTube's playlist loop button now has 3 states, just like WebNowPlaying has: loop off, loop all, and loop one. The new info and bang behavior handles that more accurately and efficiently. It has pretty intelligent logic for retrieving the title, artist, and album from roughly equivalent values on YouTube. Album will usually show playlist title but if not, it will show a type of genre, a hashtag, or a content category. It will also seek the highest resolution thumbnail but fall back to smaller images if necessary. The thumbs up/down and rating bangs and info correctly map to YouTube's like/dislike feature, assuming you use [Return YouTube Dislike](https://www.returnyoutubedislike.com/).

The Next and Previous bangs have been given a major overhaul, allowing them to follow different behavior depending on whether a playlist is active, loop or shuffle are enabled, etc. One of the best features is that when you're in a playlist and loop is enabled, the Next bang will return to the beginning of the playlist, as it would if you let the video play to the end. Without this, it might skip to YouTube's autoplay suggested video instead.

While shuffle is enabled, the Next bang will jump to a random video instead of the next video in the playlist. And the Previous bang will mimic the behavior of the Previous button on YouTube — go to the start of the video if we're more than 3 seconds into the video; otherwise, go back in playlist order if in a playlist; otherwise, go back in session history.

Shuffle and loop functions have also been made to work in embedded YouTube videos, even though the embedded player doesn't natively support these states. It's internal logic but it works for WebNowPlaying bangs. Generally, embedded players work just like full YouTube players. The main missing feature is the like/dislike rating feature.

Source code and downloads for the Rainmeter plugin can be found [here](https://github.com/tjhrulz/WebNowPlaying-BrowserExtension)

The original extension can be found in both the [Chrome Web Store](https://chrome.google.com/webstore/detail/webnowplaying-companion/jfakgfcdgpghbbefmdfjkbdlibjgnbli) and the [Firefox Addons Store](https://addons.mozilla.org/en-US/firefox/addon/webnowplaying-companion/).

#### If you would like to support this extension please check out the original author's [patreon](https://www.patreon.com/tjhrulz)

### List of supported sites:
- YouTube (Both new and old layouts)
- Embedded YouTube videos/playlists on any webpage
- Netflix
- Soundcloud
- Google Play Music
- Amazon Music
- Pandora
- Spotify
- Tidal
- Deezer
- Plex (Music)
- PocketCasts
- Generic site support that can be turned on in the settings

46 changes: 0 additions & 46 deletions WNPCListener.uc.js

This file was deleted.

209 changes: 102 additions & 107 deletions WebNowPlaying.js
Original file line number Diff line number Diff line change
@@ -1,29 +1,25 @@
var currTitle;
var currArtist;
var currAlbum;
var currCover;
var currPos;
var currDur;
var currVolume;
var currRating;
var currRepeat;
var currShuffle;
var currState;

//Always make sure this is set
var currPlayer;

var currTrackID;
var currArtistID;
var currAlbumID;

var ws;
var connected = false;
var reconnect;
var sendData;

var musicEvents;
var musicInfo;
var currTitle,
currArtist,
currAlbum,
currCover,
currPos,
currDur,
currVolume,
currRating,
currRepeat,
currShuffle,
currState,
//Always make sure this is set
currPlayer,
currTrackID,
currArtistID,
currAlbumID,
ws,
connected = false,
reconnect,
sendData,
musicEvents,
musicInfo;

/*
ooooo ooooo oooooooooooo ooooo ooooooooo. oooooooooooo ooooooooo. .oooooo..o
Expand All @@ -35,14 +31,14 @@ ooooo ooooo oooooooooooo ooooo ooooooooo. oooooooooooo ooooooooo.
o888o o888o o888ooooood8 o888ooooood8 o888o o888ooooood8 o888o o888o 8""88888P'
*/
function pad(number, length) {
var str = String(number);
let str = String(number);
while (str.length < length) str = "0" + str;
return str;
}

//Convert seconds to a time string acceptable to Rainmeter
function convertTimeToString(timeInSeconds) {
var timeInMinutes = parseInt(timeInSeconds / 60);
let timeInMinutes = parseInt(timeInSeconds / 60);
if (timeInMinutes < 60) return timeInMinutes + ":" + pad(parseInt(timeInSeconds % 60), 2);
return (
parseInt(timeInMinutes / 60) +
Expand All @@ -53,12 +49,28 @@ function convertTimeToString(timeInSeconds) {
);
}

// A fallback currently used as a fallback on YouTube.
function fancyTimeFormat(time) {
// Hours, minutes and seconds
let days = ~~(time / 86400);
let hrs = ~~((time % 86400) / 3600);
let mins = ~~((time % 3600) / 60);
let secs = ~~time % 60;

// Output like "1:01" or "4:03:59" or "123:03:59"
let ret = "";
if (days > 0) ret += "" + days + ":" + (hrs < 10 ? "0" : "");
if (hrs > 0) ret += "" + hrs + ":" + (mins < 10 ? "0" : "");
if (mins > 0) ret += "" + mins + ":" + (secs < 10 ? "0" : "");
else ret += "" + 0 + ":";
ret += "" + secs;
return ret;
}

//Convert every words to start with capital (Note: Does NOT ignore words that should not be)
function capitalize(str) {
str = str.replace(/-/g, " ");
return str.replace(/\w\S*/g, function (txt) {
return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
});
return str.replace(/\w\S*/g, txt => txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase());
}

/*
Expand All @@ -73,54 +85,49 @@ function capitalize(str) {
*/
//Use this object to define custom event logic
function createNewMusicEventHandler() {
musicEvents = {};

musicEvents.readyCheck = null;

musicEvents.playpause = null;
musicEvents.next = null;
musicEvents.previous = null;
musicEvents.progress = null;
musicEvents.progressSeconds = null;
musicEvents.volume = null;
musicEvents.repeat = null;
musicEvents.shuffle = null;
musicEvents.toggleThumbsUp = null;
musicEvents.toggleThumbsDown = null;
musicEvents.rating = null;

musicEvents = {
readyCheck: null,
playpause: null,
next: null,
previous: null,
progress: null,
progressSeconds: null,
volume: null,
repeat: null,
shuffle: null,
toggleThumbsUp: null,
toggleThumbsDown: null,
rating: null,
};
return musicEvents;
}

//Use this object to define custom logic to retrieve data
function createNewMusicInfo() {
musicInfo = {};

//Mandatory, just give the player name
musicInfo.player = null;
//Check player is ready to start doing info checks. ie. it is fully loaded and has the song title
//While false no other info checks will be called
musicInfo.readyCheck = null;

musicInfo.state = null;
musicInfo.title = null;
musicInfo.artist = null;
musicInfo.album = null;
musicInfo.cover = null;
musicInfo.duration = null;
musicInfo.position = null;
musicInfo.durationString = null;
musicInfo.positionString = null;
musicInfo.volume = null;
musicInfo.rating = null;
musicInfo.repeat = null;
musicInfo.shuffle = null;

//Optional, only use if more data parsing needed in the Rainmeter plugin
musicInfo.trackID = null;
musicInfo.artistID = null;
musicInfo.albumID = null;

musicInfo = {
//Mandatory, just give the player name
player: null,
//Check player is ready to start doing info checks. ie. it is fully loaded and has the song title
//While false no other info checks will be called
readyCheck: null,
state: null,
title: null,
artist: null,
album: null,
cover: null,
duration: null,
position: null,
durationString: null,
positionString: null,
volume: null,
rating: null,
repeat: null,
shuffle: null,
//Optional, only use if more data parsing needed in the Rainmeter plugin
trackID: null,
artistID: null,
albumID: null,
};
return musicInfo;
}

Expand All @@ -138,7 +145,7 @@ function updateInfo() {
//This would be a lot cleaner if javascript had nice things like enums, then I could just foreach this
//UPDATE STATE
if (musicInfo.readyCheck === null || musicInfo.readyCheck()) {
var temp;
let temp;
try {
if (musicInfo.state !== null) {
temp = musicInfo.state();
Expand Down Expand Up @@ -383,50 +390,39 @@ o888ooooood8 `8' o888ooooood8 o8o `8 o888o 8""88888P
function fireEvent(event) {
try {
if (musicEvents.readyCheck === null || musicEvents.readyCheck()) {
if (event.data.toLowerCase() == "playpause" && musicEvents.playpause !== null)
musicEvents.playpause();
else if (event.data.toLowerCase() == "next" && musicEvents.next !== null) musicEvents.next();
else if (event.data.toLowerCase() == "previous" && musicEvents.previous !== null)
musicEvents.previous();
else if (
event.data.toLowerCase().includes("setprogress ") ||
event.data.toLowerCase().includes("setposition ")
) {
let type = event.data.toLowerCase();

if (type == "playpause" && musicEvents.playpause !== null) musicEvents.playpause();
else if (type == "next" && musicEvents.next !== null) musicEvents.next();
else if (type == "previous" && musicEvents.previous !== null) musicEvents.previous();
else if (type.includes("setprogress ") || type.includes("setposition ")) {
if (musicEvents.progress !== null) {
var progress = event.data.toLowerCase();
//+9 because "progress " is 9 chars
progress = progress.substring(progress.indexOf("progress ") + 9);
let progress = type.substring(type.indexOf("progress ") + 9);
//Goto the : at the end of the command, this command is now a compound command the first half is seconds the second is percent
progress = parseFloat(progress.substring(0, progress.indexOf(":")));

musicEvents.progress(progress);
} else if (musicEvents.progressSeconds !== null) {
var position = event.data.toLowerCase();
//+9 because "position " is 9 chars
position = position.substring(position.indexOf("position ") + 9);
let position = type.substring(type.indexOf("position ") + 9);
//Goto the : at the end of the command, this command is now a compound command the first half is seconds the second is percent
position = parseInt(position.substring(0, position.indexOf(":")));

musicEvents.progressSeconds(position);
}
} else if (event.data.toLowerCase().includes("setvolume ") && musicEvents.volume !== null) {
var volume = event.data.toLowerCase();
} else return;
} else if (type.includes("setvolume ") && musicEvents.volume !== null) {
//+7 because "volume " is 7 chars
volume = parseInt(volume.substring(volume.indexOf("volume ") + 7)) / 100;
let volume = parseInt(type.substring(type.indexOf("volume ") + 7)) / 100;
musicEvents.volume(volume);
} else if (event.data.toLowerCase() == "repeat" && musicEvents.repeat !== null)
musicEvents.repeat();
else if (event.data.toLowerCase() == "shuffle" && musicEvents.shuffle !== null)
musicEvents.shuffle();
else if (event.data.toLowerCase() == "togglethumbsup" && musicEvents.toggleThumbsUp !== null)
} else if (type == "repeat" && musicEvents.repeat !== null) musicEvents.repeat();
else if (type == "shuffle" && musicEvents.shuffle !== null) musicEvents.shuffle();
else if (type == "togglethumbsup" && musicEvents.toggleThumbsUp !== null) {
musicEvents.toggleThumbsUp();
else if (
event.data.toLowerCase() == "togglethumbsdown" &&
musicEvents.toggleThumbsDown !== null
)
} else if (type == "togglethumbsdown" && musicEvents.toggleThumbsDown !== null) {
musicEvents.toggleThumbsDown();
else if (event.data.toLowerCase() == "rating " && musicEvents.rating !== null)
musicEvents.rating();
} else if (type == "rating " && musicEvents.rating !== null) musicEvents.rating();
else return;

if (connected) updateInfo();
}
} catch (e) {
ws.send("Error:Error sending event to " + musicInfo.player);
Expand All @@ -446,8 +442,7 @@ oo .d8P 888 o 888 `88. .8' 888
*/

function init() {
var url = "ws://127.0.0.1:8974/";
ws = new WebSocket(url);
ws = new WebSocket("ws://127.0.0.1:8974/");
ws.onopen = function () {
connected = true;
currPlayer = musicInfo.player();
Expand Down
Loading

0 comments on commit 8d7ca6a

Please sign in to comment.