1
0
mirror of https://github.com/krateng/maloja.git synced 2023-08-10 21:12:55 +03:00

Updated Chromium scrobbler to be more modularized

This commit is contained in:
Krateng 2019-05-13 13:18:22 +02:00
parent 5aa92a1ab8
commit b99cf8b77d
8 changed files with 146 additions and 89 deletions

View File

@ -58,6 +58,7 @@ If you didn't install Maloja from the package (and therefore don't have it in `/
The `update` command will always fetch the latest version, while packages are only offered for release versions.
3) Various folders have `.info` files with more information on how to use their associated features.
## How to scrobble
@ -65,7 +66,7 @@ If you didn't install Maloja from the package (and therefore don't have it in `/
If you use Plex Web or Youtube Music on Chromium, you can use the included extension. Make sure to enter the random key Maloja generates on first startup in the extension settings.
If you want to implement your own method of scrobbling, it's very simple: You only need one POST request with the keys `artist`, `title` and `key`.
If you want to implement your own method of scrobbling, it's very simple: You only need one POST request to `/api/newscrobble` with the keys `artist`, `title` and `key`.
### Standard-compliant API
@ -75,10 +76,12 @@ GNU FM |  
------ | ---------
Gnukebox URL | Your Maloja URL followed by `/api/s/audioscrobbler`
Username | Any name, doesn't matter
Password | Any of your API keys (you can define new ones in `clients/authenticated_machines` in your Maloja folder)
Password | Any of your API keys
ListenBrainz |  
------ | ---------
API URL | Your Maloja URL followed by `api/s/listenbrainz`
API URL | Your Maloja URL followed by `/api/s/listenbrainz`
Username | Any name, doesn't matter
Auth Token | Any of your API keys (you can define new ones in `clients/authenticated_machines` in your Maloja folder)
Auth Token | Any of your API keys
It is recommended to define a different API key for every scrobbler you use in `clients/authenticated_machines.tsv` in your Maloja folder.

View File

