diff --git a/README.md b/README.md index 721530c..b9ffe78 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,8 @@ Styled to match the existing `gate.php` / `index.php` dark theme exactly. External URLs must use `http://` or `https://`. Uploaded files and remote URLs can be mixed on the same video. Recognized provider URLs such as YouTube, Vimeo, Dailymotion, Imgur, Twitch, Facebook, TikTok, Instagram, X/Twitter, SoundCloud, Spotify, Google Drive, Wistia, Streamable, and Loom render through their embed players. +Imgur links use the official Imgur embed script inside a local wrapper, so album, gallery, image, GIF, GIFV, and video URLs can render without direct iframe blocking. + When adding an external URL, the admin form can fetch title, description, duration, thumbnail metadata, and readable provider view counts through oEmbed, page metadata, and JSON-LD. The save action repeats the metadata lookup as a fallback, so blank titles can be filled from supported external pages even if the browser autofill was not used. If no thumbnail image is uploaded, the add/edit forms can auto-grab one from the first supported source. YouTube and Dailymotion use direct thumbnail URLs; Vimeo, Wistia, TikTok, SoundCloud, and Spotify use oEmbed when available. Local uploaded videos can generate a first-frame thumbnail when `ffmpeg` is installed on the server. diff --git a/admin/index.php b/admin/index.php index 4309753..30b6c80 100644 --- a/admin/index.php +++ b/admin/index.php @@ -95,6 +95,7 @@ $list_stmt->execute(); $videos = $list_stmt->fetchAll(); $youtube_timestamp_candidates = media_youtube_timestamp_candidate_count(); $external_view_candidates = media_external_view_count_candidate_count(); +$imgur_view_candidates = media_imgur_view_count_candidate_count(); $admin_total_views = total_video_views(false); $qs = fn($p) => '?' . http_build_query(array_filter(['q' => $search, 'page' => $p])); @@ -241,7 +242,8 @@ render_head('Admin — TyClifford.com', '

Correct external view counts

Scrapes readable counts from supported providers and adds them to the media-site totals alongside local plays. - external video can be checked. + external video can be checked, + including Imgur-backed item. Current database total: .

diff --git a/includes/db.php b/includes/db.php index e31e0bb..07f4835 100644 --- a/includes/db.php +++ b/includes/db.php @@ -543,8 +543,12 @@ function media_imgur_page_url(string $url): string { } function media_imgur_embed_url(string $url): string { - $page = media_imgur_page_url($url); - return $page !== '' ? rtrim($page, '/') . '/embed' : ''; + $path = media_imgur_path_from_url($url); + if ($path === '') return ''; + return media_append_query(public_url_path('/provider_embed.php'), [ + 'provider' => 'imgur', + 'id' => $path, + ]); } function media_normalize_datetime(string $value): string { @@ -677,6 +681,26 @@ function media_view_count_from_html(string $html): int { return 0; } +function media_imgur_view_count_from_html(string $html): int { + $count = media_view_count_from_html($html); + if ($count > 0) return $count; + + $html = html_entity_decode($html, ENT_QUOTES | ENT_HTML5, 'UTF-8'); + $patterns = [ + '/\bviews?\b[^0-9]{0,80}([0-9][0-9,.\s]*\s*[KMBkmb]?)/i', + '/([0-9][0-9,.\s]*\s*[KMBkmb]?)\s+\bviews?\b/i', + '/"views_count"\s*:\s*"?(\d+)"?/i', + '/"viewsCount"\s*:\s*"?(\d+)"?/i', + ]; + foreach ($patterns as $pattern) { + if (!preg_match($pattern, $html, $match)) continue; + $count = media_count_from_text($match[1]); + if ($count > 0) return $count; + } + + return 0; +} + function media_youtube_view_count_from_html(string $html): int { return media_view_count_from_html($html); } @@ -708,6 +732,10 @@ function media_external_view_count_from_url(string $url): int { $fetch = media_http_get($url, 15, 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'); if ($fetch['body'] === '' || $fetch['error'] !== '') return 0; + if (media_host_matches($host, 'imgur.com') || media_host_matches($host, 'i.imgur.com')) { + return media_imgur_view_count_from_html($fetch['body']); + } + return media_view_count_from_html($fetch['body']); } @@ -784,6 +812,19 @@ function media_external_view_count_candidate_count(): int { ")->fetchColumn(); } +function media_imgur_view_count_candidate_count(): int { + return (int)get_db()->query(" + SELECT COUNT(DISTINCT v.id) + FROM videos v + INNER JOIN video_sources vs ON vs.video_id = v.id + WHERE vs.source_type='remote' + AND ( + vs.source_url LIKE '%imgur.com%' + OR vs.source_url LIKE '%i.imgur.com%' + ) + ")->fetchColumn(); +} + function media_restore_external_view_counts(int $limit = 25): array { $result = [ 'checked' => 0, diff --git a/provider_embed.php b/provider_embed.php new file mode 100644 index 0000000..b18a93d --- /dev/null +++ b/provider_embed.php @@ -0,0 +1,68 @@ + + + + + + + + <?= h($title) ?> + + + +
+ View on Imgur +
+ + +