Example #1
0
 /**
  * Extract a frame from a movie file.  Valid movie_options are start_time (in seconds),
  * input_args (extra ffmpeg input args) and output_args (extra ffmpeg output args).  Extra args
  * are added at the end of the list, so they can override any prior args.
  *
  * @param string     $input_file
  * @param string     $output_file
  * @param array      $movie_options (optional)
  */
 static function extract_frame($input_file, $output_file, $movie_options = null)
 {
     $ffmpeg = movie::find_ffmpeg();
     if (empty($ffmpeg)) {
         throw new Exception("@todo MISSING_FFMPEG");
     }
     list($width, $height, $mime_type, $extension, $duration) = movie::get_file_metadata($input_file);
     if (isset($movie_options["start_time"]) && is_numeric($movie_options["start_time"])) {
         $start_time = max(0, $movie_options["start_time"]);
         // ensure it's non-negative
     } else {
         $start_time = module::get_var("gallery", "movie_extract_frame_time", 3);
         // use default
     }
     // extract frame at start_time, unless movie is too short
     $start_time_arg = $duration >= $start_time + 0.1 ? "-ss " . movie::seconds_to_hhmmssdd($start_time) : "";
     $input_args = isset($movie_options["input_args"]) ? $movie_options["input_args"] : "";
     $output_args = isset($movie_options["output_args"]) ? $movie_options["output_args"] : "";
     $cmd = escapeshellcmd($ffmpeg) . " {$input_args} -i " . escapeshellarg($input_file) . " -an {$start_time_arg} -an -r 1 -vframes 1" . " -s {$width}x{$height}" . " -y -f mjpeg {$output_args} " . escapeshellarg($output_file) . " 2>&1";
     exec($cmd, $exec_output, $exec_return);
     clearstatcache();
     // use $filename parameter when PHP_version is 5.3+
     if (filesize($output_file) == 0 || $exec_return) {
         // Maybe the movie needs the "-threads 1" argument added
         // (see http://sourceforge.net/apps/trac/gallery/ticket/1924)
         $cmd = escapeshellcmd($ffmpeg) . " -threads 1 {$input_args} -i " . escapeshellarg($input_file) . " -an {$start_time_arg} -an -r 1 -vframes 1" . " -s {$width}x{$height}" . " -y -f mjpeg {$output_args} " . escapeshellarg($output_file) . " 2>&1";
         exec($cmd, $exec_output, $exec_return);
         clearstatcache();
         if (filesize($output_file) == 0 || $exec_return) {
             throw new Exception("@todo FFMPEG_FAILED");
         }
     }
 }
Example #2
0
 /**
  * Process the data file info.  Get its metadata and extension.
  * If valid, use it to sanitize the item name and update the
  * width, height, and mime type.
  */
 private function _process_data_file_info()
 {
     try {
         if ($this->is_photo()) {
             list($this->width, $this->height, $this->mime_type, $extension) = photo::get_file_metadata($this->data_file);
         } else {
             if ($this->is_movie()) {
                 list($this->width, $this->height, $this->mime_type, $extension) = movie::get_file_metadata($this->data_file);
             } else {
                 // Albums don't have data files.
                 $this->data_file = null;
                 return;
             }
         }
         // Sanitize the name based on the idenified extension, but only set $this->name if different
         // to ensure it isn't unnecessarily marked as "changed"
         $name = legal_file::sanitize_filename($this->name, $extension, $this->type);
         if ($this->name != $name) {
             $this->name = $name;
         }
         // Data file valid - make sure the flag is reset to false.
         $this->data_file_error = false;
     } catch (Exception $e) {
         // Data file invalid - set the flag so it's reported during item validation.
         $this->data_file_error = true;
     }
 }
