mirror of
https://github.com/krateng/maloja.git
synced 2023-08-10 21:12:55 +03:00
Added generic extendible Chromium scrobbler
This commit is contained in:
parent
669007bd19
commit
18e8114016
237
scrobblers/chromium-generic/background.js
Normal file
237
scrobblers/chromium-generic/background.js
Normal file
@ -0,0 +1,237 @@
|
|||||||
|
|
||||||
|
|
||||||
|
chrome.tabs.onUpdated.addListener(onTabUpdated);
|
||||||
|
chrome.tabs.onRemoved.addListener(onTabRemoved);
|
||||||
|
//chrome.tabs.onActivated.addListener(onTabChanged);
|
||||||
|
chrome.runtime.onMessage.addListener(onPlaybackUpdate);
|
||||||
|
|
||||||
|
tabManagers = {}
|
||||||
|
|
||||||
|
pages = {
|
||||||
|
"plex":{
|
||||||
|
"patterns":[
|
||||||
|
"https://app.plex.tv",
|
||||||
|
"http://app.plex.tv",
|
||||||
|
"https://plex.",
|
||||||
|
"http://plex."
|
||||||
|
],
|
||||||
|
"script":"plex.js"
|
||||||
|
},
|
||||||
|
"youtube_music":{
|
||||||
|
"patterns":[
|
||||||
|
"https://music.youtube.com",
|
||||||
|
"http://music.youtube.com"
|
||||||
|
],
|
||||||
|
"script":"ytmusic.js"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function onTabUpdated(tabId, changeInfo, tab) {
|
||||||
|
|
||||||
|
|
||||||
|
// still same page?
|
||||||
|
//console.log("Update to tab " + tabId + "!")
|
||||||
|
if (tabManagers.hasOwnProperty(tabId)) {
|
||||||
|
//console.log("Yes!")
|
||||||
|
page = tabManagers[tabId].page
|
||||||
|
patterns = pages[page]["patterns"]
|
||||||
|
//console.log("Page was managed by a " + page + " manager")
|
||||||
|
for (var i=0;i<patterns.length;i++) {
|
||||||
|
if (tab.url.startsWith(patterns[i])) {
|
||||||
|
//console.log("Still on same page!")
|
||||||
|
tabManagers[tabId].update()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log("Page on tab " + tabId + " changed, removing old " + page + " manager!")
|
||||||
|
delete tabManagers[tabId]
|
||||||
|
}
|
||||||
|
|
||||||
|
//check if pattern matches
|
||||||
|
for (var key in pages) {
|
||||||
|
if (pages.hasOwnProperty(key)) {
|
||||||
|
patterns = pages[key]["patterns"]
|
||||||
|
for (var i=0;i<patterns.length;i++) {
|
||||||
|
if (tab.url.startsWith(patterns[i])) {
|
||||||
|
console.log("New page on tab " + tabId + " will be handled by new " + key + " manager!")
|
||||||
|
tabManagers[tabId] = new Controller(tabId,key)
|
||||||
|
return
|
||||||
|
//chrome.tabs.executeScript(tab.id,{"file":"sitescripts/" + pages[key]["script"]})
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function onTabRemoved(tabId,removeInfo) {
|
||||||
|
|
||||||
|
console.log(tabId + " closed")
|
||||||
|
if (tabManagers.hasOwnProperty(tabId)) {
|
||||||
|
page = tabManagers[tabId].page
|
||||||
|
console.log("this tab was " + page + ", now removing manager")
|
||||||
|
tabManagers[tabId].stopPlayback("","") //in case we want to scrobble the playing track
|
||||||
|
delete tabManagers[tabId]
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function onPlaybackUpdate(request,sender) {
|
||||||
|
tabId = sender.tab.id
|
||||||
|
//console.log("Message was sent from tab id " + tabId)
|
||||||
|
if (tabManagers.hasOwnProperty(tabId)) {
|
||||||
|
//console.log("This is managed! Seems to be " + tabManagers[tabId].page)
|
||||||
|
tabManagers[tabId].playbackUpdate(request)
|
||||||
|
|
||||||
|
}
|
||||||
|
//console.log("Got update from Plex Web!")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class Controller {
|
||||||
|
|
||||||
|
constructor(tabId,page) {
|
||||||
|
this.tabId = tabId;
|
||||||
|
this.page = page;
|
||||||
|
|
||||||
|
this.currentTitle;
|
||||||
|
this.currentArtist;
|
||||||
|
this.currentLength;
|
||||||
|
this.alreadyPlayed;
|
||||||
|
this.currentlyPlaying = false;
|
||||||
|
this.lastUpdate = 0;
|
||||||
|
this.alreadyScrobbled = false;
|
||||||
|
|
||||||
|
this.messageID = 0;
|
||||||
|
|
||||||
|
this.update()
|
||||||
|
}
|
||||||
|
|
||||||
|
// the tab has been updated, we need to run the script
|
||||||
|
update() {
|
||||||
|
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"]})
|
||||||
|
}
|
||||||
|
|
||||||
|
// an actual update message from the script has arrived
|
||||||
|
playbackUpdate(request) {
|
||||||
|
//console.log("Update message from our tab " + this.tabId + " (" + this.page + ")")
|
||||||
|
if (request.type == "stopPlayback" && this.currentlyPlaying) {
|
||||||
|
this.stopPlayback(request.artist,request.title);
|
||||||
|
}
|
||||||
|
else if (request.type == "startPlayback") {
|
||||||
|
this.startPlayback(request.artist,request.title,request.duration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
startPlayback(artist,title,seconds) {
|
||||||
|
|
||||||
|
// CASE 1: Resuming playback of previously played title
|
||||||
|
if (artist == this.currentArtist && title == this.currentTitle && !this.currentlyPlaying) {
|
||||||
|
console.log("Resuming playback of " + this.currentTitle)
|
||||||
|
|
||||||
|
// Already played full song
|
||||||
|
while (this.alreadyPlayed > this.currentLength) {
|
||||||
|
this.alreadyPlayed = this.alreadyPlayed - this.currentLength
|
||||||
|
scrobble(this.currentArtist,this.currentTitle,this.currentLength)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setUpdate()
|
||||||
|
this.currentlyPlaying = true
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// CASE 2: New track is being played
|
||||||
|
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)
|
||||||
|
//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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// the artist and title arguments are not attributes of the track being stopped, but of the track active now
|
||||||
|
// they are here to recognize whether the playback has been paused or completely ended / replaced
|
||||||
|
stopPlayback(artist,title) {
|
||||||
|
|
||||||
|
//CASE 1: Playback just paused OR CASE 2: Playback ended
|
||||||
|
if (this.currentlyPlaying) {
|
||||||
|
var d = this.setUpdate()
|
||||||
|
this.alreadyPlayed = this.alreadyPlayed + d
|
||||||
|
console.log(d + " seconds played since last update, " + this.alreadyPlayed + " seconds played overall")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Already played full song
|
||||||
|
while (this.alreadyPlayed > this.currentLength) {
|
||||||
|
this.alreadyPlayed = this.alreadyPlayed - this.currentLength
|
||||||
|
scrobble(this.currentArtist,this.currentTitle,this.currentLength)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.currentlyPlaying = false
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//ONLY CASE 2: Playback ended
|
||||||
|
if (artist != this.currentArtist || title != this.currentTitle) {
|
||||||
|
if (this.alreadyPlayed > this.currentLength / 2) {
|
||||||
|
scrobble(this.currentArtist,this.currentTitle,this.alreadyPlayed)
|
||||||
|
this.alreadyPlayed = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// sets last updated to now and returns how long since then
|
||||||
|
setUpdate() {
|
||||||
|
var d = new Date()
|
||||||
|
var t = Math.floor(d.getTime()/1000)
|
||||||
|
var delta = t - this.lastUpdate
|
||||||
|
this.lastUpdate = t
|
||||||
|
return delta
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function scrobble(artist,title,seconds) {
|
||||||
|
console.log("Scrobbling " + artist + " - " + title + "; " + seconds + " seconds playtime")
|
||||||
|
artiststring = encodeURIComponent(artist)
|
||||||
|
titlestring = encodeURIComponent(title)
|
||||||
|
chrome.storage.local.get("apikey",function(result) {
|
||||||
|
APIKEY = result["apikey"]
|
||||||
|
chrome.storage.local.get("serverurl",function(result) {
|
||||||
|
URL = result["serverurl"]
|
||||||
|
var xhttp = new XMLHttpRequest();
|
||||||
|
xhttp.open("POST",URL + "/db/newscrobble",true);
|
||||||
|
xhttp.send("artist=" + artiststring + "&title=" + titlestring + "&duration=" + seconds + "&key=" + APIKEY)
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
}
|
BIN
scrobblers/chromium-generic/icon128.png
Normal file
BIN
scrobblers/chromium-generic/icon128.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.3 KiB |
BIN
scrobblers/chromium-generic/icon256.png
Normal file
BIN
scrobblers/chromium-generic/icon256.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
BIN
scrobblers/chromium-generic/icon48.png
Normal file
BIN
scrobblers/chromium-generic/icon48.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.7 KiB |
38
scrobblers/chromium-generic/manifest.json
Normal file
38
scrobblers/chromium-generic/manifest.json
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"name": "Maloja Scrobbler",
|
||||||
|
"version": "0.1",
|
||||||
|
"description": "Scrobbles tracks from various sites to your Maloja server",
|
||||||
|
"manifest_version": 2,
|
||||||
|
"permissions": ["activeTab",
|
||||||
|
"declarativeContent",
|
||||||
|
"tabs",
|
||||||
|
"storage",
|
||||||
|
"http://*/",
|
||||||
|
"https://*/",
|
||||||
|
"<all_urls>"
|
||||||
|
],
|
||||||
|
"background":
|
||||||
|
{
|
||||||
|
"scripts":
|
||||||
|
[
|
||||||
|
"background.js"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
"browser_action":
|
||||||
|
{
|
||||||
|
"default_icon":
|
||||||
|
{
|
||||||
|
"128":"icon128.png",
|
||||||
|
"48":"icon48.png"
|
||||||
|
},
|
||||||
|
"default_popup": "settings.html",
|
||||||
|
"default_title": "Settings"
|
||||||
|
},
|
||||||
|
"icons":
|
||||||
|
{
|
||||||
|
"128":"icon128.png",
|
||||||
|
"48":"icon48.png"
|
||||||
|
}
|
||||||
|
}
|
31
scrobblers/chromium-generic/settings.html
Normal file
31
scrobblers/chromium-generic/settings.html
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
<!doctype html />
|
||||||
|
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Wat</title>
|
||||||
|
<script type="text/javascript" src="settings.js"></script>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<style>
|
||||||
|
@import url('https://fonts.googleapis.com/css?family=Ubuntu');
|
||||||
|
|
||||||
|
body {
|
||||||
|
width:300px;
|
||||||
|
background-color:#333337;
|
||||||
|
color:beige;
|
||||||
|
font-family:'Ubuntu';
|
||||||
|
}
|
||||||
|
input {
|
||||||
|
width:270px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="wat">
|
||||||
|
<span id="checkmark_url"></span> <span style="line-height:20px">Server:</span><br />
|
||||||
|
<input type="text" id="serverurl" value="http://localhost:42010" />
|
||||||
|
<br /><br />
|
||||||
|
<span id="checkmark_key"></span> <span style="line-height:20px">API key:</span><br />
|
||||||
|
<input type="text" id="apikey" />
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
77
scrobblers/chromium-generic/settings.js
Normal file
77
scrobblers/chromium-generic/settings.js
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
|
||||||
|
document.addEventListener("DOMContentLoaded",function() {
|
||||||
|
document.getElementById("serverurl").addEventListener("input",updateServer);
|
||||||
|
document.getElementById("apikey").addEventListener("input",updateAPIKey);
|
||||||
|
|
||||||
|
document.getElementById("serverurl").addEventListener("change",checkServer);
|
||||||
|
document.getElementById("apikey").addEventListener("change",checkServer);
|
||||||
|
|
||||||
|
|
||||||
|
chrome.storage.local.get({"serverurl":"http://localhost:42010"},function(result) {
|
||||||
|
document.getElementById("serverurl").value = result["serverurl"]
|
||||||
|
checkServer()
|
||||||
|
});
|
||||||
|
chrome.storage.local.get({"apikey":"BlackPinkInYourArea"},function(result) {
|
||||||
|
document.getElementById("apikey").value = result["apikey"]
|
||||||
|
checkServer()
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function updateServer() {
|
||||||
|
|
||||||
|
text = document.getElementById("serverurl").value
|
||||||
|
|
||||||
|
|
||||||
|
chrome.storage.local.set({"serverurl":text})
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateAPIKey() {
|
||||||
|
text = document.getElementById("apikey").value
|
||||||
|
chrome.storage.local.set({"apikey":text})
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkServer() {
|
||||||
|
url = document.getElementById("serverurl").value + "/db/test?key=" + document.getElementById("apikey").value
|
||||||
|
|
||||||
|
var xhttp = new XMLHttpRequest();
|
||||||
|
xhttp.onreadystatechange = createCheckmarks;
|
||||||
|
try {
|
||||||
|
xhttp.open("GET",url,true);
|
||||||
|
xhttp.send();
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
//document.getElementById("checkmark_url").innerHTML = "❌"
|
||||||
|
//document.getElementById("checkmark_key").innerHTML = "❌"
|
||||||
|
document.getElementById("serverurl").style.backgroundColor = "red"
|
||||||
|
document.getElementById("apikey").style.backgroundColor = "red"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function createCheckmarks() {
|
||||||
|
if (this.readyState == 4) {
|
||||||
|
if ((this.status == 204) || (this.status == 205)) {
|
||||||
|
//document.getElementById("checkmark_url").innerHTML = "✔️"
|
||||||
|
//document.getElementById("checkmark_key").innerHTML = "✔️"
|
||||||
|
document.getElementById("serverurl").style.backgroundColor = "lawngreen"
|
||||||
|
document.getElementById("apikey").style.backgroundColor = "lawngreen"
|
||||||
|
}
|
||||||
|
else if (this.status == 403) {
|
||||||
|
//document.getElementById("checkmark_url").innerHTML = "✔️"
|
||||||
|
//document.getElementById("checkmark_key").innerHTML = "❌"
|
||||||
|
document.getElementById("serverurl").style.backgroundColor = "lawngreen"
|
||||||
|
document.getElementById("apikey").style.backgroundColor = "red"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
//document.getElementById("checkmark_url").innerHTML = "❌"
|
||||||
|
//document.getElementById("checkmark_key").innerHTML = "❌"
|
||||||
|
document.getElementById("serverurl").style.backgroundColor = "red"
|
||||||
|
document.getElementById("apikey").style.backgroundColor = "red"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
38
scrobblers/chromium-generic/sitescripts/plex.js
Normal file
38
scrobblers/chromium-generic/sitescripts/plex.js
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
bar = document.querySelector("div[class*=PlayerControls]")
|
||||||
|
if (bar == null) {
|
||||||
|
console.log("Nothing playing right now!")
|
||||||
|
chrome.runtime.sendMessage({type:"stopPlayback",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",artist:artist,title:title})
|
||||||
|
//stopPlayback()
|
||||||
|
}
|
||||||
|
else if (control == "Pause") {
|
||||||
|
console.log("Playing " + artist + " - " + title)
|
||||||
|
chrome.runtime.sendMessage({type:"startPlayback",artist:artist,title:title,duration:durationSeconds})
|
||||||
|
//startPlayback(artist,title,durationSeconds)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
40
scrobblers/chromium-generic/sitescripts/ytmusic.js
Normal file
40
scrobblers/chromium-generic/sitescripts/ytmusic.js
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
bar = document.querySelector("ytmusic-player-bar")
|
||||||
|
if (bar == null) {
|
||||||
|
console.log("Nothing playing right now!")
|
||||||
|
chrome.runtime.sendMessage({type:"stopPlayback",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")
|
||||||
|
artist = metadata.querySelector("span > span[class*=subtitle] > yt-formatted-string > a:nth-child(1)").innerHTML
|
||||||
|
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",artist:artist,title:title})
|
||||||
|
//stopPlayback()
|
||||||
|
}
|
||||||
|
else if (control == "Pause") {
|
||||||
|
console.log("Playing " + artist + " - " + title)
|
||||||
|
chrome.runtime.sendMessage({type:"startPlayback",artist:artist,title:title,duration:durationSeconds})
|
||||||
|
//startPlayback(artist,title,durationSeconds)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user