- Added Upkeeping → Correct catalogue order.
It now recalculates video sort_order from created_at newest-first, so videos with corrected/original publish dates appear first in the homepage and public catalogue. I also updated catalogue/admin/latest-video ordering to use publish date as a tie-breaker after stored sort order.
This commit is contained in:
@@ -91,7 +91,7 @@ The homepage can show a **Videos / Live** switch above the main player. Configur
|
|||||||
|
|
||||||
Use **Admin → YouTube Importer** to paste a YouTube channel URL, `@handle`, or `UC...` channel ID. The importer scrapes the channel's `/videos` page, extracts video data from `ytInitialData`, previews the results, and imports selected videos as remote YouTube embed sources. Duplicate YouTube watch URLs are skipped.
|
Use **Admin → YouTube Importer** to paste a YouTube channel URL, `@handle`, or `UC...` channel ID. The importer scrapes the channel's `/videos` page, extracts video data from `ytInitialData`, previews the results, and imports selected videos as remote YouTube embed sources. Duplicate YouTube watch URLs are skipped.
|
||||||
|
|
||||||
Imported YouTube videos use the video's original publish date for the database `created_at` timestamp when YouTube exposes it. The admin dashboard also includes **Upkeeping → Correct YouTube timestamps** to backfill existing YouTube entries in batches.
|
Imported YouTube videos use the video's original publish date for the database `created_at` timestamp when YouTube exposes it. The admin dashboard also includes **Upkeeping → Correct YouTube timestamps** to backfill existing YouTube entries in batches. Use **Upkeeping → Correct catalogue order** to rebuild catalogue sorting from those publish dates so newer dated videos appear first.
|
||||||
|
|
||||||
### View Counts and Public Statistics
|
### View Counts and Public Statistics
|
||||||
|
|
||||||
@@ -131,7 +131,7 @@ Only published videos appear on the public-facing pages.
|
|||||||
|
|
||||||
### Sorting
|
### Sorting
|
||||||
|
|
||||||
Use the **Sort Order** field — lower numbers appear first. Default is 0.
|
Use the **Sort Order** field — lower numbers appear first. Default is 0. The **Upkeeping → Correct catalogue order** action can recalculate sort order from video publish dates, newest first.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+23
-1
@@ -59,6 +59,12 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
if ($result['not_found']) $msg .= ', ' . $result['not_found'] . ' without a readable external count';
|
if ($result['not_found']) $msg .= ', ' . $result['not_found'] . ' without a readable external count';
|
||||||
$msg .= '.';
|
$msg .= '.';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($action === 'correct_catalogue_order') {
|
||||||
|
$result = media_correct_catalogue_sort_order();
|
||||||
|
$msg = 'Upkeeping checked ' . $result['checked'] . ' video' . ($result['checked'] === 1 ? '' : 's') . ': ';
|
||||||
|
$msg .= $result['updated'] . ' video' . ($result['updated'] === 1 ? '' : 's') . ' reordered newest-first by publish date.';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Search + pagination
|
// Search + pagination
|
||||||
@@ -85,7 +91,7 @@ $list_stmt = $db->prepare("
|
|||||||
LEFT JOIN video_sources vs ON vs.video_id = v.id
|
LEFT JOIN video_sources vs ON vs.video_id = v.id
|
||||||
$where_sql
|
$where_sql
|
||||||
GROUP BY v.id
|
GROUP BY v.id
|
||||||
ORDER BY v.sort_order ASC, v.id DESC
|
ORDER BY v.sort_order ASC, datetime(v.created_at) DESC, v.created_at DESC, v.id DESC
|
||||||
LIMIT :limit OFFSET :offset
|
LIMIT :limit OFFSET :offset
|
||||||
");
|
");
|
||||||
foreach ($bind as $k => $val) $list_stmt->bindValue($k, $val);
|
foreach ($bind as $k => $val) $list_stmt->bindValue($k, $val);
|
||||||
@@ -95,6 +101,7 @@ $list_stmt->execute();
|
|||||||
$videos = $list_stmt->fetchAll();
|
$videos = $list_stmt->fetchAll();
|
||||||
$youtube_timestamp_candidates = media_youtube_timestamp_candidate_count();
|
$youtube_timestamp_candidates = media_youtube_timestamp_candidate_count();
|
||||||
$external_view_candidates = media_external_view_count_candidate_count();
|
$external_view_candidates = media_external_view_count_candidate_count();
|
||||||
|
$catalogue_order_candidates = media_catalogue_order_candidate_count();
|
||||||
$admin_total_views = total_video_views(false);
|
$admin_total_views = total_video_views(false);
|
||||||
|
|
||||||
$qs = fn($p) => '?' . http_build_query(array_filter(['q' => $search, 'page' => $p]));
|
$qs = fn($p) => '?' . http_build_query(array_filter(['q' => $search, 'page' => $p]));
|
||||||
@@ -236,6 +243,21 @@ render_head('Admin — TyClifford.com', '
|
|||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="upkeep-row">
|
||||||
|
<div class="upkeep-copy">
|
||||||
|
<h2 class="card-title">Correct catalogue order</h2>
|
||||||
|
<p class="hint">
|
||||||
|
Rebuilds video sort order from the database publish dates so newer dated videos appear first in the homepage and public catalogue.
|
||||||
|
<?= $catalogue_order_candidates ?> video<?= $catalogue_order_candidates === 1 ? '' : 's' ?> can be reordered.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<form class="upkeep-form" method="post" action="index.php">
|
||||||
|
<input type="hidden" name="action" value="correct_catalogue_order">
|
||||||
|
<button class="btn btn-secondary btn-sm" type="submit">
|
||||||
|
Correct Order
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
<div class="upkeep-row">
|
<div class="upkeep-row">
|
||||||
<div class="upkeep-copy">
|
<div class="upkeep-copy">
|
||||||
<h2 class="card-title">Correct external view counts</h2>
|
<h2 class="card-title">Correct external view counts</h2>
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ All notable changes to this media player project.
|
|||||||
- Added an **Upkeeping** section to the admin dashboard for batch maintenance actions.
|
- Added an **Upkeeping** section to the admin dashboard for batch maintenance actions.
|
||||||
- Added per-video local view counts, external provider view counts, total public view counts, and configurable public statistics display.
|
- Added per-video local view counts, external provider view counts, total public view counts, and configurable public statistics display.
|
||||||
- Added upkeep actions to correct external view counts in batches.
|
- Added upkeep actions to correct external view counts in batches.
|
||||||
|
- Added an upkeep action to correct catalogue order from video publish dates so newer dated videos appear first.
|
||||||
- Added public search for published videos.
|
- Added public search for published videos.
|
||||||
- Added configurable URL rewriting, defaulting to `/v/<slug>`, `/all`, and `/search/<query>`.
|
- Added configurable URL rewriting, defaulting to `/v/<slug>`, `/all`, and `/search/<query>`.
|
||||||
- Added a configurable homepage live section with a Videos/Live toggle and default Stream.Place embed support.
|
- Added a configurable homepage live section with a Videos/Live toggle and default Stream.Place embed support.
|
||||||
@@ -28,6 +29,7 @@ All notable changes to this media player project.
|
|||||||
- Public display settings now control per-video counts, total counts, and page statistics.
|
- Public display settings now control per-video counts, total counts, and page statistics.
|
||||||
- Admin video creation and editing now attempt to fill blank metadata from external media information.
|
- Admin video creation and editing now attempt to fill blank metadata from external media information.
|
||||||
- The main player now chooses between local media playback, direct remote media playback, and provider embeds based on stored source data.
|
- The main player now chooses between local media playback, direct remote media playback, and provider embeds based on stored source data.
|
||||||
|
- Catalogue ordering now uses publish date as a tie-breaker after stored sort order.
|
||||||
- README documentation now reflects the current remote URL, provider embed, live section, view count, and pretty URL behavior.
|
- README documentation now reflects the current remote URL, provider embed, live section, view count, and pretty URL behavior.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|||||||
+38
-1
@@ -56,6 +56,7 @@ function get_db(): PDO {
|
|||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_videos_published ON videos(published);
|
CREATE INDEX IF NOT EXISTS idx_videos_published ON videos(published);
|
||||||
CREATE INDEX IF NOT EXISTS idx_videos_sort ON videos(sort_order, id);
|
CREATE INDEX IF NOT EXISTS idx_videos_sort ON videos(sort_order, id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_videos_created ON videos(created_at, id);
|
||||||
CREATE INDEX IF NOT EXISTS idx_videos_title ON videos(title);
|
CREATE INDEX IF NOT EXISTS idx_videos_title ON videos(title);
|
||||||
CREATE INDEX IF NOT EXISTS idx_sources_video ON video_sources(video_id);
|
CREATE INDEX IF NOT EXISTS idx_sources_video ON video_sources(video_id);
|
||||||
");
|
");
|
||||||
@@ -768,6 +769,42 @@ function media_correct_external_view_counts(int $limit = 25): array {
|
|||||||
return media_restore_external_view_counts($limit);
|
return media_restore_external_view_counts($limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function media_catalogue_order_candidate_count(): int {
|
||||||
|
return (int)get_db()->query("SELECT COUNT(*) FROM videos")->fetchColumn();
|
||||||
|
}
|
||||||
|
|
||||||
|
function media_correct_catalogue_sort_order(): array {
|
||||||
|
$db = get_db();
|
||||||
|
$videos = $db->query("
|
||||||
|
SELECT id, sort_order
|
||||||
|
FROM videos
|
||||||
|
ORDER BY datetime(created_at) DESC, created_at DESC, id DESC
|
||||||
|
")->fetchAll();
|
||||||
|
|
||||||
|
$result = [
|
||||||
|
'checked' => count($videos),
|
||||||
|
'updated' => 0,
|
||||||
|
];
|
||||||
|
if (!$videos) return $result;
|
||||||
|
|
||||||
|
$stmt = $db->prepare("UPDATE videos SET sort_order=?, updated_at=datetime('now') WHERE id=?");
|
||||||
|
$db->beginTransaction();
|
||||||
|
try {
|
||||||
|
foreach ($videos as $index => $video) {
|
||||||
|
$sort_order = $index * 10;
|
||||||
|
if ((int)$video['sort_order'] === $sort_order) continue;
|
||||||
|
$stmt->execute([$sort_order, (int)$video['id']]);
|
||||||
|
$result['updated']++;
|
||||||
|
}
|
||||||
|
$db->commit();
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
$db->rollBack();
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
function media_youtube_source_url_for_video(int $video_id): string {
|
function media_youtube_source_url_for_video(int $video_id): string {
|
||||||
$stmt = get_db()->prepare("
|
$stmt = get_db()->prepare("
|
||||||
SELECT source_url
|
SELECT source_url
|
||||||
@@ -1436,7 +1473,7 @@ function get_videos_paginated(int $page, int $per_page, bool $published_only = t
|
|||||||
LEFT JOIN video_sources vs ON vs.video_id = v.id
|
LEFT JOIN video_sources vs ON vs.video_id = v.id
|
||||||
$where
|
$where
|
||||||
GROUP BY v.id
|
GROUP BY v.id
|
||||||
ORDER BY v.sort_order ASC, v.id DESC
|
ORDER BY v.sort_order ASC, datetime(v.created_at) DESC, v.created_at DESC, v.id DESC
|
||||||
LIMIT :limit OFFSET :offset
|
LIMIT :limit OFFSET :offset
|
||||||
");
|
");
|
||||||
foreach ($bind as $key => $value) $rows->bindValue($key, $value);
|
foreach ($bind as $key => $value) $rows->bindValue($key, $value);
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ $video = $slug ? get_video_by_slug($slug) : null;
|
|||||||
// Latest video as default if none specified
|
// Latest video as default if none specified
|
||||||
if (!$video) {
|
if (!$video) {
|
||||||
$db = get_db();
|
$db = get_db();
|
||||||
$latest = $db->query("SELECT slug FROM videos WHERE published=1 ORDER BY sort_order ASC, id DESC LIMIT 1")->fetch();
|
$latest = $db->query("SELECT slug FROM videos WHERE published=1 ORDER BY sort_order ASC, datetime(created_at) DESC, created_at DESC, id DESC LIMIT 1")->fetch();
|
||||||
if ($latest) $video = get_video_by_slug($latest['slug']);
|
if ($latest) $video = get_video_by_slug($latest['slug']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user