Example #3
0
 /**
  * Handle any business logic necessary to create or modify an item.
  * @see ORM::save()
  *
  * @return ORM Item_Model
  */
 public function save()
 {
     $significant_changes = $this->changed;
     unset($significant_changes["view_count"]);
     unset($significant_changes["relative_url_cache"]);
     unset($significant_changes["relative_path_cache"]);
     if (!empty($this->changed) && $significant_changes || isset($this->data_file)) {
         $this->updated = time();
         if (!$this->loaded()) {
             // Create a new item.
             module::event("item_before_create", $this);
             // Set a weight if it's missing.  We don't do this in the constructor because it's not a
             // simple assignment.
             if (empty($this->weight)) {
                 $this->weight = item::get_max_weight();
             }
             // Make an url friendly slug from the name, if necessary
             if (empty($this->slug)) {
                 $this->slug = item::convert_filename_to_slug(pathinfo($this->name, PATHINFO_FILENAME));
                 // If the filename is all invalid characters, then the slug may be empty here.  Pick a
                 // random value.
                 if (empty($this->slug)) {
                     $this->slug = (string) rand(1000, 9999);
                 }
             }
             // Get the width, height and mime type from our data file for photos and movies.
             if ($this->is_photo() || $this->is_movie()) {
                 if ($this->is_photo()) {
                     list($this->width, $this->height, $this->mime_type, $extension) = photo::get_file_metadata($this->data_file);
                 } else {
                     if ($this->is_movie()) {
                         list($this->width, $this->height, $this->mime_type, $extension) = movie::get_file_metadata($this->data_file);
                     }
                 }
                 // Force an extension onto the name if necessary
                 $pi = pathinfo($this->data_file);
                 if (empty($pi["extension"])) {
                     $this->name = "{$this->name}.{$extension}";
                 }
             }
             $this->_randomize_name_or_slug_on_conflict();
             parent::save();
             // Build our url caches, then save again.  We have to do this after it's already been
             // saved once because we use only information from the database to build the paths.  If we
             // could depend on a save happening later we could defer this 2nd save.
             $this->_build_relative_caches();
             parent::save();
             // Take any actions that we can only do once all our paths are set correctly after saving.
             switch ($this->type) {
                 case "album":
                     mkdir($this->file_path());
                     mkdir(dirname($this->thumb_path()));
                     mkdir(dirname($this->resize_path()));
                     break;
                 case "photo":
                 case "movie":
                     // The thumb or resize may already exist in the case where a movie and a photo generate
                     // a thumbnail of the same name (eg, foo.flv movie and foo.jpg photo will generate
                     // foo.jpg thumbnail).  If that happens, randomize and save again.
                     if (file_exists($this->resize_path()) || file_exists($this->thumb_path())) {
                         $pi = pathinfo($this->name);
                         $this->name = $pi["filename"] . "-" . random::int() . "." . $pi["extension"];
                         parent::save();
                     }
                     copy($this->data_file, $this->file_path());
                     break;
             }
             // This will almost definitely trigger another save, so put it at the end so that we're
             // tail recursive.  Null out the data file variable first, otherwise the next save will
             // trigger an item_updated_data_file event.
             $this->data_file = null;
             module::event("item_created", $this);
         } else {
             // Update an existing item
             module::event("item_before_update", $item);
             // If any significant fields have changed, load up a copy of the original item and
             // keep it around.
             $original = ORM::factory("item", $this->id);
             // Preserve the extension of the data file. Many helpers, (e.g. ImageMagick), assume
             // the MIME type from the extension. So when we adopt the new data file, it's important
             // to adopt the new extension. That ensures that the item's extension is always
             // appropriate for its data. We don't try to preserve the name of the data file, though,
             // because the name is typically a temporary randomly-generated name.
             if (isset($this->data_file)) {
                 $extension = pathinfo($this->data_file, PATHINFO_EXTENSION);
                 $new_name = pathinfo($this->name, PATHINFO_FILENAME) . ".{$extension}";
                 if (!empty($extension) && strcmp($this->name, $new_name)) {
                     $this->name = $new_name;
                 }
                 if ($this->is_photo()) {
                     list($this->width, $this->height, $this->mime_type, $extension) = photo::get_file_metadata($this->data_file);
                 } else {
                     if ($this->is_movie()) {
                         list($this->width, $this->height, $this->mime_type, $extension) = movie::get_file_metadata($this->data_file);
                     }
                 }
             }
             if (array_intersect($this->changed, array("parent_id", "name", "slug"))) {
                 $original->_build_relative_caches();
                 $this->relative_path_cache = null;
                 $this->relative_url_cache = null;
             }
             $this->_randomize_name_or_slug_on_conflict();
             parent::save();
             // Now update the filesystem and any database caches if there were significant value
             // changes.  If anything past this point fails, then we'll have an inconsistent database
             // so this code should be as robust as we can make it.
             // Update the MPTT pointers, if necessary.  We have to do this before we generate any
             // cached paths!
             if ($original->parent_id != $this->parent_id) {
                 parent::move_to($this->parent());
             }
             if ($original->parent_id != $this->parent_id || $original->name != $this->name) {
                 $this->_build_relative_caches();
                 // If there is a data file, then we want to preserve both the old data and the new data.
                 // (Third-party event handlers would like access to both). The old data file will be
                 // accessible via the $original item, and the new one via $this item. But in that case,
                 // we don't want to rename the original as below, because the old data would end up being
                 // clobbered by the new data file. Also, the rename isn't necessary, because the new item
                 // data is coming from the data file anyway. So we only perform the rename if there isn't
                 // a data file. Another way to solve this would be to copy the original file rather than
                 // conditionally rename it, but a copy would cost far more than the rename.
                 if (!isset($this->data_file)) {
                     @rename($original->file_path(), $this->file_path());
                 }
                 // Move all of the items associated data files
                 if ($this->is_album()) {
                     @rename(dirname($original->resize_path()), dirname($this->resize_path()));
                     @rename(dirname($original->thumb_path()), dirname($this->thumb_path()));
                 } else {
                     @rename($original->resize_path(), $this->resize_path());
                     @rename($original->thumb_path(), $this->thumb_path());
                 }
                 if ($original->parent_id != $this->parent_id) {
                     // This will result in 2 events since we'll still fire the item_updated event below
                     module::event("item_moved", $this, $original->parent());
                 }
             }
             // Changing the name, slug or parent ripples downwards
             if ($this->is_album() && ($original->name != $this->name || $original->slug != $this->slug || $original->parent_id != $this->parent_id)) {
                 db::build()->update("items")->set("relative_url_cache", null)->set("relative_path_cache", null)->where("left_ptr", ">", $this->left_ptr)->where("right_ptr", "<", $this->right_ptr)->execute();
             }
             // Replace the data file, if requested.
             if ($this->data_file && ($this->is_photo() || $this->is_movie())) {
                 copy($this->data_file, $this->file_path());
                 // Get the width, height and mime type from our data file for photos and movies.
                 if ($this->is_photo()) {
                     list($this->width, $this->height) = photo::get_file_metadata($this->file_path());
                 } else {
                     if ($this->is_movie()) {
                         list($this->width, $this->height) = movie::get_file_metadata($this->file_path());
                     }
                 }
                 $this->thumb_dirty = 1;
                 $this->resize_dirty = 1;
             }
             module::event("item_updated", $original, $this);
             if ($this->data_file) {
                 // Null out the data file variable here, otherwise this event will trigger another
                 // save() which will think that we're doing another file move.
                 $this->data_file = null;
                 if ($original->file_path() != $this->file_path()) {
                     @unlink($original->file_path());
                 }
                 module::event("item_updated_data_file", $this);
             }
         }
     } else {
         if (!empty($this->changed)) {
             // Insignificant changes only.  Don't fire events or do any special checking to try to keep
             // this lightweight.
             parent::save();
         }
     }
     return $this;
 }
