Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions aggro-db.sql
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ CREATE TABLE `aggro_videos` (
`flag_bad` int(1) NOT NULL DEFAULT '0',
`flag_favorite` int(1) NOT NULL DEFAULT '0',
`notes` text,
`thumbnail_issue_count` int NOT NULL DEFAULT '0',
PRIMARY KEY (`aggro_id`),
UNIQUE KEY `videoid` (`video_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
Expand Down
5 changes: 5 additions & 0 deletions app/Controllers/Aggro.php
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,11 @@ public function getSweep(): bool|ResponseInterface
echo "\nThumbnails cleaned up.\n";
}

$flagged = $aggroModel->flagBrokenThumbnails();
if ($flagged > 0) {
echo "\n" . $flagged . " broken thumbnail(s) flagged.\n";
}

return true;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

namespace App\Database\Migrations;

use CodeIgniter\Database\Migration;

class AddThumbnailIssueCount extends Migration
{
public function up()
{
$this->forge->addColumn('aggro_videos', [
'thumbnail_issue_count' => [
'type' => 'INT',
'null' => false,
'default' => 0,
],
]);
}

public function down()
{
$this->forge->dropColumn('aggro_videos', 'thumbnail_issue_count');
}
}
43 changes: 24 additions & 19 deletions app/Helpers/aggro_helper.php
Original file line number Diff line number Diff line change
Expand Up @@ -199,20 +199,22 @@ function fetch_feed(string $feed, bool|int $spoof, ?int $cache = null)
/**
* Fetch thumbnail image from video provider, process image, and save locally.
*
* @param string $videoid
* The videoid.
* @param string $thumbnail
* The remote URL of the video thumbnail.
* @param string $videoid
* The videoid.
* @param string $thumbnail
* The remote URL of the video thumbnail.
* @param int|null &$httpStatus
* Optional. Populated with the HTTP status code from the fetch.
*
* @return bool
* Video thumbnail fetched and processed.
*/
function fetch_thumbnail($videoid, $thumbnail)
function fetch_thumbnail($videoid, $thumbnail, &$httpStatus = null)
{
helper('aggro');
$storageConfig = config('Storage');
$path = $storageConfig->getThumbnailPath($videoid);
$buffer = fetch_url($thumbnail);
$buffer = fetch_url($thumbnail, 'text', 0, $httpStatus);

if (! empty($buffer)) {
$file = fopen($path, 'wb');
Expand Down Expand Up @@ -259,20 +261,22 @@ function fetch_thumbnail($videoid, $thumbnail)
/**
* Fetch contents of URL (via CURL). Decode if XML or JSON.
*
* @param string $url
* URL to be fetched.
* @param string $format
* Format to be returned:
* - text: return as text, no decoding.
* - simplexml: return as decoded XML.
* - json: return as decoded JSON.
* @param string $spoof
* Spoof user agent string (1/0).
* @param string $url
* URL to be fetched.
* @param string $format
* Format to be returned:
* - text: return as text, no decoding.
* - simplexml: return as decoded XML.
* - json: return as decoded JSON.
* @param string $spoof
* Spoof user agent string (1/0).
* @param int|null &$httpStatus
* Optional. Populated with the HTTP response code.
*
* @return string
* Contents of requested url with optional decoding.
*/
function fetch_url($url, $format = 'text', $spoof = 0)
function fetch_url($url, $format = 'text', $spoof = 0, &$httpStatus = null)
{
$storageConfig = config('Storage');
$agent = env('UA_BMXFEED', 'Aggro/1.0');
Expand All @@ -286,9 +290,10 @@ function fetch_url($url, $format = 'text', $spoof = 0)
curl_setopt($fetch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($fetch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($fetch, CURLOPT_MAXREDIRS, $storageConfig->urlMaxRedirects);
$response = curl_exec($fetch);
$httpCode = curl_getinfo($fetch, CURLINFO_HTTP_CODE);
$errorInfo = curl_error($fetch);
$response = curl_exec($fetch);
$httpCode = curl_getinfo($fetch, CURLINFO_HTTP_CODE);
$httpStatus = $httpCode;
$errorInfo = curl_error($fetch);
curl_close($fetch);

if ($httpCode === 403 || $httpCode === 404 || $httpCode === 500) {
Expand Down
10 changes: 10 additions & 0 deletions app/Models/AggroModels.php
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,16 @@ public function cleanThumbs()
return $this->thumbnailService->cleanThumbs();
}

/**
* Flag videos with chronic thumbnail failures as bad.
*
* @return int Number of videos flagged
*/
public function flagBrokenThumbnails()
{
return $this->thumbnailService->flagBrokenThumbnails();
}

/**
* Get list of video channels that haven't been updated within timeframe.
*
Expand Down
69 changes: 67 additions & 2 deletions app/Services/ThumbnailService.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,16 @@ public function checkThumbs()
continue;
}

$message = $thumb->video_id . ' missing thumbnail';
if (fetch_thumbnail($thumb->video_id, $thumb->video_thumbnail_url)) {
$httpStatus = null;
$message = $thumb->video_id . ' missing thumbnail';

if (fetch_thumbnail($thumb->video_id, $thumb->video_thumbnail_url, $httpStatus)) {
$message .= ' — fetched.';
$this->resetThumbnailIssueCount($thumb->video_id);
} elseif ($httpStatus === 404) {
$this->incrementThumbnailIssueCount($thumb->video_id);
}

$this->utilityModel->sendLog($message);
}

Expand All @@ -63,6 +69,40 @@ public function checkThumbs()
return true;
}

/**
* Flag videos with chronic thumbnail failures as bad.
*
* Videos with thumbnail_issue_count above the threshold
* are permanently flagged bad so they stop being fetched,
* displayed, and retried.
*
* @return int Number of videos flagged
*/
public function flagBrokenThumbnails()
{
$threshold = 10;

$videos = $this->db->table('aggro_videos')
->select('video_id')
->where('thumbnail_issue_count >', $threshold)
->where('flag_bad', 0)
->get()
->getResult();

$count = 0;

foreach ($videos as $video) {
$this->db->table('aggro_videos')
->where('video_id', $video->video_id)
->update(['flag_bad' => 1]);

log_message('error', 'Flagged video ' . $video->video_id . ' as bad — thumbnail 404 count exceeded threshold (' . $threshold . ').');
$count++;
}

return $count;
}

/**
* Clean thumbnail directory.
*
Expand Down Expand Up @@ -161,4 +201,29 @@ private function logCleanupResults($deletedCount, $errorCount)

$this->utilityModel->sendLog($message);
}

/**
* Increment the thumbnail issue count for a video.
*
* @param string $videoId The video ID
*/
private function incrementThumbnailIssueCount($videoId)
{
$this->db->table('aggro_videos')
->where('video_id', $videoId)
->set('thumbnail_issue_count', 'thumbnail_issue_count + 1', false)
->update();
}

/**
* Reset the thumbnail issue count for a video.
*
* @param string $videoId The video ID
*/
private function resetThumbnailIssueCount($videoId)
{
$this->db->table('aggro_videos')
->where('video_id', $videoId)
->update(['thumbnail_issue_count' => 0]);
}
}
Loading