Updated job and source classes

This commit is contained in:
2010-04-03 23:02:50 +01:00
parent 539344ccd1
commit 817b4b6388
4 changed files with 266 additions and 47 deletions

View File

@@ -2,10 +2,12 @@
class HandBrakeCluster_Job { class HandBrakeCluster_Job {
protected $source;
private $id; private $id;
private $name; private $name;
private $source; private $source_filename;
private $destination; private $destination_filename;
private $title; private $title;
private $format; private $format;
private $video_codec; private $video_codec;
@@ -20,27 +22,31 @@ class HandBrakeCluster_Job {
private $statuses = null; private $statuses = null;
private static $cache = array();
public function __construct($id, $name, $source, $destination, $title, $format, $video_codec, $video_width, $video_height, $quantizer, $deinterlace, $audio_tracks, $audio_codecs, $audio_names, $subtitle_tracks) { protected function __construct($source, $id, $name, $source_filename, $destination_filename, $title, $format, $video_codec, $video_width, $video_height, $quantizer, $deinterlace,
$this->id = $id; $audio_tracks, $audio_codecs, $audio_names, $subtitle_tracks) {
$this->name = $name; $this->source = $source;
$this->source = $source; $this->id = $id;
$this->destination = $destination; $this->name = $name;
$this->title = $title; $this->source_filename = $source_filename;
$this->format = $format; $this->destination_filename = $destination_filename;
$this->video_codec = $video_codec; $this->title = $title;
$this->video_width = $video_width; $this->format = $format;
$this->video_height = $video_height; $this->video_codec = $video_codec;
$this->quantizer = $quantizer; $this->video_width = $video_width;
$this->deinterlace = $deinterlace; $this->video_height = $video_height;
$this->audio_tracks = $audio_tracks; $this->quantizer = $quantizer;
$this->audio_codecs = $audio_codecs; $this->deinterlace = $deinterlace;
$this->audio_names = $audio_names; $this->audio_tracks = $audio_tracks;
$this->subtitle_tracks = $subtitle_tracks; $this->audio_codecs = $audio_codecs;
$this->audio_names = $audio_names;
$this->subtitle_tracks = $subtitle_tracks;
} }
public static function fromDatabaseRow($row) { public static function fromDatabaseRow($row) {
return new HandBrakeCluster_Job( return new HandBrakeCluster_Job(
HandBrakeCluster_Rips_Source::load($rips['source']),
$row['id'], $row['id'],
$row['name'], $row['name'],
$row['source'], $row['source'],
@@ -59,14 +65,28 @@ class HandBrakeCluster_Job {
); );
} }
/**
*
* @todo Implement cache of previously loaded jobs
*
* @param int $id
* @return HandBrakeCluster_Job
*/
public static function fromId($id) { public static function fromId($id) {
$database = HandBrakeCluster_Main::instance()->database(); $database = HandBrakeCluster_Main::instance()->database();
return HandBrakeCluster_Job::fromDatabaseRow(
if (isset(self::$cache[$id])) {
return self::$cache[$id];
}
$job = HandBrakeCluster_Job::fromDatabaseRow(
$database->selectOne('SELECT * FROM jobs WHERE id=:id', array( $database->selectOne('SELECT * FROM jobs WHERE id=:id', array(
array('name' => 'id', 'value' => $id, 'type' => PDO::PARAM_INT) array('name' => 'id', 'value' => $id, 'type' => PDO::PARAM_INT)
) )
) )
); );
self::$cache[$job->id] = $job;
} }
public static function all() { public static function all() {
@@ -74,7 +94,10 @@ class HandBrakeCluster_Job {
$database = HandBrakeCluster_Main::instance()->database(); $database = HandBrakeCluster_Main::instance()->database();
foreach ($database->selectList('SELECT * FROM jobs') as $row) { foreach ($database->selectList('SELECT * FROM jobs') as $row) {
$jobs[] = self::fromDatabaseRow($row); $job = self::fromDatabaseRow($row);
self::$cache[$job->id] = $job;
$jobs[] = $job;
} }
return $jobs; return $jobs;
@@ -84,7 +107,7 @@ class HandBrakeCluster_Job {
$jobs = array(); $jobs = array();
$database = HandBrakeCluster_Main::instance()->database(); $database = HandBrakeCluster_Main::instance()->database();
foreach ($database->selectList('SELECT * FROM jobs WHERE id IN (SELECT id FROM job_status_current WHERE status=:status)', array( foreach ($database->selectList('SELECT * FROM jobs WHERE id IN (SELECT job_id FROM job_status_current WHERE status=:status)', array(
array('name' => 'status', 'value' => $status, 'type' => PDO::PARAM_INT) array('name' => 'status', 'value' => $status, 'type' => PDO::PARAM_INT)
)) as $row) { )) as $row) {
$jobs[] = self::fromDatabaseRow($row); $jobs[] = self::fromDatabaseRow($row);
@@ -92,17 +115,124 @@ class HandBrakeCluster_Job {
return $jobs; return $jobs;
} }
public static function fromPostRequest($source_id, $config) {
$source_filename = base64_decode(str_replace('-', '/', HandBrakeCluster_Main::issetelse($source_id, HandBrakeCluster_Exception_InvalidParameters)));
$source = HandBrakeCluster_Rips_Source::load($source_filename);
protected function loadStatuses() { $jobs = array();
if ($this->statuses == null) { foreach ($config as $title => $details) {
$this->statuses = HandBrakeCluster_JobStatus::allForJob($this->id); if (HandBrakeCluster_Main::issetelse($details['queue'])) {
$job = new HandBrakeCluster_Job(
$source,
null,
HandBrakeCluster_Main::issetelse($details['name'], 'unnamed job'),
$source->filename(),
HandBrakeCluster_Main::issetelse($details['output_filename'], HandBrakeCluster_Exception_InvalidParameters),
$title,
'mkv', // @todo Make this configurable
'x264', // @todo Make this configurable
0, // @todo Make this configurable
0, // @todo Make this configurable
0.61, // @todo Make this configurable
HandBrakeCluster_Main::issetelse($details['deinterlace'], 2),
implode(',', HandBrakeCluster_Main::issetelse($details['audio'], array())),
implode(',', array_pad(array(), count($details['audio']), 'ac3')), // @todo Make this configurable
implode(',', array_pad(array(), count($details['audio']), 'Unknown')), // @todo Make this configurable
implode(',', HandBrakeCluster_Main::issetelse($details['subtitles'], array()))
);
$job->create();
$jobs[] = $job;
}
}
return $jobs;
}
protected function create() {
$database = HandBrakeCluster_Main::instance()->database();
$database->insert(
'INSERT INTO jobs
(id,name,source,destination,title,format,video_codec,video_width,video_height,quantizer,deinterlace,audio_tracks,audio_codecs,audio_names,subtitle_tracks)
VALUES(NULL,:name,:source,:destination,:title,:format,:video_codec,:video_width,:video_height,:quantizer,:deinterlace,:audio_tracks,:audio_codecs,:audio_names,:subtitle_tracks)',
array(
array(name => 'name', value => $this->name, type => PDO::PARAM_STR),
array(name => 'source', value => $this->source_filename, type => PDO::PARAM_STR),
array(name => 'destination', value => $this->destination_filename, type => PDO::PARAM_STR),
array(name => 'title', value => $this->title, type => PDO::PARAM_INT),
array(name => 'format', value => $this->format, type => PDO::PARAM_STR),
array(name => 'video_codec', value => $this->video_codec, type => PDO::PARAM_STR),
array(name => 'video_width', value => $this->video_width, type => PDO::PARAM_INT),
array(name => 'video_height', value => $this->video_height, type => PDO::PARAM_INT),
array(name => 'quantizer', value => $this->quantizer, type => PDO::PARAM_INT),
array(name => 'deinterlace', value => $this->deinterlace, type => PDO::PARAM_INT),
array(name => 'audio_tracks', value => $this->audio_tracks, type => PDO::PARAM_STR),
array(name => 'audio_codecs', value => $this->audio_codecs, type => PDO::PARAM_STR),
array(name => 'audio_names', value => $this->audio_names, type => PDO::PARAM_STR),
array(name => 'subtitle_tracks', value => $this->subtitle_tracks, type => PDO::PARAM_STR),
)
);
$this->id = $database->lastInsertId();
$status = HandBrakeCluster_JobStatus::updateStatusForJob($this, HandBrakeCluster_JobStatus::CREATED);
}
public function queue($gearman) {
$main = HandBrakeCluster_Main::instance();
$config = $main->config();
$log = $main->log();
$log->info('Starting job', $this->id);
// Construct the rip options
$rip_options = array(
'nice' => $config->get('rips.nice', 15),
'input_dir' => dirname($this->source_filename) . DIRECTORY_SEPARATOR,
'input_filename' => basename($this->source_filename),
'output_dir' => dirname($this->destination_filename) . DIRECTORY_SEPARATOR,
'output_filename' => basename($this->destination_filename),
'title' => $this->title,
'format' => $this->format,
'video_codec' => $this->video_codec,
'video_width' => $this->video_width,
'video_height' => $this->video_height,
'quantizer' => $this->quantizer,
'deinterlace' => $this->deinterlace,
'audio_tracks' => $this->audio_tracks,
'audio_codec' => $this->audio_codecs,
'audio_names' => $this->audio_names,
'subtitle_tracks' => $this->subtitle_tracks,
);
// Enqueue this rip
$task = $gearman->addTask('handbrake_rip', serialize($rip_options), $config->get('rips.context'), $this->id);
if ($task) {
$log->debug("Queued job", $this->id);
$this->updateStatus(HandBrakeCluster_JobStatus::QUEUED);
} else {
$log->warning("Failed to queue job", $this->id);
$this->updateStatus(HandBrakeCluster_JobStatus::FAILED);
} }
} }
protected function loadStatuses() {
if ($this->statuses == null) {
$this->statuses = HandBrakeCluster_JobStatus::allForJob($this);
}
}
/**
*
* @return HandBrakeCluster_JobStatus
*/
public function currentStatus() { public function currentStatus() {
$this->loadStatuses(); $this->loadStatuses();
return $this->statuses[count($this->statuses) - 1]; return $this->statuses[count($this->statuses) - 1];
} }
public function updateStatus($new_status, $rip_progress = null) {
return HandBrakeCluster_JobStatus::updateStatusForJob($this, $new_status, $rip_progress);
}
public function id() { public function id() {
return $this->id; return $this->id;
@@ -112,18 +242,22 @@ class HandBrakeCluster_Job {
return $this->name; return $this->name;
} }
public function source() { public function sourceFilename() {
return $this->source; return $this->source_filename;
} }
public function destination() { public function destinationFilename() {
return $this->destination; return $this->destination_filename;
} }
public function title() { public function title() {
return $this->title; return $this->title;
} }
public static function runAllJobs() {
HandBrakeCluster_BackgroundTask::run('/usr/bin/php run-jobs.php');
}
}; };
?> ?>

View File

@@ -2,28 +2,32 @@
class HandBrakeCluster_JobStatus { class HandBrakeCluster_JobStatus {
const QUEUED = 0; const CREATED = 0;
const FAILED = 1; const QUEUED = 1;
const RUNNING = 2; const FAILED = 2;
const COMPLETE = 3; const RUNNING = 3;
const COMPLETE = 4;
private static $status_names = array( private static $status_names = array(
self::QUEUED => 'Queued', self::CREATED => 'Created',
self::FAILED => 'Failed', self::QUEUED => 'Queued',
self::RUNNING => 'Running', self::FAILED => 'Failed',
self::COMPLETE => 'Complete' self::RUNNING => 'Running',
self::COMPLETE => 'Complete',
); );
protected $id; protected $id;
protected $job_id; protected $job_id;
protected $status; protected $status;
protected $ctime; protected $ctime;
protected $rip_progress;
protected function __construct($id, $job_id, $status, $ctime) { protected function __construct($id, $job_id, $status, $ctime, $rip_progress) {
$this->id = $id; $this->id = $id;
$this->job_id = $job_id; $this->job_id = $job_id;
$this->status = $status; $this->status = $status;
$this->ctime = $ctime; $this->ctime = $ctime;
$this->rip_progress = $rip_progress;
} }
public static function fromDatabaseRow($row) { public static function fromDatabaseRow($row) {
@@ -31,22 +35,68 @@ class HandBrakeCluster_JobStatus {
$row['id'], $row['id'],
$row['job_id'], $row['job_id'],
$row['status'], $row['status'],
$row['ctime'] $row['ctime'],
$row['rip_progress']
); );
} }
public static function updateStatusForJob($job, $status, $rip_progress = null) {
$status = new HandBrakeCluster_JobStatus(null, $job->id(), $status, time(), $rip_progress);
$status->create();
return $status;
}
public function updateRipProgress($rip_progress) {
$this->rip_progress = $rip_progress;
$this->save();
}
public static function allForJob($job_id) { public static function allForJob(HandBrakeCluster_Job $job) {
$statuses = array(); $statuses = array();
$database = HandBrakeCluster_Main::instance()->database(); $database = HandBrakeCluster_Main::instance()->database();
foreach ($database->selectList('SELECT * FROM job_status WHERE job_id=:job_id ORDER BY ctime ASC', array( foreach ($database->selectList('SELECT * FROM job_status WHERE job_id=:job_id ORDER BY ctime ASC', array(
array('name' => 'job_id', 'value' => $job_id, 'type' => PDO::PARAM_INT), array('name' => 'job_id', 'value' => $job->id(), 'type' => PDO::PARAM_INT),
)) as $row) { )) as $row) {
$statuses[] = HandBrakeCluster_JobStatus::fromDatabaseRow($row); $statuses[] = HandBrakeCluster_JobStatus::fromDatabaseRow($row);
} }
return $statuses; return $statuses;
} }
protected function create() {
$database = HandBrakeCluster_Main::instance()->database();
$database->insert(
'INSERT INTO job_status
(id, job_id, status, ctime, rip_progress)
VALUES(NULL,:job_id,:status,:ctime,:rip_progress)',
array(
array(name => 'job_id', value => $this->job_id, type => PDO::PARAM_INT),
array(name => 'status', value => $this->status, type => PDO::PARAM_INT),
array(name => 'ctime', value => $this->ctime, type => PDO::PARAM_INT),
array(name => 'rip_progress', value => $this->rip_progress),
)
);
$this->id = $database->lastInsertId();
}
public function save() {
$database = HandBrakeCluster_Main::instance()->database();
$database->update(
'UPDATE job_status SET
job_id=:job_id, status=:status, ctime=:ctime, rip_progress=:rip_progress
WHERE id=:id',
array(
array(name => 'id', value => $this->id, type => PDO::PARAM_INT),
array(name => 'job_id', value => $this->job_id, type => PDO::PARAM_INT),
array(name => 'status', value => $this->status, type => PDO::PARAM_INT),
array(name => 'ctime', value => $this->ctime, type => PDO::PARAM_INT),
array(name => 'rip_progress', value => $this->rip_progress),
)
);
}
public function id() { public function id() {
return $this->id; return $this->id;
@@ -67,6 +117,10 @@ class HandBrakeCluster_JobStatus {
public function ctime() { public function ctime() {
return $this->ctime; return $this->ctime;
} }
public function ripProgress() {
return $this->rip_progress;
}
}; };

View File

@@ -11,29 +11,50 @@ class HandBrakeCluster_Rips_Source {
protected $output; protected $output;
protected $titles = array(); protected $titles = array();
public function __construct($source_filename, $use_cache) { protected function __construct($source_filename, $scan_dir, $use_cache) {
$this->source = $source_filename; $this->source = $source_filename;
$this->scan(); if ($scan_dir) {
$this->scan();
}
$main = HandBrakeCluster_Main::instance(); $main = HandBrakeCluster_Main::instance();
$cache = $main->cache(); $cache = $main->cache();
$config = $main->config(); $config = $main->config();
if ($use_cache) { if ($scan_dir && $use_cache) {
$cache->store($this->source, serialize($this), $config->get('rips.cache_ttl')); $cache->store($this->source, serialize($this), $config->get('rips.cache_ttl'));
} }
} }
public static function load($source_filename, $use_cache = true) { public static function load($source_filename, $scan_dir = true, $use_cache = true) {
$cache = HandBrakeCluster_Main::instance()->cache(); $cache = HandBrakeCluster_Main::instance()->cache();
if ($use_cache && $cache->exists($source_filename)) { if ($use_cache && $cache->exists($source_filename)) {
return unserialize($cache->fetch($source_filename)); return unserialize($cache->fetch($source_filename));
} else { } else {
return new HandBrakeCluster_Rips_Source($source_filename, $use_cache); return new HandBrakeCluster_Rips_Source($source_filename, $scan_dir, $use_cache);
} }
} }
public static function loadEncoded($encoded_filename, $scan_dir = true, $use_cache = true) {
// Decode the filename
$source_filename = base64_decode(str_replace('-', '/', $encoded_filename));
// Ensure the source is a valid directory, and lies below the configured source_dir
$real_source_filename = realpath($source_filename);
if (!is_dir($source_filename)) {
throw new HandBrakeCluster_Exception_InvalidSourceDirectory($source_filename);
}
$config = HandBrakeCluster_Main::instance()->config();
$real_source_basedir = realpath($config->get('rips.source_dir'));
if (substr($real_source_filename, 0, strlen($real_source_basedir)) != $real_source_basedir) {
throw new HandBrakeCluster_Exception_InvalidSourceDirectory($source_filename);
}
return self::load($source_filename, $scan_dir, $use_cache);
}
protected function scan() { protected function scan() {
$source_shell = escapeshellarg($this->source); $source_shell = escapeshellarg($this->source);
@@ -150,6 +171,10 @@ class HandBrakeCluster_Rips_Source {
return $cache->exists($source_filename, $config->get('rips.cache_ttl')); return $cache->exists($source_filename, $config->get('rips.cache_ttl'));
} }
public static function encodeFilename($filename) {
return str_replace("/", "-", base64_encode($filename));
}
public function addTitle(HandBrakeCluster_Rips_SourceTitle $title) { public function addTitle(HandBrakeCluster_Rips_SourceTitle $title) {
$this->titles[] = $title; $this->titles[] = $title;
} }
@@ -173,7 +198,13 @@ class HandBrakeCluster_Rips_Source {
return $longest_title; return $longest_title;
} }
public function filename() {
return $this->source;
}
public function filenameEncoded() {
return self::encodeFilename($this->source);
}
public function output() { public function output() {
return $this->output; return $this->output;

View File

@@ -37,7 +37,7 @@ class HandBrakeCluster_Rips_SourceLister {
// otherwise add the dir to the queue to scan deeper // otherwise add the dir to the queue to scan deeper
$source_vts = $source . DIRECTORY_SEPARATOR . 'VIDEO_TS'; $source_vts = $source . DIRECTORY_SEPARATOR . 'VIDEO_TS';
if (is_dir($source_vts)) { if (is_dir($source_vts)) {
$this->sources[] = $source_vts; $this->sources[] = HandBrakeCluster_Rips_Source::load($source_vts, false);
} else { } else {
$scan_directories[] = $source; $scan_directories[] = $source;
} }