33 Commits

Author SHA1 Message Date
6648fd8b24 Merge branch 'release-0.2.1' 2011-08-23 02:11:32 +01:00
073da2c4e2 Bumped version to 0.2.1 2011-08-23 02:11:02 +01:00
d474fba2b3 Merge branch 'feature-summaries' into develop 2011-08-23 02:08:40 +01:00
c5eb93dd46 Add hostname to job details worker logs 2011-08-23 01:35:51 +01:00
fbc6f7da48 Add date to job details page. 2011-08-23 01:29:54 +01:00
49e5635a71 Bug fix: save plugin information in job record 2011-08-23 01:28:37 +01:00
8739f6c516 Expose job log display options in the UI 2011-08-23 00:05:21 +01:00
5f786d16d7 Add control of job log message count and sort order via URL 2011-08-22 23:18:57 +01:00
22a3d94dc3 Move job details page and update references 2011-08-22 22:51:46 +01:00
f3415ff57a Merge commit 'release-0.2' into develop 2011-08-22 19:05:40 +01:00
6a57a6fca5 Merge branch 'release-0.2' 2011-08-22 19:04:36 +01:00
a061c23041 Bumped version number to 0.2 2011-08-22 19:03:36 +01:00
4300034afa Merge branch 'feature-ajax' into develop 2011-08-22 18:48:59 +01:00
eb1e330bc4 Added ajax support, and verified with an ajaxified Delete Source feature 2011-08-22 18:47:45 +01:00
41fc0a2cc3 Improve logging in HandBrake worker 2011-08-21 16:12:32 +01:00
95fe2e7641 Add additional logging for batch job runner 2011-08-21 16:11:51 +01:00
8d2ca716df Updated worker to copy rather than move output to final location
This causes ACLs to be inherited from the destination, rather than
preserving permissions from the temporary build location on the local
machine.
2011-08-20 11:07:30 +01:00
47946bcf98 Update handbrake backend to rip to temp dir 2011-08-07 15:40:17 +01:00
d2b2dc7925 Add output filesize to jobs summary page 2011-08-06 23:11:15 +01:00
e84c1eba42 Set logging program name 2011-08-06 14:57:14 +01:00
b93efc9878 Replace if block with switch case for extensibility 2011-08-06 14:55:29 +01:00
9697654594 Propagate changes to logging system from framework library 2011-08-06 14:54:22 +01:00
5121f78cea Merge branch 'master' of git+ssh://git.sihnon.net/home/git/public/handbrake-cluster-webui 2011-08-05 19:40:42 +01:00
dbc1252bef Added config file override for worker script 2011-08-05 19:35:55 +01:00
a3e58e4ee4 Merge branch 'master' of git+ssh://git.sihnon.net/home/git/public/handbrake-cluster-webui 2011-06-28 20:11:50 +01:00
3b22b0f2c9 Bug fix: Undefined index $values[9]
RippingCluster_LogEntry overwrote base class field list, causing
undefined index notices when the base class was explicitly used.
2011-06-28 20:06:24 +01:00
efb7db35d8 Fixed typo in _inc.php 2011-06-20 22:33:18 +01:00
841a5b9f92 Revert previous untested change in include statements 2011-06-20 22:04:44 +01:00
506a6e189c Update paths for packaging 2011-06-20 19:34:27 +01:00
3da59727de Bug fix: typo in variable name 2011-06-20 19:29:11 +01:00
c0d8747b21 Update init script to background the worker process 2011-06-19 22:48:45 +01:00
e1bd324e84 Bugfix: error in client log call 2011-06-19 00:13:39 +01:00
8fe8f8ba08 Use defines for library paths 2011-06-18 23:53:25 +01:00
34 changed files with 664 additions and 168 deletions

View File

