Compare commits
No commits in common. "665468a1a64c9ac656e51dbbf06abe186655ec4b" and "3ed9eee2f713ab83933733ee45fc68960482588e" have entirely different histories.
665468a1a6
...
3ed9eee2f7
|
@ -4,9 +4,3 @@ data/music/*.webp
|
||||||
data/music/*.ogg*
|
data/music/*.ogg*
|
||||||
__pycache__
|
__pycache__
|
||||||
settings.py
|
settings.py
|
||||||
data/*
|
|
||||||
!data/agents.txt
|
|
||||||
venv/
|
|
||||||
ircradio/static/favicons/
|
|
||||||
ircradio/favicon.ico
|
|
||||||
ircradio/site.manifest
|
|
||||||
|
|
BIN
favicon.ico
BIN
favicon.ico
Binary file not shown.
Before Width: | Height: | Size: 12 KiB |
|
@ -1 +0,0 @@
|
||||||
1797
|
|
|
@ -1,13 +1,9 @@
|
||||||
# SPDX-License-Identifier: BSD-3-Clause
|
# SPDX-License-Identifier: BSD-3-Clause
|
||||||
# Copyright (c) 2021, dsc@xmr.pm
|
# Copyright (c) 2021, dsc@xmr.pm
|
||||||
|
|
||||||
import asyncio
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import Tuple, Optional
|
from typing import Tuple, Optional
|
||||||
from quart import request, render_template, abort, jsonify, Response, websocket
|
from quart import request, render_template, abort
|
||||||
|
|
||||||
import time
|
|
||||||
import json
|
|
||||||
|
|
||||||
import settings
|
import settings
|
||||||
from ircradio.factory import app
|
from ircradio.factory import app
|
||||||
|
@ -43,52 +39,6 @@ async def history():
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
@app.route("/search")
|
|
||||||
async def search():
|
|
||||||
# search json api endpoint
|
|
||||||
# e.g: /search?name=test&limit=5&offset=0
|
|
||||||
if not settings.enable_search_route:
|
|
||||||
abort(404)
|
|
||||||
|
|
||||||
from ircradio.models import Song
|
|
||||||
name = request.args.get("name")
|
|
||||||
limit = request.args.get("limit", '20')
|
|
||||||
offset = request.args.get("offset", '0')
|
|
||||||
|
|
||||||
try:
|
|
||||||
limit = int(limit)
|
|
||||||
offset = int(offset)
|
|
||||||
except:
|
|
||||||
limit = 50
|
|
||||||
offset = 0
|
|
||||||
|
|
||||||
if not name or len(name) <= 2:
|
|
||||||
abort(404)
|
|
||||||
|
|
||||||
if limit > 50:
|
|
||||||
limit = 50
|
|
||||||
|
|
||||||
name = f"%{name}%"
|
|
||||||
|
|
||||||
try:
|
|
||||||
q = Song.select()
|
|
||||||
q = q.where((Song.added_by ** name) | (Song.title ** name))
|
|
||||||
q = q.order_by(Song.date_added.desc())
|
|
||||||
q = q.limit(limit).offset(offset)
|
|
||||||
results = [{
|
|
||||||
"added_by": s.added_by,
|
|
||||||
"karma": s.karma,
|
|
||||||
"id": s.id,
|
|
||||||
"title": s.title,
|
|
||||||
"utube_id": s.utube_id,
|
|
||||||
"date_added": s.date_added.strftime("%Y-%m-%d")
|
|
||||||
} for s in q]
|
|
||||||
except:
|
|
||||||
return jsonify([])
|
|
||||||
|
|
||||||
return jsonify(results)
|
|
||||||
|
|
||||||
|
|
||||||
@app.route("/library")
|
@app.route("/library")
|
||||||
async def user_library():
|
async def user_library():
|
||||||
from ircradio.models import Song
|
from ircradio.models import Song
|
||||||
|
@ -112,27 +62,3 @@ async def user_library():
|
||||||
by_karma = []
|
by_karma = []
|
||||||
|
|
||||||
return await render_template("library.html", name=name, by_date=by_date, by_karma=by_karma)
|
return await render_template("library.html", name=name, by_date=by_date, by_karma=by_karma)
|
||||||
|
|
||||||
|
|
||||||
@app.websocket("/ws")
|
|
||||||
async def np():
|
|
||||||
last_song = ""
|
|
||||||
while True:
|
|
||||||
|
|
||||||
"""get current song from history"""
|
|
||||||
history = Radio.history()
|
|
||||||
val = ""
|
|
||||||
if not history:
|
|
||||||
val = f"Nothing is playing?!"
|
|
||||||
else:
|
|
||||||
song = history[0]
|
|
||||||
val = song.title
|
|
||||||
|
|
||||||
if val != last_song:
|
|
||||||
data = json.dumps({"now_playing": val})
|
|
||||||
await websocket.send(f"{data}")
|
|
||||||
|
|
||||||
last_song = val
|
|
||||||
|
|
||||||
await asyncio.sleep(5)
|
|
||||||
|
|
||||||
|
|
|
@ -1,19 +0,0 @@
|
||||||
{
|
|
||||||
"name": "IRC!Radio ala Scoob!",
|
|
||||||
"short_name": "IRC!Radio ala Scoob!",
|
|
||||||
"icons": [
|
|
||||||
{
|
|
||||||
"src": "/favicons/android-chrome-192x192.png",
|
|
||||||
"sizes": "192x192",
|
|
||||||
"type": "image/png"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"src": "/favicons/android-chrome-512x512.png",
|
|
||||||
"sizes": "512x512",
|
|
||||||
"type": "image/png"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"theme_color": "#ffffff",
|
|
||||||
"background_color": "#ffffff",
|
|
||||||
"display": "standalone"
|
|
||||||
}
|
|
|
@ -1,79 +0,0 @@
|
||||||
// tracks input in 'search' field (id=general)
|
|
||||||
let input_so_far = "";
|
|
||||||
|
|
||||||
// cached song list and cached queries
|
|
||||||
var queries = [];
|
|
||||||
var songs = new Map([]);
|
|
||||||
|
|
||||||
// track async fetch and processing
|
|
||||||
var returned = false;
|
|
||||||
|
|
||||||
$("#general").keyup( function() {
|
|
||||||
|
|
||||||
input_so_far = document.getElementsByName("general")[0].value;
|
|
||||||
|
|
||||||
if (input_so_far.length < 3) {
|
|
||||||
$("#table tr").remove();
|
|
||||||
return
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!queries.includes(input_so_far.toLowerCase() ) ) {
|
|
||||||
queries.push(input_so_far.toLowerCase() );
|
|
||||||
returned = false;
|
|
||||||
|
|
||||||
const sanitized_input = encodeURIComponent( input_so_far );
|
|
||||||
const url = 'https://' + document.domain + ':' + location.port + '/search?name=' + sanitized_input + '&limit=15&offset=0'
|
|
||||||
|
|
||||||
const LoadData = async () => {
|
|
||||||
try {
|
|
||||||
const res = await fetch(url);
|
|
||||||
console.log("Status code 200 or similar: " + res.ok);
|
|
||||||
const data = await res.json();
|
|
||||||
return data;
|
|
||||||
} catch(err) {
|
|
||||||
console.error(err)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
LoadData().then(newSongsJson => {
|
|
||||||
newSongsJson.forEach( (new_song) => {
|
|
||||||
let already_have = false;
|
|
||||||
songs.forEach( (_v, key) => {
|
|
||||||
if (new_song.id == key) { already_have = true; return; };
|
|
||||||
})
|
|
||||||
if (!already_have) { songs.set(new_song.utube_id, new_song) }
|
|
||||||
})
|
|
||||||
}).then( () => { returned = true } );
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
function renderTable () {
|
|
||||||
|
|
||||||
if (returned) {
|
|
||||||
|
|
||||||
$("#table tr").remove();
|
|
||||||
|
|
||||||
var filtered = new Map(
|
|
||||||
[...songs]
|
|
||||||
.filter(([k, v]) =>
|
|
||||||
( v.title.toLowerCase().includes( input_so_far.toLowerCase() ) ) ||
|
|
||||||
( v.added_by.toLowerCase().includes( input_so_far.toLowerCase() ) ) )
|
|
||||||
);
|
|
||||||
|
|
||||||
filtered.forEach( (song) => {
|
|
||||||
let added = song.added_by;
|
|
||||||
let added_link = '<a href="/library?name=' + added + '" target="_blank" rel="noopener noreferrer">' + added + '</a>';
|
|
||||||
let title = song.title;
|
|
||||||
let id = song.utube_id;
|
|
||||||
let id_link = '<a href="https://www.youtube.com/watch?v=' + id + '" target="_blank" rel="noopener noreferrer">' + id + '</a>';
|
|
||||||
$('#table tbody').append('<tr><td>'+id_link+'</td><td>'+added_link+'</td><td>'+title+'</td></tr>')
|
|
||||||
})
|
|
||||||
|
|
||||||
} else {
|
|
||||||
setTimeout(renderTable, 30); // try again in 30 milliseconds
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
renderTable();
|
|
||||||
|
|
||||||
});
|
|
|
@ -27,48 +27,12 @@
|
||||||
<meta name="application-name" content="IRC!Radio">
|
<meta name="application-name" content="IRC!Radio">
|
||||||
<meta name="msapplication-TileColor" content="#da532c">
|
<meta name="msapplication-TileColor" content="#da532c">
|
||||||
<meta name="description" content="IRC!Radio"/>
|
<meta name="description" content="IRC!Radio"/>
|
||||||
<title>IRC!Radio ala Scoob!</title>
|
<title>IRC!Radio</title>
|
||||||
|
|
||||||
<link rel="apple-touch-icon" sizes="180x180" href="static/favicons/apple-touch-icon.png">
|
|
||||||
<link rel="icon" type="image/png" sizes="32x32" href="static/favicons/favicon-32x32.png">
|
|
||||||
<link rel="icon" type="image/png" sizes="16x16" href="static/favicons/favicon-16x16.png">
|
|
||||||
<!-- <link rel="manifest" href="/site.webmanifest"> -->
|
|
||||||
<link rel="mask-icon" href="static/favicons/safari-pinned-tab.svg" color="#5bbad5">
|
|
||||||
<meta name="msapplication-TileColor" content="#2b5797">
|
|
||||||
<meta name="theme-color" content="#ffffff">
|
|
||||||
|
|
||||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
|
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
|
||||||
|
|
||||||
<link rel="stylesheet" href="https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css"></link>
|
|
||||||
|
|
||||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
|
|
||||||
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js" integrity="sha256-VazP97ZCwtekAsvgPBSUwPFKdrwD3unUfSGVYrahUqU=" crossorigin="anonymous"></script>
|
|
||||||
|
|
||||||
|
|
||||||
<script>
|
|
||||||
|
|
||||||
var ws = new WebSocket('wss://' + document.domain + ':' + location.port + '/ws');
|
|
||||||
|
|
||||||
ws.onmessage = function (event) {
|
|
||||||
// console.log(event.data);
|
|
||||||
json = JSON.parse(event.data);
|
|
||||||
np = json.now_playing;
|
|
||||||
document.querySelector("#prev_two").innerText = document.querySelector("#prev_one").innerText;
|
|
||||||
document.querySelector("#prev_one").innerText = document.querySelector("#now_playing").innerText;
|
|
||||||
document.querySelector("#now_playing").innerText = np;
|
|
||||||
console.log(np);
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
ws.onclose = function(event) {
|
|
||||||
console.log("WebSocket is closed now.");
|
|
||||||
};
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
{% block content %} {% endblock %}
|
{% block content %} {% endblock %}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
|
@ -4,28 +4,14 @@
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<!-- Post Content Column -->
|
<!-- Post Content Column -->
|
||||||
<div class="col-lg-9">
|
<div class="col-lg-12">
|
||||||
<!-- Title -->
|
<!-- Title -->
|
||||||
<h1 class="mt-4" style="margin-bottom: 0rem;">
|
<h1 class="mt-4" style="margin-bottom: 2rem;">
|
||||||
IRC!Radio ala Scoob!
|
IRC!Radio
|
||||||
</h1>
|
</h1>
|
||||||
<h5 class="mt-4" style="margin-bottom: 1rem;">
|
|
||||||
Thanks, dsc_!
|
|
||||||
</h5>
|
|
||||||
<p>Enjoy the music :)</p>
|
<p>Enjoy the music :)</p>
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
<audio controls src="/{{ settings.icecast2_mount }}">Your browser does not support the<code>audio</code> element.</audio>
|
<audio controls src="/{{ settings.icecast2_mount }}">Your browser does not support the<code>audio</code> element.</audio>
|
||||||
<p> </p>
|
|
||||||
|
|
||||||
<h3>Now playing: </h3>
|
|
||||||
<div id="now_playing" style="padding-top:6px; margin-bottom: 1.75rem;"></div>
|
|
||||||
|
|
||||||
<h5>Previous: </h5>
|
|
||||||
<div id="prev_one" style="font-size:12px;">Nothing here yet</div>
|
|
||||||
<div id="prev_two" style="font-size:12px;"></div>
|
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
<h4>Command list:</h4>
|
<h4>Command list:</h4>
|
||||||
|
@ -46,52 +32,29 @@
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-3">
|
<div class="col-md-3">
|
||||||
<h4>History</h4>
|
<h4>History</h4>
|
||||||
<a href="/history.txt" target="_blank">View in new tab</a>
|
<a href="/history.txt">history.txt</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-md-5">
|
<div class="col-md-3">
|
||||||
|
<h4>Library
|
||||||
<h4>View User Library
|
<small style="font-size:12px">(by user)</small>
|
||||||
<small style="font-size:12px"></small>
|
|
||||||
</h4>
|
</h4>
|
||||||
<form target="_blank" method="GET" action="/library">
|
<form method="GET" action="/library">
|
||||||
<div class="input-group mb-3 style=no-gutters">
|
<div class="input-group mb-3">
|
||||||
<input type="text" class="form-control" id="name" name="name" placeholder="username...">
|
<input type="text" class="form-control" id="name" name="name" placeholder="username...">
|
||||||
<div class="input-group-append">
|
<div class="input-group-append">
|
||||||
<input class="btn btn-outline-secondary" type="submit" value="Search">
|
<input class="btn btn-outline-secondary" type="submit" value="Search">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="col-md-3"></div>
|
||||||
|
|
||||||
<hr>
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-8">
|
|
||||||
<h4>Quick Search
|
|
||||||
<small style="font-size:12px">(general)</small>
|
|
||||||
</h4>
|
|
||||||
<div class="input-group mb-3">
|
|
||||||
<input type="text" class="form-control" id="general" name="general" placeholder="query...">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-12">
|
|
||||||
<table class="table table-sm table-hover table-bordered" id="table" style="font-size:12px">
|
|
||||||
<thead>
|
|
||||||
<tbody style="">
|
|
||||||
</tbody>
|
|
||||||
</thead>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
<h4>IRC</h4>
|
<h4>IRC</h4>
|
||||||
<pre>{{ settings.irc_host }}:{{ settings.irc_port }}
|
<pre>{{ settings.irc_host }}:{{ settings.irc_port }}
|
||||||
{{ settings.irc_channels | join(" ") }}
|
{{ settings.irc_channels | join(" ") }}
|
||||||
|
@ -99,7 +62,4 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{% endblock %}
|
||||||
<script src="static/search.js"></script>
|
|
||||||
|
|
||||||
{% endblock %}
|
|
|
@ -155,18 +155,18 @@ def liquidsoap_version():
|
||||||
|
|
||||||
|
|
||||||
def liquidsoap_check_symlink():
|
def liquidsoap_check_symlink():
|
||||||
# msg = """
|
msg = """
|
||||||
# Due to a bug you need to create this symlink:
|
Due to a bug you need to create this symlink:
|
||||||
#
|
|
||||||
# $ sudo ln -s /usr/share/liquidsoap/ /usr/share/liquidsoap/1.4.1
|
$ sudo ln -s /usr/share/liquidsoap/ /usr/share/liquidsoap/1.4.1
|
||||||
#
|
|
||||||
# info: https://github.com/savonet/liquidsoap/issues/1224
|
info: https://github.com/savonet/liquidsoap/issues/1224
|
||||||
# """
|
"""
|
||||||
# version = liquidsoap_version()
|
version = liquidsoap_version()
|
||||||
# if not os.path.exists(f"/usr/share/liquidsoap/{version}"):
|
if not os.path.exists(f"/usr/share/liquidsoap/{version}"):
|
||||||
# print(msg)
|
print(msg)
|
||||||
# sys.exit()
|
sys.exit()
|
||||||
pass
|
|
||||||
|
|
||||||
async def httpget(url: str, json=True, timeout: int = 5, raise_for_status=True, verify_tls=True):
|
async def httpget(url: str, json=True, timeout: int = 5, raise_for_status=True, verify_tls=True):
|
||||||
headers = {"User-Agent": random_agent()}
|
headers = {"User-Agent": random_agent()}
|
||||||
|
|
|
@ -36,9 +36,7 @@ class YouTube:
|
||||||
|
|
||||||
try:
|
try:
|
||||||
proc = await asyncio.create_subprocess_exec(
|
proc = await asyncio.create_subprocess_exec(
|
||||||
*[
|
*["youtube-dl",
|
||||||
#"/home/radio/ircradio/venv/bin/youtube-dl",
|
|
||||||
"youtube-dl",
|
|
||||||
"--add-metadata",
|
"--add-metadata",
|
||||||
"--write-all-thumbnails",
|
"--write-all-thumbnails",
|
||||||
"--write-info-json",
|
"--write-info-json",
|
||||||
|
|
|
@ -4,6 +4,7 @@ aiofiles
|
||||||
aiohttp
|
aiohttp
|
||||||
bottom
|
bottom
|
||||||
tinytag
|
tinytag
|
||||||
|
peewee
|
||||||
python-dateutil
|
python-dateutil
|
||||||
mutagen
|
mutagen
|
||||||
peewee
|
peewee
|
|
@ -16,8 +16,6 @@ timezone = "Europe/Amsterdam"
|
||||||
|
|
||||||
dir_music = os.environ.get("DIR_MUSIC", os.path.join(cwd, "data", "music"))
|
dir_music = os.environ.get("DIR_MUSIC", os.path.join(cwd, "data", "music"))
|
||||||
|
|
||||||
enable_search_route = bool_env(os.environ.get("ENABLE_SEARCH_ROUTE", False))
|
|
||||||
|
|
||||||
irc_admins_nicknames = ["dsc_"]
|
irc_admins_nicknames = ["dsc_"]
|
||||||
irc_host = os.environ.get('IRC_HOST', 'localhost')
|
irc_host = os.environ.get('IRC_HOST', 'localhost')
|
||||||
irc_port = int(os.environ.get('IRC_PORT', 6667))
|
irc_port = int(os.environ.get('IRC_PORT', 6667))
|
||||||
|
|
Loading…
Reference in New Issue