diff --git a/.gitignore b/.gitignore index c9c7f01..f5ba7d5 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ videos/* +videocommander.db \ No newline at end of file diff --git a/config.json b/config.json index 32bb620..3841a3c 100644 --- a/config.json +++ b/config.json @@ -4,28 +4,95 @@ "display_config": ":1", "disabled_name": "Disable Video", "video_tool": "ffplay", - "buttons": [ + "data": [ { - "name": "Goofy", - "video_url": "videos/goofy.mp4", - "video_tool_arguments": [ - "-fs", - "-loop", - "0" - ], - "default": true - }, - { - "name": "Neutron Stars", - "video_url": "videos/neutronstars.mp4", - "video_tool_arguments": [ - "-fs", - "-loop", - "0" - ], + "type": "Button", + "name": "Disable Video", + "video_url": "", + "video_tool_arguments": [], "default": false }, { + "type": "Group", + "name": "Animals", + "data": [ + { + "type": "Group", + "name": "Dogs", + "data": [ + { + "type": "Button", + "name": "Goofy", + "video_url": "videos/goofy.mp4", + "video_tool_arguments": [ + "-fs", + "-loop", + "0" + ], + "default": true + }, + { + "type": "Button", + "name": "Dog of Wisdom", + "video_url": "videos/dog.mp4", + "video_tool_arguments": [ + "-fs", + "-loop", + "0" + ], + "default": false + } + ] + }, + { + "type": "Group", + "name": "Cats", + "data": [ + { + "type": "Button", + "name": "Nyan Cat", + "video_url": "videos/nyan.mp4", + "video_tool_arguments": [ + "-fs", + "-loop", + "0" + ], + "default": false + }, + { + "type": "Button", + "name":"Smooth Jazz Nyan Cat", + "video_url": "videos/smoothjazznyan.mp4", + "video_tool_arguments": [ + "-fs", + "-loop", + "0" + ], + "default": false + } + ] + } + ] + }, + { + "type": "Group", + "name": "Space", + "data": [ + { + "type": "Button", + "name": "Neutron Stars", + "video_url": "videos/neutronstars.mp4", + "video_tool_arguments": [ + "-fs", + "-loop", + "0" + ], + "default": false + } + ] + }, + { + "type": "Button", "name": "Gimme The Yeet Boys", "video_url": "videos/yeet.mp4", "video_tool_arguments": [ @@ -36,6 +103,7 @@ "default": false }, { + "type": "Button", "name": "Hey Bender", "video_url": "videos/bender.mp4", "video_tool_arguments": [ @@ -44,36 +112,6 @@ "0" ], "default": false - }, - { - "name": "Dog of Wisdom", - "video_url": "videos/dog.mp4", - "video_tool_arguments": [ - "-fs", - "-loop", - "0" - ], - "default": false - }, - { - "name": "Nyan Cat", - "video_url": "videos/nyan.mp4", - "video_tool_arguments": [ - "-fs", - "-loop", - "0" - ], - "default": false - }, - { - "name": "Smooth Jazz Nyan Cat", - "video_url": "videos/smoothjazznyan.mp4", - "video_tool_arguments": [ - "-fs", - "-loop", - "0" - ], - "default": false } ] -} +} \ No newline at end of file diff --git a/index.html b/index.html index 8f12647..fefdd74 100644 --- a/index.html +++ b/index.html @@ -1,5 +1,5 @@ - + Video Commander @@ -7,45 +7,68 @@ - +

Video Commander Instance {{ instance_name }}

-

Which video stream do you want to show?

+
+ {% if buttons | length != 0 %} +

Current group ({{ current_group }}) streams

{% for index in range(buttons | length) %} {% if index % 3 == 0 %}
{% endif %} - {% set current_name = buttons[index]["name"] %} - + {% set current_name = buttons[index][0] %} + {% if index + 1 == buttons | length or (index + 1) % 3 == 0 %}
{% endif %} {% endfor %} + {% else %} +

There are no stream buttons in this group

+ {% endif %} + {% if groups | length != 0 %} +

Current group ({{ current_group }}) sub-groups

+ {% for index in range(groups | length) %} + {% if index % 3 == 0 %} +
+ {% endif %} + {% set current_name = groups[index][0] %} + + {% if index + 1 == groups | length or (index + 1) % 3 == 0 %} +
+ {% endif %} + {% endfor %} + {% endif %} +
\ No newline at end of file diff --git a/video_dbbuilder.py b/video_dbbuilder.py index 3eb9947..65bd2ac 100644 --- a/video_dbbuilder.py +++ b/video_dbbuilder.py @@ -2,80 +2,124 @@ import sqlite3 import json import sys -destroy_table = r''' +destroy_button_table = r''' DROP TABLE IF EXISTS buttons ''' -make_table = r''' +destroy_group_table = r''' +DROP TABLE IF EXISTS groups +''' + +make_button_table = r''' CREATE TABLE IF NOT EXISTS buttons -(button_name TEXT PRIMARY KEY, +(button_name TEXT PRIMARY KEY, +page_group TEXT NOT NULL, video_url TEXT NOT NULL, video_arguments 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 -(?, ?, ?, ?, ?) +make_group_table = r''' +CREATE TABLE IF NOT EXISTS groups +(group_name TEXT PRIMARY KEY, +parent_group TEXT NOT NULL +) ''' +insert_button_row = r''' +INSERT INTO buttons VALUES +(?, ?, ?, ?, ?, ?) +''' + +insert_group_row = r''' +INSERT INTO groups VALUES +(?, ?) +''' + +default_count = 0 +failed_a_test = False + +def recursive_data_check(next_list: list, current_group_name: str): + global default_count + global failed_a_test + + for element in next_list: + if not "type" in element.keys(): + print(f"An element in {current_group_name} is missing the type key") + failed_a_test = True + + if not "name" in element.keys(): + print(f"An element in {current_group_name} with type {element["type"]} is missing the name key") + failed_a_test = True + + if element["type"] == "Group": + if not "data" in element.keys(): + print(f"The element in {current_group_name} with name {element["name"]} is missing the data key") + failed_a_test = True + + recursive_data_check(element["data"], element["name"]) + else: + if not "video_url" in element.keys(): + print(f"The element in {current_group_name} with name {element["name"]} is missing the video_url key") + failed_a_test = True + + if not "video_tool_arguments" in element.keys(): + print(f"The element in {current_group_name} with name {element["name"]} is missing the video_tool_arguments key") + failed_a_test = True + + if not "default" in element.keys(): + print(f"The element in {current_group_name} with name {element["name"]} is missing the default key") + failed_a_test = True + + if element["default"]: + default_count = default_count + 1 + +def recursive_insert(db: sqlite3.Connection, next_list: list, current_group_name: str): + for element in next_list: + if element["type"] == "Group": + db.execute(insert_group_row, [ + element["name"], + current_group_name + ]) + db.commit() + recursive_insert(db, element["data"], element["name"]) + else: + db.execute(insert_button_row, [ + element["name"], + current_group_name, + element["video_url"], + " ".join(element["video_tool_arguments"]) if len(element["video_tool_arguments"]) != 0 else "", + 1 if element["default"] else 0, + 1 if element["default"] else 0 + ]) + + db.commit() + 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" + print("The config item instance_name is required") -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)) +recursive_data_check(config_json["data"], "Home") - 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 default_count > 1: + print(f"There can only be one default button configured, current number configured: {default_count}") + failed_a_test = True +elif default_count == 0: + print("There must be one default button configured, currently there are none") - 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) +if failed_a_test: sys.exit(1) db = sqlite3.connect(config_json["db_name"]) -db.execute(destroy_table) -db.execute(make_table) +db.execute(destroy_button_table) +db.execute(destroy_group_table) +db.execute(make_button_table) +db.execute(make_group_table) -db.execute(insert_row, [ - config_json["disabled_name"], - "", - "", - 0, - 0 -]) - -db.commit() - -for button in config_json["buttons"]: - db.execute(insert_row, [ - button["name"], - button["video_url"], - " ".join(button["video_tool_arguments"]) if len(button["video_tool_arguments"]) != 0 else "", - 1 if button["default"] else 0, - 1 if button["default"] else 0 - ]) - - db.commit() +recursive_insert(db, config_json["data"], "Home") db.close() diff --git a/video_player.py b/video_player.py index c11191b..3695644 100644 --- a/video_player.py +++ b/video_player.py @@ -5,7 +5,7 @@ import sqlite3 import time import json -get_active_video = r''' +get_active_video_sql = r''' SELECT video_url, video_arguments, button_name FROM buttons WHERE is_active = 1 LIMIT 1 @@ -29,7 +29,7 @@ def play_video(previous_process, video_player_path, player_arguments, video_url, ) def get_active_video(db): - cursor = db.execute(get_active_video) + cursor = db.execute(get_active_video_sql) return cursor.fetchall()[0] with open("config.json", "r") as config_file: @@ -45,7 +45,7 @@ db = sqlite3.connect(config_json["db_name"]) while True: active_video = get_active_video(db) - if current_video[2] != active_video[2]: + if current_video is None or current_video[2] != active_video[2]: current_video = active_video if current_video[2] == config_json["disabled_name"] and not current_process is None: diff --git a/video_webserver.py b/video_webserver.py index 038b2d7..39bf88a 100644 --- a/video_webserver.py +++ b/video_webserver.py @@ -13,6 +13,16 @@ UPDATE buttons SET is_active = 1 WHERE button_name = ? ''' +find_buttons_for_group = r''' +SELECT * FROM buttons +WHERE page_group = ? +''' + +find_groups_for_parent = r''' +SELECT * FROM groups +WHERE parent_group = ? +''' + with open("config.json", "r") as config_file: config_json = json.loads(config_file.read()) @@ -36,8 +46,17 @@ def set_active_by_name(name): @app.route("/") def root_route(): with open("index.html", "r") as html_file: + db = get_db() + + group = request.args.get(key="group", default="Home") + template = Template(html_file.read()) - return template.render(config_json), 200 + return template.render({ + "instance_name": config_json["instance_name"], + "current_group": group, + "buttons": db.execute(find_buttons_for_group, [group]).fetchall(), + "groups": db.execute(find_groups_for_parent, [group]).fetchall() + }), 200 @app.route("/stream", methods=["POST"]) def stream_route(): @@ -51,16 +70,6 @@ def stream_route(): } 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() @@ -68,7 +77,7 @@ def stream_route(): db.execute(set_active_by_name_sql, [body["name"]]) db.commit() - return jsonify(resource_list[0]), 200 + return jsonify(body["name"]), 200 else: error_response = { 'status': 'ERROR',