diff --git a/GUI.py b/GUI.py index a4f1e01..47dfc25 100644 --- a/GUI.py +++ b/GUI.py @@ -1,5 +1,3 @@ -import json -import re import webbrowser from pathlib import Path @@ -7,7 +5,6 @@ from pathlib import Path import tomlkit from flask import ( Flask, - flash, redirect, render_template, request, @@ -15,6 +12,8 @@ from flask import ( url_for, ) +import utils.gui_utils as gui + # Set the hostname HOST = "localhost" # Set the port number @@ -39,7 +38,53 @@ def after_request(response): # Display index.html @app.route("/") def index(): - return render_template("index.html") + return render_template("index.html", file="videos.json") + + +@app.route("/backgrounds", methods=["GET"]) +def backgrounds(): + return render_template("backgrounds.html", file="backgrounds.json") + + +@app.route("/background/add", methods=["POST"]) +def background_add(): + # Get form values + youtube_uri = request.form.get("youtube_uri").strip() + filename = request.form.get("filename").strip() + citation = request.form.get("citation").strip() + position = request.form.get("position").strip() + + gui.add_background(youtube_uri, filename, citation, position) + + return redirect(url_for("backgrounds")) + + +@app.route("/background/delete", methods=["POST"]) +def background_delete(): + key = request.form.get("background-key") + gui.delete_background(key) + + return redirect(url_for("backgrounds")) + + +@app.route("/settings", methods=["GET", "POST"]) +def settings(): + config_load = tomlkit.loads(Path("config.toml").read_text()) + config = gui.get_config(config_load) + + # Get checks for all values + checks = gui.get_checks() + + if request.method == "POST": + # Get data from form as dict + data = request.form.to_dict() + + # Change settings + config = gui.modify_settings(data, config_load, checks) + + return render_template( + "settings.html", file="config.toml", data=config, checks=checks + ) # Make videos.json accessible @@ -48,77 +93,22 @@ def videos_json(): return send_from_directory("video_creation/data", "videos.json") +# Make backgrounds.json accessible +@app.route("/backgrounds.json") +def backgrounds_json(): + return send_from_directory("utils", "backgrounds.json") + + # Make videos in results folder accessible @app.route("/results/") def results(name): return send_from_directory("results", name, as_attachment=True) -@app.route("/add_background", methods=["POST"]) -def add_background(): - # Get form values - youtube_uri = request.form.get("youtube_uri").strip() - filename = request.form.get("filename").strip() - citation = request.form.get("citation").strip() - position = request.form.get("position").strip() - - # Validate YouTube URI - regex = re.compile( - r"(?:\/|%3D|v=|vi=)([0-9A-z-_]{11})(?:[%#?&]|$)" - ).search(youtube_uri) - - if not regex: - flash("YouTube URI is invalid!", "error") - return redirect(url_for("index")) - - youtube_uri = f"https://www.youtube.com/watch?v={regex.group(1)}" - - # Check if position is valid - if position == "" or position == "center": - position = "center" - - elif position.isdecimal(): - position = int(position) - - else: - flash('Position is invalid! It can be "center" or decimal number.', "error") - return redirect(url_for("index")) - - # Sanitize filename - filename = filename.replace(" ", "-").split(".")[0] - - # Check if background doesn't already exist - with open("utils/backgrounds.json", "r", encoding="utf-8") as backgrounds: - data = json.load(backgrounds) - - # Check if key isn't already taken - if filename in list(data.keys()): - flash("Background video with this name already exist!", "error") - return redirect(url_for("index")) - - # Check if the YouTube URI isn't already used under different name - if youtube_uri in [data[i][0] for i in list(data.keys())]: - flash("Background video with this YouTube URI is already added!", "error") - return redirect(url_for("index")) - - # Add background video to json file - with open("utils/backgrounds.json", "r+", encoding="utf-8") as backgrounds: - data = json.load(backgrounds) - - data[filename] = [youtube_uri, filename + ".mp4", citation, position] - backgrounds.seek(0) - json.dump(data, backgrounds, ensure_ascii=False, indent=4) - - # Add background video to ".config.template.toml" to make it accessible - config = tomlkit.loads(Path("utils/.config.template.toml").read_text()) - config["settings"]["background"]["background_choice"]["options"].append(filename) - - with Path("utils/.config.template.toml").open("w") as toml_file: - toml_file.write(tomlkit.dumps(config)) - - flash(f'Added "{citation}-{filename}.mp4" as a new background video!') - - return redirect(url_for("index")) +# Make voices samples in voices folder accessible +@app.route("/voices/") +def voices(name): + return send_from_directory("GUI/voices", name, as_attachment=True) # Run browser and start the app diff --git a/GUI/backgrounds.html b/GUI/backgrounds.html new file mode 100644 index 0000000..541e39f --- /dev/null +++ b/GUI/backgrounds.html @@ -0,0 +1,263 @@ +{% extends "layout.html" %} +{% block main %} + + + + + + + +
+
+
+ +
+
+ +
+
+ +
+
+ +
+ +
+
+
+
+ + + +{% endblock %} \ No newline at end of file diff --git a/GUI/index.html b/GUI/index.html index 00deeb7..fe64e36 100644 --- a/GUI/index.html +++ b/GUI/index.html @@ -1,404 +1,182 @@ - +{% extends "layout.html" %} +{% block main %} - - - - - RedditVideoMakerBot - - - - - - - - - -
- {% if get_flashed_messages() %} - {% for category, message in get_flashed_messages(with_categories=true) %} - - {% if category == "error" %} - - - {% else %} - - {% endif %} - {% endfor %} - {% endif %} - -
- -
+
-
+
-
-
- -
+
+
+ +
+
+ +
+ +
- -
- -
-
-
+
- - - - - - - - - - - - \ No newline at end of file + +{% endblock %} \ No newline at end of file diff --git a/GUI/layout.html b/GUI/layout.html new file mode 100644 index 0000000..d56299e --- /dev/null +++ b/GUI/layout.html @@ -0,0 +1,142 @@ + + + + + + + RedditVideoMakerBot + + + + + + + + + + + + + + +
+ {% if get_flashed_messages() %} + {% for category, message in get_flashed_messages(with_categories=true) %} + + {% if category == "error" %} + + + {% else %} + + {% endif %} + {% endfor %} + {% endif %} + +
+ + {% block main %}{% endblock %} + + + + + \ No newline at end of file diff --git a/GUI/settings.html b/GUI/settings.html new file mode 100644 index 0000000..2b6b014 --- /dev/null +++ b/GUI/settings.html @@ -0,0 +1,576 @@ +{% extends "layout.html" %} +{% block main %} + +
+
+
+
+ + +

Reddit Credentials

+
+ +
+
+
+ +
+ +
+
+
+
+ +
+
+
+ +
+ +
+
+
+
+ +
+
+
+ +
+ +
+
+
+
+ +
+
+
+ +
+ +
+
+
+
+ + +
+ + +

Reddit Thread

+
+ +
+
+ +
+
+
+
+ +
+
+
+ +
+ +
+
+
+
+ +
+
+
+ +
+ +
+
+
+
+ +
+
+ +
+ Max number of characters a comment can have. +
+
+
+ +
+
+
+ +
+ +
+
+
+
+ +
+
+ +
+ The minimum number of comments a post should have to be + included. +
+
+ + +

General Settings

+
+ +
+
+ +
+
+
+
+ +
+ +
+
+
+ +
+
+ +
+ Used if you want to create multiple videos. +
+
+
+ +
+
+ +
+ Sets the opacity of the comments when overlayed over the + background. +
+
+
+ +
+
+ +
+ Sets the transition time (in seconds) between the + comments. Set to 0 if you want to disable it. +
+
+
+ +
+ + See all available + backgrounds +
+
+ + +

TTS Settings

+
+ +
+ +
+
+
+ +
+
+ + + +
+
+
+
+ +
+
+ + + +
+
+
+
+ +
+
+ + + +
+
+
+
+ +
+
+
+ +
+ +
+
+
+
+ +
+
+
+ +
+ +
+
+
+
+ +
+
+ +
+ Time in seconds between TTS comments. +
+
+
+
+ + +
+
+
+ +
+ + + +{% endblock %} \ No newline at end of file diff --git a/GUI/voices/amy.mp3 b/GUI/voices/amy.mp3 new file mode 100644 index 0000000..29ea64b Binary files /dev/null and b/GUI/voices/amy.mp3 differ diff --git a/GUI/voices/br_001.mp3 b/GUI/voices/br_001.mp3 new file mode 100644 index 0000000..3b343dc Binary files /dev/null and b/GUI/voices/br_001.mp3 differ diff --git a/GUI/voices/br_003.mp3 b/GUI/voices/br_003.mp3 new file mode 100644 index 0000000..9a6b845 Binary files /dev/null and b/GUI/voices/br_003.mp3 differ diff --git a/GUI/voices/br_004.mp3 b/GUI/voices/br_004.mp3 new file mode 100644 index 0000000..47d0010 Binary files /dev/null and b/GUI/voices/br_004.mp3 differ diff --git a/GUI/voices/br_005.mp3 b/GUI/voices/br_005.mp3 new file mode 100644 index 0000000..104cc3a Binary files /dev/null and b/GUI/voices/br_005.mp3 differ diff --git a/GUI/voices/brian.mp3 b/GUI/voices/brian.mp3 new file mode 100644 index 0000000..9304703 Binary files /dev/null and b/GUI/voices/brian.mp3 differ diff --git a/GUI/voices/de_001.mp3 b/GUI/voices/de_001.mp3 new file mode 100644 index 0000000..ef4a1b5 Binary files /dev/null and b/GUI/voices/de_001.mp3 differ diff --git a/GUI/voices/de_002.mp3 b/GUI/voices/de_002.mp3 new file mode 100644 index 0000000..4d7f89f Binary files /dev/null and b/GUI/voices/de_002.mp3 differ diff --git a/GUI/voices/emma.mp3 b/GUI/voices/emma.mp3 new file mode 100644 index 0000000..07895f0 Binary files /dev/null and b/GUI/voices/emma.mp3 differ diff --git a/GUI/voices/en_au_001.mp3 b/GUI/voices/en_au_001.mp3 new file mode 100644 index 0000000..b940e5c Binary files /dev/null and b/GUI/voices/en_au_001.mp3 differ diff --git a/GUI/voices/en_au_002.mp3 b/GUI/voices/en_au_002.mp3 new file mode 100644 index 0000000..a6421bb Binary files /dev/null and b/GUI/voices/en_au_002.mp3 differ diff --git a/GUI/voices/en_uk_001.mp3 b/GUI/voices/en_uk_001.mp3 new file mode 100644 index 0000000..666cac6 Binary files /dev/null and b/GUI/voices/en_uk_001.mp3 differ diff --git a/GUI/voices/en_uk_003.mp3 b/GUI/voices/en_uk_003.mp3 new file mode 100644 index 0000000..eecf6f6 Binary files /dev/null and b/GUI/voices/en_uk_003.mp3 differ diff --git a/GUI/voices/en_us_001.mp3 b/GUI/voices/en_us_001.mp3 new file mode 100644 index 0000000..56f1e01 Binary files /dev/null and b/GUI/voices/en_us_001.mp3 differ diff --git a/GUI/voices/en_us_002.mp3 b/GUI/voices/en_us_002.mp3 new file mode 100644 index 0000000..56f1e01 Binary files /dev/null and b/GUI/voices/en_us_002.mp3 differ diff --git a/GUI/voices/en_us_006.mp3 b/GUI/voices/en_us_006.mp3 new file mode 100644 index 0000000..f980665 Binary files /dev/null and b/GUI/voices/en_us_006.mp3 differ diff --git a/GUI/voices/en_us_007.mp3 b/GUI/voices/en_us_007.mp3 new file mode 100644 index 0000000..446a911 Binary files /dev/null and b/GUI/voices/en_us_007.mp3 differ diff --git a/GUI/voices/en_us_009.mp3 b/GUI/voices/en_us_009.mp3 new file mode 100644 index 0000000..2dd624d Binary files /dev/null and b/GUI/voices/en_us_009.mp3 differ diff --git a/GUI/voices/en_us_010.mp3 b/GUI/voices/en_us_010.mp3 new file mode 100644 index 0000000..34a37fe Binary files /dev/null and b/GUI/voices/en_us_010.mp3 differ diff --git a/GUI/voices/en_us_c3po.mp3 b/GUI/voices/en_us_c3po.mp3 new file mode 100644 index 0000000..eeabfac Binary files /dev/null and b/GUI/voices/en_us_c3po.mp3 differ diff --git a/GUI/voices/en_us_chewbacca.mp3 b/GUI/voices/en_us_chewbacca.mp3 new file mode 100644 index 0000000..6257d10 Binary files /dev/null and b/GUI/voices/en_us_chewbacca.mp3 differ diff --git a/GUI/voices/en_us_ghostface.mp3 b/GUI/voices/en_us_ghostface.mp3 new file mode 100644 index 0000000..0ad75be Binary files /dev/null and b/GUI/voices/en_us_ghostface.mp3 differ diff --git a/GUI/voices/en_us_rocket.mp3 b/GUI/voices/en_us_rocket.mp3 new file mode 100644 index 0000000..66a0356 Binary files /dev/null and b/GUI/voices/en_us_rocket.mp3 differ diff --git a/GUI/voices/en_us_stitch.mp3 b/GUI/voices/en_us_stitch.mp3 new file mode 100644 index 0000000..3c2fcc0 Binary files /dev/null and b/GUI/voices/en_us_stitch.mp3 differ diff --git a/GUI/voices/en_us_stormtrooper.mp3 b/GUI/voices/en_us_stormtrooper.mp3 new file mode 100644 index 0000000..8773ee4 Binary files /dev/null and b/GUI/voices/en_us_stormtrooper.mp3 differ diff --git a/GUI/voices/es_002.mp3 b/GUI/voices/es_002.mp3 new file mode 100644 index 0000000..4fb1d01 Binary files /dev/null and b/GUI/voices/es_002.mp3 differ diff --git a/GUI/voices/es_mx_002.mp3 b/GUI/voices/es_mx_002.mp3 new file mode 100644 index 0000000..18d0dfe Binary files /dev/null and b/GUI/voices/es_mx_002.mp3 differ diff --git a/GUI/voices/fr_001.mp3 b/GUI/voices/fr_001.mp3 new file mode 100644 index 0000000..deec4c8 Binary files /dev/null and b/GUI/voices/fr_001.mp3 differ diff --git a/GUI/voices/fr_002.mp3 b/GUI/voices/fr_002.mp3 new file mode 100644 index 0000000..c2b7512 Binary files /dev/null and b/GUI/voices/fr_002.mp3 differ diff --git a/GUI/voices/geraint.mp3 b/GUI/voices/geraint.mp3 new file mode 100644 index 0000000..403e0b6 Binary files /dev/null and b/GUI/voices/geraint.mp3 differ diff --git a/GUI/voices/id_001.mp3 b/GUI/voices/id_001.mp3 new file mode 100644 index 0000000..19febf9 Binary files /dev/null and b/GUI/voices/id_001.mp3 differ diff --git a/GUI/voices/ivy.mp3 b/GUI/voices/ivy.mp3 new file mode 100644 index 0000000..0d649c3 Binary files /dev/null and b/GUI/voices/ivy.mp3 differ diff --git a/GUI/voices/joanna.mp3 b/GUI/voices/joanna.mp3 new file mode 100644 index 0000000..f53b393 Binary files /dev/null and b/GUI/voices/joanna.mp3 differ diff --git a/GUI/voices/joey.mp3 b/GUI/voices/joey.mp3 new file mode 100644 index 0000000..d83c9d2 Binary files /dev/null and b/GUI/voices/joey.mp3 differ diff --git a/GUI/voices/jp_001.mp3 b/GUI/voices/jp_001.mp3 new file mode 100644 index 0000000..b6505cd Binary files /dev/null and b/GUI/voices/jp_001.mp3 differ diff --git a/GUI/voices/jp_003.mp3 b/GUI/voices/jp_003.mp3 new file mode 100644 index 0000000..1ef5782 Binary files /dev/null and b/GUI/voices/jp_003.mp3 differ diff --git a/GUI/voices/jp_005.mp3 b/GUI/voices/jp_005.mp3 new file mode 100644 index 0000000..5d3d5bf Binary files /dev/null and b/GUI/voices/jp_005.mp3 differ diff --git a/GUI/voices/jp_006.mp3 b/GUI/voices/jp_006.mp3 new file mode 100644 index 0000000..3e493fb Binary files /dev/null and b/GUI/voices/jp_006.mp3 differ diff --git a/GUI/voices/justin.mp3 b/GUI/voices/justin.mp3 new file mode 100644 index 0000000..1275f14 Binary files /dev/null and b/GUI/voices/justin.mp3 differ diff --git a/GUI/voices/kendra.mp3 b/GUI/voices/kendra.mp3 new file mode 100644 index 0000000..2bb35a8 Binary files /dev/null and b/GUI/voices/kendra.mp3 differ diff --git a/GUI/voices/kimberly.mp3 b/GUI/voices/kimberly.mp3 new file mode 100644 index 0000000..e51652c Binary files /dev/null and b/GUI/voices/kimberly.mp3 differ diff --git a/GUI/voices/kr_002.mp3 b/GUI/voices/kr_002.mp3 new file mode 100644 index 0000000..e9ef657 Binary files /dev/null and b/GUI/voices/kr_002.mp3 differ diff --git a/GUI/voices/kr_003.mp3 b/GUI/voices/kr_003.mp3 new file mode 100644 index 0000000..8411cd2 Binary files /dev/null and b/GUI/voices/kr_003.mp3 differ diff --git a/GUI/voices/kr_004.mp3 b/GUI/voices/kr_004.mp3 new file mode 100644 index 0000000..182b407 Binary files /dev/null and b/GUI/voices/kr_004.mp3 differ diff --git a/GUI/voices/matthew.mp3 b/GUI/voices/matthew.mp3 new file mode 100644 index 0000000..cce7967 Binary files /dev/null and b/GUI/voices/matthew.mp3 differ diff --git a/GUI/voices/nicole.mp3 b/GUI/voices/nicole.mp3 new file mode 100644 index 0000000..5e972e2 Binary files /dev/null and b/GUI/voices/nicole.mp3 differ diff --git a/GUI/voices/raveena.mp3 b/GUI/voices/raveena.mp3 new file mode 100644 index 0000000..558403f Binary files /dev/null and b/GUI/voices/raveena.mp3 differ diff --git a/GUI/voices/russell.mp3 b/GUI/voices/russell.mp3 new file mode 100644 index 0000000..b8d8e7a Binary files /dev/null and b/GUI/voices/russell.mp3 differ diff --git a/GUI/voices/salli.mp3 b/GUI/voices/salli.mp3 new file mode 100644 index 0000000..467f98d Binary files /dev/null and b/GUI/voices/salli.mp3 differ diff --git a/utils/.config.template.toml b/utils/.config.template.toml index 7e3f9ec..50cf413 100644 --- a/utils/.config.template.toml +++ b/utils/.config.template.toml @@ -25,16 +25,16 @@ storymode = { optional = true, type = "bool", default = false, example = false, [settings.background] -background_choice = { optional = true, default = "minecraft", example = "rocket-league", options = ["minecraft", "gta", "rocket-league", "motor-gta", "csgo-surf", "cluster-truck", ""], explanation = "Sets the background for the video based on game name" } +background_choice = { optional = true, default = "minecraft", example = "rocket-league", options = ["", "minecraft", "gta", "rocket-league", "motor-gta", "csgo-surf", "cluster-truck"], explanation = "Sets the background for the video based on game name" } #background_audio = { optional = true, type = "bool", default = false, example = false, options = [true, false,], explanation = "Sets a audio to play in the background (put a background.mp3 file in the assets/backgrounds directory for it to be used.)" } #background_audio_volume = { optional = true, type = "float", default = 0.3, example = 0.1, explanation="Sets the volume of the background audio. only used if the background_audio is also set to true" } [settings.tts] -voice_choice = { optional = false, default = "", options = ["streamlabspolly", "tiktok", "googletranslate", "awspolly", "pyttsx", ], example = "tiktok", explanation = "The voice platform used for TTS generation. This can be left blank and you will be prompted to choose at runtime." } +voice_choice = { optional = false, default = "tiktok", options = ["streamlabspolly", "tiktok", "googletranslate", "awspolly", "pyttsx", ], example = "tiktok", explanation = "The voice platform used for TTS generation. This can be left blank and you will be prompted to choose at runtime." } aws_polly_voice = { optional = false, default = "Matthew", example = "Matthew", explanation = "The voice used for AWS Polly" } streamlabs_polly_voice = { optional = false, default = "Matthew", example = "Matthew", explanation = "The voice used for Streamlabs Polly" } tiktok_voice = { optional = false, default = "en_us_006", example = "en_us_006", explanation = "The voice used for TikTok TTS" } python_voice = { optional = false, default = "1", example = "1", explanation = "The index of the system tts voices (can be downloaded externally, run ptt.py to find value, start from zero)" } -py_voice_num = { optional = false, default = "2", example = "2", explanation = "the number of system voices(2 are pre-installed in windows)" } -silence_duration = { optional = true, example = "0.1", explanation = "time in seconds between TTS comments", default = 0.3, type = "float" } \ No newline at end of file +py_voice_num = { optional = false, default = "2", example = "2", explanation = "The number of system voices (2 are pre-installed in Windows)" } +silence_duration = { optional = true, example = "0.1", explanation = "Time in seconds between TTS comments", default = 0.3, type = "float" } diff --git a/utils/gui_utils.py b/utils/gui_utils.py new file mode 100644 index 0000000..b539ff7 --- /dev/null +++ b/utils/gui_utils.py @@ -0,0 +1,226 @@ +import json +import re +from pathlib import Path + +import toml +import tomlkit +from flask import flash + + +# Get validation checks from template +def get_checks(): + template = toml.load("utils/.config.template.toml") + checks = {} + + def unpack_checks(obj: dict): + for key in obj.keys(): + if "optional" in obj[key].keys(): + checks[key] = obj[key] + else: + unpack_checks(obj[key]) + + unpack_checks(template) + + return checks + + +# Get current config (from config.toml) as dict +def get_config(obj: dict, done={}): + for key in obj.keys(): + if not isinstance(obj[key], dict): + done[key] = obj[key] + else: + get_config(obj[key], done) + + return done + + +# Checks if value is valid +def check(value, checks): + incorrect = False + + if value == "False": + value = "" + + if not incorrect and "type" in checks: + try: + value = eval(checks["type"])(value) + except Exception: + incorrect = True + + if ( + not incorrect and "options" in checks and value not in checks["options"] + ): # FAILSTATE Value is not one of the options + incorrect = True + if ( + not incorrect + and "regex" in checks + and ( + (isinstance(value, str) and re.match(checks["regex"], value) is None) + or not isinstance(value, str) + ) + ): # FAILSTATE Value doesn't match regex, or has regex but is not a string. + incorrect = True + + if ( + not incorrect + and not hasattr(value, "__iter__") + and ( + ("nmin" in checks and checks["nmin"] is not None and value < checks["nmin"]) + or ( + "nmax" in checks + and checks["nmax"] is not None + and value > checks["nmax"] + ) + ) + ): + incorrect = True + + if ( + not incorrect + and hasattr(value, "__iter__") + and ( + ( + "nmin" in checks + and checks["nmin"] is not None + and len(value) < checks["nmin"] + ) + or ( + "nmax" in checks + and checks["nmax"] is not None + and len(value) > checks["nmax"] + ) + ) + ): + incorrect = True + + if incorrect: + return "Error" + + return value + + +# Modify settings (after form is submitted) +def modify_settings(data: dict, config_load, checks: dict): + # Modify config settings + def modify_config(obj: dict, name: str, value: any): + for key in obj.keys(): + if name == key: + obj[key] = value + elif not isinstance(obj[key], dict): + continue + else: + modify_config(obj[key], name, value) + + # Remove empty/incorrect key-value pairs + data = {key: value for key, value in data.items() if value and key in checks.keys()} + + # Validate values + for name in data.keys(): + value = check(data[name], checks[name]) + + # Value is invalid + if value == "Error": + flash("Some values were incorrect and didn't save!", "error") + else: + # Value is valid + modify_config(config_load, name, value) + + # Save changes in config.toml + with Path("config.toml").open("w") as toml_file: + toml_file.write(tomlkit.dumps(config_load)) + + flash("Settings saved!") + + return get_config(config_load) + + +# Delete background video +def delete_background(key): + # Read backgrounds.json + with open("utils/backgrounds.json", "r", encoding="utf-8") as backgrounds: + data = json.load(backgrounds) + + # Remove background from backgrounds.json + with open("utils/backgrounds.json", "w", encoding="utf-8") as backgrounds: + if data.pop(key, None): + json.dump(data, backgrounds, ensure_ascii=False, indent=4) + else: + flash("Couldn't find this background. Try refreshing the page.", "error") + return + + # Remove background video from ".config.template.toml" + config = tomlkit.loads(Path("utils/.config.template.toml").read_text()) + config["settings"]["background"]["background_choice"]["options"].remove(key) + + with Path("utils/.config.template.toml").open("w") as toml_file: + toml_file.write(tomlkit.dumps(config)) + + flash(f'Successfully removed "{key}" background!') + + +# Add background video +def add_background(youtube_uri, filename, citation, position): + # Validate YouTube URI + regex = re.compile(r"(?:\/|%3D|v=|vi=)([0-9A-z-_]{11})(?:[%#?&]|$)").search( + youtube_uri + ) + + if not regex: + flash("YouTube URI is invalid!", "error") + return + + youtube_uri = f"https://www.youtube.com/watch?v={regex.group(1)}" + + # Check if position is valid + if position == "" or position == "center": + position = "center" + + elif position.isdecimal(): + position = int(position) + + else: + flash('Position is invalid! It can be "center" or decimal number.', "error") + return + + # Sanitize filename + regex = re.compile(r"^([a-zA-Z0-9\s_-]{1,100})$").match(filename) + + if not regex: + flash("Filename is invalid!", "error") + return + + filename = filename.replace(" ", "_") + + # Check if background doesn't already exist + with open("utils/backgrounds.json", "r", encoding="utf-8") as backgrounds: + data = json.load(backgrounds) + + # Check if key isn't already taken + if filename in list(data.keys()): + flash("Background video with this name already exist!", "error") + return + + # Check if the YouTube URI isn't already used under different name + if youtube_uri in [data[i][0] for i in list(data.keys())]: + flash("Background video with this YouTube URI is already added!", "error") + return + + # Add background video to json file + with open("utils/backgrounds.json", "r+", encoding="utf-8") as backgrounds: + data = json.load(backgrounds) + + data[filename] = [youtube_uri, filename + ".mp4", citation, position] + backgrounds.seek(0) + json.dump(data, backgrounds, ensure_ascii=False, indent=4) + + # Add background video to ".config.template.toml" + config = tomlkit.loads(Path("utils/.config.template.toml").read_text()) + config["settings"]["background"]["background_choice"]["options"].append(filename) + + with Path("utils/.config.template.toml").open("w") as toml_file: + toml_file.write(tomlkit.dumps(config)) + + flash(f'Added "{citation}-{filename}.mp4" as a new background video!') + + return