- Done. The media platform now tracks sources as either local uploads or remote URLs.
Changed: [includes/db.php (line 43)](/Users/tyemeclifford/Documents/GH/mediaplayer/includes/db.php:43) adds source_type and source_url, with an automatic migration for existing SQLite databases. [admin/add.php (line 260)](/Users/tyemeclifford/Documents/GH/mediaplayer/admin/add.php:260) and [admin/edit.php (line 383)](/Users/tyemeclifford/Documents/GH/mediaplayer/admin/edit.php:383) now let each source be an upload or an external URL. [index.php (line 226)](/Users/tyemeclifford/Documents/GH/mediaplayer/index.php:226) and [embed.php (line 193)](/Users/tyemeclifford/Documents/GH/mediaplayer/embed.php:193) resolve playback URLs based on where the source lives. [catalogue.php (line 79)](/Users/tyemeclifford/Documents/GH/mediaplayer/catalogue.php:79) now has public search for published videos only. [README.md (line 63)](/Users/tyemeclifford/Documents/GH/mediaplayer/README.md:63) documents local vs remote sources and public search.
This commit is contained in:
+89
-24
@@ -45,42 +45,76 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
|
||||
$video_id = (int)$db->lastInsertId();
|
||||
|
||||
// Handle video source files
|
||||
// Handle local upload and remote URL sources
|
||||
$source_types = $_POST['source_type'] ?? [];
|
||||
$source_labels = $_POST['source_label'] ?? [];
|
||||
$source_quality = $_POST['source_quality'] ?? [];
|
||||
$source_order = $_POST['source_sort'] ?? [];
|
||||
$source_urls = $_POST['source_url'] ?? [];
|
||||
$source_files = $_FILES['source_file'] ?? [];
|
||||
|
||||
$videos_dir = MEDIA_DIR . 'videos/';
|
||||
if (!is_dir($videos_dir)) mkdir($videos_dir, 0755, true);
|
||||
|
||||
$inserted_sources = 0;
|
||||
$moved_files = [];
|
||||
$allowed_video = ['mp4','webm','ogv','ogg','mov','mkv','avi'];
|
||||
|
||||
if (!empty($source_files['tmp_name'])) {
|
||||
foreach ($source_files['tmp_name'] as $i => $tmp) {
|
||||
if (empty($tmp) || !is_uploaded_file($tmp)) continue;
|
||||
$orig_name = $source_files['name'][$i];
|
||||
$ext = strtolower(pathinfo($orig_name, PATHINFO_EXTENSION));
|
||||
if (!in_array($ext, $allowed_video)) continue;
|
||||
$source_count = max(
|
||||
count($source_types),
|
||||
count($source_labels),
|
||||
count($source_urls),
|
||||
isset($source_files['tmp_name']) && is_array($source_files['tmp_name']) ? count($source_files['tmp_name']) : 0
|
||||
);
|
||||
|
||||
$fname = $slug . '_' . ($i+1) . '_' . time() . '.' . $ext;
|
||||
if (move_uploaded_file($tmp, $videos_dir . $fname)) {
|
||||
$label = trim($source_labels[$i] ?? '');
|
||||
$quality = trim($source_quality[$i] ?? '');
|
||||
$mime = mime_from_ext($fname);
|
||||
$sort = (int)($source_order[$i] ?? $i);
|
||||
$db->prepare("
|
||||
INSERT INTO video_sources (video_id, label, file_path, mime_type, quality, sort_order)
|
||||
VALUES (?,?,?,?,?,?)
|
||||
")->execute([$video_id, $label, 'videos/' . $fname, $mime, $quality, $sort]);
|
||||
$inserted_sources++;
|
||||
for ($i = 0; $i < $source_count; $i++) {
|
||||
$type = ($source_types[$i] ?? 'local') === 'remote' ? 'remote' : 'local';
|
||||
$label = trim($source_labels[$i] ?? '');
|
||||
$quality = trim($source_quality[$i] ?? '');
|
||||
$sort = (int)($source_order[$i] ?? $i);
|
||||
|
||||
if ($type === 'remote') {
|
||||
$url = normalize_media_url($source_urls[$i] ?? '');
|
||||
if ($url === '') {
|
||||
if (trim($source_urls[$i] ?? '') !== '') $error = 'External video URLs must start with http:// or https://.';
|
||||
continue;
|
||||
}
|
||||
|
||||
$db->prepare("
|
||||
INSERT INTO video_sources (video_id, label, source_type, file_path, source_url, mime_type, quality, sort_order)
|
||||
VALUES (?,?,?,?,?,?,?,?)
|
||||
")->execute([$video_id, $label, 'remote', '', $url, mime_from_ext($url), $quality, $sort]);
|
||||
$inserted_sources++;
|
||||
continue;
|
||||
}
|
||||
|
||||
$tmp = $source_files['tmp_name'][$i] ?? '';
|
||||
if (empty($tmp) || !is_uploaded_file($tmp)) continue;
|
||||
$orig_name = $source_files['name'][$i] ?? '';
|
||||
$ext = strtolower(pathinfo($orig_name, PATHINFO_EXTENSION));
|
||||
if (!in_array($ext, $allowed_video, true)) continue;
|
||||
|
||||
$fname = $slug . '_' . ($i+1) . '_' . time() . '.' . $ext;
|
||||
if (move_uploaded_file($tmp, $videos_dir . $fname)) {
|
||||
$moved_files[] = $videos_dir . $fname;
|
||||
$db->prepare("
|
||||
INSERT INTO video_sources (video_id, label, source_type, file_path, source_url, mime_type, quality, sort_order)
|
||||
VALUES (?,?,?,?,?,?,?,?)
|
||||
")->execute([$video_id, $label, 'local', 'videos/' . $fname, '', mime_from_ext($fname), $quality, $sort]);
|
||||
$inserted_sources++;
|
||||
}
|
||||
}
|
||||
|
||||
if ($error) {
|
||||
foreach ($moved_files as $moved) {
|
||||
if (file_exists($moved)) unlink($moved);
|
||||
}
|
||||
if ($thumbnail && file_exists(MEDIA_DIR . $thumbnail)) unlink(MEDIA_DIR . $thumbnail);
|
||||
$db->prepare("DELETE FROM videos WHERE id=?")->execute([$video_id]);
|
||||
} else {
|
||||
header('Location: edit.php?id=' . $video_id . '&saved=1');
|
||||
exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -94,11 +128,13 @@ render_head('Add Video — Admin', '
|
||||
.source-row {
|
||||
background:var(--surface-2); border:1px solid var(--border);
|
||||
border-radius:var(--r); padding:1rem;
|
||||
display:grid; grid-template-columns:1fr 1fr 1fr auto;
|
||||
display:grid; grid-template-columns:140px minmax(220px,1.5fr) 1fr 1fr 80px auto;
|
||||
gap:.75rem; align-items:end;
|
||||
}
|
||||
@media(max-width:700px) { .source-row { grid-template-columns:1fr 1fr; } }
|
||||
@media(max-width:900px) { .source-row { grid-template-columns:1fr 1fr; } }
|
||||
@media(max-width:560px) { .source-row { grid-template-columns:1fr; } }
|
||||
.add-source-btn { align-self:flex-start; }
|
||||
.source-location-panel[hidden] { display:none; }
|
||||
|
||||
.section-divider {
|
||||
border:none; border-top:1px solid var(--border); margin:1.5rem 0 .5rem;
|
||||
@@ -221,21 +257,32 @@ render_head('Add Video — Admin', '
|
||||
|
||||
<!-- ── Video sources ── -->
|
||||
<div class="card">
|
||||
<p class="section-heading">Video Sources (Multiple Formats)</p>
|
||||
<p class="section-heading">Video Sources</p>
|
||||
<p class="hint" style="margin-bottom:1rem;">
|
||||
Upload the same video in multiple formats/qualities. The browser will automatically pick the best one it can play.
|
||||
Accepted: <strong>MP4, WebM, OGV, MOV, MKV, AVI</strong>.
|
||||
Upload a video file or link to an external video URL. The database stores where each source lives, and the player uses that location at playback time.
|
||||
Uploaded files: <strong>MP4, WebM, OGV, MOV, MKV, AVI</strong>. Remote URLs must use <strong>http://</strong> or <strong>https://</strong>.
|
||||
</p>
|
||||
|
||||
<div class="sources-section" id="sources-container">
|
||||
<!-- Row template (JS clones this) -->
|
||||
<div class="source-row" data-source-row="0">
|
||||
<div class="form-group">
|
||||
<label class="form-label">Source</label>
|
||||
<select class="form-select source-type-select" name="source_type[]">
|
||||
<option value="local" selected>Upload</option>
|
||||
<option value="remote">External URL</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group source-location-panel source-location-local">
|
||||
<label class="form-label">Video File</label>
|
||||
<input class="form-input" type="file" name="source_file[]"
|
||||
accept="video/mp4,video/webm,video/ogg,.mp4,.webm,.ogv,.mov,.mkv,.avi"
|
||||
style="padding:.45rem .6rem;">
|
||||
</div>
|
||||
<div class="form-group source-location-panel source-location-remote" hidden>
|
||||
<label class="form-label">External URL</label>
|
||||
<input class="form-input" type="url" name="source_url[]" placeholder="https://example.com/video.mp4">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Quality Label</label>
|
||||
<select class="form-select" name="source_quality[]">
|
||||
@@ -260,7 +307,7 @@ render_head('Add Video — Admin', '
|
||||
|
||||
<button type="button" class="btn btn-secondary btn-sm add-source-btn" id="add-source" style="margin-top:.75rem;align-self:flex-start;">
|
||||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
|
||||
Add Another Format
|
||||
Add Another Source
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -280,6 +327,19 @@ render_head('Add Video — Admin', '
|
||||
</main>
|
||||
|
||||
<script>
|
||||
function toggleSourceRow(row) {
|
||||
var select = row.querySelector('.source-type-select');
|
||||
var isRemote = select && select.value === 'remote';
|
||||
row.querySelectorAll('.source-location-local').forEach(function(el){ el.hidden = isRemote; });
|
||||
row.querySelectorAll('.source-location-remote').forEach(function(el){ el.hidden = !isRemote; });
|
||||
}
|
||||
|
||||
document.querySelectorAll('[data-source-row]').forEach(function(row) {
|
||||
toggleSourceRow(row);
|
||||
var select = row.querySelector('.source-type-select');
|
||||
if (select) select.addEventListener('change', function(){ toggleSourceRow(row); });
|
||||
});
|
||||
|
||||
document.getElementById('add-source').addEventListener('click', function() {
|
||||
var container = document.getElementById('sources-container');
|
||||
var rows = container.querySelectorAll('[data-source-row]');
|
||||
@@ -289,7 +349,9 @@ document.getElementById('add-source').addEventListener('click', function() {
|
||||
// Clear file inputs (can't clone values for file inputs)
|
||||
first.querySelectorAll('input[type="file"]').forEach(function(el){ el.value=''; });
|
||||
first.querySelectorAll('input[type="text"]').forEach(function(el){ el.value=''; });
|
||||
first.querySelectorAll('input[type="url"]').forEach(function(el){ el.value=''; });
|
||||
first.querySelectorAll('input[type="number"]').forEach(function(el){ el.value=idx; });
|
||||
first.querySelectorAll('.source-type-select').forEach(function(el){ el.value='local'; });
|
||||
// Add remove button if not already there
|
||||
if (!first.querySelector('.remove-row')) {
|
||||
var btn = document.createElement('button');
|
||||
@@ -300,6 +362,9 @@ document.getElementById('add-source').addEventListener('click', function() {
|
||||
btn.onclick=function(){ this.closest('[data-source-row]').remove(); };
|
||||
first.appendChild(btn);
|
||||
}
|
||||
toggleSourceRow(first);
|
||||
var select = first.querySelector('.source-type-select');
|
||||
if (select) select.addEventListener('change', function(){ toggleSourceRow(first); });
|
||||
container.appendChild(first);
|
||||
});
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user