diff --git a/3.0/modules/transcode/controllers/admin_transcode.php b/3.0/modules/transcode/controllers/admin_transcode.php
new file mode 100644
index 00000000..b91b1284
--- /dev/null
+++ b/3.0/modules/transcode/controllers/admin_transcode.php
@@ -0,0 +1,166 @@
+ 0) {
+ module::set_var("transcode", "ffmpeg_path", $_REQUEST['ffmpeg_path']);
+ $data['success'] = true;
+ $data['codecs'] = self::_get_supported_audio_codecs($_REQUEST['ffmpeg_path']);
+ }
+ else {
+ $error = "";
+ switch ($val) {
+ case 0: $error = "Empty file path provided"; break;
+ case -1: $error = "File does not exist"; break;
+ case -2: $error = "Path is a directory"; break;
+ default: $error = "Unspecified error";
+ }
+ $data['error'] = $error;
+ }
+ echo json_encode($data);
+ }
+
+ public function index() {
+ $form = $this->_get_form();
+
+ if (request::method() == "post") {
+ access::verify_csrf();
+
+ if ($form->validate()) {
+ module::set_var("transcode", "ffmpeg_path", $_POST['ffmpeg_path']);
+ module::set_var("transcode", "ffmpeg_flags", $_POST['ffmpeg_flags']);
+ module::set_var("transcode", "audio_codec", $_POST['audio_codec']);
+ module::set_var("transcode", "ffmpeg_audio_kbits", (isset($_POST['ffmpeg_audio_kbits']) ? $_POST['ffmpeg_audio_kbits'] : false));
+
+ module::set_var("transcode", "resolution_240p", (isset($_POST['resolution_240p']) ? $_POST['resolution_240p'] : false));
+ module::set_var("transcode", "resolution_360p", (isset($_POST['resolution_360p']) ? $_POST['resolution_360p'] : false));
+ module::set_var("transcode", "resolution_480p", (isset($_POST['resolution_480p']) ? $_POST['resolution_480p'] : false));
+ module::set_var("transcode", "resolution_576p", (isset($_POST['resolution_576p']) ? $_POST['resolution_576p'] : false));
+ module::set_var("transcode", "resolution_720p", (isset($_POST['resolution_720p']) ? $_POST['resolution_720p'] : false));
+ module::set_var("transcode", "resolution_1080p", (isset($_POST['resolution_1080p']) ? $_POST['resolution_1080p'] : false));
+
+ message::success(t("Settings have been saved"));
+ url::redirect("admin/transcode");
+ }
+ else {
+ message::error(t("There was a problem with the submitted form. Please check your values and try again."));
+ }
+ }
+
+ print $this->_get_view();
+ }
+
+ private function _get_view($form = null) {
+ $v = new Admin_View("admin.html");
+ $v->page_title = t("Gallery 3 :: Manage Transcoding Settings");
+
+ $v->content = new View("admin_transcode.html");
+ $v->content->form = empty($form) ? $this->_get_form() : $form;
+
+ return $v;
+ }
+
+ private function _get_supported_audio_codecs($given_path = null) {
+
+ $flv_compatible_codecs = array("aac" => "aac", "adpcm_swf" => "adpcm_swf", "mp3" => "libmp3lame");
+
+ if ($given_path)
+ $ffmpegPath = $given_path;
+ else
+ $ffmpegPath = module::get_var("transcode", "ffmpeg_path", transcode::whereis("ffmpeg"));
+
+ $legacy = false;
+ exec($ffmpegPath . " -codecs", $codecs);
+ if (count($codecs) == 0) {
+ $legacy = true;
+ exec($ffmpegPath . " -formats 2>&1", $codecs);
+ }
+
+ $search = true;
+ if ($legacy) $search = false;
+
+ $found = array();
+ foreach ($codecs as $line) {
+ if ($search) {
+ if (strpos($line, "DEA")) {
+ $bits = preg_split("/[\s]+/", $line);
+ if (in_array($bits[2], $flv_compatible_codecs)) {
+ $key = array_search($bits[2], $flv_compatible_codecs);
+ $found[$key] = $flv_compatible_codecs[$key];
+ }
+ }
+ }
+
+ if ($legacy && strpos($line, "Codecs:") !== false) {
+ $search = true;
+ continue;
+ }
+ if ($legacy && $search && strpos($line, "Bitstream filters:")) {
+ $search = false;
+ continue;
+ }
+ }
+ return $found;
+ }
+
+ private function _get_form() {
+ $form = new Forge("admin/transcode", "", "post", array("id" => "g-admin-transcode-form"));
+
+ $group = $form->group("system")->label(t("System"));
+
+ $ffmpegPath = transcode::whereis("ffmpeg");
+ $codecs = $this->_get_supported_audio_codecs();
+
+ $group ->input("ffmpeg_path")
+ ->id("ffmpeg_path")
+ ->label(t("Path to ffmpeg binary:"))
+ ->value(module::get_var("transcode", "ffmpeg_path", $ffmpegPath))
+ ->callback("transcode::verify_ffmpeg_path")
+ ->error_messages("required", t("You must enter the path to ffmpeg"))
+ ->error_messages("invalid", t("File does not exist"))
+ ->error_messages("is_dir", t("File is a directory"))
+ ->message("Auto detected ffmpeg here: " . $ffmpegPath . "
Click here to verify ffmpeg path and continue.");
+
+ $group ->input("ffmpeg_flags")
+ ->id("ffmpeg_flags")
+ ->label(t("Extra ffmpeg flags:"))
+ ->value(module::get_var("transcode", "ffmpeg_flags"));
+
+ $group ->dropdown("audio_codec")
+ ->id("audio_codec")
+ ->label(t("Audio codec to use:"))
+ ->options($codecs)
+ ->selected(module::get_var("transcode", "audio_codec"));
+
+ $group ->checkbox("ffmpeg_audio_kbits")
+ ->label(t("Send audio bitrate as kbits instead of bits/s"))
+ ->checked(module::get_var("transcode", "ffmpeg_audio_kbits"));
+
+ $group = $form->group("resolutions")->label(t("Resolutions"));
+
+ $group ->checkbox("resolution_240p")
+ ->label("240p")
+ ->checked(module::get_var("transcode", "resolution_240p"));
+ $group ->checkbox("resolution_360p")
+ ->label("360p")
+ ->checked(module::get_var("transcode", "resolution_360p"));
+ $group ->checkbox("resolution_480p")
+ ->label("480p")
+ ->checked(module::get_var("transcode", "resolution_480p"));
+ $group ->checkbox("resolution_576p")
+ ->label("576p")
+ ->checked(module::get_var("transcode", "resolution_576p"));
+ $group ->checkbox("resolution_720p")
+ ->label("720p")
+ ->checked(module::get_var("transcode", "resolution_720p"));
+ $group ->checkbox("resolution_1080p")
+ ->label("1080p")
+ ->checked(module::get_var("transcode", "resolution_1080p"));
+
+ $form->submit("submit")->value(t("Save"));
+ return $form;
+ }
+}
diff --git a/3.0/modules/transcode/debug.php b/3.0/modules/transcode/debug.php
new file mode 100644
index 00000000..7e9d31a9
--- /dev/null
+++ b/3.0/modules/transcode/debug.php
@@ -0,0 +1,50 @@
+ffmpeg info";
+ $ffmpegPath = whereis("ffmpeg");
+ echo "path: " . $ffmpegPath . "
";
+ $version = @shell_exec($ffmpegPath . " -version"); $version = explode("\n", $version); $version = $version[0]; $version = explode(" ", $version); $version = $version[1];
+ echo "version: " . $version . "
";
+ echo "codecs:
" . @shell_exec($ffmpegPath . " -codecs 2>&1") . "
";
+ echo "formats:" . @shell_exec($ffmpegPath . " -formats") . "
";
+}
+
+function whereis($app) {
+ $op = @shell_exec("whereis " . $app);
+ if ($op != "") {
+ $op = explode(" ", $op);
+ for ($i = 1; $i < count($op); $i++) {
+ if (file_exists($op[$i]) && !is_dir($op[$i]))
+ return $op[$i];
+ }
+ }
+ return false;
+}
+
+/*
+stdClass Object
+(
+ [video] => stdClass Object
+ (
+ [codec] => h264,
+ [height] => 576
+ [width] => 640
+ )
+
+ [audio] => stdClass Object
+ (
+ )
+
+)
+
+ */
\ No newline at end of file
diff --git a/3.0/modules/transcode/helpers/transcode.php b/3.0/modules/transcode/helpers/transcode.php
new file mode 100644
index 00000000..f6026ec2
--- /dev/null
+++ b/3.0/modules/transcode/helpers/transcode.php
@@ -0,0 +1,59 @@
+value);
+ switch ($v) {
+ case 0: $field->add_error("required", 1); break;
+ case -1: $field->add_error("invalid", 1); break;
+ case -2: $field->add_error("is_dir", 1); break;
+ }
+ }
+
+ static function log($item) {
+
+ if (is_string($item) || is_numeric($item)) {}
+ else
+ $item = print_r($item, true);
+
+ $fh = fopen(VARPATH . "modules/transcode/log/transcode.log", "a");
+ fwrite($fh, date("Y-m-d H:i:s") . ": " . $item . "\n");
+ fclose($fh);
+
+ }
+
+}
diff --git a/3.0/modules/transcode/helpers/transcode_event.php b/3.0/modules/transcode/helpers/transcode_event.php
new file mode 100644
index 00000000..774f1275
--- /dev/null
+++ b/3.0/modules/transcode/helpers/transcode_event.php
@@ -0,0 +1,204 @@
+get("settings_menu")
+ ->append(Menu::factory("link")
+ ->id("transcode_menu")
+ ->label(t("Video Transcoding"))
+ ->url(url::site("admin/transcode")));
+ }
+
+ static function item_deleted($item) {
+ if ($item->is_movie()) {
+ transcode::log("Deleting transcoded files for item " . $item->id);
+ if (is_dir(VARPATH . "modules/transcode/flv/" . $item->id)) {
+ self::rrmdir(VARPATH . "modules/transcode/flv/" . $item->id);
+ }
+ db::build()->delete("transcode_resolutions")->where("item_id", "=", $item->id)->execute();
+ }
+ }
+
+ static function rrmdir($dir) {
+ if (is_dir($dir)) {
+ $objects = scandir($dir);
+ foreach ($objects as $object) {
+ if ($object != "." && $object != "..") {
+ if (filetype($dir."/".$object) == "dir")
+ self::rrmdir($dir."/".$object);
+ else
+ unlink($dir."/".$object);
+ }
+ }
+ reset($objects);
+ rmdir($dir);
+ }
+ }
+
+ private static function _getVideoInfo($file) {
+ $ffmpegPath = module::get_var("transcode", "ffmpeg_path");
+
+ $op = array();
+ @exec($ffmpegPath . ' -i "' . $file . '" 2>&1', $op);
+
+ $file = new stdclass();
+ $file->video = new stdclass();
+ $file->audio = new stdclass();
+ $file->audio->has = false;
+
+ foreach ($op as $line) {
+ transcode::log($line);
+
+ if (preg_match('/Duration\: (\d{2}):(\d{2}):(\d{2})\.(\d{2})/', $line, $matches)) {
+ $file->video->duration = $matches[3] . "." . $matches[4];
+ $file->video->duration += $matches[2] * 60;
+ $file->video->duration += $matches[1] * 3600;
+ }
+ else if (preg_match('/Stream #0\.\d(\(\w*\))?\: Video\:/', $line)) {
+ $bits = preg_split('/[\s]+/', $line);
+
+ for ($i = 0; $i < count($bits); $i++) {
+ if ($bits[$i] == "fps,") $file->video->fps = $bits[$i - 1];
+ else if ($bits[$i] == "kb/s,") $file->video->bitrate = $bits[$i - 1] * 1024;
+ }
+
+ $file->video->codec = $bits[4];
+ list($file->video->width, $file->video->height) = explode('x', $bits[6]);
+ }
+ else if (preg_match('/Stream #0\.\d(\(\w*\))?+\: Audio\: (\w*)\,/', $line, $matches)) {
+ $file->audio->has = true;
+ $file->audio->codec = $matches[2];
+
+ if (preg_match('/(\d*) Hz/', $line, $hz))
+ $file->audio->samplerate = $hz[1];
+ if (preg_match('/(\d*) channels?/', $line, $channels))
+ $file->audio->channels = $channels[1];
+ if (preg_match('/(\d*) kb\/s/', $line, $bitrate))
+ $file->audio->bitrate = $bitrate[1];
+ }
+ }
+
+ return $file;
+ }
+
+ static function item_created($item) {
+ if ($item->is_movie()) {
+ transcode::log("Item created - is a movie. Let's create a transcode task or 2..");
+ $ffmpegPath = module::get_var("transcode", "ffmpeg_path");
+
+ $fileObj = self::_getVideoInfo($item->file_path());
+ transcode::log($fileObj);
+
+ // Save our needed variables
+ $srcFile = $item->file_path();
+ $srcWidth = transcode::makeMultipleTwo($fileObj->video->width);
+ $srcHeight = transcode::makeMultipleTwo($fileObj->video->height);
+ $aspect = $srcWidth / $srcHeight;
+
+ $srcFPS = $fileObj->video->fps;
+
+ $srcAR = $fileObj->audio->samplerate;
+ if ($srcAR > 44100) $srcAR = 44100;
+
+ $accepted_sample_rates = array(11025, 22050, 44100);
+
+ if (!in_array($srcAR, $accepted_sample_rates)) {
+ // if the input sample rate isn't an accepted rate, find the next lowest rate that is
+ $below = true; $rate = 0;
+ if ($srcAR < 11025) {
+ $rate = 11025;
+ }
+ else {
+ foreach ($accepted_sample_rates as $r) {
+ transcode::log("testing audio rate '" . $r . "' against input rate '" . $srcAR . "'");
+ if ($r < $srcAR) {
+ $rate = $r;
+ }
+ }
+ }
+ $srcAR = $rate;
+ }
+
+ $srcACodec = module::get_var("transcode", "audio_codec");
+
+ $heights = array();
+ if (module::get_var("transcode", "resolution_240p")) array_push($heights, 240);
+ if (module::get_var("transcode", "resolution_360p")) array_push($heights, 360);
+ if (module::get_var("transcode", "resolution_480p")) array_push($heights, 480);
+ if (module::get_var("transcode", "resolution_576p")) array_push($heights, 576);
+ if (module::get_var("transcode", "resolution_720p")) array_push($heights, 720);
+ if (module::get_var("transcode", "resolution_1080p")) array_push($heights, 1080);
+
+ if (!is_dir(VARPATH . "modules/transcode/flv/" . $item->id))
+ @mkdir(VARPATH . "modules/transcode/flv/" . $item->id);
+
+ $xtraFlags = module::get_var("transcode", "ffmpeg_flags", "");
+
+ foreach ($heights as $destHeight) {
+ transcode::log("srcHeight: " . $srcHeight . ", destheight: " . $destHeight);
+
+ // don't bother upscaling, there's no advantage to it...
+ if ($destHeight > $srcHeight) continue;
+
+ $destFormat = module::get_var("transcode", "format", "flv");
+ $destWidth = floor($destHeight * $aspect);
+ if ($destWidth % 2)
+ $destWidth = ceil($destHeight * $aspect);
+
+ transcode::log("destination resolution: " . $destWidth . "x" . $destHeight);
+
+ $destFile = VARPATH . "modules/transcode/flv/" . $item->id . "/" . $destWidth . "x" . $destHeight . ".flv";
+
+ switch ($destHeight) {
+ case 240: $destVB = 128; $srcAB = 16 * ($fileObj->audio->channels ? $fileObj->audio->channels : 2); break;
+ case 360: $destVB = 384; $srcAB = 16 * ($fileObj->audio->channels ? $fileObj->audio->channels : 2); break;
+ case 480: $destVB = 1024; $srcAB = 32 * ($fileObj->audio->channels ? $fileObj->audio->channels : 2); break;
+ case 576: $destVB = 2048; $srcAB = 32 * ($fileObj->audio->channels ? $fileObj->audio->channels : 2); break;
+ case 720: $destVB = 4096; $srcAB = 64 * ($fileObj->audio->channels ? $fileObj->audio->channels : 2); break;
+ case 1080; $destVB = 8192; $srcAB = 64 * ($fileObj->audio->channels ? $fileObj->audio->channels : 2); break;
+ }
+ $destVB *= 1024;
+ $srcAB *= 1024;
+
+ $cmd =
+ $ffmpegPath . " " .
+ "-i \"" . $srcFile . "\" ";
+ if ($fileObj->audio->has)
+ $cmd .=
+ "-y -acodec " . $srcACodec . " " .
+ "-ar " . $srcAR . " " .
+ "-ab " . $srcAB . " ";
+ else
+ $cmd .=
+ "-an ";
+
+ $cmd .=
+ "-vb " . $destVB . " " .
+ "-f " . $destFormat . " " .
+ "-s " . $destWidth . "x" . $destHeight . " " .
+ $xtraFlags . " " .
+ "\"" . $destFile . "\"";
+
+ transcode::log($cmd);
+
+ $task_def =
+ Task_Definition::factory()
+ ->callback("transcode_task::transcode")
+ ->name("Video Transcode to " . $destWidth . "x" . $destHeight)
+ ->severity(log::SUCCESS);
+
+ $task =
+ task::create($task_def,
+ array("ffmpeg_cmd" => $cmd,
+ "width" => $destWidth,
+ "height" => $destHeight,
+ "item_id" => $item->id)
+ );
+
+ task::run($task->id);
+ }
+ }
+ }
+}
diff --git a/3.0/modules/transcode/helpers/transcode_installer.php b/3.0/modules/transcode/helpers/transcode_installer.php
new file mode 100644
index 00000000..6501e4f0
--- /dev/null
+++ b/3.0/modules/transcode/helpers/transcode_installer.php
@@ -0,0 +1,42 @@
+query("CREATE TABLE IF NOT EXISTS {transcode_resolutions} (
+ `id` int(9) NOT NULL auto_increment,
+ `item_id` int(9) NOT NULL,
+ `resolution` varchar(16) NOT NULL,
+ PRIMARY KEY (`id`)
+ ) DEFAULT CHARSET=utf8;");
+
+ @mkdir(VARPATH . "modules/transcode");
+ @mkdir(VARPATH . "modules/transcode/log");
+ @mkdir(VARPATH . "modules/transcode/flv");
+
+ self::setversion();
+ }
+ static function uninstall() {
+ Database::instance()->query("DROP TABLE {transcode_resolutions}");
+ dir::unlink(VARPATH . "modules/transcode");
+ }
+
+ static function upgrade($version) {
+ if ($version < self::getversion())
+ self::setversion();
+ }
+
+ static function deactivate() {}
+ static function activate() {}
+ static function can_activate() {
+ $messages = array();
+ if (!function_exists("exec")) {
+ $messages["warn"][] = t("exec() is required to auto-detect the ffmpeg binary. You must specify the path to the ffmpeg binary manually before you can convert videos.");
+ }
+ return $messages;
+ }
+}
diff --git a/3.0/modules/transcode/helpers/transcode_task.php b/3.0/modules/transcode/helpers/transcode_task.php
new file mode 100644
index 00000000..da51bc27
--- /dev/null
+++ b/3.0/modules/transcode/helpers/transcode_task.php
@@ -0,0 +1,109 @@
+id . " started.");
+
+ $cmd = $task->get("ffmpeg_cmd");
+
+ $logfile = VARPATH . "modules/transcode/log/" . time() . ".log";
+ $task->set("logfile", $logfile);
+
+ $output = "";
+ $return_val = 0;
+ self::fork($cmd, $logfile);
+
+ // wait for ffmpeg to fire up..
+ sleep(2);
+
+ while ($time = self::GetEncodedTime($logfile)) {
+ transcode::log("encoded time: " . $time);
+
+ if (self::$duration > 0) {
+ $task->state = "encoding";
+ $task->status = "Encoding...";
+ $pct = sprintf("%0.0f", ($time / self::$duration) * 100);
+ $task->percent_complete = $pct;
+ $task->save();
+ }
+ usleep(500000);
+ }
+
+ $output = @file_get_contents($logfile);
+
+ transcode::log("ffmpeg job completed.");
+
+ if ($output) {
+ $task->percent_complete = 100;
+ $task->done = true;
+ $task->state = "success";
+ $task->status = "Transcoding complete.";
+
+ transcode::log("insert into transcode table to indicate success");
+ $res = ORM::factory('transcode_resolution');
+ $res->resolution = $task->get("width") . "x" . $task->get("height");
+ $res->item_id = $task->get("item_id");
+ $res->save();
+ }
+ else {
+ transcode::log("Error transcoding. ffmpeg output:");
+ transcode::log($output);
+ $task->percent_complete = 100;
+ $task->done = true;
+ $task->state = "error";
+ $task->status = "Transcoding error.";
+ }
+ $task->save();
+
+ }
+
+ static function fork($shellCmd, $logfile) {
+ $cmd = "nice " . $shellCmd . " > " . $logfile . " 2>&1 &";
+ transcode::log("executing: " . $cmd);
+ exec($cmd);
+ }
+
+ static function GetEncodedTime($logfile) {
+ if (!file_exists($logfile)) {
+ transcode::log("can't open FFMPEG-Log '" . $logfile . "'");
+ return false;
+ }
+ else {
+ $FFMPEGLog = @file_get_contents($logfile);
+
+ $dPos = strpos($FFMPEGLog, " Duration: ");
+ self::$duration = self::durationToSecs(substr($FFMPEGLog, $dPos + 11, 11));
+
+ $FFMPEGLog = str_replace("\r", "\n", $FFMPEGLog);
+
+ $lines = explode("\n", $FFMPEGLog);
+ $line = $lines[count($lines) - 2];
+
+ if ($tpos = strpos($line, "time=")) {
+ $bpos = strpos($line, " bitrate=");
+
+ $time = substr($line, $tpos + 5, $bpos - ($tpos + 5));
+ return $time;
+ }
+ else {
+ return false;
+ }
+ }
+ }
+
+ static function durationToSecs($durstr) {
+ list($hr, $min, $sec) = explode(":", $durstr);
+
+ $secs = $hr * 3600;
+ $secs += $min * 60;
+ $secs += $sec;
+
+ return $secs;
+ }
+}
diff --git a/3.0/modules/transcode/helpers/transcode_theme.php b/3.0/modules/transcode/helpers/transcode_theme.php
new file mode 100644
index 00000000..371576ac
--- /dev/null
+++ b/3.0/modules/transcode/helpers/transcode_theme.php
@@ -0,0 +1,19 @@
+css_id = "g-resolutions";
+ $block->title = t("Alternative Resolutions");
+
+ $view = new View("transcode_resolution_variants.html");
+ $view->item = $theme->item();
+ $view->resolutions = ORM::factory("transcode_resolution")->where("item_id", "=", $view->item->id)->find_all();
+
+ $block->content = $view;
+ return $block;
+ }
+
+}
\ No newline at end of file
diff --git a/3.0/modules/transcode/models/transcode_resolution.php b/3.0/modules/transcode/models/transcode_resolution.php
new file mode 100644
index 00000000..f02cf11c
--- /dev/null
+++ b/3.0/modules/transcode/models/transcode_resolution.php
@@ -0,0 +1,4 @@
+
+
+
+
+
= t("Transcoding Settings") ?>
+
+
= t("Setup the preferred video format to transcode all uploaded videos to below. Select one or multiple resolutions/formats to use."); ?>
+
+
+
+
+
+
+
diff --git a/3.0/modules/transcode/views/transcode_resolution_variants.html.php b/3.0/modules/transcode/views/transcode_resolution_variants.html.php
new file mode 100644
index 00000000..6c1b5256
--- /dev/null
+++ b/3.0/modules/transcode/views/transcode_resolution_variants.html.php
@@ -0,0 +1,29 @@
+
+
+is_movie()): ?>
+ 0): ?>
+
+
+
+ No alternative resolutions available.
+
+