@@ -12,7 +12,7 @@ depend() {
start() {
ebegin "Starting ripping-cluster-worker"
start-stop-daemon --start --quiet \
--pidfile ${PID_FILE} \
--background --make-pidfile --pidfile ${PID_FILE} \
--exec /usr/bin/php /usr/lib/ripping-cluster/worker/ripping-cluster-worker.php
eend $? "Failed to start ripping-cluster-worker"
}

View File

@@ -30,11 +30,14 @@ class Net_Gearman_Job_HandBrake extends Net_Gearman_Job_Common implements Rippin
$this->job = RippingCluster_Job::fromId($args['rip_options']['id']);
// Substitute a temporary output filename into the rip options
$args['temp_output_filename'] = tempnam($config->get('rips.temp_dir', '/tmp'), 'hbr_');
$handbrake_cmd_raw = array(
'-n', $config->get('rips.nice'),
$config->get('rips.handbrake_binary'),
self::evaluateOption($args['rip_options'], 'input_filename', '-i'),
self::evaluateOption($args['rip_options'], 'output_filename', '-o'),
self::evaluateOption($args, 'temp_output_filename', '-o'),
self::evaluateOption($args['rip_options'], 'title'),
self::evaluateOption($args['rip_options'], 'format', '-f'),
self::evaluateOption($args['rip_options'], 'video_codec', '-e'),
@@ -61,12 +64,34 @@ class Net_Gearman_Job_HandBrake extends Net_Gearman_Job_Common implements Rippin
list($return_val, $stdout, $stderr) = RippingCluster_ForegroundTask::execute($handbrake_cmd, null, null, null, array($this, 'callbackOutput'), array($this, 'callbackOutput'), $this);
if ($return_val) {
$this->fail($return_val);
// Remove any temporary output files
if (file_exists($args['temp_output_filename'])) {
$result = unlink($args['temp_output_filename']);
if (!$result) {
RippingCluster_WorkerLogEntry::warning($log, $this->job->id(), "Failed to remove temporary output file, still exists at '{$args['temp_output_filename']}'.");
}
}
$this->fail("Call to HandBrake failed with return code {$return_val}.");
} else {
$this->job->updateStatus(RippingCluster_JobStatus::COMPLETE);
$this->complete( array(
'id' => $this->job->id()
));
// Copy the temporary output file to the desired destination
$move = copy($args['temp_output_filename'], $args['rip_options']['output_filename']);
if ($move) {
// Remove the temporary output file
$result = unlink($args['temp_output_filename']);
if (!$result) {
RippingCluster_WorkerLogEntry::warning($log, $this->job->id(), "Failed to remove temporary output file, still exists at '{$args['temp_output_filename']}'.");
}
//Report success
$this->job->updateStatus(RippingCluster_JobStatus::COMPLETE);
$this->complete( array(
'id' => $this->job->id()
));
} else {
RippingCluster_WorkerLogEntry::error($log, $this->job->id(), "Failed to copy temporary output file to proper destination. File retained as '{$args['temp_output_filename']}'.");
$this->job->updateStatus(RippingCluster_JobStatus::FAILED);
$this->fail('Encode complete, but output file could not be copied to the correct place.');
}
}
}
@@ -108,7 +133,7 @@ class Net_Gearman_Job_HandBrake extends Net_Gearman_Job_Common implements Rippin
$status = $rip->job->currentStatus();
$status->updateRipProgress($matches[1]);
$this->status($matches[1], 100);
} else {
} else if (!preg_match('/^\s+$/', $line)) {
$log = RippingCluster_Main::instance()->log();
RippingCluster_WorkerLogEntry::debug($log, $rip->job->id(), $line);
}

View File