@ -41,8 +41,8 @@ function onTabUpdated(tabId, changeInfo, tab) {
for (var i=0;i<patterns.length;i++) {
if (tab.url.startsWith(patterns[i])) {
//console.log("Still on same page!")
//tabManagers[tabId].update()
window.setTimeout(tabManagers[tabId].update(),1000);
tabManagers[tabId].update()
return
}
}
@ -132,14 +132,30 @@ class Controller {
this.messageID = 0;
this.lastMessage = 0;
this.update()
this.alreadyQueued = false;
// we reject update requests when we're already planning to run an update!
this.update();
}
// the tab has been updated, we need to run the script
update() {
if (this.alreadyQueued) {
}
else {
this.alreadyQueued = true;
setTimeout(() => { this.actuallyupdate(); },800);
//this.actuallyupdate();
}
}
actuallyupdate() {
this.messageID++;
//console.log("Update! Our page is " + this.page + ", our tab id " + this.tabId)
chrome.tabs.executeScript(this.tabId,{"file":"sitescripts/" + pages[this.page]["script"]})
chrome.tabs.executeScript(this.tabId,{"file":"sites/" + pages[this.page]["script"]});
chrome.tabs.executeScript(this.tabId,{"file":"sitescript.js"});
this.alreadyQueued = false;
}
// an actual update message from the script has arrived
@ -184,16 +200,22 @@ class Controller {
else if (artist != this.currentArtist || title != this.currentTitle) {
//first inform ourselves that the previous track has now been stopped for good
this.stopPlayback(artist,title)
this.stopPlayback(artist,title);
//then initialize new playback
console.log("New track")
this.setUpdate()
this.alreadyPlayed = 0
this.currentTitle = title
this.currentArtist = artist
this.currentLength = seconds
console.log(artist + " - " + title + " is playing!")
this.currentlyPlaying = true
console.log("New track");
this.setUpdate();
this.alreadyPlayed = 0;
this.currentTitle = title;
this.currentArtist = artist;
if (Number.isInteger(seconds)) {
this.currentLength = seconds;
}
else {
this.currentLength = 300;
// avoid excessive scrobbling when the selector breaks
}
console.log(artist + " - " + title + " is playing! (" + this.currentLength + " seconds)");
this.currentlyPlaying = true;
}
}

View File

@ -1,6 +1,6 @@
{
"name": "Maloja Scrobbler",
"version": "1.1",
"version": "1.2",
"description": "Scrobbles tracks from various sites to your Maloja server",
"manifest_version": 2,
"permissions": ["activeTab",

View File

@ -0,0 +1,11 @@
maloja_scrobbler_selector_playbar = "//div[contains(@class,'PlayerControls')]"
maloja_scrobbler_selector_metadata = ".//div[contains(@class,'PlayerControlsMetadata-container')]"
maloja_scrobbler_selector_title = ".//a[@data-qa-id='metadataTitleLink']/@title"
maloja_scrobbler_selector_artist = ".//span[contains(@class,'MetadataPosterTitle-title')]/a[1]/@title"
maloja_scrobbler_selector_duration = ".//button[@data-qa-id='mediaDuration']/text()[3]"
maloja_scrobbler_selector_control = ".//div[contains(@class,'PlayerControls-buttonGroupCenter')]/button[2]/@title"

View File

@ -0,0 +1,13 @@
maloja_scrobbler_selector_playbar = "//ytmusic-player-bar"
maloja_scrobbler_selector_metadata = ".//div[contains(@class,'middle-controls')]/div[contains(@class,'content-info-wrapper')]"
maloja_scrobbler_selector_title = ".//yt-formatted-string[contains(@class,'title')]/@title"
maloja_scrobbler_selector_artists = ".//span/span[contains(@class,'subtitle')]/yt-formatted-string/a[position()<last()]"
maloja_scrobbler_selector_artist = "./text()"
maloja_scrobbler_selector_duration = ".//div[contains(@class,'left-controls')]/span[contains(@class,'time-info')]/text()"
duration_needs_split = true
maloja_scrobbler_selector_control = ".//div[contains(@class,'left-controls')]/div/paper-icon-button[contains(@class,'play-pause-button')]/@title"

View File

@ -0,0 +1,79 @@
function getxpath(path,type) {
result = document.evaluate(path, this, null, type, null);
if (type == XPathResult.FIRST_ORDERED_NODE_TYPE) {
return result.singleNodeValue;
}
else if (type == XPathResult.ORDERED_NODE_ITERATOR_TYPE) {
resultarray = [];
while(node = result.iterateNext()) {
resultarray.push(node);
}
return resultarray;
}
else if (type == XPathResult.STRING_TYPE) {
return result.stringValue;
}
// if (path.split("/").slice(-1)[0].startsWith("text()") || path.split("/").slice(-1)[0].startsWith("@")) {
// result = document.evaluate(path, this, null, XPathResult.STRING_TYPE, null);
// return result.stringValue;
// }
// else {
// result = document.evaluate(path, this, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
// return result.singleNodeValue;
// }
}
Node.prototype.xpath = getxpath;
bar = document.xpath(maloja_scrobbler_selector_playbar, XPathResult.FIRST_ORDERED_NODE_TYPE);
if (bar == null) {
console.log("Nothing playing right now!");
chrome.runtime.sendMessage({type:"stopPlayback",time:Math.floor(Date.now()),artist:"",title:""});
}
else {
metadata = bar.xpath(maloja_scrobbler_selector_metadata, XPathResult.FIRST_ORDERED_NODE_TYPE);
duration = bar.xpath(maloja_scrobbler_selector_duration, XPathResult.STRING_TYPE);
duration = duration + '';
title = metadata.xpath(maloja_scrobbler_selector_title, XPathResult.STRING_TYPE);
if (typeof maloja_scrobbler_selector_artists !== "undefined") {
artistnodes = metadata.xpath(maloja_scrobbler_selector_artists, XPathResult.ORDERED_NODE_ITERATOR_TYPE);
artists = artistnodes.map(x => x.xpath(maloja_scrobbler_selector_artist, XPathResult.STRING_TYPE));
artist = artists.join(";");
}
else {
artist = metadata.xpath(maloja_scrobbler_selector_artist, XPathResult.STRING_TYPE);
}
if (typeof duration_needs_split !== "undefined" && duration_needs_split) {
duration = duration.split("/").slice(-1)[0].trim();
}
if (duration.split(":").length == 2) {
durationSeconds = parseInt(duration.split(":")[0]) * 60 + parseInt(duration.split(":")[1]);
}
else {
durationSeconds = parseInt(duration.split(":")[0]) * 60 * 60 + parseInt(duration.split(":")[1]) * 60 + parseInt(duration.split(":")[2]);
}
control = bar.xpath(maloja_scrobbler_selector_control, XPathResult.STRING_TYPE);
if (control == "Play") {
console.log("Not playing right now");
chrome.runtime.sendMessage({type:"stopPlayback",time:Math.floor(Date.now()),artist:artist,title:title});
//stopPlayback()
}
else if (control == "Pause") {
console.log("Playing " + artist + " - " + title);
chrome.runtime.sendMessage({type:"startPlayback",time:Math.floor(Date.now()),artist:artist,title:title,duration:durationSeconds});
//startPlayback(artist,title,durationSeconds)
}
}

View File

@ -1,31 +0,0 @@
bar = document.querySelector("div[class*=PlayerControls]")
if (bar == null) {
console.log("Nothing playing right now!")
chrome.runtime.sendMessage({type:"stopPlayback",time:Math.floor(Date.now()),artist:"",title:""})
exit()
}
metadata = bar.querySelector("div[class*=PlayerControlsMetadata-container]")
title = metadata.querySelector("a[class*=MetadataPosterTitle-singleLineTitle]").getAttribute("title")
artist = metadata.querySelector("span[class*=MetadataPosterTitle-title] > a:nth-child(1)").getAttribute("title")
duration = metadata.querySelector("[data-qa-id=mediaDuration]").innerHTML.split("/")[1]
if (duration.split(":").length == 2) {
durationSeconds = parseInt(duration.split(":")[0]) * 60 + parseInt(duration.split(":")[1])
}
else {
durationSeconds = parseInt(duration.split(":")[0]) * 60 * 60 + parseInt(duration.split(":")[1]) * 60 + parseInt(duration.split(":")[2])
}
control = bar.querySelector("div[class*=PlayerControls-buttonGroupCenter] > button:nth-child(2)").getAttribute("title")
if (control == "Play") {
console.log("Not playing right now")
chrome.runtime.sendMessage({type:"stopPlayback",time:Math.floor(Date.now()),artist:artist,title:title})
//stopPlayback()
}
else if (control == "Pause") {
console.log("Playing " + artist + " - " + title)
chrome.runtime.sendMessage({type:"startPlayback",time:Math.floor(Date.now()),artist:artist,title:title,duration:durationSeconds})
//startPlayback(artist,title,durationSeconds)
}

View File

@ -1,40 +0,0 @@
bar = document.querySelector("ytmusic-player-bar")
if (bar == null) {
console.log("Nothing playing right now!")
chrome.runtime.sendMessage({type:"stopPlayback",time:Math.floor(Date.now()),artist:"",title:""})
exit()
}
metadata = bar.querySelector("div[class*=middle-controls] > div[class*=content-info-wrapper]")
ctrl = bar.querySelector("div[class*=left-controls]")
title = metadata.querySelector("yt-formatted-string[class*=title]").getAttribute("title")
artistlist = metadata.querySelector("span > span[class*=subtitle] > yt-formatted-string")
artistelements = artistlist.getElementsByTagName("a")
artists = []
for (var i=0;i<artistelements.length-1;i++) {
artists.push(artistelements[i].innerHTML)
}
//artist = metadata.querySelector("span > span[class*=subtitle] > yt-formatted-string > a:nth-child(1)").innerHTML
artist = artists.join(";");
duration = ctrl.querySelector("[class*=time-info]").innerHTML.split("/")[1]
if (duration.split(":").length == 2) {
durationSeconds = parseInt(duration.split(":")[0]) * 60 + parseInt(duration.split(":")[1])
}
else {
durationSeconds = parseInt(duration.split(":")[0]) * 60 * 60 + parseInt(duration.split(":")[1]) * 60 + parseInt(duration.split(":")[2])
}
control = ctrl.querySelector("div > paper-icon-button[class*=play-pause-button]").getAttribute("title")
if (control == "Play") {
console.log("Not playing right now")
chrome.runtime.sendMessage({type:"stopPlayback",time:Math.floor(Date.now()),artist:artist,title:title})
//stopPlayback()
}
else if (control == "Pause") {
console.log("Playing " + artist + " - " + title)
chrome.runtime.sendMessage({type:"startPlayback",time:Math.floor(Date.now()),artist:artist,title:title,duration:durationSeconds})
//startPlayback(artist,title,durationSeconds)
}