Merge branch 'feature-mkv-plugins'; commit 'HEAD^'
This commit is contained in:
@@ -132,8 +132,8 @@ class RippingCluster_Config {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch ($this->settings[$key]['type']) {
|
switch ($this->settings[$key]['type']) {
|
||||||
case TYPE_STRING_LIST:
|
case self::TYPE_STRING_LIST:
|
||||||
return explode("\n", $this->settings[$key]['value']);
|
return array_map('trim', explode("\n", $this->settings[$key]['value']));
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return $this->settings[$key]['value'];
|
return $this->settings[$key]['value'];
|
||||||
|
|||||||
@@ -28,26 +28,50 @@ class RippingCluster_Rips_SourceAudioTrack {
|
|||||||
return $name;
|
return $name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setName($name) {
|
||||||
|
$this->name = $name;
|
||||||
|
}
|
||||||
|
|
||||||
public function format() {
|
public function format() {
|
||||||
return $this->format;
|
return $this->format;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setFormat($format) {
|
||||||
|
$this->format = $format;
|
||||||
|
}
|
||||||
|
|
||||||
public function channels() {
|
public function channels() {
|
||||||
return $this->channels;
|
return $this->channels;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setChannels($channels) {
|
||||||
|
$this->channels = $channels;
|
||||||
|
}
|
||||||
|
|
||||||
public function language() {
|
public function language() {
|
||||||
return $this->language;
|
return $this->language;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setLanguage($language) {
|
||||||
|
$this->language = $language;
|
||||||
|
}
|
||||||
|
|
||||||
public function samplerate() {
|
public function samplerate() {
|
||||||
return $this->samplerate;
|
return $this->samplerate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setSampleRate($sample_rate) {
|
||||||
|
$this->samplerate = $sample_rate;
|
||||||
|
}
|
||||||
|
|
||||||
public function bitrate() {
|
public function bitrate() {
|
||||||
return $this->bitrate;
|
return $this->bitrate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setBitRate($bit_rate) {
|
||||||
|
$this->bitrate = $bit_rate;
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
?>
|
?>
|
||||||
@@ -22,14 +22,26 @@ class RippingCluster_Rips_SourceSubtitleTrack {
|
|||||||
return $this->name;
|
return $this->name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setName($name) {
|
||||||
|
$this->name = $name;
|
||||||
|
}
|
||||||
|
|
||||||
public function language() {
|
public function language() {
|
||||||
return $this->language;
|
return $this->language;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setLanguage($language) {
|
||||||
|
$this->language = $language;
|
||||||
|
}
|
||||||
|
|
||||||
public function format() {
|
public function format() {
|
||||||
return $this->format;
|
return $this->format;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setFormat($format) {
|
||||||
|
$this->format = $format;
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
?>
|
?>
|
||||||
@@ -54,22 +54,42 @@ class RippingCluster_Rips_SourceTitle {
|
|||||||
return $this->width;
|
return $this->width;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setWidth($width) {
|
||||||
|
$this->width = $width;
|
||||||
|
}
|
||||||
|
|
||||||
public function height() {
|
public function height() {
|
||||||
return $this->height;
|
return $this->height;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setHeight($height) {
|
||||||
|
$this->height = $height;
|
||||||
|
}
|
||||||
|
|
||||||
public function displayAspect() {
|
public function displayAspect() {
|
||||||
return $this->display_aspect;
|
return $this->display_aspect;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setDisplayAspect($display_aspect) {
|
||||||
|
$this->display_aspect = $display_aspect;
|
||||||
|
}
|
||||||
|
|
||||||
public function pixelAspect() {
|
public function pixelAspect() {
|
||||||
return $this->pixel_aspect;
|
return $this->pixel_aspect;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setPixelAspect($pixel_aspect) {
|
||||||
|
$this->pixel_aspect = $pixel_aspect;
|
||||||
|
}
|
||||||
|
|
||||||
public function framerate() {
|
public function framerate() {
|
||||||
return $this->framerate;
|
return $this->framerate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setFramerate($framerate) {
|
||||||
|
$this->framerate = $framerate;
|
||||||
|
}
|
||||||
|
|
||||||
public function setDisplayInfo($width, $height, $display_aspect, $pixel_aspect, $framerate) {
|
public function setDisplayInfo($width, $height, $display_aspect, $pixel_aspect, $framerate) {
|
||||||
$this->width = $width;
|
$this->width = $width;
|
||||||
$this->height = $height;
|
$this->height = $height;
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ class RippingCluster_Source {
|
|||||||
$this->plugin = $plugin;
|
$this->plugin = $plugin;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function isCached($source_filename) {
|
public static function isSourceCached($source_filename) {
|
||||||
$main = RippingCluster_Main::instance();
|
$main = RippingCluster_Main::instance();
|
||||||
$cache = $main->cache();
|
$cache = $main->cache();
|
||||||
$config = $main->config();
|
$config = $main->config();
|
||||||
@@ -26,6 +26,14 @@ class RippingCluster_Source {
|
|||||||
return $cache->exists($source_filename, $config->get('rips.cache_ttl'));
|
return $cache->exists($source_filename, $config->get('rips.cache_ttl'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function isCached() {
|
||||||
|
$main = RippingCluster_Main::instance();
|
||||||
|
$cache = $main->cache();
|
||||||
|
$config = $main->config();
|
||||||
|
|
||||||
|
return $cache->exists($this->filename, $config->get('rips.cache_ttl'));
|
||||||
|
}
|
||||||
|
|
||||||
public function cache() {
|
public function cache() {
|
||||||
if (!$this->exists) {
|
if (!$this->exists) {
|
||||||
throw new RippingCluster_Exception_InvalidSourceDirectory();
|
throw new RippingCluster_Exception_InvalidSourceDirectory();
|
||||||
|
|||||||
@@ -19,13 +19,12 @@ class RippingCluster_Source_Plugin_Bluray extends RippingCluster_PluginBase impl
|
|||||||
$config = RippingCluster_Main::instance()->config();
|
$config = RippingCluster_Main::instance()->config();
|
||||||
$directories = $config->get('source.bluray.dir');
|
$directories = $config->get('source.bluray.dir');
|
||||||
|
|
||||||
|
$sources = array();
|
||||||
foreach ($directories as $directory) {
|
foreach ($directories as $directory) {
|
||||||
if (!is_dir($directory)) {
|
if (!is_dir($directory)) {
|
||||||
throw new RippingCluster_Exception_InvalidSourceDirectory($directory);
|
throw new RippingCluster_Exception_InvalidSourceDirectory($directory);
|
||||||
}
|
}
|
||||||
|
|
||||||
$sources = array();
|
|
||||||
|
|
||||||
$iterator = new RippingCluster_Utility_BlurayDirectoryIterator(new RippingCluster_Utility_VisibleFilesIterator(new DirectoryIterator($directory)));
|
$iterator = new RippingCluster_Utility_BlurayDirectoryIterator(new RippingCluster_Utility_VisibleFilesIterator(new DirectoryIterator($directory)));
|
||||||
foreach ($iterator as /** @var SplFileInfo */ $source_vts) {
|
foreach ($iterator as /** @var SplFileInfo */ $source_vts) {
|
||||||
$sources[] = self::load($source_vts->getPathname(), false);
|
$sources[] = self::load($source_vts->getPathname(), false);
|
||||||
|
|||||||
@@ -25,13 +25,12 @@ class RippingCluster_Source_Plugin_HandBrake extends RippingCluster_PluginBase i
|
|||||||
$config = RippingCluster_Main::instance()->config();
|
$config = RippingCluster_Main::instance()->config();
|
||||||
$directories = $config->get('source.handbrake.dir');
|
$directories = $config->get('source.handbrake.dir');
|
||||||
|
|
||||||
|
$sources = array();
|
||||||
foreach ($directories as $directory) {
|
foreach ($directories as $directory) {
|
||||||
if (!is_dir($directory)) {
|
if (!is_dir($directory)) {
|
||||||
throw new RippingCluster_Exception_InvalidSourceDirectory($directory);
|
throw new RippingCluster_Exception_InvalidSourceDirectory($directory);
|
||||||
}
|
}
|
||||||
|
|
||||||
$sources = array();
|
|
||||||
|
|
||||||
$iterator = new RippingCluster_Utility_DvdDirectoryIterator(new RippingCluster_Utility_VisibleFilesIterator(new DirectoryIterator($directory)));
|
$iterator = new RippingCluster_Utility_DvdDirectoryIterator(new RippingCluster_Utility_VisibleFilesIterator(new DirectoryIterator($directory)));
|
||||||
foreach ($iterator as /** @var SplFileInfo */ $source_vts) {
|
foreach ($iterator as /** @var SplFileInfo */ $source_vts) {
|
||||||
$sources[] = self::load($source_vts->getPathname(), false);
|
$sources[] = self::load($source_vts->getPathname(), false);
|
||||||
@@ -210,12 +209,12 @@ class RippingCluster_Source_Plugin_HandBrake extends RippingCluster_PluginBase i
|
|||||||
foreach ($source_directories as $source_basedir) {
|
foreach ($source_directories as $source_basedir) {
|
||||||
$real_source_basedir = realpath($source_basedir);
|
$real_source_basedir = realpath($source_basedir);
|
||||||
|
|
||||||
if (substr($real_source_filename, 0, strlen($real_source_basedir)) != $real_source_basedir) {
|
if (substr($real_source_filename, 0, strlen($real_source_basedir)) == $real_source_basedir) {
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
249
lib/RippingCluster/Source/Plugin/MkvInfo.class.php
Normal file
249
lib/RippingCluster/Source/Plugin/MkvInfo.class.php
Normal file
@@ -0,0 +1,249 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class RippingCluster_Source_Plugin_MkvInfo extends RippingCluster_PluginBase implements RippingCluster_Source_IPlugin {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name of this plugin
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
const PLUGIN_NAME = 'MkvInfo';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name of the config setting that stores the list of source directories for this pluing
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
const CONFIG_SOURCE_DIR = 'source.mkvinfo.dir';
|
||||||
|
|
||||||
|
const PM_HEADERS = 0;
|
||||||
|
const PM_TRACK = 1;
|
||||||
|
const PM_TITLE = 2;
|
||||||
|
const PM_CHAPTER = 3;
|
||||||
|
const PM_AUDIO = 4;
|
||||||
|
const PM_SUBTITLE = 5;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of all Sources discovered by this plugin.
|
||||||
|
*
|
||||||
|
* The sources are not scanned until specifically requested.
|
||||||
|
*
|
||||||
|
* @return array(RippingCluster_Source)
|
||||||
|
*/
|
||||||
|
public static function enumerate() {
|
||||||
|
$config = RippingCluster_Main::instance()->config();
|
||||||
|
$directories = $config->get(self::CONFIG_SOURCE_DIR);
|
||||||
|
|
||||||
|
$sources = array();
|
||||||
|
foreach ($directories as $directory) {
|
||||||
|
if (!is_dir($directory)) {
|
||||||
|
throw new RippingCluster_Exception_InvalidSourceDirectory($directory);
|
||||||
|
}
|
||||||
|
|
||||||
|
$iterator = new RippingCluster_Utility_MkvFileIterator(new RecursiveIteratorIterator(new RippingCluster_Utility_VisibleFilesRecursiveIterator(new RecursiveDirectoryIterator($directory))));
|
||||||
|
foreach ($iterator as /** @var SplFileInfo */ $source_mkv) {
|
||||||
|
$sources[] = self::load($source_mkv->getPathname(), false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $sources;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an object to represent the given source.
|
||||||
|
*
|
||||||
|
* The source is not actually scanned unless specifically requested.
|
||||||
|
* An unscanned object cannot be used until it has been manually scanned.
|
||||||
|
*
|
||||||
|
* If requested, the source can be cached to prevent high load, and long scan times.
|
||||||
|
*
|
||||||
|
* @param string $source_filename Filename of the source
|
||||||
|
* @param bool $scan Request that the source be scanned for content. Defaults to true.
|
||||||
|
* @param bool $use_cache Request that the cache be used. Defaults to true.
|
||||||
|
* @return RippingCluster_Source
|
||||||
|
*/
|
||||||
|
public static function load($source_filename, $scan = true, $use_cache = true) {
|
||||||
|
$cache = RippingCluster_Main::instance()->cache();
|
||||||
|
$config = RippingCluster_Main::instance()->config();
|
||||||
|
|
||||||
|
// Ensure the source is a valid directory, and lies below the configured source_dir
|
||||||
|
if ( ! self::isValidSource($source_filename)) {
|
||||||
|
return new RippingCluster_Source($source_filename, self::name(), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
$source = null;
|
||||||
|
if ($use_cache && $cache->exists($source_filename)) {
|
||||||
|
$source = unserialize($cache->fetch($source_filename));
|
||||||
|
} else {
|
||||||
|
$source = new RippingCluster_Source($source_filename, self::name(), true);
|
||||||
|
|
||||||
|
if ($scan) {
|
||||||
|
$cmd = escapeshellcmd($config->get('source.mkvinfo.bin')) . ' ' . escapeshellarg($source_filename);
|
||||||
|
list($retval, $output, $error) = RippingCluster_ForegroundTask::execute($cmd);
|
||||||
|
|
||||||
|
// Process the output
|
||||||
|
$lines = explode("\n", $output);
|
||||||
|
$track = null;
|
||||||
|
$track_details = null;
|
||||||
|
$duration = null;
|
||||||
|
$mode = self::PM_HEADERS;
|
||||||
|
|
||||||
|
foreach ($lines as $line) {
|
||||||
|
// Skip any line that doesn't begin with a |+ (with optional whitespace)
|
||||||
|
if ( ! preg_match('/^|\s*\+/', $line)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$matches = array();
|
||||||
|
switch (true) {
|
||||||
|
|
||||||
|
case $mode == self::PM_HEADERS && preg_match('/^| \+ Duration: [\d\.]+s ([\d:]+])$/', $line, $matches): {
|
||||||
|
$duration = $matches['duration'];
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case preg_match('/^| \+ A track$/', $line, $matches): {
|
||||||
|
$mode = self::PM_TRACK;
|
||||||
|
$track_details = array();
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case $mode == self::PM_TRACK && preg_match('/^| \+ Track number: (?P<id>\d+):$/', $line, $matches): {
|
||||||
|
$track_details['id'] = $matches['id'];
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case $mode == self::PM_TRACK && preg_match('/^| \+ Track type: (?P<type>.+)$/', $line, $matches): {
|
||||||
|
switch ($type) {
|
||||||
|
case 'video': {
|
||||||
|
$mode = self::PM_TITLE;
|
||||||
|
$track = new RippingCluster_Rips_SourceTitle($track_details['id']);
|
||||||
|
$track->setDuration($duration);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case 'audio': {
|
||||||
|
$mode = self::PM_AUDIO;
|
||||||
|
$track = new RippingCluster_Rips_SourceAudioTrack($track_details['id']);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case 'subtitles': {
|
||||||
|
$mode = self::PM_SUBTITLE;
|
||||||
|
$track = new RippingCluster_Rips_SourceSubtitleTrack($track_details['id']);
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case $mode == self::PM_AUDIO && $track && preg_match('/^| \+ Codec ID: (?P<codec>.+)$/', $line, $matches): {
|
||||||
|
$track->setFormat($matches['codec']);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case $mode == self::PM_AUDIO && $track && preg_match('/^| \+ Language: (?P<language>.+)$/', $line, $matches): {
|
||||||
|
$track->setLanguage($matches['language']);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case $mode == self::PM_AUDIO && $track && preg_match('/^| \+ Sampling frequency: (?P<samplerate>.+)$/', $line, $matches): {
|
||||||
|
$track->setSampleRate($matches['samplerate']);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case $mode == self::PM_AUDIO && $track && preg_match('/^| \+ Channels: (?P<channels>.+)$/', $line, $matches): {
|
||||||
|
$track->setFormat($matches['channels']);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case $mode == self::PM_SUBTITLE && $track && preg_match('/^| \+ Language: (?P<language>.*)$/', $line): {
|
||||||
|
$track->setLanguage($matches['language']);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case $mode == self::PM_TITLE && $track && preg_match('/^ \+ Default duration: [\d\.]+ \((?P<framerate>[\d\.]+ fps for a video track)\)$/', $line, $matches): {
|
||||||
|
$title->setFramerate($matches['framerate']);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case $mode == self::PM_TITLE && $track && preg_match('/^ \+ Pixel width: (?P<width>\d+)$/', $line, $matches): {
|
||||||
|
$title->setWidth($matches['width']);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case $mode == self::PM_TITLE && $track && preg_match('/^ \+ Pixel height: (?P<height>\d+)$/', $line, $matches): {
|
||||||
|
$title->setHeight($matches['height']);
|
||||||
|
} 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 RippingCluster_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 RippingCluster_Rips_SourceSubtitleTrack(
|
||||||
|
$matches['id'], $matches['name'], $matches['language'], $matches['format']
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
default: {
|
||||||
|
// Ignore this unmatched line
|
||||||
|
} break;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// If requested, store the new source object in the cache
|
||||||
|
if ($use_cache) {
|
||||||
|
$source->cache();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an object to represent the given source using an encoded filename.
|
||||||
|
*
|
||||||
|
* Wraps the call to load the source after the filename has been decoded.
|
||||||
|
*
|
||||||
|
* @param string $encoded_filename Encoded filename of the source
|
||||||
|
* @param bool $scan Request that the source be scanned for content. Defaults to true.
|
||||||
|
* @param bool $use_cache Request that the cache be used. Defaults to true.
|
||||||
|
* @return RippingCluster_Source
|
||||||
|
*
|
||||||
|
* @see RippingCluster_Source_IPlugin::load()
|
||||||
|
*/
|
||||||
|
public static function loadEncoded($encoded_filename, $scan = true, $use_cache = true) {
|
||||||
|
// Decode the filename
|
||||||
|
$source_filename = base64_decode(str_replace('-', '/', $encoded_filename));
|
||||||
|
|
||||||
|
return self::load($source_filename, $scan, $use_cache);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determins if a filename is a valid source loadable using this plugin
|
||||||
|
*
|
||||||
|
* @param string $source_filename Filename of the source
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public static function isValidSource($source_filename) {
|
||||||
|
$config = RippingCluster_Main::instance()->config();
|
||||||
|
|
||||||
|
// Ensure the source is a valid directory, and lies below the configured source_dir
|
||||||
|
if ( ! is_dir($source_filename)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$real_source_filename = realpath($source_filename);
|
||||||
|
|
||||||
|
// Check all of the source directories specified in the config
|
||||||
|
$source_directories = $config->get(self::CONFIG_SOURCE_DIR);
|
||||||
|
foreach ($source_directories as $source_basedir) {
|
||||||
|
$real_source_basedir = realpath($source_basedir);
|
||||||
|
|
||||||
|
if (substr($real_source_filename, 0, strlen($real_source_basedir)) != $real_source_basedir) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
@@ -31,7 +31,7 @@ class RippingCluster_Source_PluginFactory extends RippingCluster_PluginFactory {
|
|||||||
|
|
||||||
$sources = array();
|
$sources = array();
|
||||||
foreach (self::getValidPlugins() as $plugin) {
|
foreach (self::getValidPlugins() as $plugin) {
|
||||||
$sources = array_merge($sources, self::enumerate($plugin));
|
$sources[$plugin] = self::enumerate($plugin);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $sources;
|
return $sources;
|
||||||
|
|||||||
9
lib/RippingCluster/Utility/MkvFileIterator.class.php
Normal file
9
lib/RippingCluster/Utility/MkvFileIterator.class.php
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class RippingCluster_Utility_MkvFileIterator extends FilterIterator {
|
||||||
|
public function accept() {
|
||||||
|
return preg_match('/\.mkv$/i', $this->current()->getFilename());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class RippingCluster_Utility_VisibleFilesRecursiveIterator extends RecursiveFilterIterator {
|
||||||
|
public function accept() {
|
||||||
|
return !(substr($this->current()->getFilename(), 0, 1) == '.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
85
lib/RippingCluster/Worker/Plugin/FfmpegTranscode.class.php
Normal file
85
lib/RippingCluster/Worker/Plugin/FfmpegTranscode.class.php
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class RippingCluster_Worker_FfmpegTranscode extends RippingCluster_PluginBase implements RippingCluster_Worker_IPlugin {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name of this plugin
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
const PLUGIN_NAME = 'FfmpegTranscode';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Output produced by the worker process
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $output;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gearman Job object describing the task distributed to this worker
|
||||||
|
* @var GearmanJob
|
||||||
|
*/
|
||||||
|
private $gearman_job;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ripping Job that is being processed by this Worker
|
||||||
|
* @var RippingCluster_Job
|
||||||
|
*/
|
||||||
|
private $job;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Associative array of options describing the rip to be carried out
|
||||||
|
* @var array(string=>string)
|
||||||
|
*/
|
||||||
|
private $rip_options;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new instance of this Worker class
|
||||||
|
*
|
||||||
|
* @param GearmanJob $gearman_job GearmanJob object describing the task distributed to this worker
|
||||||
|
* @throws RippingCluster_Exception_LogicException
|
||||||
|
*/
|
||||||
|
private function __construct(GearmanJob $gearman_job) {
|
||||||
|
$this->output = '';
|
||||||
|
|
||||||
|
$this->gearman_job = $gearman_job;
|
||||||
|
|
||||||
|
$this->rip_options = unserialize($this->gearman_job->workload());
|
||||||
|
|
||||||
|
if ( ! $this->rip_options['id']) {
|
||||||
|
throw new RippingCluster_Exception_LogicException("Job ID must not be zero/null");
|
||||||
|
}
|
||||||
|
$this->job = RippingCluster_Job::fromId($this->rip_options['id']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the list of functions (and names) implemented by this plugin for registration with Gearman
|
||||||
|
*
|
||||||
|
* @return array(string => callback)
|
||||||
|
*/
|
||||||
|
public static function workerFunctions() {
|
||||||
|
return array(
|
||||||
|
'bluray_rip' => array(__CLASS__, 'rip'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an instance of the Worker plugin, and uses it to execute a single job
|
||||||
|
*
|
||||||
|
* @param GearmanJob $job Gearman Job object, describing the work to be done
|
||||||
|
*/
|
||||||
|
public static function rip(GearmanJob $job) {
|
||||||
|
$rip = new self($job);
|
||||||
|
$rip->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes the process for ripping the source to the final output
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private function execute() {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
@@ -3,14 +3,7 @@
|
|||||||
$main = RippingCluster_Main::instance();
|
$main = RippingCluster_Main::instance();
|
||||||
$config = $main->config();
|
$config = $main->config();
|
||||||
|
|
||||||
$sources = RippingCluster_Source_PluginFactory::enumerateAll();
|
$all_sources = RippingCluster_Source_PluginFactory::enumerateAll();
|
||||||
|
$this->smarty->assign('all_sources', $all_sources);
|
||||||
$sources_cached = array();
|
|
||||||
foreach ($sources as $source) {
|
|
||||||
$sources_cached[$source->filename()] = RippingCluster_Source::isCached($source->filename());
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->smarty->assign('sources', $sources);
|
|
||||||
$this->smarty->assign('sources_cached', $sources_cached);
|
|
||||||
|
|
||||||
?>
|
?>
|
||||||
@@ -14,7 +14,6 @@ try {
|
|||||||
$gearman->addServers($config->get('rips.job_servers'));
|
$gearman->addServers($config->get('rips.job_servers'));
|
||||||
$gearman->setCreatedCallback("gearman_created_callback");
|
$gearman->setCreatedCallback("gearman_created_callback");
|
||||||
$gearman->setDataCallback("gearman_data_callback");
|
$gearman->setDataCallback("gearman_data_callback");
|
||||||
$gearman->setStatusCallback("gearman_status_callback");
|
|
||||||
$gearman->setCompleteCallback("gearman_complete_callback");
|
$gearman->setCompleteCallback("gearman_complete_callback");
|
||||||
$gearman->setFailCallback("gearman_fail_callback");
|
$gearman->setFailCallback("gearman_fail_callback");
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<h2>Sources</h2>
|
<h2>Sources</h2>
|
||||||
|
|
||||||
{if $sources}
|
{if $all_sources}
|
||||||
<p>
|
<p>
|
||||||
The list below contains all the DVD sources that are available and ready for ripping.
|
The list below contains all the DVD sources that are available and ready for ripping.
|
||||||
</p>
|
</p>
|
||||||
@@ -9,19 +9,32 @@
|
|||||||
Sources that have not been cached will be scanned when the link is clicked, and this may take several minutes so please be patient.
|
Sources that have not been cached will be scanned when the link is clicked, and this may take several minutes so please be patient.
|
||||||
</p>
|
</p>
|
||||||
<ul>
|
<ul>
|
||||||
{foreach from=$sources item=source}
|
{foreach from=$all_sources key=type item=sources}
|
||||||
{assign var='source_plugin' value=$source->plugin()}
|
<li>{$type}
|
||||||
{assign var='source_filename' value=$source->filename()}
|
{if $sources}
|
||||||
{assign var='source_filename_encoded' value=$source->filenameEncoded()}
|
<ul>
|
||||||
<li>
|
{foreach from=$sources item=source}
|
||||||
[ <a href="{$base_uri}rips/source-details/plugin/{$source_plugin}/id/{$source_filename_encoded}" title="Browse source details">Browse</a> |
|
{assign var='source_plugin' value=$source->plugin()}
|
||||||
<a href="{$base_uri}rips/setup-rip/plugin/{$source_plugin}/id/{$source_filename_encoded}" title="Rip this source">Rip</a> ]
|
{assign var='source_filename' value=$source->filename()}
|
||||||
{$source_filename|escape:'html'}{if $sources_cached.$source_filename} (cached){/if}
|
{assign var='source_filename_encoded' value=$source->filenameEncoded()}
|
||||||
|
{assign var='source_cached' value="$source->isCached()}
|
||||||
|
<li>
|
||||||
|
[ <a href="{$base_uri}rips/source-details/plugin/{$source_plugin}/id/{$source_filename_encoded}" title="Browse source details">Browse</a> |
|
||||||
|
<a href="{$base_uri}rips/setup-rip/plugin/{$source_plugin}/id/{$source_filename_encoded}" title="Rip this source">Rip</a> ]
|
||||||
|
{$source_filename|escape:'html'}{if $source_cached} (cached){/if}
|
||||||
|
</li>
|
||||||
|
{/foreach}
|
||||||
|
</ul>
|
||||||
|
{else}
|
||||||
|
<p>
|
||||||
|
<em>There are no {$type} sources available to rip.</em>
|
||||||
|
</p>
|
||||||
|
{/if}
|
||||||
</li>
|
</li>
|
||||||
{/foreach}
|
{/foreach}
|
||||||
</ul>
|
</ul>
|
||||||
{else}
|
{else}
|
||||||
<p>
|
<p>
|
||||||
<em>There are currently no DVD sources available to rip.</em>
|
<em>There are currently no sources available to rip.</em>
|
||||||
</p>
|
</p>
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
Reference in New Issue
Block a user