Compare commits

..

1 Commits

Author SHA1 Message Date
Christian Richter
239a5faa73 update readme
Some checks reported errors
continuous-integration/drone/pr Build was killed
Signed-off-by: Christian Richter <crichter@owncloud.com>
2022-07-18 15:27:24 +02:00
9 changed files with 201 additions and 285 deletions

View File

@@ -2,8 +2,8 @@ def main(ctx):
return [ return [
stepPR("amd64", "weirdradio"), stepPR("amd64", "weirdradio"),
stepPR("arm64", "weirdradio"), stepPR("arm64", "weirdradio"),
stepMergeMain("amd64", "weirdradio"), stepMergeMaster("amd64", "weirdradio"),
stepMergeMain("arm64", "weirdradio"), stepMergeMaster("arm64", "weirdradio"),
stepBuildWeekly("amd64", "weirdradio"), stepBuildWeekly("amd64", "weirdradio"),
stepBuildWeekly("arm64", "weirdradio"), stepBuildWeekly("arm64", "weirdradio"),
@@ -51,7 +51,7 @@ def notify(ctx):
], ],
"trigger": { "trigger": {
"ref": [ "ref": [
"refs/heads/main", "refs/heads/master",
"refs/heads/release*", "refs/heads/release*",
"refs/tags/**", "refs/tags/**",
"refs/pull/**", "refs/pull/**",
@@ -95,7 +95,7 @@ def stepPR(arch, path):
}, },
} }
def stepMergeMain(arch, path): def stepMergeMaster(arch, path):
return { return {
"kind": "pipeline", "kind": "pipeline",
"type": "docker", "type": "docker",
@@ -124,7 +124,7 @@ def stepMergeMain(arch, path):
], ],
"trigger": { "trigger": {
"ref": [ "ref": [
"refs/heads/main", "refs/heads/master",
], ],
"status": [ "status": [
"success", "success",
@@ -162,7 +162,7 @@ def stepBuildWeekly(arch, path):
], ],
"trigger": { "trigger": {
"ref": [ "ref": [
"refs/heads/main", "refs/heads/master",
], ],
"event": [ "event": [
"cron" "cron"

1
.gitignore vendored
View File

@@ -1,4 +1,3 @@
**/*.json **/*.json
node_modules node_modules
.drone.yml .drone.yml
assets/baseurl.js

View File

@@ -1,6 +1,6 @@
FROM node:latest FROM node:latest
LABEL maintainer="dragonchaser <weirdradio@datenschmutz.space>" LABEL maintainer="dragonchaser <weirdradio@datenschmutz.space>"
RUN git clone https://git.wardrum.de/dragonchaser/weirdradio /weirdradio RUN git clone https://github.com/dragonchaser/weirdradio /weirdradio
WORKDIR /weirdradio WORKDIR /weirdradio
RUN npm install RUN npm install
CMD node weirdradio CMD node weirdradio

View File

@@ -1,12 +1,11 @@
[![Build Status](https://drone.services.datenschmutz.space/api/badges/dragonchaser/weirdradio/status.svg)](https://drone.services.datenschmutz.space/dragonchaser/weirdradio) [![Build Status](https://drone.services.datenschmutz.space/api/badges/dragonchaser/weirdradio/status.svg)](https://drone.services.datenschmutz.space/dr
# Weirdradio # Weirdradio
Weirdradio is a matrix bot that will queue youtube videos into a webfrontend for listening. Weirdradio is a matrix bot that will queue youtube videos into a webfrontend for listening.
This can be used as party juke box (especially in combination with the whatsapp or signal bridge). This can be used as party juke box (especially in combination with the whatsapp or signal bridge).
**NOTE:** The software is WIP, expect breaking changes, weird behaviour and gargoyle spitting wormholes!
## License ## License
MIT see [LICENSE](https://github.com/dragonchaser/matrix-feeder/blob/master/LICENSE) file in this repository. MIT see [LICENSE](https://github.com/dragonchaser/matrix-feeder/blob/master/LICENSE) file in this repository.
@@ -14,8 +13,8 @@ MIT see [LICENSE](https://github.com/dragonchaser/matrix-feeder/blob/master/LICE
## Run ## Run
``` ```
$> docker build . -t local/weirdradio:latest docker build . -t local/weirdradio:latest
$> docker run -it \ docker run -it \
-p8090:8090 \ -p8090:8090 \
-p8080:8080 \ -p8080:8080 \
-v $(pwd)/config:/weirdradio/config \ -v $(pwd)/config:/weirdradio/config \

View File

@@ -2,28 +2,200 @@
<head> <head>
<script <script
type="text/javascript" type="text/javascript"
src="https://www.youtube.com/player_api" src="http://www.youtube.com/player_api"
></script> ></script>
<script type="text/javascript" src="baseurl.js"></script>
<script type="text/javascript" src="logic.js"></script>
<link rel="stylesheet" href="style.css" />
<title>Weirdradio</title> <title>Weirdradio</title>
</head> </head>
<style>
div.body {
width: 1024px;
height: 768px;
border: solid 1px;
padding: 1pc;
}
div.leftcolumn {
width: 30%;
float: left;
}
div.title {
height: 20%;
}
div.playlist {
height: 80%;
}
div.rightcolumn {
width: 60%;
float: left;
}
#playerframe {
height: 100%;
width: 100%;
background-color: purple;
}
</style>
<body onload="connectSocket()"> <body onload="connectSocket()">
<div class="body"> <div class="body">
<div class="title"> <div class="leftcolumn">
Weirdradio
</div>
<div class="listeners">Listeners: <span id="listeners">1</span></div>
<div class="playerframe">
<div id="player"></div>
</div>
<div class="playlistframe">
<div class="title"> <div class="title">
Playlist <h1>Weirdradio</h1>
</div> </div>
<div id="playlist" class="playlist"></div> <div id="playlist" class="playlist"></div>
</div> </div>
<div class="rightcolumn">
<div class="playerframe">
<div id="player"></div>
</div>
</div>
</div> </div>
</body> </body>
<script>
var playlist = [];
var socketConnector = null;
var socketSemaphore = false;
var tag = document.createElement("script");
var currentVideoID = null;
var player = null;
tag.src = "https://www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName("script")[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
// 3. This function creates an <iframe> (and YouTube player)
// after the API code downloads.
var player;
function onYouTubeIframeAPIReady() {
currentVideoID = "Wch3gJG2GJ4";
player = new YT.Player("player", {
height: "390",
width: "640",
videoId: currentVideoID,
playerVars: {
playsinline: 1,
},
events: {
onReady: onPlayerReady,
onStateChange: onPlayerStateChange,
},
});
}
function onPlayerReady(event) {
event.target.playVideo();
}
var done = false;
function onPlayerStateChange(event) {
if (event.data == 0) {
console.log("playing stopped");
removeFromPlaylist(currentVideoID);
playNext();
}
//if (event.data == YT.PlayerState.PLAYING && !done) {
// setTimeout(stopVideo, 6000);
// done = true;
//}
}
function stopVideo() {
player.stopVideo();
}
function connectSocket() {
if (socketSemaphore) {
console.log(
"Another connection attempt already in progress, canceling!"
);
return;
}
console.log("Connecting to socket");
socketSemaphore = true;
baseUrl = window.location.host.replace("8080", "8090");
let socket = new WebSocket("ws://" + baseUrl);
socket.onopen = function (e) {
clearInterval(socketConnector);
socketConnector = null;
socketSemaphore = false;
console.log("[open] Connection established");
};
// TODO: on close and on error attempt reconnect
socket.onclose = function (event) {
if (event.wasClean) {
console.log(
`[close] Connection closed cleanly, code=${event.code} reason=${event.reason}`
);
} else {
// e.g. server process killed or network down
// event.code is usually 1006 in this case
// TODO: add some nice ui thingi to show reconnect
// TODO: also add some timeout function to repeat this until connect is successful, let it interact with 'onopen'
console.log("[close] Connection died, attempting reconnect");
if (!socketConnector) {
socketConnector = setInterval(connectSocket, 5000);
socketSemaphore = false;
}
}
};
socket.onmessage = function (event) {
data = JSON.parse(event.data);
if (data["videoId"] != undefined && data["videoId"] != null) {
addToPlaylist(data);
console.log(`[message] Data received from server: ${event.data}`);
} else {
console.log(
`[unparseable message] Data received from server ${event.data}`
);
}
};
socketSemaphore = false;
}
function removeFromPlaylist(videoId) {
tmp = [];
playlist.forEach((item) => {
if (item.videoId != videoId) {
tmp.push(item);
} else {
console.log(`removing ${videoId}`);
}
});
playlist = tmp;
// remove from DOM
rm = document.getElementById(videoId);
if (rm != null) {
rm.remove();
}
currentVideoID = null;
}
function playNext() {
console.log(playlist)
console.log(playlist[0].videoId)
if (playlist[0] != null && playlist[0] != "") {
player.loadVideoById(playlist[0].videoId);
player.playVideo();
}
}
function addToPlaylist(data) {
if (document.getElementById(data.videoId) == null) {
playlist.push(data);
pl = document.getElementById(
"playlist"
).innerHTML += `<div id="${data.videoId}"><div>${data.title}</div><div><a target="_blank" href="${data.link}">${data.link}</a></div></div>`;
if (player.getPlayerState() == YT.PlayerState.ENDED) {
player.loadVideoById(data["videoId"]);
player.playVideo();
currentVideoID = data["videoId"];
}
} else {
console.log(`item ${data.videoId} already exists in playlist skipping`);
}
}
//function myDebugger() {
// console.log(socketSemaphore);
// console.log(socketConnector);
// console.log("=================");
//}
//myDebugger = setInterval(myDebugger, 2000);
</script>
</html> </html>

View File

@@ -1,142 +0,0 @@
var playlist = [];
var socketConnector = null;
var socketSemaphore = false;
var tag = document.createElement("script");
var currentVideoID = null;
var player = null;
tag.src = "https://www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName("script")[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
// 3. This function creates an <iframe> (and YouTube player)
// after the API code downloads.
var player;
function onYouTubeIframeAPIReady() {
currentVideoID = "tbnLqRW9Ef0";
player = new YT.Player("player", {
//height: "390",
//width: "100%",
videoId: currentVideoID,
playerVars: {
playsinline: 1,
},
events: {
onReady: onPlayerReady,
onStateChange: onPlayerStateChange,
},
});
}
function onPlayerReady(event) {
event.target.playVideo();
}
var done = false;
function onPlayerStateChange(event) {
if (event.data == 0) {
playNext();
console.log("playing stopped");
}
//if (event.data == YT.PlayerState.PLAYING && !done) {
// setTimeout(stopVideo, 6000);
// done = true;
//}
}
function stopVideo() {
player.stopVideo();
}
function connectSocket() {
if (socketSemaphore) {
console.log("Another connection attempt already in progress, canceling!");
return;
}
console.log("Connecting to socket");
socketSemaphore = true;
let socket = new WebSocket(wsBaseUrl);
socket.onopen = function (e) {
clearInterval(socketConnector);
socketConnector = null;
socketSemaphore = false;
console.log("[open] Connection established");
};
// TODO: on close and on error attempt reconnect
socket.onclose = function (event) {
if (event.wasClean) {
console.log(
`[close] Connection closed cleanly, code=${event.code} reason=${event.reason}`
);
} else {
// e.g. server process killed or network down
// event.code is usually 1006 in this case
// TODO: add some nice ui thingi to show reconnect
// TODO: also add some timeout function to repeat this until connect is successful, let it interact with 'onopen'
console.log("[close] Connection died, attempting reconnect");
if (!socketConnector) {
socketConnector = setInterval(connectSocket, 5000);
socketSemaphore = false;
}
}
};
socket.onmessage = function (event) {
data = JSON.parse(event.data);
if (data["videoId"] != undefined && data["videoId"] != null) {
addToPlaylist(data);
updateListeners(data);
console.log(`[message] Data received from server: ${event.data}`);
} else {
console.log(
`[unparseable message] Data received from server ${event.data}`
);
}
};
socketSemaphore = false;
}
function updateListeners(data) {
document.getElementById("listeners").innerHTML = data.listeners;
}
function removeFromPlaylist(videoId) {
delete playlist[currentVideoID];
// remove from DOM
rm = document.getElementById(videoId);
if (rm != null) {
rm.remove();
}
currentVideoID = null;
}
function playNext() {
removeFromPlaylist(currentVideoID);
if (playlist[0] != null && playlist[0] != "") {
player.loadVideoById(playlist[0].videoId);
player.playVideo();
}
}
function addToPlaylist(data) {
if (document.getElementById(data.videoId) == null) {
playlist.push(data);
pl = document.getElementById(
"playlist"
).innerHTML += `<div class="playlistrow" id="${data.videoId}">
<div class="title">${data.title}</div>
<div class="ytlink">
<a target="_blank" href="${data.link}">${data.link}</a>
</div>
<div class="controls">
<div class="control" onclick="removeFromPlaylist('${data.videoId}')">Remove</div>
<div class="control">Play</div>
</div>
</div>`;
if (player.getPlayerState() == YT.PlayerState.ENDED) {
player.loadVideoById(data["videoId"]);
player.playVideo();
currentVideoID = data["videoId"];
}
} else {
console.log(`item ${data.videoId} already exists in playlist skipping`);
}
}

View File

@@ -1,79 +0,0 @@
body {
padding: 0;
margin: 0;
}
div.body {
position: relative;
width: 100%;
padding: 0;
margin: 0;
}
div.body > div.title {
padding: 1pc;
font-size: 2pc;
font-weight: bold;
float: left;
width: 50%;
}
div.body > div.listeners {
float: right;
width: 20%;
padding: 2pc 2pc 0 0;
font-size: xx-small;
font-weight: bold;
text-align: right;
}
div.playlistframe {
}
div.playlistframe > div.title {
padding: 1pc;
font-size: 1.5pc;
font-weight: bold;
}
div.playlist {
padding: 0;
}
div.playlist > div.playlistrow {
padding: 1pc;
position: relative;
}
div.playlist > div.playlistrow:nth-child(even) {
background: #fff;
}
div.playlist > div.playlistrow:nth-child(odd) {
background: #ddd;
}
div.playlist > div.playlistrow > div.title{
font-weight: bold;
font-size: medium;
}
div.playlist > div.playlistrow > div.ytlink{
font-size: xx-small;
}
div.playlist > div.playlistrow > div.controls {
position: absolute;
top: 0;
right: 0;
}
div.playlist > div.playlistrow > div.controls > div.control {
float: right;
background-color: #aaa;
margin: 1pc 0.5pc 0 0;
cursor: pointer;
}
div.playerframe {
}
#player {
width: 100%;
}

View File

@@ -1,9 +1,6 @@
{ {
"homeServerUrl": "https://matrix.your-homeserver.org", "homeServerUrl": "https://matrix.your-homeserver.org",
"accessToken": "youraccesstokencanbeobtainedthroughriot", "accessToken": "youraccesstokencanbeobtainedthroughriot",
"domain": "yourdomain.tld",
"webSocketDomain": "ws.yourdomain.tld",
"secure":true,
"storage": "config/bot.json", "storage": "config/bot.json",
"assetDir": "assets/", "assetDir": "assets/",
"webServerPort": "8080", "webServerPort": "8080",

View File

@@ -20,27 +20,14 @@ const client = new MatrixClient(
new SimpleFsStorageProvider(config.storage) new SimpleFsStorageProvider(config.storage)
); );
// write javascript for baseurl
if (config.secure) {
urlConfigData = `var baseUrl = "https://${config.domain}";\nvar wsBaseUrl = "wss://${config.webSocketDomain}";\n`;
} else {
urlConfigData = `var baseUrl = "http://${config.domain}";\nvar wsBaseUrl = "ws://${config.webSocketDomain}";\n`;
}
fs.writeFileSync(
config.assetDir + "/baseurl.js",
urlConfigData,
function (err) {
if (err) return console.log(err);
}
);
// load assets // load assets
console.log("Reading assets..."); console.log("Reading assets...")
let assets = []; let assets = [];
var files = fs.readdirSync(config.assetDir); var files = fs.readdirSync(config.assetDir);
files.forEach((name) => { files.forEach((name) => {
assets[name] = fs.readFileSync("assets/" + name); assets[name] = fs.readFileSync("assets/" + name);
}); });
console.log("[DONE]"); console.log("[DONE]")
// create server, this is for delivering the iframe page // create server, this is for delivering the iframe page
const webServer = http const webServer = http
.createServer((req, res) => { .createServer((req, res) => {
@@ -49,23 +36,7 @@ const webServer = http
reqFileName = "index.html"; reqFileName = "index.html";
} }
if (assets[reqFileName] != undefined && assets[reqFileName] != null) { if (assets[reqFileName] != undefined && assets[reqFileName] != null) {
extension = reqFileName.split(".").pop(); res.writeHead(200, { "Content-Type": "text/html" });
if (extension == "html" || extension == "html") {
res.writeHead(200, {
"Content-Type": "text/html",
"Access-Control-Allow-Origin": "https://" + config.webSocketDomain,
});
} else if (extension == "js") {
res.writeHead(200, {
"Content-Type": "text/javascript",
"Access-Control-Allow-Origin": "https://" + config.webSocketDomain,
});
} else {
res.writeHead(200, {
"Content-TYpe": "text/plain",
"Access-Control-Allow-Origin": "https://" + config.webSocketDomain,
});
}
res.end(assets[reqFileName]); res.end(assets[reqFileName]);
} else { } else {
res.writeHead(404, { "Content-Type": "text/plain" }); res.writeHead(404, { "Content-Type": "text/plain" });
@@ -114,7 +85,7 @@ client.on("room.message", (roomId, event) => {
//link_matches = body.match(/https?:\/\/[^\ ]*youtu[^\ ]*/g); //link_matches = body.match(/https?:\/\/[^\ ]*youtu[^\ ]*/g);
var r = new RegExp(/https?:\/\/[^\ ]*youtube.com\/watch\?v=([^\ ]*)/g); var r = new RegExp(/https?:\/\/[^\ ]*youtube.com\/watch\?v=([^\ ]*)/g);
link_matches = r.exec(body); link_matches = r.exec(body);
if (link_matches && link_matches.length > 1 && link_matches[0] != null) { if (link_matches && link_matches.length > 1) {
// get title // get title
request(link_matches[0], function (err, _res, body) { request(link_matches[0], function (err, _res, body) {
if (err) return console.error(err); if (err) return console.error(err);
@@ -129,7 +100,6 @@ client.on("room.message", (roomId, event) => {
link: link_matches[0], link: link_matches[0],
videoId: link_matches[1], videoId: link_matches[1],
title: title, title: title,
listeners: sockets.length,
}; };
console.log("Relaying: " + obj.link); console.log("Relaying: " + obj.link);
sockets.forEach((s) => s.send(JSON.stringify(obj))); sockets.forEach((s) => s.send(JSON.stringify(obj)));