Example #4
0
 public function get_file_metadata_with_valid_extension_but_illegal_file_contents_test()
 {
     copy(MODPATH . "gallery/tests/Photo_Helper_Test.php", TMPPATH . "test_php_with_flv_extension.flv");
     // Since mime type and extension are based solely on the filename, this is considered valid.
     // Of course, FFmpeg cannot extract width, height, or duration from the file.  Note that this
     // isn't a really a security problem, since the filename doesn't have a php extension and
     // therefore will never be executed.
     $this->assert_equal(array(0, 0, "video/x-flv", "flv", 0), movie::get_file_metadata(TMPPATH . "test_php_with_flv_extension.flv"));
     unlink(TMPPATH . "test_php_with_flv_extension.flv");
 }
Example #5
0
 /**
  * Rebuild the thumb and resize for the given item.
  * @param Item_Model $item
  */
 static function generate($item)
 {
     if ($item->thumb_dirty) {
         $ops["thumb"] = $item->thumb_path();
     }
     if ($item->resize_dirty && $item->is_photo()) {
         $ops["resize"] = $item->resize_path();
     }
     try {
         foreach ($ops as $target => $output_file) {
             $working_file = $item->file_path();
             // Delete anything that might already be there
             @unlink($output_file);
             switch ($item->type) {
                 case "movie":
                     // Run movie_extract_frame events, which can either:
                     //  - generate an output file, bypassing the ffmpeg-based movie::extract_frame
                     //  - add to the options sent to movie::extract_frame (e.g. change frame extract time,
                     //    add de-interlacing arguments to ffmpeg... see movie helper for more info)
                     // Note that the args are similar to those of the events in gallery_graphics
                     $movie_options_wrapper = new stdClass();
                     $movie_options_wrapper->movie_options = array();
                     module::event("movie_extract_frame", $working_file, $output_file, $movie_options_wrapper, $item);
                     // If no output_file generated by events, run movie::extract_frame with movie_options
                     clearstatcache();
                     if (@filesize($output_file) == 0) {
                         try {
                             movie::extract_frame($working_file, $output_file, $movie_options_wrapper->movie_options);
                             // If we're here, we know ffmpeg is installed and the movie is valid.  Because the
                             // user may not always have had ffmpeg installed, the movie's width, height, and
                             // mime type may need updating.  Let's use this opportunity to make sure they're
                             // correct.  It's not optimal to do it at this low level, but it's not trivial to find
                             // these cases quickly in an upgrade script.
                             list($width, $height, $mime_type) = movie::get_file_metadata($working_file);
                             // Only set them if they need updating to avoid marking them as "changed"
                             if ($item->width != $width || $item->height != $height || $item->mime_type != $mime_type) {
                                 $item->width = $width;
                                 $item->height = $height;
                                 $item->mime_type = $mime_type;
                             }
                         } catch (Exception $e) {
                             // Didn't work, likely because of MISSING_FFMPEG - use placeholder
                             graphics::_replace_image_with_placeholder($item, $target);
                             break;
                         }
                     }
                     $working_file = $output_file;
                 case "photo":
                     // Run the graphics rules (for both movies and photos)
                     foreach (self::_get_rules($target) as $rule) {
                         $args = array($working_file, $output_file, unserialize($rule->args), $item);
                         call_user_func_array($rule->operation, $args);
                         $working_file = $output_file;
                     }
                     break;
                 case "album":
                     if (!($cover = $item->album_cover())) {
                         // This album has no cover; copy its placeholder image.  Because of an old bug, it's
                         // possible that there's an album cover item id that points to an invalid item.  In that
                         // case, just null out the album cover item id.  It's not optimal to do that at this low
                         // level, but it's not trivial to find these cases quickly in an upgrade script and if we
                         // don't do this, the album may be permanently marked as "needs rebuilding"
                         //
                         // ref: http://sourceforge.net/apps/trac/gallery/ticket/1172
                         //      http://galleryproject.org/node/96926
                         if ($item->album_cover_item_id) {
                             $item->album_cover_item_id = null;
                             $item->save();
                         }
                         graphics::_replace_image_with_placeholder($item, $target);
                         break;
                     }
                     if ($cover->thumb_dirty) {
                         graphics::generate($cover);
                     }
                     if (!$cover->thumb_dirty) {
                         // Make the album cover from the cover item's thumb.  Run gallery_graphics::resize with
                         // null options and it will figure out if this is a direct copy or conversion to jpg.
                         $working_file = $cover->thumb_path();
                         gallery_graphics::resize($working_file, $output_file, null, $item);
                     }
                     break;
             }
         }
         if (!empty($ops["thumb"])) {
             if (file_exists($item->thumb_path())) {
                 $item->thumb_dirty = 0;
             } else {
                 Kohana_Log::add("error", "Failed to rebuild thumb image: {$item->title}");
                 graphics::_replace_image_with_placeholder($item, "thumb");
             }
         }
         if (!empty($ops["resize"])) {
             if (file_exists($item->resize_path())) {
                 $item->resize_dirty = 0;
             } else {
                 Kohana_Log::add("error", "Failed to rebuild resize image: {$item->title}");
                 graphics::_replace_image_with_placeholder($item, "resize");
             }
         }
         graphics::_update_item_dimensions($item);
         $item->save();
     } catch (Exception $e) {
         // Something went wrong rebuilding the image.  Replace with the placeholder images,
         // leave it dirty and move on.
         Kohana_Log::add("error", "Caught exception rebuilding images: {$item->title}\n" . $e->getMessage() . "\n" . $e->getTraceAsString());
         if ($item->is_photo()) {
             graphics::_replace_image_with_placeholder($item, "resize");
         }
         graphics::_replace_image_with_placeholder($item, "thumb");
         try {
             graphics::_update_item_dimensions($item);
         } catch (Exception $e) {
             // Looks like get_file_metadata couldn't identify our placeholders.  We should never get
             // here, but in the odd case we do, we need to do something.  Let's put in hardcoded values.
             if ($item->is_photo()) {
                 list($item->resize_width, $item->resize_height) = array(200, 200);
             }
             list($item->thumb_width, $item->thumb_height) = array(200, 200);
         }
         $item->save();
         throw $e;
     }
 }
