From 2ef47de25c32646cafdd0f1f0fa0581aedbecf02 Mon Sep 17 00:00:00 2001
From: Ben Roberts
Date: Fri, 24 Sep 2010 20:05:37 +0100
Subject: [PATCH] Added MKV source/worker plugins, tidied sources page
Added placeholder for source plugin to read from an existing mkv file.
Added corresponding worker placeholder to transcode an mkv file using
ffmpeg.
Updated the sources page to show which sources come from which plugins.
---
lib/RippingCluster/Config.class.php | 4 +-
lib/RippingCluster/Source.class.php | 10 +-
.../Source/Plugin/Bluray.class.php | 3 +-
.../Source/Plugin/HandBrake.class.php | 11 +-
.../Source/Plugin/MkvInfo.class.php | 128 ++++++++++++++++++
.../Source/PluginFactory.class.php | 2 +-
.../Utility/MkvFileIterator.class.php | 9 ++
.../VisibleFilesRecursiveIterator.class.php | 9 ++
.../Worker/Plugin/FfmpegTranscode.class.php | 85 ++++++++++++
webui/pages/rips/sources.php | 11 +-
webui/templates/rips/sources.tpl | 33 +++--
11 files changed, 274 insertions(+), 31 deletions(-)
create mode 100644 lib/RippingCluster/Source/Plugin/MkvInfo.class.php
create mode 100644 lib/RippingCluster/Utility/MkvFileIterator.class.php
create mode 100644 lib/RippingCluster/Utility/VisibleFilesRecursiveIterator.class.php
create mode 100644 lib/RippingCluster/Worker/Plugin/FfmpegTranscode.class.php
diff --git a/lib/RippingCluster/Config.class.php b/lib/RippingCluster/Config.class.php
index 88d6064..66785a5 100644
--- a/lib/RippingCluster/Config.class.php
+++ b/lib/RippingCluster/Config.class.php
@@ -132,8 +132,8 @@ class RippingCluster_Config {
}
switch ($this->settings[$key]['type']) {
- case TYPE_STRING_LIST:
- return explode("\n", $this->settings[$key]['value']);
+ case self::TYPE_STRING_LIST:
+ return array_map('trim', explode("\n", $this->settings[$key]['value']));
default:
return $this->settings[$key]['value'];
diff --git a/lib/RippingCluster/Source.class.php b/lib/RippingCluster/Source.class.php
index e602767..c3e907c 100644
--- a/lib/RippingCluster/Source.class.php
+++ b/lib/RippingCluster/Source.class.php
@@ -18,7 +18,7 @@ class RippingCluster_Source {
$this->plugin = $plugin;
}
- public static function isCached($source_filename) {
+ public static function isSourceCached($source_filename) {
$main = RippingCluster_Main::instance();
$cache = $main->cache();
$config = $main->config();
@@ -26,6 +26,14 @@ class RippingCluster_Source {
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() {
if (!$this->exists) {
throw new RippingCluster_Exception_InvalidSourceDirectory();
diff --git a/lib/RippingCluster/Source/Plugin/Bluray.class.php b/lib/RippingCluster/Source/Plugin/Bluray.class.php
index b3a8c80..96c6082 100644
--- a/lib/RippingCluster/Source/Plugin/Bluray.class.php
+++ b/lib/RippingCluster/Source/Plugin/Bluray.class.php
@@ -19,13 +19,12 @@ class RippingCluster_Source_Plugin_Bluray extends RippingCluster_PluginBase impl
$config = RippingCluster_Main::instance()->config();
$directories = $config->get('source.bluray.dir');
+ $sources = array();
foreach ($directories as $directory) {
if (!is_dir($directory)) {
throw new RippingCluster_Exception_InvalidSourceDirectory($directory);
}
- $sources = array();
-
$iterator = new RippingCluster_Utility_BlurayDirectoryIterator(new RippingCluster_Utility_VisibleFilesIterator(new DirectoryIterator($directory)));
foreach ($iterator as /** @var SplFileInfo */ $source_vts) {
$sources[] = self::load($source_vts->getPathname(), false);
diff --git a/lib/RippingCluster/Source/Plugin/HandBrake.class.php b/lib/RippingCluster/Source/Plugin/HandBrake.class.php
index d961023..aec5f88 100644
--- a/lib/RippingCluster/Source/Plugin/HandBrake.class.php
+++ b/lib/RippingCluster/Source/Plugin/HandBrake.class.php
@@ -25,13 +25,12 @@ class RippingCluster_Source_Plugin_HandBrake extends RippingCluster_PluginBase i
$config = RippingCluster_Main::instance()->config();
$directories = $config->get('source.handbrake.dir');
+ $sources = array();
foreach ($directories as $directory) {
if (!is_dir($directory)) {
throw new RippingCluster_Exception_InvalidSourceDirectory($directory);
}
- $sources = array();
-
$iterator = new RippingCluster_Utility_DvdDirectoryIterator(new RippingCluster_Utility_VisibleFilesIterator(new DirectoryIterator($directory)));
foreach ($iterator as /** @var SplFileInfo */ $source_vts) {
$sources[] = self::load($source_vts->getPathname(), false);
@@ -207,15 +206,15 @@ class RippingCluster_Source_Plugin_HandBrake extends RippingCluster_PluginBase i
// Check all of the source directories specified in the config
$source_directories = $config->get('source.handbrake.dir');
- foreach ($source_directories as $source_basedir) {
+ 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;
+ if (substr($real_source_filename, 0, strlen($real_source_basedir)) == $real_source_basedir) {
+ return true;
}
}
- return true;
+ return false;
}
}
diff --git a/lib/RippingCluster/Source/Plugin/MkvInfo.class.php b/lib/RippingCluster/Source/Plugin/MkvInfo.class.php
new file mode 100644
index 0000000..c16ea8c
--- /dev/null
+++ b/lib/RippingCluster/Source/Plugin/MkvInfo.class.php
@@ -0,0 +1,128 @@
+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();
+
+ // 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);
+
+ // TODO Populate source object with content
+
+ // 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;
+ }
+
+}
+
+?>
\ No newline at end of file
diff --git a/lib/RippingCluster/Source/PluginFactory.class.php b/lib/RippingCluster/Source/PluginFactory.class.php
index 89d6618..ec25bdc 100644
--- a/lib/RippingCluster/Source/PluginFactory.class.php
+++ b/lib/RippingCluster/Source/PluginFactory.class.php
@@ -31,7 +31,7 @@ class RippingCluster_Source_PluginFactory extends RippingCluster_PluginFactory {
$sources = array();
foreach (self::getValidPlugins() as $plugin) {
- $sources = array_merge($sources, self::enumerate($plugin));
+ $sources[$plugin] = self::enumerate($plugin);
}
return $sources;
diff --git a/lib/RippingCluster/Utility/MkvFileIterator.class.php b/lib/RippingCluster/Utility/MkvFileIterator.class.php
new file mode 100644
index 0000000..37bd51d
--- /dev/null
+++ b/lib/RippingCluster/Utility/MkvFileIterator.class.php
@@ -0,0 +1,9 @@
+current()->getFilename());
+ }
+}
+
+?>
\ No newline at end of file
diff --git a/lib/RippingCluster/Utility/VisibleFilesRecursiveIterator.class.php b/lib/RippingCluster/Utility/VisibleFilesRecursiveIterator.class.php
new file mode 100644
index 0000000..151d938
--- /dev/null
+++ b/lib/RippingCluster/Utility/VisibleFilesRecursiveIterator.class.php
@@ -0,0 +1,9 @@
+current()->getFilename(), 0, 1) == '.');
+ }
+}
+
+?>
\ No newline at end of file
diff --git a/lib/RippingCluster/Worker/Plugin/FfmpegTranscode.class.php b/lib/RippingCluster/Worker/Plugin/FfmpegTranscode.class.php
new file mode 100644
index 0000000..8d63063
--- /dev/null
+++ b/lib/RippingCluster/Worker/Plugin/FfmpegTranscode.class.php
@@ -0,0 +1,85 @@
+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
+ }
+
+}
+
+?>
\ No newline at end of file
diff --git a/webui/pages/rips/sources.php b/webui/pages/rips/sources.php
index 3aadca0..b9fc539 100644
--- a/webui/pages/rips/sources.php
+++ b/webui/pages/rips/sources.php
@@ -3,14 +3,7 @@
$main = RippingCluster_Main::instance();
$config = $main->config();
-$sources = RippingCluster_Source_PluginFactory::enumerateAll();
-
-$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);
+$all_sources = RippingCluster_Source_PluginFactory::enumerateAll();
+$this->smarty->assign('all_sources', $all_sources);
?>
\ No newline at end of file
diff --git a/webui/templates/rips/sources.tpl b/webui/templates/rips/sources.tpl
index 46951af..c0cbb1a 100644
--- a/webui/templates/rips/sources.tpl
+++ b/webui/templates/rips/sources.tpl
@@ -1,6 +1,6 @@
Sources
-{if $sources}
+{if $all_sources}
The list below contains all the DVD sources that are available and ready for ripping.
@@ -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.
{else}
- There are currently no DVD sources available to rip.
+ There are currently no sources available to rip.
{/if}