Skip to content

Commit

Permalink
Fix get observation date videos (#398)
Browse files Browse the repository at this point in the history
* Add traceback to logException

* Selecting count(*) is faster than count(id)

* Update HelioviewerMovie

added exceptions for functions that can't be called before the movie is processed.
added tests

* Use TEST_LAYER

* Throw an exception in _getTimeStamps on failure
  • Loading branch information
dgarciabriseno authored Jul 16, 2024
1 parent a11245d commit 2bfb71e
Show file tree
Hide file tree
Showing 5 changed files with 282 additions and 37 deletions.
14 changes: 7 additions & 7 deletions src/Database/ImgIndex.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,12 @@ public function insertScreenshot($date, $imageScale, $roi, $watermark, $layers,

// old implementation removed for events strings
// used to be $this->events->serialize();
$old_events_layer_string = "";
$old_events_layer_string = "";

// old if events labels are shown switch , removed for new implementation
// used to be $this->eventsLabels;
$old_events_labels_bool = false;
$old_events_labels_bool = false;


$sql = sprintf(
"INSERT INTO screenshots "
Expand Down Expand Up @@ -108,7 +108,7 @@ public function insertScreenshot($date, $imageScale, $roi, $watermark, $layers,
try {
$result = $this->_dbConnection->query($sql);
} catch (Exception $e) {
throw new \Exception("Could not create screenshot in our database", 2, $e);
throw new \Exception("Could not create screenshot in our database", 2, $e);
}

return $this->_dbConnection->getInsertId();
Expand Down Expand Up @@ -516,7 +516,7 @@ protected function _getGroupForSourceId($sourceId) {
*
* @return array Array containing 1 next image and 1 prev date image
*/
public function getClosestDataBeforeAndAfter($date, $sourceId)
public function getClosestDataBeforeAndAfter($date, $sourceId)
{
include_once HV_ROOT_DIR.'/../src/Helper/DateTimeConversions.php';

Expand Down Expand Up @@ -764,15 +764,15 @@ public function getDataCount($start, $end, $sourceId, $switchSources = false) {

if($dataSplit){
$sql = sprintf(
"SELECT COUNT(id) as count FROM data WHERE (".$this->getDatasourceIDsString($sourceId)." AND date BETWEEN '%s' AND '%s') OR (".$this->getDatasourceIDsString($sourceId2)." AND date BETWEEN '%s' AND '%s') LIMIT 1;",
"SELECT COUNT(*) as count FROM data WHERE (".$this->getDatasourceIDsString($sourceId)." AND date BETWEEN '%s' AND '%s') OR (".$this->getDatasourceIDsString($sourceId2)." AND date BETWEEN '%s' AND '%s') LIMIT 1;",
$this->_dbConnection->link->real_escape_string($startDate),
$this->_dbConnection->link->real_escape_string($middleDate),
$this->_dbConnection->link->real_escape_string($middleDate),
$this->_dbConnection->link->real_escape_string($endDate)
);
}else{
$sql = sprintf(
"SELECT COUNT(id) as count FROM data WHERE ".$this->getDatasourceIDsString($sourceId)." AND date BETWEEN '%s' AND '%s' LIMIT 1;",
"SELECT COUNT(*) as count FROM data WHERE ".$this->getDatasourceIDsString($sourceId)." AND date BETWEEN '%s' AND '%s' LIMIT 1;",
$this->_dbConnection->link->real_escape_string($startDate),
$this->_dbConnection->link->real_escape_string($endDate)
);
Expand Down
2 changes: 1 addition & 1 deletion src/Helper/ErrorHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ function handleError($msg, $errorCode=255) {
}

function logException(Exception $exception, string $prefix='') {
$message = $exception->getFile() . ":" . $exception->getLine() . " - " . $exception->getMessage();
$message = $exception->getFile() . ":" . $exception->getLine() . " - " . $exception->getMessage() . "\n" . $exception->getTraceAsString();
logErrorMsg($message, $prefix);
}

Expand Down
13 changes: 9 additions & 4 deletions src/Module/Movies.php
Original file line number Diff line number Diff line change
Expand Up @@ -1294,10 +1294,15 @@ public function isYouTubeVideoExist($videoID) {
}

/**
*
*
* Checks if the given movie exists on disk.
* If the movie doesn't exist and allowRegeneration is true, then the movie
* will be queued for processing.
*/
public function _verifyMediaExists($movie, $allowRegeneration=true) {
public function _verifyMediaExists(Movie_HelioviewerMovie $movie, $allowRegeneration=true): bool {
// If the movie isn't complete, it's guaranteed to not exist
if (!$movie->isComplete()) {
return false;
}

// Check for missing movie or preview images
$media_exists = true;
Expand Down Expand Up @@ -1354,7 +1359,7 @@ public function playMovie() {
// Check that the movie (in the requested format) as well as
// its thumbnail images exist on disk. If not, silently
// queue the movie for re-generation.
$this->_verifyMediaExists($movie, $allowRegeneration=true);
$this->_verifyMediaExists($movie, true);

// Default options
$defaults = array(
Expand Down
103 changes: 78 additions & 25 deletions src/Movie/HelioviewerMovie.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,13 @@

use Helioviewer\Api\Event\EventsStateManager;

/**
* Exception to throw when performing an operation on a movie instance
* that hasn't been processed into a movie.
*/
class MovieNotCompletedException extends Exception {}
class MovieLookupException extends Exception {}

class Movie_HelioviewerMovie {
const STATUS_QUEUED = 0;
const STATUS_PROCESSING = 1;
Expand Down Expand Up @@ -158,7 +165,7 @@ public function __construct($publicId, $format='mp4') {

// ATTENTION! These two fields eventsLabels and eventSourceString needs to be kept in DB schema
// We are keeping them to support old takeScreenshot , queueMovie requests

// Events Manager
$events_state_from_info = json_decode($info['eventsState'], true);

Expand All @@ -170,9 +177,6 @@ public function __construct($publicId, $format='mp4') {

// Regon of interest
$this->_roi = Helper_RegionOfInterest::parsePolygonString($info['roi'], $info['imageScale']);

// Get timestamps for frames in the key movie layer
$this->_getTimeStamps();
}

private function _dbSetup() {
Expand Down Expand Up @@ -268,9 +272,13 @@ public function build() {
/**
* Returns information about the completed movie
*
* @throws
* @return array A list of movie properties and a URL to the finished movie
*/
public function getCompletedMovieInformation($verbose=false) {
if (!$this->isComplete()) {
throw new MovieNotCompletedException("Movie $this->publicId has not been completed.");
}

$info = array(
'frameRate' => $this->frameRate,
Expand All @@ -288,7 +296,7 @@ public function getCompletedMovieInformation($verbose=false) {
if ($verbose) {
$extra = array(
'timestamp' => $this->timestamp,
'duration' => $this->getDuration(),
'duration' => $this->_getDuration(),
'imageScale' => $this->imageScale,
'layers' => $this->_layers->serialize(),
'events' => $this->_eventsManager->getState(),
Expand All @@ -303,6 +311,10 @@ public function getCompletedMovieInformation($verbose=false) {
return $info;
}

public function isComplete(): bool {
return $this->status == Movie_HelioviewerMovie::STATUS_COMPLETED;
}

/**
* Returns an array of filepaths to the movie's preview images
*/
Expand All @@ -325,10 +337,21 @@ public function getFilepath($highQuality=false) {
return $this->_buildDir().$this->_buildFilename($highQuality);
}

public function getDuration() {
private function _getDuration() {
return $this->numFrames / $this->frameRate;
}

/**
* Returns the duration of the movie in seconds
* @throws MovieNotCreatedException if the movie has not been processed
*/
public function getDuration() {
if (!$this->isComplete()) {
throw new MovieNotCompletedException("Duration for $this->publicId is unknown since the movie has not been processed yet.");
}
return $this->_getDuration();
}

public function getURL() {
return str_replace(HV_CACHE_DIR, HV_CACHE_URL, $this->_buildDir()) . $this->_buildFilename();
}
Expand Down Expand Up @@ -397,6 +420,20 @@ private function _buildDir() {
$this->publicId);
}

private function _getStartDate() {
if (is_null($this->startDate)) {
$this->_prepDates();
}
return $this->startDate;
}

private function _getEndDate() {
if (is_null($this->endDate)) {
$this->_prepDates();
}
return $this->endDate;
}

/**
* Determines filename to use for the movie
*
Expand All @@ -405,11 +442,8 @@ private function _buildDir() {
* @return string Movie filename
*/
private function _buildFilename($highQuality=false) {
if (is_null($this->startDate)) {
$this->_prepDates();
}
$start = str_replace(array(':', '-', ' '), '_', $this->startDate);
$end = str_replace(array(':', '-', ' '), '_', $this->endDate);
$start = str_replace(array(':', '-', ' '), '_', $this->_getStartDate());
$end = str_replace(array(':', '-', ' '), '_', $this->_getEndDate());

$suffix = ($highQuality && $this->format == 'mp4') ? '-hq' : '';

Expand Down Expand Up @@ -440,7 +474,7 @@ private function _buildMovieFrames($watermark) {
'movie' => true,
'size' => $this->size,
'followViewport' => $this->followViewport,
'startDate' => $this->startDate,
'startDate' => $this->_getStartDate(),
'reqStartDate' => $this->reqStartDate,
'reqEndDate' => $this->reqEndDate,
'reqObservationDate' => $this->reqObservationDate,
Expand All @@ -454,7 +488,7 @@ private function _buildMovieFrames($watermark) {
$numFailures = 0;

// Compile frames
foreach ($this->_timestamps as $time) {
foreach ($this->_getTimeStamps() as $time) {

$filepath = sprintf('%sframes/frame%d.bmp', $this->directory, $frameNum);

Expand Down Expand Up @@ -546,6 +580,11 @@ private function _createPreviewImages(&$screenshot) {
$preview->destroy();
}

private function markFinished(string $format, float $time_to_build) {
$this->_db->markMovieAsFinished($this->id, $format, $time_to_build);
$this->status = Movie_HelioviewerMovie::STATUS_COMPLETED;
}

/**
* Builds the requested movie
*
Expand Down Expand Up @@ -630,7 +669,7 @@ private function _encodeMovie() {

// Mark mp4 movie as completed
$t2 = time();
$this->_db->markMovieAsFinished($this->id, 'mp4', $t2 - $t1);
$this->markFinished('mp4', $t2 - $t1);


// Create a low-quality webm movie for in-browser use if requested
Expand All @@ -640,13 +679,18 @@ private function _encodeMovie() {

// Mark movie as completed
$t4 = time();
$this->_db->markMovieAsFinished($this->id, 'webm', $t4 - $t3);
$this->markFinished('webm', $t4 - $t3);
}

/**
* Returns a human-readable title for the video
* @throws MovieNotCompletedException since the title relies on the layer dates from processing the movie.
*/
public function getTitle() {
if (!$this->isComplete()) {
throw new MovieNotCompletedException("The title for $this->publicId is not available yet");
}

date_default_timezone_set('UTC');

$layerString = $this->_layers->toHumanReadableString();
Expand All @@ -661,14 +705,14 @@ public function getTitle() {
public function getDateString() {
date_default_timezone_set('UTC');

if (substr($this->startDate, 0, 9) == substr($this->endDate, 0, 9)) {
$endDate = substr($this->endDate, 11);
if (substr($this->_getStartDate(), 0, 9) == substr($this->_getEndDate(), 0, 9)) {
$endDate = substr($this->_getEndDate(), 11);
}
else {
$endDate = $this->endDate;
$endDate = $this->_getEndDate();
}

return sprintf('%s - %s UTC', $this->startDate, $endDate);
return sprintf('%s - %s UTC', $this->_getStartDate(), $endDate);
}

/**
Expand All @@ -680,7 +724,12 @@ public function getDateString() {
* included may be reduced to ensure that the total number of
* SubFieldImages needed does not exceed HV_MAX_MOVIE_FRAMES
*/
private function _getTimeStamps() {
private function _getTimeStamps(): array {
// If timestamps have already been processed, return them.
if (!empty($this->_timestamps)) {
return $this->_timestamps;
}

$this->_dbSetup();

$layerCounts = array();
Expand All @@ -689,6 +738,9 @@ private function _getTimeStamps() {
// duration for each layer
foreach ($this->_layers->toArray() as $layer) {
$n = $this->_db->getDataCount($this->reqStartDate, $this->reqEndDate, $layer['sourceId'], $this->switchSources);
if ($n === false) {
throw new MovieLookupException("Failed to query data count for $this->publicId on source " . $layer['sourceId']);
}

$layerCounts[$layer['sourceId']] = $n;
}
Expand Down Expand Up @@ -721,6 +773,8 @@ private function _getTimeStamps() {
$index = round($i * (sizeOf($entireRange) / $numFrames));
array_push($this->_timestamps, $entireRange[$index]['date']);
}

return $this->_timestamps;
}

/**
Expand All @@ -746,8 +800,8 @@ private function _setMovieDimensions() {
private function _prepDates() {
if ($this->status != 2) {
// Store actual start and end dates that will be used for the movie
$this->startDate = $this->_timestamps[0];
$this->endDate = $this->_timestamps[sizeOf($this->_timestamps) - 1];
$this->startDate = $this->_getTimeStamps()[0];
$this->endDate = $this->_getTimeStamps()[sizeOf($this->_getTimeStamps()) - 1];
}
}

Expand All @@ -762,7 +816,7 @@ private function _setMovieProperties() {

$this->filename = $this->_buildFilename();

$this->numFrames = sizeOf($this->_timestamps);
$this->numFrames = sizeOf($this->_getTimeStamps());

if ($this->numFrames == 0) {
$this->_abort('No images available for the requested time range');
Expand Down Expand Up @@ -796,7 +850,7 @@ private function _setMovieProperties() {

// Update movie entry in database with new details
$this->_db->storeMovieProperties(
$this->id, $this->startDate, $this->endDate, $this->numFrames,
$this->id, $this->_getStartDate(), $this->_getEndDate(), $this->numFrames,
$this->frameRate, $this->movieLength, $width, $height
);
}
Expand Down Expand Up @@ -869,6 +923,5 @@ public function getMoviePlayerHTML() {
</html>
<?php
}

}
?>
Loading

0 comments on commit 2bfb71e

Please sign in to comment.