<?php
/**
* SeekQuarry/Yioop --
* Open Source Pure PHP Search Engine, Crawler, and Indexer
*
* Copyright (C) 2009 - 2026 Chris Pollett chris@pollett.org
*
* LICENSE:
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* END LICENSE
*
* @author Chris Pollett chris@pollett.org
* @license https://www.gnu.org/licenses/ GPL3
* @link https://www.seekquarry.com/
* @copyright 2009 - 2026
* @filesource
*/
namespace seekquarry\yioop\library\processors;
use seekquarry\yioop\configs as C;
use seekquarry\yioop\library as L;
use seekquarry\yioop\library\av_processing\VideoExtractor;
/**
* Base abstract class common to all processors used to create crawl summary
* information from videos
*
* @author Chris Pollett
*/
class VideoProcessor extends PageProcessor
{
/**
* Number of images to use for an animated thumbnail
*/
/**
* How many seconds into a video the still thumbnail is taken from,
* where the video is long enough to reach it. The opening frame of a
* video is often black or a title card, so a moment a little way in
* shows more of what the video is.
* @var int
*/
const STILL_PICTURE_TIME = 3;
/**
* How long each picture of a moving thumbnail is shown, in
* thousandths of a second. One second a picture is slow enough to
* read at a glance in a list of videos.
* @var int
*/
const MOVING_PICTURE_DELAY = 1000;
const NUM_ANIMATED_THUMBS = 10;
/**
* Minimum duration movie (in seconds ) before make an animated thumbnail
*/
const MIN_ANIMATE_LENGTH = 60;
/**
* Extract summary data from the image provided in $page together the url
* in $url where it was downloaded from
*
* VideoProcessor class defers a proper implementation of this method to
* subclasses
*
* @param string $page the image represented as a character string
* @param string $url the url where the image was downloaded from
* @return array summary information including a thumbnail and a
* description (where the description is just the url)
*/
public function process($page, $url)
{
return null;
}
/**
* Used to save a temporary file with the data downloaded for a url
* while carrying out image processing
*
* @param string $page contains data about an image that one needs to save
* @param string $url where $page data came from
* @param string $file_extension to be associated with the $page data
* @return mixed temporary filesystem path the video was written to;
* null when the temp directory could not be created
*/
public function saveTempFile($page, $url, $file_extension)
{
static $call_count = 0;
$temp_dir = C\TEMP_DIR . "/";
if (!file_exists($temp_dir)) {
mkdir($temp_dir);
}
if (!file_exists($temp_dir)) {
return null;
}
$temp_file = $temp_dir . $call_count .
L\crawlHash($url) . ".$file_extension";
$call_count++;
file_put_contents($temp_file, $page);
return $temp_file;
}
/**
* createThumbs writes the thumbnails for a video into a thumb
* folder. It writes a still picture taken a little way in, and, for
* a video long enough to warrant one, a moving picture built from
* frames spread across its length. Both are WebP, the moving one
* under a name ending MOVING_THUMB_ENDING, which says a picture
* moves. Yioop's own video library reads the file, so no outside
* program is started. A video the library cannot read leaves no
* thumbnail rather than stopping the caller.
*
* @param string $folder with video in it
* @param string $thumb_folder folder to generate
* @param string $file_name of video file in $folder
* @param int $width = width in pixels of thumb
* @param int $height = height in pixels of thumb
* @param int $num_frames number of frames to put in the moving thumb
* @param int $min_animate_length minimum duration of movie to
* try to make a moving thumb for
*/
public static function createThumbs($folder, $thumb_folder, $file_name,
$width = C\THUMB_DIM, $height = -1,
$num_frames = self::NUM_ANIMATED_THUMBS,
$min_animate_length = self::MIN_ANIMATE_LENGTH)
{
if (!function_exists("imagecreatetruecolor")) {
return;
}
if (file_exists("$thumb_folder/$file_name.webp")) {
@unlink("$thumb_folder/$file_name.webp");
}
$moving_name = "$thumb_folder/$file_name" . C\MOVING_THUMB_ENDING;
if (file_exists($moving_name)) {
@unlink($moving_name);
}
try {
$reader = VideoExtractor::open("$folder/$file_name");
$duration = $reader->durationSeconds();
} catch (\Throwable $trouble) {
return;
}
if ($duration <= 0) {
return;
}
$num_thumbs = ($duration > $min_animate_length) ? $num_frames : 1;
/* The still picture is taken a little way in, since the opening
frame of a video is often black. */
$thumb_time = min(ceil($duration / 2), self::STILL_PICTURE_TIME);
try {
$picture = $reader->thumbnail($thumb_time, $width);
} catch (\Throwable $trouble) {
return;
}
if ($picture === false) {
return;
}
imagewebp($picture, "$thumb_folder/$file_name.webp");
clearstatcache(true, "$thumb_folder/$file_name.webp");
if ($num_thumbs <= 1) {
return;
}
/* A video long enough to be worth a moving picture gets one,
built from frames spread across its length. */
try {
$moving = $reader->animatedThumbnail($num_thumbs,
self::MOVING_PICTURE_DELAY, $width);
} catch (\Throwable $trouble) {
return;
}
if ($moving === "") {
return;
}
/* The library writes a moving picture as an animated WebP, and
the name says so: a served file's type is read from the last
characters of its name, so a name ending .gif would be sent as
image/gif whatever the bytes are. */
file_put_contents($moving_name, $moving);
clearstatcache(true, $moving_name);
}
}