diff --git a/GUI.py b/GUI.py index bc41838..a658e60 100644 --- a/GUI.py +++ b/GUI.py @@ -1,32 +1,128 @@ -# Import the server module -import http.server +import json +import re import webbrowser +from pathlib import Path + +# Used "tomlkit" instead of "toml" because it doesn't change formatting on "dump" +import tomlkit +from flask import ( + Flask, + flash, + redirect, + render_template, + request, + send_from_directory, + url_for, +) # Set the hostname HOST = "localhost" # Set the port number PORT = 4000 -# Define class to display the index page of the web server -class PythonServer(http.server.SimpleHTTPRequestHandler): - def do_GET(self): - if self.path == "/GUI": - self.path = "index.html" - return http.server.SimpleHTTPRequestHandler.do_GET(self) - - -# Declare object of the class -webServer = http.server.HTTPServer((HOST, PORT), PythonServer) -# Print the URL of the webserver, new =2 opens in a new tab -print(f"Server started at http://{HOST}:{PORT}/GUI/") -webbrowser.open(f"http://{HOST}:{PORT}/GUI/", new=2) -print("Website opened in new tab") -print("Press Ctrl+C to quit") -try: - # Run the web server - webServer.serve_forever() -except KeyboardInterrupt: - # Stop the web server - webServer.server_close() - print("The server is stopped.") - exit() +# Configure application +app = Flask(__name__, template_folder="GUI") + +# Configure secret key only to use 'flash' +app.secret_key = b'_5#y2L"F4Q8z\n\xec]/' + + +# Ensure responses aren't cached +@app.after_request +def after_request(response): + response.headers["Cache-Control"] = "no-cache, no-store, must-revalidate" + response.headers["Expires"] = 0 + response.headers["Pragma"] = "no-cache" + return response + + +# Display index.html +@app.route("/") +def index(): + return render_template("index.html") + + +# Make videos.json accessible +@app.route("/videos.json") +def videos_json(): + return send_from_directory("video_creation/data", "videos.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"(?:https?:\/\/)?(?:www\.)?youtu\.?be(?:\.com)?\/?.*(?:watch|embed)?(?:.*v=|v\/|\/)([\w\-_]+)\&?" + ).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")) + + +# Run browser and start the app +if __name__ == "__main__": + webbrowser.open(f"http://{HOST}:{PORT}", new=2) + print("Website opened in new tab. Refresh if it didn't load.") + app.run(port=PORT) diff --git a/GUI/index.html b/GUI/index.html index c771adc..00deeb7 100644 --- a/GUI/index.html +++ b/GUI/index.html @@ -1,141 +1,263 @@ - - - - - RedditVideoMakerBot - - - - - - - - - -
- -
+ .nav-scroller { + position: relative; + z-index: 2; + height: 2.75rem; + overflow-y: hidden; + } -
-
-
+ .nav-scroller .nav { + display: flex; + flex-wrap: nowrap; + padding-bottom: 1rem; + margin-top: -1px; + overflow-x: auto; + text-align: center; + white-space: nowrap; + -webkit-overflow-scrolling: touch; + } -
-
- + #tooltip { + background-color: #333; + color: white; + padding: 5px 10px; + border-radius: 4px; + font-size: 13px; + } + + + + + +
+ {% 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 diff --git a/requirements.txt b/requirements.txt index a954c20..084ab29 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,3 +11,5 @@ toml==0.10.2 translators==5.3.1 pyttsx3==2.90 Pillow~=9.1.1 +tomlkit==0.11.4 +Flask==2.2.2 diff --git a/utils/CONSTANTS.py b/utils/CONSTANTS.py deleted file mode 100644 index e46ebbc..0000000 --- a/utils/CONSTANTS.py +++ /dev/null @@ -1,45 +0,0 @@ -# Supported Background. Can add/remove background video here.... -# - : key -> used as keyword for TOML file. value -> background configuration -# Format (value): -# 1. Youtube URI -# 2. filename -# 3. Citation (owner of the video) -# 4. Position of image clips in the background. See moviepy reference for more information. (https://zulko.github.io/moviepy/ref/VideoClip/VideoClip.html#moviepy.video.VideoClip.VideoClip.set_position) -background_options = { - "motor-gta": ( # Motor-GTA Racing - "https://www.youtube.com/watch?v=vw5L4xCPy9Q", - "bike-parkour-gta.mp4", - "Achy Gaming", - lambda t: ("center", 480 + t), - ), - "rocket-league": ( # Rocket League - "https://www.youtube.com/watch?v=2X9QGY__0II", - "rocket_league.mp4", - "Orbital Gameplay", - lambda t: ("center", 200 + t), - ), - "minecraft": ( # Minecraft parkour - "https://www.youtube.com/watch?v=n_Dv4JMiwK8", - "parkour.mp4", - "bbswitzer", - "center", - ), - "gta": ( # GTA Stunt Race - "https://www.youtube.com/watch?v=qGa9kWREOnE", - "gta-stunt-race.mp4", - "Achy Gaming", - lambda t: ("center", 480 + t), - ), - "csgo-surf": ( # CSGO Surf - "https://www.youtube.com/watch?v=E-8JlyO59Io", - "csgo-surf.mp4", - "Aki", - "center", - ), - "cluster-truck": ( # Cluster Truck Gameplay - "https://www.youtube.com/watch?v=uVKxtdMgJVU", - "cluster_truck.mp4", - "No Copyright Gameplay", - lambda t: ("center", 480 + t), - ), -} diff --git a/utils/backgrounds.json b/utils/backgrounds.json new file mode 100644 index 0000000..a8d8d41 --- /dev/null +++ b/utils/backgrounds.json @@ -0,0 +1,38 @@ +{ + "motor-gta": [ + "https://www.youtube.com/watch?v=vw5L4xCPy9Q", + "bike-parkour-gta.mp4", + "Achy Gaming", + 480 + ], + "rocket-league": [ + "https://www.youtube.com/watch?v=2X9QGY__0II", + "rocket_league.mp4", + "Orbital Gameplay", + 200 + ], + "minecraft": [ + "https://www.youtube.com/watch?v=n_Dv4JMiwK8", + "parkour.mp4", + "bbswitzer", + "center" + ], + "gta": [ + "https://www.youtube.com/watch?v=qGa9kWREOnE", + "gta-stunt-race.mp4", + "Achy Gaming", + 480 + ], + "csgo-surf": [ + "https://www.youtube.com/watch?v=E-8JlyO59Io", + "csgo-surf.mp4", + "Aki", + "center" + ], + "cluster-truck": [ + "https://www.youtube.com/watch?v=uVKxtdMgJVU", + "cluster_truck.mp4", + "No Copyright Gameplay", + 480 + ] +} \ No newline at end of file diff --git a/video_creation/background.py b/video_creation/background.py index f0f38d5..2521fcc 100644 --- a/video_creation/background.py +++ b/video_creation/background.py @@ -1,3 +1,4 @@ +import json import random import re from pathlib import Path @@ -8,11 +9,23 @@ from moviepy.editor import VideoFileClip from moviepy.video.io.ffmpeg_tools import ffmpeg_extract_subclip from pytube import YouTube from pytube.cli import on_progress - from utils import settings -from utils.CONSTANTS import background_options from utils.console import print_step, print_substep +# Supported Background. +# (https://zulko.github.io/moviepy/ref/VideoClip/VideoClip.html#moviepy.video.VideoClip.VideoClip.set_position) + +# Load background videos +with open("utils/backgrounds.json") as json_file: + background_options = json.load(json_file) + +# Add position lambda function +for name in list(background_options.keys()): + pos = background_options[name][3] + + if pos != "center": + background_options[name][3] = lambda t: ("center", pos + t) + def get_start_and_end_times(video_length: int, length_of_clip: int) -> Tuple[int, int]: """Generates a random interval of time to be used as the background of the video.