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:
parent
5aa92a1ab8
commit
b99cf8b77d
11
README.md
11
README.md
@ -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.
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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",
|
||||||
|
11
scrobblers/chromium-generic/sites/plex.js
Normal file
11
scrobblers/chromium-generic/sites/plex.js
Normal 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"
|
13
scrobblers/chromium-generic/sites/ytmusic.js
Normal file
13
scrobblers/chromium-generic/sites/ytmusic.js
Normal 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"
|
79
scrobblers/chromium-generic/sitescript.js
Normal file
79
scrobblers/chromium-generic/sitescript.js
Normal 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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)
|
|
||||||
}
|
|
@ -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)
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user