Reorganised code layout
Separated class library and web interface code Added a common config/database config to be referenced by all subprojects Fixed previous commit
This commit is contained in:
243
lib/HandBrakeCluster/Rips/Source.class.php
Normal file
243
lib/HandBrakeCluster/Rips/Source.class.php
Normal file
@@ -0,0 +1,243 @@
|
||||
<?php
|
||||
|
||||
class HandBrakeCluster_Rips_Source {
|
||||
|
||||
const PM_TITLE = 0;
|
||||
const PM_CHAPTER = 1;
|
||||
const PM_AUDIO = 2;
|
||||
const PM_SUBTITLE = 3;
|
||||
|
||||
protected $source;
|
||||
protected $output;
|
||||
protected $titles = array();
|
||||
|
||||
protected function __construct($source_filename, $scan_dir, $use_cache) {
|
||||
$this->source = $source_filename;
|
||||
|
||||
if ($scan_dir) {
|
||||
$this->scan();
|
||||
}
|
||||
|
||||
$main = HandBrakeCluster_Main::instance();
|
||||
$cache = $main->cache();
|
||||
$config = $main->config();
|
||||
|
||||
if ($scan_dir && $use_cache) {
|
||||
$cache->store($this->source, serialize($this), $config->get('rips.cache_ttl'));
|
||||
}
|
||||
}
|
||||
|
||||
public static function load($source_filename, $scan_dir = true, $use_cache = true) {
|
||||
$cache = HandBrakeCluster_Main::instance()->cache();
|
||||
|
||||
if ($use_cache && $cache->exists($source_filename)) {
|
||||
return unserialize($cache->fetch($source_filename));
|
||||
} else {
|
||||
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() {
|
||||
$source_shell = escapeshellarg($this->source);
|
||||
$handbrake_cmd = "HandBrakeCLI -i {$source_shell} -t 0";
|
||||
|
||||
$handbrake_pid = proc_open($handbrake_cmd, array(
|
||||
0 => array("pipe", "r"),
|
||||
1 => array("pipe", "w"),
|
||||
2 => array("pipe", "w")
|
||||
), $pipes);
|
||||
|
||||
$handbrake_output = stream_get_contents($pipes[2]);
|
||||
fclose($pipes[0]);
|
||||
fclose($pipes[1]);
|
||||
fclose($pipes[2]);
|
||||
proc_close($handbrake_pid);
|
||||
|
||||
// Process the output
|
||||
$lines = explode("\n", $handbrake_output);
|
||||
$title = null;
|
||||
$mode = self::PM_TITLE;
|
||||
|
||||
foreach ($lines as $line) {
|
||||
// Skip any line that doesn't begin with a + (with optional leading whitespace)
|
||||
if ( ! preg_match('/\s*\+/', $line)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$matches = array();
|
||||
switch (true) {
|
||||
case preg_match('/^\+ title (?P<id>\d+):$/', $line, $matches): {
|
||||
if ($title) {
|
||||
$this->addTitle($title);
|
||||
}
|
||||
|
||||
$mode = self::PM_TITLE;
|
||||
$title = new HandBrakeCluster_Rips_SourceTitle($matches['id']);
|
||||
} break;
|
||||
|
||||
case $title && preg_match('/^ \+ chapters:$/', $line): {
|
||||
$mode = self::PM_CHAPTER;
|
||||
} break;
|
||||
|
||||
case $title && preg_match('/^ \+ audio tracks:$/', $line): {
|
||||
$mode = self::PM_AUDIO;
|
||||
|
||||
} break;
|
||||
|
||||
case $title && preg_match('/^ \+ subtitle tracks:$/', $line): {
|
||||
$mode = self::PM_SUBTITLE;
|
||||
} break;
|
||||
|
||||
case $title && $mode == self::PM_TITLE && preg_match('/^ \+ duration: (?P<duration>\d+:\d+:\d+)$/', $line, $matches): {
|
||||
$title->setDuration($matches['duration']);
|
||||
} break;
|
||||
|
||||
case $title && $mode == self::PM_TITLE && preg_match('/^ \+ angle\(s\) (?P<angles>\d+)$/', $line, $matches): {
|
||||
$title->setAngles($matches['angles']);
|
||||
} break;
|
||||
|
||||
//" + size: 720x576, pixel aspect: 64/45, display aspect: 1.78, 25.000 fps"
|
||||
case $title && $mode == self::PM_TITLE && preg_match('/^ \+ size: (?P<width>\d+)x(?P<height>\d+), pixel aspect: (?P<pixel_aspect>\d+\/\d+), display aspect: (?P<display_aspect>[\d\.]+), (?<framerate>[\d\.]+) fps$/', $line, $matches): {
|
||||
$title->setDisplayInfo(
|
||||
$matches['width'], $matches['height'], $matches['pixel_aspect'],
|
||||
$matches['display_aspect'], $matches['framerate']
|
||||
);
|
||||
} break;
|
||||
|
||||
case $title && $mode == self::PM_TITLE && preg_match('/^ \+ autocrop: (?P<autocrop>(?:\d+\/?){4})$/', $line, $matches): {
|
||||
$title->setAutocrop($matches['autocrop']);
|
||||
} break;
|
||||
|
||||
case $title && $mode == self::PM_CHAPTER && preg_match('/^ \+ (?P<id>\d+): cells \d+->\d+, \d+ blocks, duration (?P<duration>\d+:\d+:\d+)$/', $line, $matches): {
|
||||
$title->addChapter($matches['id'], $matches['duration']);
|
||||
} break;
|
||||
|
||||
case $title && $mode == self::PM_AUDIO && preg_match('/^ \+ (?P<id>\d+), (?P<name>.+) \((?P<format>.+)\) \((?P<channels>(.+ ch|Dolby Surround))\) \((?P<language>.+)\), (?P<samplerate>\d+)Hz, (?P<bitrate>\d+)bps$/', $line, $matches): {
|
||||
$title->addAudioTrack(
|
||||
new HandBrakeCluster_Rips_SourceAudioTrack(
|
||||
$matches['id'], $matches['name'], $matches['format'], $matches['channels'],
|
||||
$matches['language'], $matches['samplerate'], $matches['bitrate']
|
||||
)
|
||||
);
|
||||
} break;
|
||||
|
||||
case $title && $mode == self::PM_SUBTITLE && preg_match('/^ \+ (?P<id>\d+), (?P<name>.+) \((?P<language>.+)\) \((?P<format>.+)\)$/', $line, $matches): {
|
||||
$title->addSubtitleTrack(
|
||||
new HandBrakeCluster_Rips_SourceSubtitleTrack(
|
||||
$matches['id'], $matches['name'], $matches['language'], $matches['format']
|
||||
)
|
||||
);
|
||||
} break;
|
||||
|
||||
default: {
|
||||
// Ignore this unmatched line
|
||||
} break;
|
||||
|
||||
}
|
||||
|
||||
$this->output .= $line . "\n";
|
||||
}
|
||||
|
||||
// Handle the last title found as a special case
|
||||
if ($title) {
|
||||
$this->addTitle($title);
|
||||
}
|
||||
}
|
||||
|
||||
public static function isCached($source_filename) {
|
||||
$main = HandBrakeCluster_Main::instance();
|
||||
$cache = $main->cache();
|
||||
$config = $main->config();
|
||||
|
||||
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) {
|
||||
$this->titles[] = $title;
|
||||
}
|
||||
|
||||
public function longestTitle() {
|
||||
$longest_title = null;
|
||||
$maximum_duration = 0;
|
||||
|
||||
if ( ! $this->titles) {
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach ($this->titles as $title) {
|
||||
$duration = $title->durationInSeconds();
|
||||
if ($duration > $maximum_duration) {
|
||||
$longest_title = $title;
|
||||
$maximum_duration = $duration;
|
||||
}
|
||||
}
|
||||
|
||||
return $longest_title;
|
||||
}
|
||||
|
||||
public function longestTitleIndex() {
|
||||
$longest_index = null;
|
||||
$maximmum_duration = 0;
|
||||
|
||||
if ( ! $this->titles) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for ($i = 0, $l = count($this->titles); $i < $l; ++$i) {
|
||||
$title = $this->titles[$i];
|
||||
$duration = $title->durationInSeconds();
|
||||
if ($duration > $maximum_duration) {
|
||||
$longest_index = $i;
|
||||
$maximum_duration = $duration;
|
||||
}
|
||||
}
|
||||
|
||||
return $longest_index;
|
||||
}
|
||||
|
||||
public function filename() {
|
||||
return $this->source;
|
||||
}
|
||||
|
||||
public function filenameEncoded() {
|
||||
return self::encodeFilename($this->source);
|
||||
}
|
||||
|
||||
public function output() {
|
||||
return $this->output;
|
||||
}
|
||||
|
||||
public function titleCount() {
|
||||
return count($this->titles);
|
||||
}
|
||||
|
||||
public function titles() {
|
||||
return $this->titles;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
?>
|
||||
53
lib/HandBrakeCluster/Rips/SourceAudioTrack.class.php
Normal file
53
lib/HandBrakeCluster/Rips/SourceAudioTrack.class.php
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
class HandBrakeCluster_Rips_SourceAudioTrack {
|
||||
|
||||
protected $id;
|
||||
protected $name;
|
||||
protected $format;
|
||||
protected $channels;
|
||||
protected $language;
|
||||
protected $samplerate;
|
||||
protected $bitrate;
|
||||
|
||||
public function __construct($id, $name, $format, $channels, $language, $samplerate, $bitrate) {
|
||||
$this->id = $id;
|
||||
$this->name = $name;
|
||||
$this->format = $format;
|
||||
$this->channels = $channels;
|
||||
$this->language = $language;
|
||||
$this->samplerate = $samplerate;
|
||||
$this->bitrate = $bitrate;
|
||||
}
|
||||
|
||||
public function id() {
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function name() {
|
||||
return $name;
|
||||
}
|
||||
|
||||
public function format() {
|
||||
return $this->format;
|
||||
}
|
||||
|
||||
public function channels() {
|
||||
return $this->channels;
|
||||
}
|
||||
|
||||
public function language() {
|
||||
return $this->language;
|
||||
}
|
||||
|
||||
public function samplerate() {
|
||||
return $this->samplerate;
|
||||
}
|
||||
|
||||
public function bitrate() {
|
||||
return $this->bitrate;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
?>
|
||||
56
lib/HandBrakeCluster/Rips/SourceLister.class.php
Normal file
56
lib/HandBrakeCluster/Rips/SourceLister.class.php
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
class HandBrakeCluster_Rips_SourceLister {
|
||||
|
||||
protected $base_directory;
|
||||
protected $sources = array();
|
||||
|
||||
public function __construct($base_directory) {
|
||||
$this->base_directory = $base_directory;
|
||||
|
||||
$this->scan();
|
||||
}
|
||||
|
||||
public function scan() {
|
||||
if (!is_dir($this->base_directory)) {
|
||||
throw new HandBrakeCluster_Exception_InvalidSourceDirectory($this->base_directory);
|
||||
}
|
||||
|
||||
// Define a queue of directories to scan, starting with the base directory,
|
||||
// and keep going until they have all been scanned
|
||||
$scan_directories = array($this->base_directory);
|
||||
while ($scan_directories) {
|
||||
$dir = dir(array_shift($scan_directories));
|
||||
|
||||
while (($entry = $dir->read()) !== false) {
|
||||
if ($entry == '.' || $entry == '..' || $entry == 'lost+found') {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip any non-directories
|
||||
$source = $dir->path . DIRECTORY_SEPARATOR . $entry;
|
||||
if (!is_dir($source)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Accept this dir as a source if it contains a VIDEO_TS dir,
|
||||
// otherwise add the dir to the queue to scan deeper
|
||||
$source_vts = $source . DIRECTORY_SEPARATOR . 'VIDEO_TS';
|
||||
if (is_dir($source_vts)) {
|
||||
$this->sources[] = HandBrakeCluster_Rips_Source::load($source_vts, false);
|
||||
} else {
|
||||
$scan_directories[] = $source;
|
||||
}
|
||||
}
|
||||
|
||||
$dir->close();
|
||||
}
|
||||
}
|
||||
|
||||
public function sources() {
|
||||
return $this->sources;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
?>
|
||||
35
lib/HandBrakeCluster/Rips/SourceSubtitleTrack.class.php
Normal file
35
lib/HandBrakeCluster/Rips/SourceSubtitleTrack.class.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
class HandBrakeCluster_Rips_SourceSubtitleTrack {
|
||||
|
||||
protected $id;
|
||||
protected $name;
|
||||
protected $language;
|
||||
protected $format;
|
||||
|
||||
public function __construct($id, $name, $language, $format) {
|
||||
$this->id = $id;
|
||||
$this->name = $name;
|
||||
$this->language = $language;
|
||||
$this->format = $format;
|
||||
}
|
||||
|
||||
public function id() {
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function name() {
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function language() {
|
||||
return $this->language;
|
||||
}
|
||||
|
||||
public function format() {
|
||||
return $this->format;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
?>
|
||||
127
lib/HandBrakeCluster/Rips/SourceTitle.class.php
Normal file
127
lib/HandBrakeCluster/Rips/SourceTitle.class.php
Normal file
@@ -0,0 +1,127 @@
|
||||
<?php
|
||||
|
||||
class HandBrakeCluster_Rips_SourceTitle {
|
||||
|
||||
protected $id;
|
||||
//protected $vts;
|
||||
//protected $ttn;
|
||||
//protected $cell_count;
|
||||
//protected $blocks;
|
||||
|
||||
protected $angles;
|
||||
protected $duration;
|
||||
protected $width;
|
||||
protected $height;
|
||||
protected $pixel_aspect;
|
||||
protected $display_aspect;
|
||||
protected $framerate;
|
||||
protected $autocrop;
|
||||
|
||||
protected $chapters = array();
|
||||
protected $audio = array();
|
||||
protected $subtitles = array();
|
||||
|
||||
public function __construct($id) {
|
||||
$this->id = $id;
|
||||
}
|
||||
|
||||
public function id() {
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function angles() {
|
||||
return $this->angles;
|
||||
}
|
||||
|
||||
public function setAngles($angles) {
|
||||
$this->angles = $angles;
|
||||
}
|
||||
|
||||
public function duration() {
|
||||
return $this->duration;
|
||||
}
|
||||
|
||||
public function durationInSeconds() {
|
||||
$time = explode(":", $this->duration);
|
||||
return ($time[0] * 3600) + ($time[1] * 60) + $time[2];
|
||||
}
|
||||
|
||||
public function setDuration($duration) {
|
||||
$this->duration = $duration;
|
||||
}
|
||||
|
||||
public function width() {
|
||||
return $this->width;
|
||||
}
|
||||
|
||||
public function height() {
|
||||
return $this->height;
|
||||
}
|
||||
|
||||
public function displayAspect() {
|
||||
return $this->display_aspect;
|
||||
}
|
||||
|
||||
public function pixelAspect() {
|
||||
return $this->pixel_aspect;
|
||||
}
|
||||
|
||||
public function framerate() {
|
||||
return $this->framerate;
|
||||
}
|
||||
|
||||
public function setDisplayInfo($width, $height, $display_aspect, $pixel_aspect, $framerate) {
|
||||
$this->width = $width;
|
||||
$this->height = $height;
|
||||
$this->pixel_aspect = $pixel_aspect;
|
||||
$this->display_aspect = $display_aspect;
|
||||
$this->framerate = $framerate;
|
||||
}
|
||||
|
||||
public function autocrop() {
|
||||
return $this->autocrop;
|
||||
}
|
||||
|
||||
public function setAutocrop($autocrop) {
|
||||
$this->autocrop = $autocrop;
|
||||
}
|
||||
|
||||
public function chapterCount() {
|
||||
return count($this->chapters);
|
||||
}
|
||||
|
||||
public function chapters() {
|
||||
return $this->chapters;
|
||||
}
|
||||
|
||||
public function addChapter($chapter_id, $duration) {
|
||||
$this->chapters[$chapter_id] = $duration;
|
||||
}
|
||||
|
||||
public function audioTrackCount() {
|
||||
return count($this->audio);
|
||||
}
|
||||
|
||||
public function audioTracks() {
|
||||
return $this->audio;
|
||||
}
|
||||
|
||||
public function addAudioTrack(HandBrakeCluster_Rips_SourceAudioTrack $audio_track) {
|
||||
$this->audio[] = $audio_track;
|
||||
}
|
||||
|
||||
public function subtitleTrackCount() {
|
||||
return count($this->subtitles);
|
||||
}
|
||||
|
||||
public function subtitleTracks() {
|
||||
return $this->subtitles;
|
||||
}
|
||||
|
||||
public function addSubtitleTrack(HandBrakeCluster_Rips_SourceSubtitleTrack $subtitle_track) {
|
||||
$this->subtitles[] = $subtitle_track;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
?>
|
||||
Reference in New Issue
Block a user