Example #6
0
 /**
  * Make sure that the data file is well formed (it exists and isn't empty).
  */
 public function valid_data_file(Validation $v, $field)
 {
     if (!is_file($this->data_file)) {
         $v->add_error("name", "bad_data_file_path");
     } else {
         if (filesize($this->data_file) == 0) {
             $v->add_error("name", "empty_data_file");
         }
     }
     if ($this->loaded()) {
         if ($this->is_photo()) {
             list($a, $b, $mime_type) = photo::get_file_metadata($this->data_file);
         } else {
             if ($this->is_movie()) {
                 list($a, $b, $mime_type) = movie::get_file_metadata($this->data_file);
             }
         }
         if ($mime_type != $this->mime_type) {
             $v->add_error("name", "cant_change_mime_type");
         }
     }
 }
Example #7
0
 /**
  * This is the task code that adds photos and albums.  It first examines all the target files
  * and creates a set of Server_Add_Entry_Models, then runs through the list of models and adds
  * them one at a time.
  */
 static function add($task)
 {
     $mode = $task->get("mode", "init");
     $start = microtime(true);
     switch ($mode) {
         case "init":
             $task->set("mode", "build-file-list");
             $task->set("dirs_scanned", 0);
             $task->percent_complete = 0;
             $task->status = t("Starting up");
             batch::start();
             break;
         case "build-file-list":
             // 0% to 10%
             // We can't fit an arbitrary number of paths in a task, so store them in a separate table.
             // Don't use an iterator here because we can't get enough control over it when we're dealing
             // with a deep hierarchy and we don't want to go over our time quota.
             $paths = unserialize(module::get_var("videos", "authorized_paths"));
             $dirs_scanned = $task->get("dirs_scanned");
             while (microtime(true) - $start < 0.5) {
                 // Process every directory that doesn't yet have a parent id, these are the
                 // paths that we're importing.
                 $entry = ORM::factory("videos_entry")->where("task_id", "=", $task->id)->where("is_directory", "=", 1)->where("checked", "=", 0)->order_by("id", "ASC")->find();
                 if ($entry->loaded()) {
                     $child_paths = glob(preg_quote($entry->path) . "/*");
                     if (!$child_paths) {
                         $child_paths = glob("{$entry->path}/*");
                     }
                     foreach ($child_paths as $child_path) {
                         if (!is_dir($child_path)) {
                             $ext = strtolower(pathinfo($child_path, PATHINFO_EXTENSION));
                             // rWatcher Edit.
                             //if (!in_array($ext, array("gif", "jpeg", "jpg", "png", "flv", "mp4", "m4v")) ||
                             //    !filesize($child_path)) {
                             if (!in_array($ext, unserialize(module::get_var("videos", "allowed_extensions"))) || !filesize($child_path)) {
                                 // Not importable, skip it.
                                 continue;
                             }
                         }
                         $child_entry = ORM::factory("videos_entry");
                         $child_entry->task_id = $task->id;
                         $child_entry->path = $child_path;
                         $child_entry->parent_id = $entry->id;
                         // null if the parent was a staging dir
                         $child_entry->is_directory = is_dir($child_path);
                         $child_entry->save();
                     }
                     // We've processed this entry, mark it as done.
                     $entry->checked = 1;
                     $entry->save();
                     $dirs_scanned++;
                 }
             }
             // We have no idea how long this can take because we have no idea how deep the tree
             // hierarchy rabbit hole goes.  Leave ourselves room here for 100 iterations and don't go
             // over 10% in percent_complete.
             $task->set("dirs_scanned", $dirs_scanned);
             $task->percent_complete = min($task->percent_complete + 0.1, 10);
             $task->status = t2("Scanned one directory", "Scanned %count directories", $dirs_scanned);
             if (!$entry->loaded()) {
                 $task->set("mode", "add-files");
                 $task->set("total_files", ORM::factory("videos_entry")->where("task_id", "=", $task->id)->count_all());
                 $task->percent_complete = 10;
             }
             break;
         case "add-files":
             // 10% to 100%
             $completed_files = $task->get("completed_files", 0);
             $total_files = $task->get("total_files");
             // Ordering by id ensures that we add them in the order that we created the entries, which
             // will create albums first.  Ignore entries which already have an Item_Model attached,
             // they're done.
             $entries = ORM::factory("videos_entry")->where("task_id", "=", $task->id)->where("item_id", "IS", null)->order_by("id", "ASC")->limit(10)->find_all();
             if ($entries->count() == 0) {
                 // Out of entries, we're done.
                 $task->set("mode", "done");
             }
             $owner_id = identity::active_user()->id;
             foreach ($entries as $entry) {
                 if (microtime(true) - $start > 0.5) {
                     break;
                 }
                 // Look up the parent item for this entry.  By now it should exist, but if none was
                 // specified, then this belongs as a child of the current item.
                 $parent_entry = ORM::factory("videos_entry", $entry->parent_id);
                 if (!$parent_entry->loaded()) {
                     $parent = ORM::factory("item", $task->get("item_id"));
                 } else {
                     $parent = ORM::factory("item", $parent_entry->item_id);
                 }
                 $name = basename($entry->path);
                 $title = item::convert_filename_to_title($name);
                 if ($entry->is_directory) {
                     $album = ORM::factory("item");
                     $album->type = "album";
                     $album->parent_id = $parent->id;
                     $album->name = $name;
                     $album->title = $title;
                     $album->owner_id = $owner_id;
                     $album->sort_order = $parent->sort_order;
                     $album->sort_column = $parent->sort_column;
                     $album->save();
                     $entry->item_id = $album->id;
                 } else {
                     try {
                         $extension = strtolower(pathinfo($name, PATHINFO_EXTENSION));
                         if (in_array($extension, array("gif", "png", "jpg", "jpeg"))) {
                             $photo = ORM::factory("item");
                             $photo->type = "photo";
                             $photo->parent_id = $parent->id;
                             $photo->set_data_file($entry->path);
                             $photo->name = $name;
                             $photo->title = $title;
                             $photo->owner_id = $owner_id;
                             $photo->save();
                             $entry->item_id = $photo->id;
                             // rWatcher EDIT
                             //} else if (in_array($extension, array("flv", "mp4", "m4v"))) {
                         } else {
                             if (in_array($extension, unserialize(module::get_var("videos", "allowed_extensions")))) {
                                 $movie = ORM::factory("item");
                                 $movie->type = "movie";
                                 $movie->parent_id = $parent->id;
                                 $movie->set_data_file($entry->path);
                                 $movie->name = $name;
                                 $movie->title = $title;
                                 $movie->owner_id = $owner_id;
                                 $movie->save();
                                 $entry->item_id = $movie->id;
                                 // rWatcher EDIT:  Add record to items_video db.
                                 $items_video = ORM::factory("items_video");
                                 $items_video->item_id = $movie->id;
                                 $items_video->save();
                                 // rWatcher EDIT: Scan for flv resizes and copy to resize directory.
                                 if (file_exists($entry->path . ".flv")) {
                                     copy($entry->path . ".flv", $movie->resize_path() . ".flv");
                                     list($vid_width, $vid_height, $mime_type) = movie::get_file_metadata($entry->path . ".flv");
                                     $movie->height = $vid_height;
                                     $movie->width = $vid_width;
                                     $movie->save();
                                 }
                             } else {
                                 // This should never happen, because we don't add stuff to the list that we can't
                                 // process.  But just in, case.. set this to a non-null value so that we skip this
                                 // entry.
                                 $entry->item_id = 0;
                                 $task->log("Skipping unknown file type: {$entry->path}");
                             }
                         }
                     } catch (Exception $e) {
                         // This can happen if a photo file is invalid, like a BMP masquerading as a .jpg
                         $entry->item_id = 0;
                         $task->log("Skipping invalid file: {$entry->file}");
                     }
                 }
                 $completed_files++;
                 $entry->save();
             }
             $task->set("completed_files", $completed_files);
             $task->status = t("Adding photos / albums (%completed of %total)", array("completed" => $completed_files, "total" => $total_files));
             $task->percent_complete = $total_files ? 10 + 100 * ($completed_files / $total_files) : 100;
             break;
         case "done":
             batch::stop();
             $task->done = true;
             $task->state = "success";
             $task->percent_complete = 100;
             ORM::factory("videos_entry")->where("task_id", "=", $task->id)->delete_all();
             message::info(t2("Successfully added one photo / album", "Successfully added %count photos / albums", $task->get("completed_files")));
     }
 }
