mirror of
				https://github.com/krateng/maloja.git
				synced 2023-08-10 21:12:55 +03:00 
			
		
		
		
	
		
			
				
	
	
		
			339 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			339 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 
 | |
| chrome.tabs.onUpdated.addListener(onTabUpdated);
 | |
| chrome.tabs.onRemoved.addListener(onTabRemoved);
 | |
| //chrome.tabs.onActivated.addListener(onTabChanged);
 | |
| chrome.runtime.onMessage.addListener(onInternalMessage);
 | |
| 
 | |
| tabManagers = {}
 | |
| 
 | |
| 
 | |
| const ALWAYS_SCROBBLE_SECONDS = 60*3;
 | |
| // Longer songs are always scrobbled when playing at least 2 minutes
 | |
| 
 | |
| pages = {
 | |
| 	"Plex Web":{
 | |
| 		"patterns":[
 | |
| 			"https://app.plex.tv",
 | |
| 			"http://app.plex.tv",
 | |
| 			"https://plex.",
 | |
| 			"http://plex."
 | |
| 		],
 | |
| 		"script":"plex.js"
 | |
| 	},
 | |
| 	"YouTube Music":{
 | |
| 		"patterns":[
 | |
| 			"https://music.youtube.com"
 | |
| 		],
 | |
| 		"script":"ytmusic.js"
 | |
| 	},
 | |
| 	"Spotify Web":{
 | |
| 		"patterns":[
 | |
| 			"https://open.spotify.com"
 | |
| 		],
 | |
| 		"script":"spotify.js"
 | |
| 	},
 | |
| 	"Bandcamp":{
 | |
| 		"patterns":[
 | |
| 			"bandcamp.com"
 | |
| 		],
 | |
| 		"script":"bandcamp.js"
 | |
| 	},
 | |
| 	"Soundcloud":{
 | |
| 		"patterns":[
 | |
| 			"https://soundcloud.com"
 | |
| 		],
 | |
| 		"script":"soundcloud.js"
 | |
| 	},
 | |
| 	"Navidrome":{
 | |
| 		"patterns":[
 | |
| 			"https://navidrome.",
 | |
| 			"http://navidrome."
 | |
| 		],
 | |
| 		"script":"navidrome.js"
 | |
| 	}
 | |
| 
 | |
| }
 | |
| 
 | |
| function updateTabNum() {
 | |
| 
 | |
| 	var amount = Object.keys(tabManagers).length;
 | |
| 	chrome.browserAction.setBadgeText({"text":amount.toString()});
 | |
| 	chrome.browserAction.setBadgeBackgroundColor({"color":"#333337"});
 | |
| }
 | |
| 
 | |
| 
 | |
