Initial Commit
This commit is contained in:
commit
7bb45b12ca
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
venv/
|
44
config.json
Normal file
44
config.json
Normal file
@ -0,0 +1,44 @@
|
||||
{
|
||||
"instance_name": "Test",
|
||||
"mpv_arguments": [
|
||||
"--loop",
|
||||
"--screen=0"
|
||||
],
|
||||
"buttons": [
|
||||
{
|
||||
"name": "Goofy",
|
||||
"video_url": "videos/goofy.mp4",
|
||||
"default": true
|
||||
},
|
||||
{
|
||||
"name": "Neutron Stars",
|
||||
"video_url": "videos/neutronstars.mp4",
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "Gimme The Bleach Boys",
|
||||
"video_url": "videos/bleach.mp4",
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "The Cloak",
|
||||
"video_url": "videos/cloak.mp4",
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "Dog of Wisdom",
|
||||
"video_url": "videos/dog.mp4",
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "Nyan Cat",
|
||||
"video_url": "videos/nyan.mp4",
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "Smooth Jazz Nyan Cat",
|
||||
"video_url": "videos/smoothjazznyan.mp4",
|
||||
"default": false
|
||||
}
|
||||
]
|
||||
}
|
51
index.html
Normal file
51
index.html
Normal file
@ -0,0 +1,51 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>MPV Commander</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
</head>
|
||||
<script>
|
||||
async function onclick_processor(name) {
|
||||
try {
|
||||
const response = await fetch(
|
||||
window.location.origin + "/stream",
|
||||
{
|
||||
method: "POST",
|
||||
"headers": {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
body: JSON.stringify({ "name": name })
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Response status: ${response.status}`)
|
||||
}
|
||||
|
||||
const json = await response.json()
|
||||
console.log(json)
|
||||
} catch (error) {
|
||||
console.log(error.message)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1 class="text-center">MPV Commander Instance {{ instance_name }}</h1>
|
||||
<h3 class="text-center">Which video stream do you want to show?</h3>
|
||||
{% for index in range(buttons | length) %}
|
||||
{% if index % 3 == 0 %}
|
||||
<div class="row">
|
||||
{% endif %}
|
||||
{% set current_name = buttons[index]["name"] %}
|
||||
<button class="col-sm-4 btn-default p-3 my-1" onclick="onclick_processor('{{ current_name }}')">{{ current_name }}</button>
|
||||
{% if index + 1 == buttons | length or (index + 1) % 3 == 0 %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
62
mpv_commander.py
Normal file
62
mpv_commander.py
Normal file
@ -0,0 +1,62 @@
|
||||
import os
|
||||
import signal
|
||||
import subprocess
|
||||
import sqlite3
|
||||
import time
|
||||
import json
|
||||
|
||||
get_active_video = r'''
|
||||
SELECT video_url FROM buttons
|
||||
WHERE is_active = 1
|
||||
LIMIT 1
|
||||
'''
|
||||
|
||||
def run_mpv(previous_process, mpv_arguments, video_url):
|
||||
if not previous_process is None:
|
||||
os.killpg(os.getpgid(previous_process.pid), signal.SIGTERM)
|
||||
|
||||
return subprocess.Popen(
|
||||
"/usr/bin/mpv {arguments} {video_url}".format(
|
||||
arguments = " ".join(mpv_arguments),
|
||||
video_url = video_url
|
||||
),
|
||||
stdout = subprocess.PIPE,
|
||||
stderr = subprocess.PIPE,
|
||||
shell = True,
|
||||
preexec_fn = os.setsid
|
||||
)
|
||||
|
||||
def get_active_video_url(db):
|
||||
cursor = db.execute(get_active_video)
|
||||
return cursor.fetchall()[0][0]
|
||||
|
||||
with open("config.json", "r") as config_file:
|
||||
config_json = json.loads(config_file.read())
|
||||
|
||||
current_process = None
|
||||
current_video_url = None
|
||||
db = sqlite3.connect("mpvcommander.db")
|
||||
|
||||
while True:
|
||||
if current_process is None:
|
||||
current_video_url = get_active_video_url(db)
|
||||
|
||||
current_process = run_mpv(
|
||||
current_process,
|
||||
config_json["mpv_arguments"],
|
||||
current_video_url
|
||||
)
|
||||
|
||||
active_video_url = get_active_video_url(db)
|
||||
|
||||
if current_video_url != active_video_url:
|
||||
current_video_url = active_video_url
|
||||
|
||||
current_process = run_mpv(
|
||||
current_process,
|
||||
config_json["mpv_arguments"],
|
||||
current_video_url
|
||||
)
|
||||
|
||||
time.sleep(1)
|
||||
|
71
mpv_dbbuilder.py
Normal file
71
mpv_dbbuilder.py
Normal file
@ -0,0 +1,71 @@
|
||||
import sqlite3
|
||||
import json
|
||||
import sys
|
||||
|
||||
destroy_table = r'''
|
||||
DROP TABLE IF EXISTS buttons
|
||||
'''
|
||||
|
||||
make_table = r'''
|
||||
CREATE TABLE IF NOT EXISTS buttons
|
||||
(button_name TEXT PRIMARY KEY,
|
||||
video_url TEXT NOT NULL,
|
||||
is_default BOOLEAN NOT NULL CHECK (is_default IN (0, 1)),
|
||||
is_active BOOLEAN NOT NULL CHECK (is_active IN (0, 1)))
|
||||
'''
|
||||
|
||||
insert_row = r'''
|
||||
INSERT INTO buttons VALUES
|
||||
(?, ?, ?, ?)
|
||||
'''
|
||||
|
||||
with open("config.json", "r") as config_file:
|
||||
config_json = json.loads(config_file.read())
|
||||
|
||||
config_issues = ""
|
||||
|
||||
if not "instance_name" in config_json.keys():
|
||||
config_issues = config_issues + "The config item instance_name is required\n"
|
||||
|
||||
for i in range(len(config_json["buttons"])):
|
||||
if not "name" in config_json["buttons"][i].keys():
|
||||
config_issues = config_issues + "Config index {index} is missing the required name parameter\n".format(index = (i + 1))
|
||||
|
||||
if not "video_url" in config_json["buttons"][i].keys():
|
||||
config_issues = config_issues + "Config index {index} is missing the required video_url parameter\n".format(index = (i + 1))
|
||||
|
||||
if not "default" in config_json["buttons"][i].keys():
|
||||
config_issues = config_issues + "Config index {index} is missing the required default parameter\n".format(index = (i + 1))
|
||||
|
||||
all_defaults = [element for element in config_json["buttons"] if element["default"]]
|
||||
|
||||
if len(all_defaults) > 1:
|
||||
config_issues = config_issues + "More than one button config is set as default, only one can be the default, remove one of these: {button_list}\n".format(
|
||||
button_list = ", ".join([element["name"] if "name" in element else "NAME MISSING" for element in all_defaults])
|
||||
)
|
||||
elif len(all_defaults) == 0:
|
||||
config_issues = config_issues = "At least one button config must be set as the default\n"
|
||||
|
||||
if len(config_issues) != 0:
|
||||
print("Config issues detected! Please correct these issues before trying again")
|
||||
print(config_issues)
|
||||
sys.exit(1)
|
||||
|
||||
db = sqlite3.connect("mpvcommander.db")
|
||||
db.execute(destroy_table)
|
||||
db.execute(make_table)
|
||||
|
||||
for button in config_json["buttons"]:
|
||||
db.execute(insert_row, [
|
||||
button["name"],
|
||||
button["video_url"],
|
||||
1 if button["default"] else 0,
|
||||
1 if button["default"] else 0
|
||||
])
|
||||
|
||||
db.commit()
|
||||
|
||||
db.close()
|
||||
|
||||
|
||||
|
90
mpv_server.py
Normal file
90
mpv_server.py
Normal file
@ -0,0 +1,90 @@
|
||||
from flask import Flask, request, jsonify, g
|
||||
from jinja2 import Template
|
||||
import json
|
||||
import sys
|
||||
import sqlite3
|
||||
|
||||
disable_all_active = r'''
|
||||
UPDATE buttons SET is_active = 0
|
||||
'''
|
||||
|
||||
set_active_by_name_sql = r'''
|
||||
UPDATE buttons SET is_active = 1
|
||||
WHERE button_name = ?
|
||||
'''
|
||||
|
||||
with open("config.json", "r") as config_file:
|
||||
config_json = json.loads(config_file.read())
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
def get_db():
|
||||
db = getattr(g, "_database", None)
|
||||
|
||||
if db is None:
|
||||
db = g._database = sqlite3.connect("mpvcommander.db")
|
||||
|
||||
return db
|
||||
|
||||
def set_active_by_name(name):
|
||||
db = get_db()
|
||||
|
||||
db.execute(disable_all_active)
|
||||
db.execute(set_active_by_name, name)
|
||||
db.commit()
|
||||
|
||||
@app.route("/")
|
||||
def root_route():
|
||||
with open("index.html", "r") as html_file:
|
||||
template = Template(html_file.read())
|
||||
return template.render(config_json), 200
|
||||
|
||||
@app.route("/stream", methods=["POST"])
|
||||
def stream_route():
|
||||
if request.content_type == 'application/json':
|
||||
body = request.get_json()
|
||||
|
||||
if not "name" in body.keys():
|
||||
error_response = {
|
||||
'status': "ERROR",
|
||||
"reason": "A name must be specified in the body of the request"
|
||||
}
|
||||
|
||||
return jsonify(error_response), 400
|
||||
|
||||
resource_list = [element for element in config_json["buttons"] if element["name"] == body["name"]]
|
||||
|
||||
if(len(resource_list) == 0):
|
||||
error_response = {
|
||||
'status': "ERROR",
|
||||
"reason": "The name {name} does not exist in config['buttons'], check your config".format(name = body["name"])
|
||||
}
|
||||
|
||||
return jsonify(error_response), 400
|
||||
|
||||
db = get_db()
|
||||
|
||||
db.execute(disable_all_active)
|
||||
db.execute(set_active_by_name_sql, [body["name"]])
|
||||
db.commit()
|
||||
|
||||
return jsonify(resource_list[0]), 200
|
||||
else:
|
||||
error_response = {
|
||||
'status': 'ERROR',
|
||||
'reason': 'Posted body must be of content type application/json'
|
||||
}
|
||||
|
||||
return jsonify(error_response)
|
||||
|
||||
@app.teardown_appcontext
|
||||
def close_connection(exception):
|
||||
db = getattr(g, '_database', None)
|
||||
|
||||
if db is not None:
|
||||
db.close()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(debug=True, host="0.0.0.0", port=1801)
|
||||
|
BIN
mpvcommander.db
Normal file
BIN
mpvcommander.db
Normal file
Binary file not shown.
BIN
videos/goofy.mp4
Normal file
BIN
videos/goofy.mp4
Normal file
Binary file not shown.
BIN
videos/neutronstars.mp4
Normal file
BIN
videos/neutronstars.mp4
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user