Example #8
0
 /**
  * This is the task code that adds photos and albums.  It first examines all the target files
  * and creates a set of Server_Add_File_Models, then runs through the list of models and adds
  * them one at a time.
  */
 static function add($task)
 {
     $mode = $task->get("mode", "init");
     $start = microtime(true);
     switch ($mode) {
         case "init":
             $task->set("mode", "build-file-list");
             $task->percent_complete = 0;
             $task->status = t("Starting up");
             batch::start();
             break;
         case "build-file-list":
             // 0% to 10%
             // We can't fit an arbitrary number of paths in a task, so store them in a separate table.
             // Don't use an iterator here because we can't get enough control over it when we're dealing
             // with a deep hierarchy and we don't want to go over our time quota.  The queue is in the
             // form [path, parent_id] where the parent_id refers to another Server_Add_File_Model.  We
             // have this extra level of abstraction because we don't know its Item_Model id yet.
             $queue = $task->get("queue");
             $paths = unserialize(module::get_var("videos", "authorized_paths"));
             while ($queue && microtime(true) - $start < 0.5) {
                 list($file, $parent_entry_id) = array_shift($queue);
                 // Ignore the staging directories as directories to be imported.
                 if (empty($paths[$file])) {
                     $entry = ORM::factory("videos_file");
                     $entry->task_id = $task->id;
                     $entry->file = $file;
                     $entry->parent_id = $parent_entry_id;
                     $entry->save();
                     $entry_id = $entry->id;
                 } else {
                     $entry_id = null;
                 }
                 $file = preg_quote($file);
                 foreach (glob("{$file}/*") as $child) {
                     if (is_dir($child)) {
                         $queue[] = array($child, $entry_id);
                     } else {
                         $ext = strtolower(pathinfo($child, PATHINFO_EXTENSION));
                         //if (in_array($ext, array("gif", "jpeg", "jpg", "png", "flv", "mp4", "m4v")) &&
                         if (in_array($ext, unserialize(module::get_var("videos", "allowed_extensions"))) && filesize($child) > 0) {
                             $child_entry = ORM::factory("videos_file");
                             $child_entry->task_id = $task->id;
                             $child_entry->file = $child;
                             $child_entry->parent_id = $entry_id;
                             $child_entry->save();
                         }
                     }
                 }
             }
             // We have no idea how long this can take because we have no idea how deep the tree
             // hierarchy rabbit hole goes.  Leave ourselves room here for 100 iterations and don't go
             // over 10% in percent_complete.
             $task->set("queue", $queue);
             $task->percent_complete = min($task->percent_complete + 0.1, 10);
             $task->status = t2("Found one file", "Found %count files", ORM::factory("videos_file")->where("task_id", "=", $task->id)->count_all());
             if (!$queue) {
                 $task->set("mode", "add-files");
                 $task->set("total_files", ORM::factory("videos_file")->where("task_id", "=", $task->id)->count_all());
                 $task->percent_complete = 10;
             }
             break;
         case "add-files":
             // 10% to 100%
             $completed_files = $task->get("completed_files", 0);
             $total_files = $task->get("total_files");
             // Ordering by id ensures that we add them in the order that we created the entries, which
             // will create albums first.  Ignore entries which already have an Item_Model attached,
             // they're done.
             $entries = ORM::factory("videos_file")->where("task_id", "=", $task->id)->where("item_id", "IS", null)->order_by("id", "ASC")->limit(10)->find_all();
             if ($entries->count() == 0) {
                 // Out of entries, we're done.
                 $task->set("mode", "done");
             }
             $owner_id = identity::active_user()->id;
             foreach ($entries as $entry) {
                 if (microtime(true) - $start > 0.5) {
                     break;
                 }
                 // Look up the parent item for this entry.  By now it should exist, but if none was
                 // specified, then this belongs as a child of the current item.
                 $parent_entry = ORM::factory("videos_file", $entry->parent_id);
                 if (!$parent_entry->loaded()) {
                     $parent = ORM::factory("item", $task->get("item_id"));
                 } else {
                     $parent = ORM::factory("item", $parent_entry->item_id);
                 }
                 $name = basename($entry->file);
                 $title = item::convert_filename_to_title($name);
                 if (is_dir($entry->file)) {
                     $album = ORM::factory("item");
                     $album->type = "album";
                     $album->parent_id = $parent->id;
                     $album->name = $name;
                     $album->title = $title;
                     $album->owner_id = $owner_id;
                     $album->save();
                     $entry->item_id = $album->id;
                 } else {
                     try {
                         $extension = strtolower(pathinfo($name, PATHINFO_EXTENSION));
                         if (in_array($extension, unserialize(module::get_var("videos", "allowed_extensions")))) {
                             $movie = ORM::factory("item");
                             $movie->type = "movie";
                             $movie->parent_id = $parent->id;
                             $movie->set_data_file($entry->file);
                             $movie->name = $name;
                             $movie->title = $title;
                             $movie->owner_id = $owner_id;
                             $movie->save();
                             $entry->item_id = $movie->id;
                             $items_video = ORM::factory("items_video");
                             $items_video->item_id = $movie->id;
                             $items_video->save();
                             if (file_exists($entry->file . ".flv")) {
                                 copy($entry->file . ".flv", $movie->resize_path() . ".flv");
                                 list($vid_width, $vid_height, $mime_type) = movie::get_file_metadata($entry->file . ".flv");
                                 $movie->height = $vid_height;
                                 $movie->width = $vid_width;
                                 $movie->save();
                             }
                         } else {
                             // This should never happen, because we don't add stuff to the list that we can't
                             // process.  But just in, case.. set this to a non-null value so that we skip this
                             // entry.
                             $entry->item_id = 0;
                             $task->log("Skipping unknown file type: {$entry->file}");
                         }
                     } catch (Exception $e) {
                         // This can happen if a photo file is invalid, like a BMP masquerading as a .jpg
                         $entry->item_id = 0;
                         $task->log("Skipping invalid file: {$entry->file}");
                     }
                 }
                 $completed_files++;
                 $entry->save();
             }
             $task->set("completed_files", $completed_files);
             $task->status = t("Adding photos / albums (%completed of %total)", array("completed" => $completed_files, "total" => $total_files));
             $task->percent_complete = $total_files ? 10 + 100 * ($completed_files / $total_files) : 100;
             break;
         case "done":
             batch::stop();
             $task->done = true;
             $task->state = "success";
             $task->percent_complete = 100;
             db::build()->delete("videos_files")->where("task_id", "=", $task->id)->execute();
             message::info(t2("Successfully added one file", "Successfully added %count files", $task->get("completed_files")));
     }
 }