Added clips module.
Dependency Audit / Dependency Audit (push) Successful in 18s
CI / Formatting (push) Successful in 14s
CI / Linting (push) Successful in 15s
CI / Tests (Python 3.12) (push) Successful in 29s
CI / Tests (Python 3.13) (push) Successful in 28s
CI / Tests (Python 3.14) (push) Successful in 27s
CI / Type Checking (push) Successful in 25s
CI / Spelling (push) Successful in 15s
Dependency Audit / Dependency Audit (push) Successful in 18s
CI / Formatting (push) Successful in 14s
CI / Linting (push) Successful in 15s
CI / Tests (Python 3.12) (push) Successful in 29s
CI / Tests (Python 3.13) (push) Successful in 28s
CI / Tests (Python 3.14) (push) Successful in 27s
CI / Type Checking (push) Successful in 25s
CI / Spelling (push) Successful in 15s
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}{{ title or "Clip #" ~ clip_id }}{% endblock %}
|
||||
{% block content %}
|
||||
<nav aria-label="breadcrumb">
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item"><a href="{{ list_url }}">Clips</a></li>
|
||||
<li class="breadcrumb-item active" aria-current="page">{{ title or "Clip #" ~ clip_id }}</li>
|
||||
</ol>
|
||||
</nav>
|
||||
<h1 class="mb-3">{{ title or "Clip #" ~ clip_id }}</h1>
|
||||
<div class="text-body-secondary mb-3">
|
||||
<p>Created by <strong>{{ creator }}</strong> on <time datetime="{{ created_at }}">{{ created_at }}</time></p>
|
||||
</div>
|
||||
<video class="w-100 rounded" controls preload="metadata">
|
||||
<source src="{{ video_url }}" type="video/mp4">
|
||||
Your browser does not support the video tag.
|
||||
</video>
|
||||
{% endblock %}
|
||||
{% block scripts %}
|
||||
<script>
|
||||
document.querySelectorAll("time[datetime]").forEach(function(el) {
|
||||
var d = new Date(el.getAttribute("datetime"));
|
||||
var month = d.toLocaleString(undefined, { month: "long" });
|
||||
var day = d.getDate();
|
||||
var year = d.getFullYear();
|
||||
var time = d.toLocaleString(undefined, { hour: "2-digit", minute: "2-digit" });
|
||||
el.textContent = month + " " + day + ", " + year + " at " + time;
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,130 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}Clip Editor{% endblock %}
|
||||
{% block head %}
|
||||
<style>
|
||||
.slider-container {
|
||||
position: relative;
|
||||
height: 40px;
|
||||
background: var(--bs-tertiary-bg);
|
||||
border-radius: 8px;
|
||||
box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.2);
|
||||
user-select: none;
|
||||
touch-action: none;
|
||||
}
|
||||
.slider-track {
|
||||
position: absolute;
|
||||
top: 0; bottom: 0;
|
||||
background: rgba(var(--bs-primary-rgb), 0.15);
|
||||
border-top: 2px solid rgba(var(--bs-primary-rgb), 0.5);
|
||||
border-bottom: 2px solid rgba(var(--bs-primary-rgb), 0.5);
|
||||
cursor: grab;
|
||||
}
|
||||
.slider-handle {
|
||||
position: absolute;
|
||||
top: -4px; bottom: -4px;
|
||||
width: 12px;
|
||||
background: var(--bs-primary);
|
||||
border-radius: 4px;
|
||||
cursor: ew-resize;
|
||||
z-index: 2;
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
|
||||
transform: translateX(-50%);
|
||||
transition: transform 0.1s ease, box-shadow 0.1s ease;
|
||||
}
|
||||
.slider-handle:hover {
|
||||
transform: translateX(-50%) scaleY(1.05);
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
.slider-handle::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%; left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 4px; height: 16px;
|
||||
border-left: 1px solid rgba(255, 255, 255, 0.3);
|
||||
border-right: 1px solid rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
.slider-playhead {
|
||||
position: absolute;
|
||||
top: -2px; bottom: -2px;
|
||||
width: 2px;
|
||||
background: var(--bs-danger);
|
||||
border-radius: 1px;
|
||||
z-index: 3;
|
||||
pointer-events: none;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<nav aria-label="breadcrumb">
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item"><a href="{{ list_url }}">Clips</a></li>
|
||||
<li class="breadcrumb-item active" aria-current="page">Clip Editor</li>
|
||||
</ol>
|
||||
</nav>
|
||||
<h1 class="mb-3">Clip Editor</h1>
|
||||
|
||||
<div class="text-center py-5" id="loading">
|
||||
<div class="spinner-border mb-3" role="status">
|
||||
<span class="visually-hidden">Loading...</span>
|
||||
</div>
|
||||
<p>Loading preview...<br><span id="loading-pct"></span></p>
|
||||
</div>
|
||||
|
||||
<div id="processing" class="text-center py-5 d-none">
|
||||
<div class="spinner-border mb-3" role="status">
|
||||
<span class="visually-hidden">Creating clip...</span>
|
||||
</div>
|
||||
<p>Your clip is being created...<br>You will be redirected automatically when it is ready.</p>
|
||||
</div>
|
||||
|
||||
<div id="editor-controls" class="d-none">
|
||||
<video class="w-100 rounded mb-3" id="video"></video>
|
||||
|
||||
<div class="slider-container mb-3" id="slider">
|
||||
<div class="slider-track" id="track"></div>
|
||||
<div class="slider-handle start" id="handle-start"></div>
|
||||
<div class="slider-handle end" id="handle-end"></div>
|
||||
<div class="slider-playhead" id="playhead"></div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-between font-monospace small mb-3">
|
||||
<span>Start: <strong id="start-time">0:00</strong></span>
|
||||
<span>Length: <strong id="clip-length">0:00</strong></span>
|
||||
<span>End: <strong id="end-time">0:00</strong></span>
|
||||
</div>
|
||||
|
||||
<div class="text-center mb-3">
|
||||
<button type="button" class="btn btn-primary" id="btn-preview">Preview Clip</button>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="title" class="form-label">Title (optional)</label>
|
||||
<input type="text" class="form-control" id="title" name="title" placeholder="Enter a title for your clip" maxlength="200">
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<button type="button" class="btn btn-success" id="btn-create">Create Clip</button>
|
||||
</div>
|
||||
|
||||
<form id="submit-form" method="POST" action="{{ submit_url }}" class="d-none">
|
||||
<input type="hidden" name="start" id="form-start">
|
||||
<input type="hidden" name="end" id="form-end">
|
||||
<input type="hidden" name="title" id="form-title">
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block scripts %}
|
||||
<script>
|
||||
window.EDITOR_CONFIG = {
|
||||
previewUrl: {{ preview_url | tojson }},
|
||||
submitUrl: {{ submit_url | tojson }},
|
||||
listUrl: {{ list_url | tojson }},
|
||||
duration: {{ duration | tojson }},
|
||||
minLength: {{ min_length | tojson }},
|
||||
maxLength: {{ max_length | tojson }}
|
||||
};
|
||||
</script>
|
||||
<script src="{{ editor_js_url }}"></script>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,13 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}Error{% endblock %}
|
||||
{% block content %}
|
||||
<nav aria-label="breadcrumb">
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item"><a href="{{ list_url }}">Clips</a></li>
|
||||
<li class="breadcrumb-item active" aria-current="page">Error</li>
|
||||
</ol>
|
||||
</nav>
|
||||
<h1 class="mb-3">Error</h1>
|
||||
<div class="alert alert-danger" role="alert">{{ message }}</div>
|
||||
<a href="{{ list_url }}">Return to clips list</a>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,32 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}Clips{% endblock %}
|
||||
{% block head %}
|
||||
<style>
|
||||
.clip-thumb {
|
||||
aspect-ratio: 16 / 9;
|
||||
object-fit: cover;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<h1 class="mb-3">Clips</h1>
|
||||
{% if clips %}
|
||||
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 g-4">
|
||||
{% for clip in clips %}
|
||||
<div class="col">
|
||||
<a href="{{ clip.url }}" class="text-decoration-none">
|
||||
<div class="card h-100">
|
||||
<img src="{{ clip.thumbnail_url }}" class="card-img-top clip-thumb" alt="{{ clip.title }}">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">{{ clip.title }}</h5>
|
||||
<p class="card-text text-body-secondary">{{ clip.created_at }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<p>No clips have been created yet.</p>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user