initial commmit
A video player controller for the video player node
This commit is contained in:
@@ -0,0 +1,253 @@
|
||||
from flask import Flask, render_template_string, request, redirect, url_for, flash
|
||||
import os
|
||||
|
||||
from controller_core import enqueue_download
|
||||
|
||||
app = Flask(__name__)
|
||||
app.secret_key = os.environ.get("FLASK_SECRET_KEY", "dev-secret")
|
||||
|
||||
HTML_FORM = """
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>YouTube Playlist Submit</title>
|
||||
|
||||
<!-- Make it behave on mobile -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
||||
|
||||
<style>
|
||||
:root {
|
||||
color-scheme: dark;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background: #020617;
|
||||
color: #e5e7eb;
|
||||
}
|
||||
|
||||
.page-wrapper {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 1.25rem;
|
||||
}
|
||||
|
||||
.card {
|
||||
width: 100%;
|
||||
max-width: 520px;
|
||||
background: rgba(15, 23, 42, 0.98);
|
||||
padding: 1.75rem 1.5rem 1.4rem;
|
||||
border-radius: 1rem;
|
||||
box-shadow: 0 18px 45px rgba(0, 0, 0, 0.7);
|
||||
border: 1px solid rgba(148, 163, 184, 0.35);
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin: 0 0 0.4rem;
|
||||
font-size: 1.35rem;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
margin: 0 0 1.1rem;
|
||||
font-size: 0.95rem;
|
||||
color: #9ca3af;
|
||||
}
|
||||
|
||||
.flash {
|
||||
margin-bottom: 0.75rem;
|
||||
padding: 0.55rem 0.75rem;
|
||||
border-radius: 0.6rem;
|
||||
background: rgba(30, 64, 175, 0.35);
|
||||
border: 1px solid rgba(129, 140, 248, 0.7);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 0.85rem;
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
margin-bottom: 0.3rem;
|
||||
font-size: 0.92rem;
|
||||
}
|
||||
|
||||
input,
|
||||
textarea {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 0.7rem 0.85rem;
|
||||
border-radius: 0.65rem;
|
||||
border: 1px solid #4b5563;
|
||||
background: #020617;
|
||||
color: #e5e7eb;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
input::placeholder,
|
||||
textarea::placeholder {
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
input:focus,
|
||||
textarea:focus {
|
||||
outline: 2px solid #38bdf8;
|
||||
outline-offset: 2px;
|
||||
border-color: #38bdf8;
|
||||
}
|
||||
|
||||
textarea {
|
||||
min-height: 3.2rem;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
button {
|
||||
width: 100%;
|
||||
padding: 0.8rem 1.5rem;
|
||||
border-radius: 999px;
|
||||
border: none;
|
||||
background: linear-gradient(135deg, #38bdf8, #6366f1);
|
||||
color: #fff;
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
cursor: pointer;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-top: 0.2rem;
|
||||
}
|
||||
|
||||
button:active {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
.helper {
|
||||
margin-top: 0.9rem;
|
||||
font-size: 0.8rem;
|
||||
color: #9ca3af;
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.page-wrapper {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.card {
|
||||
padding: 1.4rem 1.1rem 1.2rem;
|
||||
border-radius: 0.9rem;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
input,
|
||||
textarea {
|
||||
font-size: 1.02rem;
|
||||
}
|
||||
|
||||
button {
|
||||
font-size: 1.02rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="page-wrapper">
|
||||
<div class="card">
|
||||
<h2>Submit YouTube Playlist</h2>
|
||||
<p class="subtitle">
|
||||
Paste a playlist link below. It’ll be queued for download and assigned to the next free slot.
|
||||
</p>
|
||||
|
||||
{% with messages = get_flashed_messages() %}
|
||||
{% if messages %}
|
||||
{% for m in messages %}
|
||||
<div class="flash">{{ m }}</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
|
||||
<form method="post" action="{{ url_for('submit') }}">
|
||||
<div class="form-group">
|
||||
<label for="url">YouTube URL</label>
|
||||
<input
|
||||
type="url"
|
||||
id="url"
|
||||
name="url"
|
||||
required
|
||||
placeholder="https://www.youtube.com/playlist?list=..."
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="submitter">Your name (optional)</label>
|
||||
<input
|
||||
type="text"
|
||||
id="submitter"
|
||||
name="submitter"
|
||||
placeholder="Simon Peter"
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="note">Note (optional)</label>
|
||||
<textarea
|
||||
id="note"
|
||||
name="note"
|
||||
rows="3"
|
||||
placeholder="Service, event, etc."
|
||||
></textarea>
|
||||
</div>
|
||||
|
||||
<button type="submit">Submit Playlist</button>
|
||||
</form>
|
||||
|
||||
<p class="helper">
|
||||
Playlists are kept for roughly 48 hours after they’re downloaded.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
@app.route("/", methods=["GET"])
|
||||
def index():
|
||||
return render_template_string(HTML_FORM)
|
||||
|
||||
|
||||
@app.route("/submit", methods=["POST"])
|
||||
def submit():
|
||||
url = request.form.get("url", "").strip()
|
||||
submitter = request.form.get("submitter", "").strip() or None
|
||||
note = request.form.get("note", "").strip() or None
|
||||
|
||||
if not url.startswith("http"):
|
||||
flash("Invalid URL.")
|
||||
return redirect(url_for("index"))
|
||||
|
||||
result = enqueue_download(url, submitter, note)
|
||||
|
||||
if result.get("status") == "queue_full":
|
||||
secs = result.get("oldest_expires_in_seconds", 0)
|
||||
hours = max(1, secs // 3600)
|
||||
flash(f"Queue is full. Oldest playlist expires in about {hours} hour(s).")
|
||||
else:
|
||||
slot = result.get("slot")
|
||||
msg = f"Playlist queued in slot {slot}."
|
||||
if result.get("reused_slot"):
|
||||
msg += " (Existing slot – only new videos will be added.)"
|
||||
else:
|
||||
msg += " It will start downloading shortly."
|
||||
flash(msg)
|
||||
|
||||
return redirect(url_for("index"))
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(host="0.0.0.0", port=int(os.environ.get("PORT", 7531)))
|
||||
Reference in New Issue
Block a user