| 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.includes(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.includes(patterns[i])) {
 | |
| 					console.log("New page on tab " + tabId + " will be handled by new " + key + " manager!");
 | |
| 					tabManagers[tabId] = new Controller(tabId,key);
 | |
| 					updateTabNum();
 | |
| 					return
 | |
| 					//chrome.tabs.executeScript(tab.id,{"file":"sitescripts/" + pages[key]["script"]})
 | |
| 
 | |
| 
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 
 | |
| function onTabRemoved(tabId,removeInfo) {
 | |
| 
 | |
| 	if (tabManagers.hasOwnProperty(tabId)) {
 | |
| 		page = tabManagers[tabId].page;
 | |
| 		console.log("closed tab was " + page + ", now removing manager");
 | |
| 		tabManagers[tabId].stopPlayback("",""); //in case we want to scrobble the playing track
 | |
| 		delete tabManagers[tabId];
 | |
| 		updateTabNum();
 | |
| 	}
 | |
| 
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| function onInternalMessage(request,sender) {
 | |
| 	// message from settings menu
 | |
| 	if (request.type == "query") {
 | |
| 		answer = [];
 | |
| 		for (tabId in tabManagers) {
 | |
| 			manager = tabManagers[tabId]
 | |
| 			if (manager.currentlyPlaying) {
 | |
| 				answer.push([manager.page,manager.currentArtist,manager.currentTitle]);
 | |
| 			}
 | |
| 			else {
 | |
| 				answer.push([manager.page,null]);
 | |
| 			}
 | |
| 
 | |
| 		}
 | |
| 		chrome.runtime.sendMessage({"type":"response","content":answer})
 | |
| 	}
 | |
| 
 | |
| 	//message from content script
 | |
| 	if (request.type == "startPlayback" || request.type == "stopPlayback") {
 | |
| 		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);
 | |
| 
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 
 | |
| }
 | |
| 
 | |
| 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.lastMessage = 0;
 | |
| 
 | |
| 		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)
 | |
| 		try {
 | |
| 			chrome.tabs.executeScript(this.tabId,{"file":"sites/" + pages[this.page]["script"]});
 | |
| 			chrome.tabs.executeScript(this.tabId,{"file":"sitescript.js"});
 | |
| 		}
 | |
| 		catch (e) {
 | |
| 			console.log("Could not run site script. Tab probably closed or something idk.")
 | |
| 		}
 | |
| 
 | |
| 		this.alreadyQueued = false;
 | |
| 	}
 | |
| 
 | |
| 	// an actual update message from the script has arrived
 | |
| 	playbackUpdate(request) {
 | |
| 
 | |
| 		if (request.time < self.lastMessage) {
 | |
| 			console.log("Got message out of order, discarding!")
 | |
| 			return
 | |
| 		}
 | |
| 		self.lastMessage = request.time
 | |
| 
 | |
| 		//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);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	backlog_scrobble() {
 | |
| 		while (this.alreadyPlayed > this.currentLength) {
 | |
| 			this.alreadyPlayed = this.alreadyPlayed - this.currentLength
 | |
| 			var secondsago = this.alreadyPlayed
 | |
| 			scrobble(this.currentArtist,this.currentTitle,this.currentLength,secondsago)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 	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?
 | |
| 			this.backlog_scrobble()
 | |
| 
 | |
| 			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;
 | |
| 			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;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// 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?
 | |
| 		this.backlog_scrobble()
 | |
| 
 | |
| 		this.currentlyPlaying = false
 | |
| 
 | |
| 
 | |
| 
 | |
| 		//ONLY CASE 2: Playback ended
 | |
| 		if (artist != this.currentArtist || title != this.currentTitle) {
 | |
| 			if ((this.alreadyPlayed > this.currentLength / 2) || (this.alreadyPlayed > ALWAYS_SCROBBLE_SECONDS)) {
 | |
| 				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,secondsago=0) {
 | |
| 	console.log("Scrobbling " + artist + " - " + title + "; " + seconds + " seconds playtime, " + secondsago + " seconds ago")
 | |
| 	var d = new Date()
 | |
| 	var time = Math.floor(d.getTime()/1000) - secondsago
 | |
| 	//console.log("Time: " + time)
 | |
| 	var payload = {
 | |
| 		"artist":artist,
 | |
| 		"title":title,
 | |
| 		"duration":seconds,
 | |
| 		"time":time
 | |
| 	}
 | |
| 
 | |
| 	chrome.storage.local.get(["serverurl","apikey"],function(result) {
 | |
| 		payload["key"] = result["apikey"];
 | |
| 		var xhttp = new XMLHttpRequest();
 | |
| 		xhttp.open("POST",result["serverurl"] + "/apis/mlj_1/newscrobble",true);
 | |
| 		xhttp.setRequestHeader("Content-Type", "application/json");
 | |
| 		//xhttp.send(requestbody + "&key=" + APIKEY)
 | |
| 		var body = JSON.stringify(payload);
 | |
| 		xhttp.send(body)
 | |
| 		//console.log("Sent: " + body)
 | |
| 	});
 | |
| 
 | |
| 
 | |
| }
 | 
