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. 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 ## 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 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 ### Standard-compliant API
@ -75,10 +76,12 @@ GNU FM |  
------ | --------- ------ | ---------
Gnukebox URL | Your Maloja URL followed by `/api/s/audioscrobbler` Gnukebox URL | Your Maloja URL followed by `/api/s/audioscrobbler`
Username | Any name, doesn't matter 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 |   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 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++) { for (var i=0;i<patterns.length;i++) {
if (tab.url.startsWith(patterns[i])) { if (tab.url.startsWith(patterns[i])) {
//console.log("Still on same page!") //console.log("Still on same page!")
//tabManagers[tabId].update() tabManagers[tabId].update()
window.setTimeout(tabManagers[tabId].update(),1000);
return return
} }
} }
@ -132,14 +132,30 @@ class Controller {
this.messageID = 0; this.messageID = 0;
this.lastMessage = 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 // the tab has been updated, we need to run the script
update() { update() {
if (this.alreadyQueued) {
}
else {
this.alreadyQueued = true;
setTimeout(() => { this.actuallyupdate(); },800);
//this.actuallyupdate();
}
}
actuallyupdate() {
this.messageID++; this.messageID++;
//console.log("Update! Our page is " + this.page + ", our tab id " + this.tabId) //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 // an actual update message from the script has arrived
@ -184,16 +200,22 @@ class Controller {
else if (artist != this.currentArtist || title != this.currentTitle) { else if (artist != this.currentArtist || title != this.currentTitle) {
//first inform ourselves that the previous track has now been stopped for good //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 //then initialize new playback
console.log("New track") console.log("New track");
this.setUpdate() this.setUpdate();
this.alreadyPlayed = 0 this.alreadyPlayed = 0;
this.currentTitle = title this.currentTitle = title;
this.currentArtist = artist this.currentArtist = artist;
this.currentLength = seconds if (Number.isInteger(seconds)) {
console.log(artist + " - " + title + " is playing!") this.currentLength = seconds;
this.currentlyPlaying = true }
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", "name": "Maloja Scrobbler",
"version": "1.1", "version": "1.2",
"description": "Scrobbles tracks from various sites to your Maloja server", "description": "Scrobbles tracks from various sites to your Maloja server",
"manifest_version": 2, "manifest_version": 2,
"permissions": ["activeTab", "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)
}