- Improved the upload/edit flow to auto-grab thumbnails.

What changed:
Added shared thumbnail helpers in 
[includes/db.php](/Users/tyemeclifford/Documents/GH/mediaplayer/includes/db.php):YouTube 
direct thumbnails
Dailymotion direct thumbnails
oEmbed thumbnails for Vimeo, Wistia, TikTok, SoundCloud, Spotify, etc.
optional first-frame thumbnail generation for local uploads when ffmpeg 
is installed

Added an “Auto-grab thumbnail” checkbox to 
[admin/add.php](/Users/tyemeclifford/Documents/GH/mediaplayer/admin/add.php) 
and 
[admin/edit.php](/Users/tyemeclifford/Documents/GH/mediaplayer/admin/edit.php).
Reused the new shared downloader in 
[admin/youtube_import.php](/Users/tyemeclifford/Documents/GH/mediaplayer/admin/youtube_import.php).
Documented the behavior in 
[README.md](/Users/tyemeclifford/Documents/GH/mediaplayer/README.md).
This commit is contained in:
Ty Clifford
2026-06-24 13:32:14 -04:00
parent 7ad9c672ca
commit 99492287e7
5 changed files with 264 additions and 24 deletions
+15 -3
View File
@@ -13,6 +13,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$duration = (int)($_POST['duration'] ?? 0);
$sort_order = (int)($_POST['sort_order'] ?? 0);
$published = isset($_POST['published']) ? 1 : 0;
$auto_thumb = isset($_POST['auto_thumbnail']);
if (!$title) {
$error = 'Title is required.';
@@ -112,8 +113,15 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
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;
if ($thumbnail === '' && $auto_thumb) {
$thumbnail = media_auto_thumbnail_for_video($video_id, $slug);
if ($thumbnail !== '') {
$db->prepare("UPDATE videos SET thumbnail=? WHERE id=?")->execute([$thumbnail, $video_id]);
}
}
header('Location: edit.php?id=' . $video_id . '&saved=1');
exit;
}
}
}
@@ -247,7 +255,11 @@ render_head('Add Video — Admin', '
<label class="form-label" for="thumbnail">Thumbnail Image</label>
<input class="form-input" type="file" id="thumbnail" name="thumbnail"
accept="image/jpeg,image/png,image/webp,image/gif" style="padding:.45rem .6rem;">
<span class="hint">JPG, PNG, WebP or GIF. Shown as 16:9 preview.</span>
<label style="display:flex;align-items:center;gap:.55rem;margin-top:.55rem;cursor:pointer;">
<input type="checkbox" name="auto_thumbnail" value="1" <?= isset($_POST['auto_thumbnail']) || $_SERVER['REQUEST_METHOD'] !== 'POST' ? 'checked' : '' ?>>
<span style="font-size:.78rem;color:var(--text-2);">Auto-grab thumbnail from source if no image is uploaded</span>
</label>
<span class="hint">JPG, PNG, WebP or GIF. Auto-grab supports YouTube, Vimeo, Dailymotion and other oEmbed providers; local uploads use ffmpeg when available.</span>
</div>
<div class="form-group" style="justify-content:flex-end;padding-bottom:.2rem;">
<label style="display:flex;align-items:center;gap:.6rem;cursor:pointer;">
+17 -1
View File
@@ -43,6 +43,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$duration = (int)($_POST['duration'] ?? 0);
$sort_order = (int)($_POST['sort_order'] ?? 0);
$published = isset($_POST['published']) ? 1 : 0;
$auto_thumb = isset($_POST['auto_thumbnail']);
if (!$title) {
$error = 'Title is required.';
@@ -51,6 +52,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// Handle thumbnail replacement
$thumbnail = $video['thumbnail'];
$uploaded_thumbnail = false;
if (!empty($_FILES['thumbnail']['tmp_name'])) {
$ext = strtolower(pathinfo($_FILES['thumbnail']['name'], PATHINFO_EXTENSION));
$allowed = ['jpg','jpeg','png','webp','gif'];
@@ -62,6 +64,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$fname = $slug . '_thumb_' . time() . '.' . $ext;
if (move_uploaded_file($_FILES['thumbnail']['tmp_name'], $thumb_dir . $fname)) {
$thumbnail = 'thumbs/' . $fname;
$uploaded_thumbnail = true;
}
} else {
$error = 'Thumbnail must be JPG, PNG, WebP, or GIF.';
@@ -178,6 +181,15 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (file_exists($moved)) unlink($moved);
}
} else {
if ($auto_thumb && !$uploaded_thumbnail) {
$auto_thumbnail = media_auto_thumbnail_for_video($id, $slug);
if ($auto_thumbnail !== '') {
if ($thumbnail && file_exists(MEDIA_DIR . $thumbnail)) unlink(MEDIA_DIR . $thumbnail);
$thumbnail = $auto_thumbnail;
$db->prepare("UPDATE videos SET thumbnail=?, updated_at=datetime('now') WHERE id=?")->execute([$thumbnail, $id]);
}
}
header('Location: edit.php?id=' . $id . '&saved=1');
exit;
}
@@ -329,7 +341,11 @@ render_head('Edit Video — Admin', '
<?php endif; ?>
<input class="form-input" type="file" id="thumbnail" name="thumbnail"
accept="image/jpeg,image/png,image/webp,image/gif" style="padding:.45rem .6rem;">
<span class="hint">Upload new image to replace current thumbnail.</span>
<label style="display:flex;align-items:center;gap:.55rem;margin-top:.55rem;cursor:pointer;">
<input type="checkbox" name="auto_thumbnail" value="1" <?= isset($_POST['auto_thumbnail']) || ($_SERVER['REQUEST_METHOD'] !== 'POST' && !$video['thumbnail']) ? 'checked' : '' ?>>
<span style="font-size:.78rem;color:var(--text-2);">Auto-grab/refresh thumbnail from source on save</span>
</label>
<span class="hint">Upload a new image to override auto-grab. Auto-grab supports YouTube, Vimeo, Dailymotion and other oEmbed providers; local uploads use ffmpeg when available.</span>
</div>
</div>
</div>
+1 -14
View File
@@ -464,20 +464,7 @@ function yt_scrape_channel_videos(string $input, int $limit): array {
}
function yt_download_thumbnail(string $url, string $slug): string {
if ($url === '') return '';
$fetch = yt_http_get($url, 12);
if ($fetch['body'] === '' || $fetch['error']) return '';
if (function_exists('getimagesizefromstring') && !@getimagesizefromstring($fetch['body'])) return '';
$content_type = strtolower((string)$fetch['content_type']);
$ext = 'jpg';
if (str_contains($content_type, 'png')) $ext = 'png';
elseif (str_contains($content_type, 'webp')) $ext = 'webp';
$thumb_dir = MEDIA_DIR . 'thumbs/';
if (!is_dir($thumb_dir)) mkdir($thumb_dir, 0755, true);
$fname = $slug . '_yt_' . time() . '.' . $ext;
return file_put_contents($thumb_dir . $fname, $fetch['body']) !== false ? 'thumbs/' . $fname : '';
return media_download_thumbnail($url, $slug, 'yt');
}
function yt_import_videos(PDO $db, array $videos, array $selected_ids, bool $published): array {