@@ -183,10 +183,12 @@ class RippingCluster_Job {
$database = RippingCluster_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)
(id,name,source_plugin,rip_plugin,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_plugin', 'value' => $this->source_plugin, 'type' => PDO::PARAM_STR),
array('name' => 'rip_plugin', 'value' => $this->rip_plugin, '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),
@@ -271,7 +273,20 @@ class RippingCluster_Job {
return $new_status;
}
public function isFinished() {
$current_status = $this->currentStatus()->status();
return ($current_status == RippingCluster_JobStatus::COMPLETE || $current_status == RippingCluster_JobStatus::FAILED);
}
public function outputFilesize() {
if (file_exists($this->destination_filename)) {
return filesize($this->destination_filename);
}
return null;
}
public function calculateETA() {
$current_status = $this->currentStatus();
if ($current_status->status() != RippingCluster_JobStatus::RUNNING) {
@@ -319,6 +334,14 @@ class RippingCluster_Job {
public function name() {
return $this->name;
}
public function sourcePlugin() {
return $this->source_plugin;
}
public function ripPlugin() {
return $this->rip_plugin;
}
public function sourceFilename() {
return $this->source_filename;

View File

@@ -4,12 +4,20 @@ class RippingCluster_LogEntry extends SihnonFramework_LogEntry {
protected $job_id;
protected static $types;
public static function initialise() {
self::$types['job_id'] = 'int';
// Copy the list of datatypes from the parent
// We can't modify it in place, else we'll break any logging done inside the SihnonFramework tree
// or other subclass trees.
static::$types = parent::$types;
// Add the new data types for this subclass
static::$types['job_id'] = 'int';
}
protected function __construct($level, $category, $ctime, $pid, $file, $line, $message, $job_id) {
parent::__construct($level, $category, $ctime, $pid, $file, $line, $message);
protected function __construct($level, $category, $ctime, $hostname, $progname, $pid, $file, $line, $message, $job_id) {
parent::__construct($level, $category, $ctime, $hostname, $progname, $pid, $file, $line, $message);
$this->job_id = $job_id;
}
@@ -19,6 +27,8 @@ class RippingCluster_LogEntry extends SihnonFramework_LogEntry {
$row['level'],
$row['category'],
$row['ctime'],
$row['hostname'],
$row['progname'],
$row['pid'],
$row['file'],
$row['line'],
@@ -32,8 +42,8 @@ class RippingCluster_LogEntry extends SihnonFramework_LogEntry {
$this->level,
$this->category,
$this->ctime,
static::$hostname,
static::$progname,
$this->hostname,
$this->progname,
$this->pid,
$this->file,
$this->line,
@@ -48,7 +58,7 @@ class RippingCluster_LogEntry extends SihnonFramework_LogEntry {
protected static function log($logger, $severity, $job_id, $message, $category = SihnonFramework_Log::CATEGORY_DEFAULT) {
$backtrace = debug_backtrace(false);
$entry = new self($severity, $category, time(), getmypid(), $backtrace[1]['file'], $backtrace[1]['line'], $message, $job_id);
$entry = new self($severity, $category, time(), static::$localHostname, static::$localProgname, getmypid(), $backtrace[1]['file'], $backtrace[1]['line'], $message, $job_id);
$logger->log($entry);
}

View File

@@ -11,24 +11,32 @@ class RippingCluster_Main extends SihnonFramework_Main {
protected function __construct() {
parent::__construct();
$request_string = isset($_GET['l']) ? $_GET['l'] : '';
$this->request = new RippingCluster_RequestParser($request_string);
if (HBC_File == 'index') {
$this->smarty = new Smarty();
$this->smarty->template_dir = './source/templates';
$this->smarty->compile_dir = './tmp/templates';
$this->smarty->cache_dir = './tmp/cache';
$this->smarty->config_dir = './config';
$this->smarty->registerPlugin('modifier', 'formatDuration', array('RippingCluster_Main', 'formatDuration'));
$this->smarty->assign('version', '0.1');
$this->smarty->assign('messages', array());
$this->smarty->assign('base_uri', $this->base_uri);
$request_string = isset($_GET['l']) ? $_GET['l'] : '';
$this->request = new RippingCluster_RequestParser($request_string);
switch (HBC_File) {
case 'ajax':
case 'index': {
$smarty_tmp = '/tmp/ripping-cluster';
$this->smarty = new Smarty();
$this->smarty->template_dir = static::makeAbsolutePath('./source/templates');
$this->smarty->compile_dir = static::makeAbsolutePath($smarty_tmp . '/tmp/templates');
$this->smarty->cache_dir = static::makeAbsolutePath($smarty_tmp . '/tmp/cache');
$this->smarty->config_dir = static::makeAbsolutePath($smarty_tmp . '/config');
$this->smarty->registerPlugin('modifier', 'formatDuration', array('RippingCluster_Main', 'formatDuration'));
$this->smarty->registerPlugin('modifier', 'formatFilesize', array('RippingCluster_Main', 'formatFilesize'));
$this->smarty->assign('version', '0.2.1');
$this->smarty->assign('messages', array());
$this->smarty->assign('base_uri', $this->base_uri);
$this->smarty->assign('base_url', static::absoluteUrl(''));
} break;
}
}
@@ -37,14 +45,14 @@ class RippingCluster_Main extends SihnonFramework_Main {
}
/**
*
*
* @return RippingCluster_RequestParser
*/
public function request() {
return $this->request;
}
}
?>

View File

@@ -1,10 +1,16 @@
<?php
require_once '../private/config.php';
require_once(SihnonFramework_Lib . 'SihnonFramework/Main.class.php');
//require_once RippingCluster_Lib . 'RippingCluster/Main.class.php';
if (isset($_SERVER['RIPPING_CLUSTER_CONFIG']) &&
file_exists($_SERVER['RIPPING_CLUSTER_CONFIG']) &&
is_readable($_SERVER['RIPPING_CLUSTER_CONFIG'])) {
require_once($_SERVER['RIPPING_CLUSTER_CONFIG']);
} else {
require_once '/etc/ripping-cluster/config.php';
}
require_once SihnonFramework_Lib . 'SihnonFramework/Main.class.php';
SihnonFramework_Main::registerAutoloadClasses('SihnonFramework', SihnonFramework_Lib,
'RippingCluster', SihnonFramework_Main::makeAbsolutePath('../source/lib/'));
'RippingCluster', RippingCluster_Lib);
?>

22
webui/a.php Normal file
View File

@@ -0,0 +1,22 @@
<?php
define('HBC_File', 'ajax');
require '_inc.php';
try {
$main = RippingCluster_Main::instance();
RippingCluster_LogEntry::setLocalProgname('webui');
$smarty = $main->smarty();
$page = new RippingCluster_Page($smarty, $main->request());
if ($page->evaluate()) {
//header('Content-Type: text/json');
$smarty->display('ajax.tpl');
}
} catch (RippingCluster_Exception $e) {
die("Uncaught Exception: " . $e->getMessage());
}
?>

View File

@@ -6,6 +6,7 @@ require '_inc.php';
try {
$main = RippingCluster_Main::instance();
RippingCluster_LogEntry::setLocalProgname('webui');
$smarty = $main->smarty();
$page = new RippingCluster_Page($smarty, $main->request());

View File

@@ -2,12 +2,12 @@
define('HBC_File', 'run-jobs');
require_once '../private/config.php';
require_once '/etc/ripping-cluster/config.php';
require_once(SihnonFramework_Lib . 'SihnonFramework/Main.class.php');
require_once 'Net/Gearman/Client.php';
SihnonFramework_Main::registerAutoloadClasses('SihnonFramework', SihnonFramework_Lib,
'RippingCluster', SihnonFramework_Main::makeAbsolutePath('../source/lib/'));
'RippingCluster', RippingCluster_Lib);
try {
$main = RippingCluster_Main::instance();
@@ -29,12 +29,16 @@ try {
$set->addTask($task);
$job->updateStatus(RippingCluster_JobStatus::QUEUED);
RippingCluster_ClientLogEntry::info($log, $rip_options['id'], 'Job queued', 'client');
}
$job_count = count($jobs);
RippingCluster_ClientLogEntry::info($log, null, "Job queue started with {$job_count} jobs.", 'batch');
// Start the job queue
$result = $client->runSet($set);
RippingCluster_LogEntry::info($log, 'Job queue completed', 'batch');
RippingCluster_ClientLogEntry::info($log, null, 'Job queue completed', 'batch');
} catch (RippingCluster_Exception $e) {
die("Uncaught Exception (" . get_class($e) . "): " . $e->getMessage() . "\n");
@@ -55,10 +59,10 @@ function gearman_fail($task) {
$main = RippingCluster_Main::instance();
$log = $main->log();
$job = RippingCluster_Job::fromId($task->args['rip_options']['id']);
$job = RippingCluster_Job::fromId($task->arg['rip_options']['id']);
$job->updateStatus(RippingCluster_JobStatus::FAILED);
RippingCluster_ClientLogEntry::info($log, $job->id(), 'Job failed');
RippingCluster_ClientLogEntry::info($log, $job->id(), "Job failed with message: {$task->result}");
}

141
webui/scripts/main.js Normal file
View File

@@ -0,0 +1,141 @@
var rc = {
init: function() {
rc.ajax.init();
rc.dialog.init();
rc.page.init();
},
ajax: {
init: function() {
},
get: function(url) {
$.ajax({
url: url,
type: "GET",
dataType: "json",
success: rc.ajax.success,
error: rc.ajax.failure
});
},
post: function(url, data) {
$.ajax(url, {
type: "POST",
dataType: "json",
data: data,
success: rc.ajax.success,
error: rc.ajax.failure
});
},
success: function(d, s, x) {
rc.page.update(d);
rc.dialog.prepare(d);
},
failure: function(x, s, e) {
console.log("Ajax Failure: " + s, e);
console.log(x.responseText);
}
},
dialog: {
init: function() {
$("#dialogheaderclose").click(rc.dialog.close);
},
prepare: function(d) {
if (d.dialog && d.dialog.show) {
if (d.dialog.buttons) {
switch (d.dialog.buttons.type) {
case 'yesno':
$("#dialogfooteryes").click(
function() {
rc.trigger(d.dialog.buttons.actions.yes, d.dialog.buttons.params);
}
);
$("#dialogfooterno").click(
function() {
rc.trigger(d.dialog.buttons.actions.no, d.dialog.buttons.params);
}
);
$("#dialogfooteryesno").show();
break;
}
}
$("#dialog").show();
}
},
close: function() {
$("#dialog").hide();
$(".dialogfooterbuttonset").hide();
$("#dialogcontent").html();
}
},
page: {
init: function() {
},
update: function(d) {
for ( var f in d.page_replacements) {
$("#" + f).html(d.page_replacements[f].content);
}
}
},
sources: {
remove: function(plugin, source) {
rc.ajax.get(base_url + "ajax/delete-source/plugin/" + plugin + "/id/" + source);
},
remove_confirmed: function(plugin, source) {
rc.ajax.get(base_url + "ajax/delete-source/plugin/" + plugin + "/id/" + source + "/confirm/");
}
},
actions: {
'close-dialog': function(params) {
rc.dialog.close();
},
'delete-source-confirm': function(params) {
rc.sources.remove_confirmed(params['plugin'], params['id']);
}
},
trigger: function(action, params) {
// Handle a list of actions by repeated calling self for each argument
if (action instanceof Array) {
for(i in action) {
rc.trigger(action[i], params);
}
return;
}
// Check if action is supported, and execute it
if (rc.actions[action]) {
rc.actions[action](params);
} else {
console.log("Action not supported: " +action);
}
}
};
$(document).ready(rc.init);

View File

@@ -0,0 +1,35 @@
<?php
$main = RippingCluster_Main::instance();
$req = $main->request();
$config = $main->config();
// Grab the name of this source
$encoded_filename = null;
if ($req->exists('confirm')) {
$this->smarty->assign('confirmed', true);
$plugin = $req->get('plugin', 'RippingCluster_Exception_InvalidParameters');
$encoded_filename = $req->get('id', 'RippingCluster_Exception_InvalidParameters');
$source = RippingCluster_Source_PluginFactory::loadEncoded($plugin, $encoded_filename, false);
$source->delete();
// Generate a new list of sources to update the page with
$all_sources = RippingCluster_Source_PluginFactory::enumerateAll();
$this->smarty->assign('all_sources', $all_sources);
} else {
$this->smarty->assign('confirmed', false);
$plugin = $req->get('plugin', 'RippingCluster_Exception_InvalidParameters');
$encoded_filename = $req->get('id', 'RippingCluster_Exception_InvalidParameters');
$source = RippingCluster_Source_PluginFactory::loadEncoded($plugin, $encoded_filename, false);
$this->smarty->assign('source', $source);
$this->smarty->assign('source_plugin', $plugin);
$this->smarty->assign('source_id', $encoded_filename);
}
?>

View File

@@ -0,0 +1,9 @@
<?php
$main = RippingCluster_Main::instance();
$config = $main->config();
$all_sources = RippingCluster_Source_PluginFactory::enumerateAll();
$this->smarty->assign('all_sources', $all_sources);
?>

View File

@@ -1,15 +0,0 @@
<?php
$job_id = $this->request->get('id');
$job = RippingCluster_Job::fromId($job_id);
$this->smarty->assign('job', $job);
$log = RippingCluster_Main::instance()->log();
$client_log_entries = RippingCluster_LogEntry::recentEntriesByField($log, 'webui', 'job_id', $job_id, 'ctime', SihnonFramework_Log::ORDER_DESC, 30);
$worker_log_entries = RippingCluster_LogEntry::recentEntriesByField($log, 'worker', 'job_id', $job_id, 'ctime', SihnonFramework_Log::ORDER_DESC, 30);
$this->smarty->assign('client_log_entries', $client_log_entries);
$this->smarty->assign('worker_log_entries', $worker_log_entries);
?>

View File

@@ -0,0 +1,45 @@
<?php
$main = RippingCluster_Main::instance();
$req = $main->request();
$log = $main->log();
$config = $main->config();
$job_id = $req->get('id');
$job = RippingCluster_Job::fromId($job_id);
$this->smarty->assign('job', $job);
// Fetch log entries for this job
$log_count = $req->get('logs', $config->get('job.logs.default_display_count'));
$default_log_order = $config->get('job.logs.default_order');
$log_order = $req->get('order', $default_log_order);
if ( ! in_array($log_order, array(SihnonFramework_Log::ORDER_ASC, SihnonFramework_Log::ORDER_DESC))) {
$log_order = $default_log_order;
}
$this->smarty->assign('log_order', $log_order);
$this->smarty->assign('log_order_reverse', ($log_order == SihnonFramework_Log::ORDER_ASC ? SihnonFramework_Log::ORDER_DESC : SihnonFramework_Log::ORDER_ASC));
$client_log_entries = array();
$worker_log_entries = array();
$log_count_display = null;
if ($log_count == 'all') {
$log_count_display = 'all';
$log_count = '18446744073709551615'; // see mysql man page for LIMIT
} else if(!is_int($log_count)) {
$log_count = $config->get('job.logs.default_display_count');
$log_count_display = $log_count;
} else {
$log_count_display = $log_count;
}
$client_log_entries = RippingCluster_LogEntry::recentEntriesByField($log, 'webui', 'job_id', $job_id, 'ctime', $log_order, $log_count);
$worker_log_entries = RippingCluster_LogEntry::recentEntriesByField($log, 'worker', 'job_id', $job_id, 'ctime', $log_order, $log_count);
$this->smarty->assign('log_count_display', $log_count_display);
$this->smarty->assign('client_log_entries', $client_log_entries);
$this->smarty->assign('worker_log_entries', $worker_log_entries);
?>

View File

@@ -15,7 +15,7 @@ if ($req->exists('submit')) {
// Spawn the background client process to run all the jobs
RippingCluster_Job::runAllJobs();
RippingCluster_Page::redirect('rips/setup-rip/queued');
RippingCluster_Page::redirect('rips/setup/queued');
} elseif ($req->exists('queued')) {
$this->smarty->assign('rips_submitted', true);

View File

@@ -0,0 +1,11 @@
{
{if $messages}
messages: [
{foreach from=$messages item=message}
'{$message}',
{/foreach}
],
{/if}
{$page_content}
}

View File

@@ -0,0 +1,37 @@
"page_replacements": {
{if $confirmed}
"source-list": {
{include file="fragments/source-list.tpl" assign="sources_html"}
"content": {$sources_html|json_encode}
}
{else}
"dialogcontent": {
{include file="fragments/delete-source.tpl" assign="delete_source_html"}
"content": {$delete_source_html|json_encode}
}
{/if}
{if ! $confirmed}
},
"dialog": {
"show": true,
"buttons": {
"type": "yesno",
"actions": {
"yes": [
"delete-source-confirm",
"close-dialog"
],
"no": "close-dialog"
},
"params": {
"plugin": {$source_plugin|json_encode},
"id": {$source_id|json_encode}
}
}
}
{else}
}
{/if}

View File

@@ -0,0 +1,6 @@
"page_replacements": {
"source-list": {
{include file="fragments/source-list.tpl" assign="sources_html"}
"content": {$sources_html|json_encode}
}
}

View File

@@ -0,0 +1,3 @@
<p>
Are you sure you want to delete {$source->plugin()|escape:"html"}:{$source->filename()|escape:"html"}?
</p>

View File

@@ -0,0 +1,25 @@
{foreach from=$all_sources key=type item=sources}
<li>{$type}
{if $sources}
<ul>
{foreach from=$sources item=source}
{assign var='source_plugin' value=$source->plugin()}
{assign var='source_filename' value=$source->filename()}
{assign var='source_filename_encoded' value=$source->filenameEncoded()}
{assign var='source_cached' value=$source->isCached()}
<li>
[ <a href="{$base_uri}sources/details/plugin/{$source_plugin}/id/{$source_filename_encoded}" title="Browse source details">Browse</a> |
<a href="{$base_uri}rips/setup/plugin/{$source_plugin}/id/{$source_filename_encoded}" title="Rip this source">Rip</a> |
<a href="javascript:rc.sources.remove('{$source_plugin|escape:'quote'}', '{$source_filename_encoded|escape:'quote'}');" title="Delete this source">Delete</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>
{/foreach}

View File

@@ -4,7 +4,7 @@
{if $running_jobs}
{foreach from=$running_jobs item=job}
<li><a href="{$base_uri}job-details/id/{$job->id()}" title="View job details">{$job->name()} ({$job->currentStatus()->ripProgress()}%)</a></li>
<li><a href="{$base_uri}jobs/details/id/{$job->id()}" title="View job details">{$job->name()} ({$job->currentStatus()->ripProgress()}%)</a></li>
{/foreach}
{else}
<em>There are no currently running jobs.</em>
@@ -14,7 +14,7 @@
{if $queued_jobs}
{foreach from=$queued_jobs item=job}
<li><a href="{$base_uri}job-details/id/{$job->id()}" title="View job details">{$job->name()}</a></li>
<li><a href="{$base_uri}jobs/details/id/{$job->id()}" title="View job details">{$job->name()}</a></li>
{/foreach}
{else}
<em>There are no currently running jobs.</em>
@@ -25,7 +25,7 @@
{if $completed_jobs}
<ul>
{foreach from=$completed_jobs item=job}
<li><a href="{$base_uri}job-details/id/{$job->id()}" title="View job details">{$job->name()}</a></li>
<li><a href="{$base_uri}jobs/details/id/{$job->id()}" title="View job details">{$job->name()}</a></li>
{/foreach}
</ul>
{else}
@@ -37,7 +37,7 @@
{if $failed_jobs}
<ul>
{foreach from=$failed_jobs item=job}
<li><a href="{$base_uri}job-details/id/{$job->id()}" title="View job details">{$job->name()}</a></li>
<li><a href="{$base_uri}jobs/details/id/{$job->id()}" title="View job details">{$job->name()}</a></li>
{/foreach}
</ul>
{else}

View File

@@ -6,9 +6,15 @@
</script>
<link rel="stylesheet" type="text/css" href="{$base_uri}styles/normal.css" />
<script type="text/javascript">
var base_uri = "{$base_uri|escape:'quote'}";
var base_url = "{$base_url|escape:'quote'}";
</script>
<link type="text/css" href="{$base_uri}styles/3rdparty/jquery-ui/smoothness/jquery-ui-1.8.custom.css" rel="Stylesheet" />
<script type="text/javascript" src="{$base_uri}scripts/3rdparty/jquery-1.4.2.js"></script>
<script type="text/javascript" src="{$base_uri}scripts/3rdparty/jquery-ui-1.8.custom.min.js"></script>
<script type="text/javascript" src="{$base_uri}scripts/main.js"></script>
</head>
<body>
@@ -49,6 +55,24 @@
</div>
</div>
<div id="centrepoint">
<div id="dialog">
<div id="dialogheader">
<div id="dialogheadertitle">Dialog</div>
<div id="dialogheaderclose">X</div>
</div>
<div id="dialogcontent"></div>
<div id="dialogfooter">
<div id="dialogfooteryesno" class="dialogfooterbuttonset">
<fieldset>
<input type="button" class="dialogbutton" id="dialogfooteryes" value="Yes" />
<input type="button" class="dialogbutton" id="dialogfooterno" value="No" />
</fieldset>
</div>
</div>
</div>
</div>
</body>
</html>

View File

@@ -1,57 +0,0 @@
<h2>Job Details</h2>
<h3>Summary</h3>
<em>Summary details here</em>
<h3>Recent Client Logs</h3>
{if $client_log_entries}
<table>
<thead>
<tr>
<th>Level</th>
<th>Time</th>
<th>Message</th>
</tr>
</thead>
<tbody>
{foreach from=$client_log_entries item=log_entry}
<tr>
<td>{$log_entry->level()}</td>
<td>{$log_entry->ctime()|date_format:"%Y-%m-%d %H:%M:%S"}</td>
<td>{$log_entry->message()}</td>
</tr>
{/foreach}
</tbody>
</table>
{else}
<em>There are no client log entries.</em>
{/if}
<h3>Recent Worker Logs</h3>
{if $worker_log_entries}
<table>
<thead>
<tr>
<th>Level</th>
<th>Time</th>
<th>Message</th>
</tr>
</thead>
<tbody>
{foreach from=$worker_log_entries item=log_entry}
<tr>
<td>{$log_entry->level()}</td>
<td>{$log_entry->ctime()|date_format:"%Y-%m-%d %H:%M:%S"}</td>
<td>{$log_entry->message()}</td>
</tr>
{/foreach}
</tbody>
</table>
{else}
<em>There are no worker log entries.</em>
{/if}

View File

@@ -42,8 +42,13 @@
{foreach from=$jobs item=job}
{assign var=current_status value=$job->currentStatus()}
<tr>
<td><a href="{$base_uri}job-details/id/{$job->id()}" title="View job details">{$job->name()}</a></td>
<td>{$job->destinationFilename()}</td>
<td><a href="{$base_uri}jobs/details/id/{$job->id()}" title="View job details">{$job->name()}</a></td>
<td>
{$job->destinationFilename()}
{if $job->isFinished()}
({$job->outputFilesize()|formatFilesize})
{/if}
</td>
<td>{$job->title()}</td>
<td>
{$current_status->statusName()}

View File

@@ -0,0 +1,91 @@
<h2>Job Details</h2>
<h3>Summary</h3>
<dl>
<dt>Source Plugin</dt>
<dd>{$job->sourcePlugin()}</dd>
<dt>Rip Plugin</dt>
<dd>{$job->ripPlugin()}</dd>
<dt>Source Filename</dt>
<dd>{$job->sourceFilename()}</dd>
<dt>Source Title</dt>
<dd>{$job->title()}</dd>
<dt>Status</dt>
<dd>{$job->currentStatus()->statusName()} ({$job->currentStatus()->mtime()|date_format:'%Y-%m-%d %H:%M:%S'})</dd>
<dt>Destination Filename</dt>
<dd>{$job->destinationFilename()}</dd>
{if $job->isFinished()}
<dt>Destination Filesize</dt>
<dd>{$job->outputFilesize()|formatFilesize}</dd>
{/if}
</dl>
<h3>Log messages</h3>
<h4>Options</h4>
<ul>
{if $log_count_display eq 'all'}
<li><a href="{$base_uri}jobs/details/id/{$job->id()}/order/{$log_order}/" title="View recent logs only">View recent messages only</a></li>
{else}
<li><a href="{$base_uri}jobs/details/id/{$job->id()}/logs/all/" title="View all logs">View all messages</a></li>
{/if}
<li><a href="{$base_uri}jobs/details/id/{$job->id()}/logs/{$log_count_display}/order/{$log_order_reverse}/" title="Reverse display order of log messages">Reverse display order</a></li>
</ul>
<h4>Recent Client Logs</h4>
{if $client_log_entries}
<table>
<thead>
<tr>
<th>Level</th>
<th>Time</th>
<th>Message</th>
</tr>
</thead>
<tbody>
{foreach from=$client_log_entries item=log_entry}
<tr>
<td>{$log_entry->level()}</td>
<td>{$log_entry->ctime()|date_format:"%Y-%m-%d %H:%M:%S"}</td>
<td>{$log_entry->message()}</td>
</tr>
{/foreach}
</tbody>
</table>
{else}
<em>There are no client log entries.</em>
{/if}
<h4>Recent Worker Logs</h4>
{if $worker_log_entries}
<table>
<thead>
<tr>
<th>Level</th>
<th>Time</th>
<th>Hostname</th>
<th>Message</th>
</tr>
</thead>
<tbody>
{foreach from=$worker_log_entries item=log_entry}
<tr>
<td>{$log_entry->level()}</td>
<td>{$log_entry->ctime()|date_format:"%Y-%m-%d %H:%M:%S"}</td>
<td>{$log_entry->hostname()}</td>
<td>{$log_entry->message()}</td>
</tr>
{/foreach}
</tbody>
</table>
{else}
<em>There are no worker log entries.</em>
{/if}

View File

@@ -10,7 +10,7 @@
{else}
<h3>{$source->filename()|escape:"html"}</h3>
<form name="setup-rips" id="setup-rips" action="{$base_uri}rips/setup-rip/submit/" method="post">
<form name="setup" id="setup-rips" action="{$base_uri}rips/setup/submit/" method="post">
<input type="hidden" name="plugin" value="{$source->plugin()|escape:"html"}" />
<fieldset>
<legend>Configure global rip options</legend>

View File

@@ -1,41 +0,0 @@
<h2>Sources</h2>
{if $all_sources}
<p>
The list below contains all the DVD sources that are available and ready for ripping.
</p>
<p>
Sources that have recently been scanned are marked <em>(cached)</em> and will load fairly quickly.
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>
<ul>
{foreach from=$all_sources key=type item=sources}
<li>{$type}
{if $sources}
<ul>
{foreach from=$sources item=source}
{assign var='source_plugin' value=$source->plugin()}
{assign var='source_filename' value=$source->filename()}
{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> |
<a href="{$base_uri}sources/delete/plugin/{$source_plugin}/id/{$source_filename_encoded}" title="Delete this source">Delete</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>
{/foreach}
</ul>
{else}
<p>
<em>There are currently no sources available to rip.</em>
</p>
{/if}

View File

@@ -5,7 +5,7 @@
<li>Browse
<ul>
<li><a href="{$base_uri}rips/sources" title="Browse Sources">Sources</a></li>
<li><a href="{$base_uri}sources/list" title="Browse Sources">Sources</a></li>
</ul>
</li>
</ul>

View File

@@ -0,0 +1,18 @@
<h2>Sources</h2>
{if $all_sources}
<p>
The list below contains all the DVD sources that are available and ready for ripping.
</p>
<p>
Sources that have recently been scanned are marked <em>(cached)</em> and will load fairly quickly.
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>
<ul id="source-list">
{include file="fragments/source-list.tpl"}
</ul>
{else}
<p>
<em>There are currently no sources available to rip.</em>
</p>
{/if}

View File

@@ -93,6 +93,55 @@ label {
margin: 1em;
}
/* Centred dialog taken from http://stackoverflow.com/questions/1205457/how-to-design-a-css-for-a-centered-floating-confirm-dialog */
#centrepoint {
top: 50%;
left: 50%;
position: absolute;
}
#dialog {
position: relative;
width: 600px;
margin-left: -300px;
/*height: 20em;*/
margin-top: -20em;
display: none;
background: #eeeeee;
border: 2px solid #a7a09a;
}
#dialogheader {
height: 2em;
width: 100%;
margin: 0.3em;
}
#dialogheadertitle {
color: black;
font-weight: bold;
float: left;
}
#dialogheaderclose {
width: 1.2em;
height: 1.2em;
background-color: crimson;
color: white;
border: 1px solid fireBrick;
float: right;
margin-right: 1em;
text-align: center;
vertical-align: middle;
display: table-cell;
font-weight: bold;
cursor: pointer;
}
#dialogcontent {
padding: 0.5em;
}
.dialogfooterbuttonset {
display: none;
text-align: right;
}
.default {
background: beige;
color: darkgray;

View File

@@ -2,7 +2,17 @@
define('HBC_File', 'worker');
require_once '/etc/ripping-cluster/config.php';
$options = array();
if (isset($_SERVER['argv'])) {
$options = getopt('c:', array('config:'));
}
if (isset($options['config'])) {
require_once $options['config'];
} else {
require_once '/etc/ripping-cluster/config.php';
}
require_once(SihnonFramework_Lib . 'SihnonFramework/Main.class.php');
require_once 'Net/Gearman/Worker.php';
@@ -16,6 +26,7 @@ try {
set_time_limit(0);
$main = RippingCluster_Main::instance();
RippingCluster_LogEntry::setLocalProgname('ripping-cluster-worker');
$smarty = $main->smarty();
$worker = new RippingCluster_Worker();