From 9c2cea16628e3496f9e74f9f59ec36df3da1054d Mon Sep 17 00:00:00 2001
From: Markil3 <75867393+Markil3@users.noreply.github.com>
Date: Tue, 17 Aug 2021 01:23:23 -0600
Subject: [PATCH] Adds full support for YouTube playback.
While a few optimizations could be made, YouTube playback is almost as seamless as local playback.
---
add-on/background.js | 47 +++++--
add-on/foreground.js | 10 ++
add-on/youtube.js | 45 ++++++-
.../universeplayer/addon/BrowserLink.java | 10 +-
.../edu/regis/universeplayer/addon/Main.java | 4 +
addonInter/src/main/resources/log4j2.xml | 2 +-
.../regis/universeplayer/PlaybackInfo.java | 63 +++++++++
.../browserCommands/MessageHandler.java | 44 ++++++-
.../browserCommands/MessageRunner.java | 97 +++++++++++---
.../browserCommands/UpdateListener.java | 25 ++++
.../edu/regis/universeplayer/data/Album.java | 2 +-
.../universeplayer/data}/InternetSong.java | 4 +-
.../regis/universeplayer/data/LocalSong.java | 0
.../edu/regis/universeplayer/data/Song.java | 2 +-
.../universeplayer/PlaybackListener.java | 65 +---------
.../regis/universeplayer/browser/Browser.java | 54 +++++---
.../data/InternetSongProvider.java | 1 -
.../regis/universeplayer/data/MP3Song.java | 120 ------------------
.../universeplayer/data/PlaybackEvent.java | 27 ++++
.../localPlayer/LocalPlayer.java | 12 +-
.../universeplayer/player/Interface.java | 4 +-
.../player/InternetSongDialog.java | 2 +-
.../universeplayer/player/PlayerControls.java | 9 +-
23 files changed, 388 insertions(+), 261 deletions(-)
create mode 100644 browserCommands/src/main/java/edu/regis/universeplayer/PlaybackInfo.java
create mode 100644 browserCommands/src/main/java/edu/regis/universeplayer/browserCommands/UpdateListener.java
rename {interface => browserCommands}/src/main/java/edu/regis/universeplayer/data/Album.java (94%)
rename {interface/src/main/java/edu/regis/universeplayer/browser => browserCommands/src/main/java/edu/regis/universeplayer/data}/InternetSong.java (64%)
rename {interface => browserCommands}/src/main/java/edu/regis/universeplayer/data/LocalSong.java (100%)
rename {interface => browserCommands}/src/main/java/edu/regis/universeplayer/data/Song.java (94%)
delete mode 100644 interface/src/main/java/edu/regis/universeplayer/data/MP3Song.java
create mode 100644 interface/src/main/java/edu/regis/universeplayer/data/PlaybackEvent.java
diff --git a/add-on/background.js b/add-on/background.js
index 6fbddc2..31b8cbf 100644
--- a/add-on/background.js
+++ b/add-on/background.js
@@ -4,13 +4,20 @@ var numTabMessages = 0;
/*
On startup, connect to the "ping_pong" app.
*/
-var port = browser.runtime.connectNative("universalmusic");
+var interfacePort = browser.runtime.connectNative("universalmusic");
var tabPort = null;
function setupTab(port)
{
- console.info("Tab " + port.sender.tab.url + " loaded.");
+ if (port.sender)
+ {
+ console.info("Tab " + port.sender.tab.url + " loaded.");
+ }
+ else
+ {
+ console.info("Tab loaded");
+ }
tabPort = port;
tabPort.onDisconnect.addListener(e => {
tabPort = null;
@@ -25,9 +32,21 @@ function setupTab(port)
browser.tabs.remove(e.sender.tab.id);
});
tabPort.onMessage.addListener(message => {
- if (typeof message == "object" && message.type == "response")
+ if (typeof message == "object")
{
- tabMessages.set(message.num, message.data);
+ if (message.type == "response")
+ {
+ tabMessages.set(message.num, message.data);
+ }
+ else if (message.type == "update")
+ {
+ returnValue = {
+ "messageNum": -1,
+ "message": message.data
+ }
+ console.trace("Sending update ", returnValue)
+ interfacePort.postMessage(returnValue);
+ }
}
});
return true;
@@ -70,15 +89,20 @@ var listeners = [function (message, returnValue) {
}
switch (type)
{
- case "CommandSong":
- returnValue = () => browser.tabs.create({url: message.song}).then(setupTab);
+ case "CommandLoadSong":
+ returnValue = () => {
+ if (message.song)
+ {
+ browser.tabs.create({url: message.song});
+ }
+ }
if (tabPort)
{
console.log("Replacing tab");
/*
* Closes the existing tab first
*/
- returnValue = browser.tabs.remove(e.sender.tab.id).then(returnValue);
+ returnValue = browser.tabs.remove(tabPort.sender.tab.id).then(returnValue);
}
else
{
@@ -89,7 +113,7 @@ var listeners = [function (message, returnValue) {
case "QueryStatus":
if (!tabPort)
{
- returnValue = 4;
+ returnValue = "EMPTY";
break;
}
case "QueryTime":
@@ -139,6 +163,7 @@ function handleMessage(message)
{
returnValue = listeners[index](message, returnValue);
}
+ console.debug("Message returned ", returnValue);
if (returnValue instanceof Promise)
{
returnValue.then(resolve).catch(reject);
@@ -158,7 +183,7 @@ function handleMessage(message)
/*
* Listen for messages from the app.
*/
-port.onMessage.addListener((message) => {
+interfacePort.onMessage.addListener((message) => {
console.log("Received from interface: ", message);
handleMessage(message.message).then((response) => {
@@ -175,9 +200,9 @@ port.onMessage.addListener((message) => {
}
}
console.log("Sending ", returnValue)
- port.postMessage(returnValue);
+ interfacePort.postMessage(returnValue);
}).catch(error => {
-
+ console.error("Error in evaluating message: ", error)
});
});
diff --git a/add-on/foreground.js b/add-on/foreground.js
index 74400f1..db6ec11 100644
--- a/add-on/foreground.js
+++ b/add-on/foreground.js
@@ -34,6 +34,16 @@ function handleMessage(message)
}
}
+function onStatusUpdate(status, time, songData)
+{
+ sendUpdate({
+ type: "edu.regis.universeplayer.PlaybackInfo",
+ currentSong: songData,
+ status: status,
+ playTime: time
+ });
+}
+
function sendUpdate(response)
{
post = data => background.postMessage({
diff --git a/add-on/youtube.js b/add-on/youtube.js
index 7dfcd87..0218fd1 100644
--- a/add-on/youtube.js
+++ b/add-on/youtube.js
@@ -3,26 +3,51 @@ var video;
function onload()
{
- video = document.getElementsByClassName('video-stream html5-main-video')[0]
+ video = document.getElementsByClassName('video-stream html5-main-video')[0];
+ statusUpdate = e => {
+ return onStatusUpdate(getState(), e.srcElement.currentTime, getSongData());
+ };
+// video.addListener("timeupdate", statusUpdate);
+// video.addListener("play", statusUpdate);
+// video.addListener("pause", statusUpdate);
+// video.addListener("ended", statusUpdate);
+ video.ontimeupdate = statusUpdate;
+ video.onplay = statusUpdate;
+ video.onpause = statusUpdate;
+ video.onended = statusUpdate;
+}
+
+function getSongData()
+{
+ return {
+ type: "edu.regis.universeplayer.browser.InternetSong",
+ location: "window.location.href",
+ title: getTitle(),
+ artists: getArtists(),
+ trackNum: 0,
+ discNum: 0,
+ duration: parseInt(getLength() * 1000),
+ album: null
+ }
}
function getState()
{
if (!video)
{
- return 4;
+ return "EMPTY";
}
else if (video.ended)
{
- return 3;
+ return "FINISHED";
}
else if (video.paused)
{
- return 1;
+ return "PAUSED";
}
else
{
- return 0;
+ return "PLAYING";
}
}
@@ -36,6 +61,16 @@ function getLength()
return video.duration;
}
+function getTitle()
+{
+ return document.getElementsByTagName("meta").title.content;
+}
+
+function getArtists()
+{
+ return [document.getElementById("channel-name").getElementsByTagName("a")[0].text];
+}
+
function play()
{
if (video != null)
diff --git a/addonInter/src/main/java/edu/regis/universeplayer/addon/BrowserLink.java b/addonInter/src/main/java/edu/regis/universeplayer/addon/BrowserLink.java
index 36e4291..f9eee2e 100644
--- a/addonInter/src/main/java/edu/regis/universeplayer/addon/BrowserLink.java
+++ b/addonInter/src/main/java/edu/regis/universeplayer/addon/BrowserLink.java
@@ -219,7 +219,15 @@ public class BrowserLink extends MessageRunner
if (clazz != null)
{
- return gson.fromJson(message, clazz);
+ try
+ {
+ return gson.fromJson(message, clazz);
+ }
+ catch (Exception e)
+ {
+ logger.error("Could not parse " + message, e);
+ throw e;
+ }
}
else
{
diff --git a/addonInter/src/main/java/edu/regis/universeplayer/addon/Main.java b/addonInter/src/main/java/edu/regis/universeplayer/addon/Main.java
index 9c38a50..f24e8a3 100644
--- a/addonInter/src/main/java/edu/regis/universeplayer/addon/Main.java
+++ b/addonInter/src/main/java/edu/regis/universeplayer/addon/Main.java
@@ -52,6 +52,10 @@ public class Main
* Pretty much just forwards any messages to the browser and
* returns their value.
*/
+ browserLink.addUpdateListener((update, link) -> {
+ logger.debug("Sending update {}", update);
+ interfaceLink.sendUpdate(update);
+ });
interfaceLink.addListener((providedValue, previousReturn) -> {
logger.debug("Forwarding message to browser: {}", providedValue);
Object returnValue = browserLink.sendObject(providedValue).get();
diff --git a/addonInter/src/main/resources/log4j2.xml b/addonInter/src/main/resources/log4j2.xml
index 35282cb..3e4c214 100644
--- a/addonInter/src/main/resources/log4j2.xml
+++ b/addonInter/src/main/resources/log4j2.xml
@@ -21,6 +21,6 @@
-
+
\ No newline at end of file
diff --git a/browserCommands/src/main/java/edu/regis/universeplayer/PlaybackInfo.java b/browserCommands/src/main/java/edu/regis/universeplayer/PlaybackInfo.java
new file mode 100644
index 0000000..b42c737
--- /dev/null
+++ b/browserCommands/src/main/java/edu/regis/universeplayer/PlaybackInfo.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2021 William Hubbard. All Rights Reserved.
+ */
+
+package edu.regis.universeplayer;
+
+import edu.regis.universeplayer.PlaybackStatus;
+import edu.regis.universeplayer.data.Song;
+
+import java.io.Serializable;
+import java.util.EventObject;
+
+public class PlaybackInfo implements Serializable
+{
+ private final Song currentSong;
+ /**
+ * The time we are currently at in the song, in seconds.
+ */
+ private final float playTime;
+
+ /**
+ * The current status of the player.
+ */
+ private final PlaybackStatus status;
+
+ public PlaybackInfo(Song song, float playTime, PlaybackStatus status)
+ {
+ this.currentSong = song;
+ this.playTime = playTime;
+ this.status = status;
+ }
+
+ /**
+ * Obtains the song currently playing.
+ *
+ * @return The current song, or null if none is loaded.
+ */
+ public Song getSong()
+ {
+ return this.currentSong;
+ }
+
+ /**
+ * Obtains the current play time.
+ *
+ * @return The play time of the player. This will be zero if the song is
+ * stopped or not loaded.
+ */
+ public float getPlayTime()
+ {
+ return this.playTime;
+ }
+
+ /**
+ * Obtains the current status of the player.
+ *
+ * @return - The player's playback status.
+ */
+ public PlaybackStatus getStatus()
+ {
+ return this.status;
+ }
+}
diff --git a/browserCommands/src/main/java/edu/regis/universeplayer/browserCommands/MessageHandler.java b/browserCommands/src/main/java/edu/regis/universeplayer/browserCommands/MessageHandler.java
index da2a521..02ad1ff 100644
--- a/browserCommands/src/main/java/edu/regis/universeplayer/browserCommands/MessageHandler.java
+++ b/browserCommands/src/main/java/edu/regis/universeplayer/browserCommands/MessageHandler.java
@@ -9,10 +9,7 @@ import org.slf4j.LoggerFactory;
import java.io.*;
import java.nio.ByteBuffer;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.Map;
+import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -33,11 +30,12 @@ public class MessageHandler implements Runnable, MessageSerializer
private final ExecutorService executor;
private final LinkedList listeners = new LinkedList<>();
protected final HashMap> messageResponses = new HashMap<>();
+ protected final Queue