Complete TV move/rename operations

This commit is contained in:
2011-12-31 01:25:41 +00:00
parent 0cbe522a67
commit 4c1fd693e4
4 changed files with 178 additions and 21 deletions

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "source/3rdparty/tvrenamer"]
path = source/3rdparty/tvrenamer
url = https://github.com/meermanr/TVSeriesRenamer.git

1
source/3rdparty/tvrenamer vendored Submodule

View File

@@ -7,11 +7,16 @@ class DownloadDispatcher_Source_Plugin_TV extends DownloadDispatcher_Source_Plug
*
* @var string
*/
const PLUGIN_NAME = "TV";
const PLUGIN_NAME = "TV";
protected $config;
protected $log;
protected $output_dir_cache;
protected $input_dirs;
protected $output_dir;
public static function create($config, $log) {
return new self($config, $log);
}
@@ -19,18 +24,20 @@ class DownloadDispatcher_Source_Plugin_TV extends DownloadDispatcher_Source_Plug
protected function __construct($config, $log) {
$this->config = $config;
$this->log = $log;
$this->input_dirs = $this->config->get('sources.TV.input');
$this->output_dir = $this->config->get('sources.TV.output');
}
public function run() {
DownloadDispatcher_LogEntry::debug($this->log, 'Running TV dispatcher');
// Iterate over source directories, and move matched files to the output directory
$source_dirs = $this->config->get('sources.TV.input-directories');
foreach ($source_dirs as $dir) {
foreach ($this->input_dirs as $dir) {
if (is_dir($dir) && is_readable($dir)) {
$iterator = new DownloadDispatcher_Utility_MediaFilesIterator(new DownloadDispatcher_Utility_VisibleFilesIterator(new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir))));
foreach ($iterator as /** @var SplFileInfo */ $file) {
$this->process_matched_file($file->getPath(), $file->getFilename());
$this->processMatchedFile($file->getPath(), $file->getFilename(), $file->getExtension());
}
} else {
DownloadDispatcher_LogEntry::warning($this->log, "TV input directory '{$dir}' does not exist or cannot be read.");
@@ -38,31 +45,177 @@ class DownloadDispatcher_Source_Plugin_TV extends DownloadDispatcher_Source_Plug
}
}
protected function process_matched_file($dir, $file) {
protected function processMatchedFile($dir, $file, $type) {
// TODO - Handle movement of the matched file to the correct output directory
// Handle direct media files, and also RAR archives
DownloadDispatcher_LogEntry::debug($this->log, "Media file: {$file}");
// Check to see if this file has been handled previously
if ($this->check_processed($dir . '/' . $file)) {
if ($this->checkProcessed($dir . '/' . $file)) {
DownloadDispatcher_LogEntry::debug($this->log, "Skipping previously seen file");
return;
}
$full_output_dir = $this->identifyOutputDir($dir, $file);
if ($full_output_dir) {
if ($this->noDuplicates($full_output_dir, $file)) {
if ($this->copyOutput($type, $dir, $file, $full_output_dir)) {
$this->renameOutput($full_output_dir);
}
}
}
}
protected function identify_output_dir($dir, $file) {
protected function identifyOutputDir($dir, $file) {
// TODO - Generate the correct output directory, apply any special case mappings, and ensure the destination exists
if (is_null($this->output_dir_cache)) {
$this->scanOutputDir();
}
$normalised_file = $this->normalise($file);
if (array_key_exists($normalised_file, $this->output_dir_cache)) {
$season = $this->season($file);
$full_output_dir = "{$this->output_dir}/{$this->output_dir_cache[$normalised_file]}/Season {$season}";
if (is_dir($full_output_dir)) {
return $full_output_dir;
}
}
DownloadDispatcher_LogEntry::warning($this->log, "TV output directory for '{$file}' could not be identified; you may need to create one.");
return null;
}
protected function identify_duplicate($dir, $file) {
// TODO - Verify that the file we've found hasn't already been processed
// Use the cache to reduce processing overhead
// TODO - Upstream caching
protected function scanOutputDir() {
// Get a list of the series and season directories available in normalised form
DownloadDispatcher_LogEntry::debug($this->log, "Scanning TV output directory ({$this->output_dir})");
$this->output_dir_cache = array();
$series_iterator = new DownloadDispatcher_Utility_VisibleFilesIterator(new DirectoryIterator($this->output_dir));
foreach ($series_iterator as $series) {
$series_name = $series->getBasename();
$normalised_series = $this->normalise($series_name);
$this->output_dir_cache[$normalised_series] = $series_name;
}
}
protected function rename_output($dir, $file) {
// TODO - use tvrenamer to update the filenames
protected function normalise($name) {
if (preg_match('/(.*?)([\s\.]us)?([\s\.]+(19|20)\d{2})?[\s\.]+(\d+x\d+|s\d+e\d+|\d{3}).*/i', $name, $matches)) {
$name = $matches[1];
}
$name = preg_replace('/[^a-zA-Z0-9]/', ' ', $name);
$name = preg_replace('/ +/', ' ', $name);
$name = strtolower($name);
$name = trim($name);
return $name;
}
protected function season($name) {
$set_season = function($a) {
for ($i = 1, $l = count($a); $i < $l; ++$i) {
if ($a[$i]) {
return trim($a[$i], '0');
}
}
return null;
};
if (preg_match('/(\d+)x\d+|s(\d+)e\d+|(?:(?:19|20)\d{2}[\s\.]+)?(\d+)\d{2}/i', $name, $matches)) {
return $set_season($matches);
} else {
return 0;
}
}
protected function episode($name) {
$set_episode = function($a) {
for ($i = 1, $l = count($a); $i < $l; ++$i) {
if ($a[$i]) {
return trim($a[$i], '0');
}
}
return null;
};
if (preg_match('/\d+x(\d+)|s\d+e(\d+)|(?:(?:19|20)\d{2}[\s\.]+)?\d+(\d{2})/i', $name, $matches)) {
return $set_episode($matches);
} else {
return 0;
}
}
protected function noDuplicates($dir, $file) {
$episode = $this->episode($file);
$iterator = new DownloadDispatcher_Utility_MediaFilesIterator(new DownloadDispatcher_Utility_VisibleFilesIterator(new DirectoryIterator($dir)));
foreach ($iterator as /** @var SplFileInfo */ $existing_file) {
$existing_episode = $this->episode($existing_file->getFilename());
if ($existing_episode == $episode) {
return false;
}
}
return true;
}
protected function copyOutput($type, $source_dir, $source_file, $destination_dir) {
switch (strtolower($type)) {
case 'rar': {
DownloadDispatcher_LogEntry::info($this->log, "Unrarring '{$source_file}' into '{$destination_dir}'.");
$command = "/usr/bin/unrar e -p- -sm8192 -y {$source_dir}/{$source_file}";
DownloadDispatcher_LogEntry::debug($this->log, "Unrarring '{$source_file}' with command: {$command}");
list($code, $output, $error) = DownloadDispatcher_ForegroundTask::execute($command, $destination_dir);
} break;
case 'avi': {
// Verify that the file isn't a fake
$safe_source_file = escapeshellarg($source_file);
$command = "file {$safe_source_file}";
DownloadDispatcher_LogEntry::debug($this->log, "Verifying '{$source_file}' contents with command: {$command}");
list($code, $output, $error) = DownloadDispatcher_ForegroundTask::execute($command, $source_dir);
var_dump($code, $output, $error);
if (preg_match('/Microsoft ASF/', $output)) {
DownloadDispatcher_LogEntry::warning($this->log, "Skipping '{$source_dir}/{$source_file}' due to dubious contents.");
return false;
}
} // continue into the next case
default: {
DownloadDispatcher_LogEntry::info($this->log, "Copying '{$source_file}' to '{$destination_dir}'.");
copy("{$source_dir}/${source_file}", "{$destination_dir}/{$source_file}");
}
}
return true;
}
protected function renameOutput($dir) {
$cwd = getcwd();
$command = <<<EOSH
{$cwd}/3rdparty/tvrenamer/tvrenamer.pl \
--include_series \
--nogroup \
--pad=2 \
--scheme=XxYY \
--preproc='s/x264//;' \
--postproc='s/(?:-+img|-+a).*(\.[a-zA-Z0-9]+$)/\1/;' \
--unattended \
--dubious \
--cache
EOSH;
DownloadDispatcher_LogEntry::debug($this->log, "Executing tvrenamer command in '{$dir}': {$command}");
list($code, $output, $error) = DownloadDispatcher_ForegroundTask::execute($command, $dir);
var_dump($code, $output, $error);
}
}

View File

@@ -5,17 +5,17 @@ class DownloadDispatcher_Source_PluginBase extends DownloadDispatcher_PluginBase
static protected $cache;
static protected $source_cache = null;
static protected $cache_file = 'source_cache';
static protected $source_cache_file = 'source_cache';
static protected $cache_lifetime = 86400;
protected function init_cache() {
protected function initSourceCache() {
if ( ! static::$cache) {
static::$cache = DownloadDispatcher_Main::instance()->cache();
}
if (is_null(static::$source_cache)) {
try {
static::$source_cache = static::$cache->fetch(static::$cache_file, static::$cache_lifetime);
static::$source_cache = static::$cache->fetch(static::$source_cache_file, static::$cache_lifetime);
} catch (SihnonFramework_Exception_CacheObjectNotFound $e) {
static::$source_cache = array();
}
@@ -26,18 +26,18 @@ class DownloadDispatcher_Source_PluginBase extends DownloadDispatcher_PluginBase
}
}
protected function mark_processed($file) {
$this->init_cache();
protected function markProcessed($file) {
$this->initSourceCache();
if ( ! in_array($file, static::$source_cache[get_called_class()])) {
static::$source_cache[get_called_class()][] = $file;
}
static::$cache->store($cache_file, static::$source_cache);
static::$cache->store(static::$source_cache_file, static::$source_cache);
}
protected function check_processed($file) {
$this->init_cache();
protected function checkProcessed($file) {
$this->initSourceCache();
return in_array($file, static::$source_cache[get_called_class()]);
}