CMS  Version 3.9
video_manager.inc
Go to the documentation of this file.
1 <?php
7 /**************************************************************
8 
9  Copyright (c) 2010 Sonjara, Inc
10 
11  Permission is hereby granted, free of charge, to any person
12  obtaining a copy of this software and associated documentation
13  files (the "Software"), to deal in the Software without
14  restriction, including without limitation the rights to use,
15  copy, modify, merge, publish, distribute, sublicense, and/or sell
16  copies of the Software, and to permit persons to whom the
17  Software is furnished to do so, subject to the following
18  conditions:
19 
20  The above copyright notice and this permission notice shall be
21  included in all copies or substantial portions of the Software.
22 
23  Except as contained in this notice, the name(s) of the above
24  copyright holders shall not be used in advertising or otherwise
25  to promote the sale, use or other dealings in this Software
26  without prior written authorization.
27 
28  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
29  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
30  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
31  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
32  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
33  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
34  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
35  OTHER DEALINGS IN THE SOFTWARE.
36 
37 *****************************************************************/
38 
39 Fakoli::using("video", "settings");
40 
42 {
43  var $movieFile = null;
44 
45  function VideoManager()
46  {
47  trace("Creating VideoManager", 3);
48  }
49 
50  static function setDefaults()
51  {
52  Settings::setDefaultValue("video", "video_folder", "videos", String, "Directory inside the web folder that is used to store the video files", "Video Uploads");
53  Settings::setDefaultValue("video", "video_upload_location", "Web Folder", String,
54  "Indicate whether videos should be stored under the Web Folder or in the document upload area.".
55  "If you do not have mod_xsendfile installed, then Web Folder is strongly recommended",
56  "Video Uploads", "Web Folder\nDocument Upload Folder\nOther");
57  Settings::setDefaultValue("video","video_upload_other_location", "", String, "Full path to alternate video location", "Video Uploads");
58  Settings::setDefaultValue("video", "use_HTML5_video", false, Boolean, "Check to enable support for HTML5 video", "Video Player", null, 1);
59  Settings::setDefaultValue("video", "use_flash_video", true, Boolean, "Check to enable support for old-style FLV (requires Flowplayer Flash)", "Video Player", null, 2);
60  Settings::setDefaultValue("video", "flowplayer_script", "", String, "URI for the Flowplayer script", "Video Player", null, 3);
61  Settings::setDefaultValue("video", "flowplayer_player", "", String, "URI for the Flowplayer SWF file", "Video Player", null, 4);
62  Settings::setDefaultValue("video", "ffmpeg_path", "", String, "Directory containing the ffmpeg binary executables", "Video Transcoder");
63  }
64 
65  static function onInitialize()
66  {
67  global $isAction;
68  global $isResource;
69 
70  global $html_editor_extra_buttons;
71  global $script;
72  global $styles;
73 
74  $html_editor_extra_buttons[] =
75  array("name" => "video_picker",
76  "image" => "/fakoli/images/video_button.png",
77  "tooltip" => "Insert Video",
78  "handler" => "videoPicker");
79 
80  $useHTML5 = Settings::getValue("video", "use_HTML5_video");
81  $useFlash = Settings::getValue("video", "use_flash_video");
82  $flowPlayerScript = Settings::getValue("video", "flowplayer_script");
83 
84  if ($isAction || $isResource) return;
85 
86  if ($useHTML5)
87  {
88  $styles = "<link href='//vjs.zencdn.net/4.12/video-js.css' rel='stylesheet'>" . $styles;
89  $script = "<script src='//vjs.zencdn.net/4.12/video.js'></script>" . $script;
90  }
91 
92  if ($useFlash && $flowPlayerScript)
93  {
94  $script .= "<script type='text/javascript' src='$flowPlayerScript'></script>\n";
95  }
96 
97  $flowPlayer = Settings::getValue("video", "flowplayer_player");
98 
99  $script .= "<script type='text/javascript'>\nvar flowplayerPath = '$flowPlayer';\n</script>\n";
100  }
101 
102  static function getVideoFolder()
103  {
104  global $config;
105 
106  $location = Settings::getValue("video", "video_upload_location");
107  $folder = Settings::getValue("video", "video_folder");
108 
109  if ($location == "Other")
110  {
111  $dir = sanitizePath(Settings::getValue("video", "video_upload_other_location") . DIRECTORY_SEPARATOR . $folder);
112  trace($dir, 3);
113  if (!file_exists($dir))
114  {
115  //throw new FakoliException("Video Folder does not exist");
116  }
117  }
118  else if ($location == "Web Folder")
119  {
120  $dir = $config['homedir']. DIRECTORY_SEPARATOR . $folder;
121  if (!file_exists($dir))
122  {
123  throw new FakoliException("Video Folder does not exist");
124  }
125  }
126  else
127  {
128  $dir = $config['uploadbase']. DIRECTORY_SEPARATOR . $folder;
129  if (!file_exists($dir))
130  {
131  if (!mkdir($dir))
132  {
133  throw new FakoliException("Could not create video upload folder");
134  }
135  }
136  }
137 
138  return $dir;
139  }
140 
141  static function getVideoURI($video)
142  {
143 
144  $location = Settings::getValue("video", "video_upload_location");
145 
146  if ($location == "Web Folder")
147  {
148  $folder = Settings::getValue("video", "video_folder");
149  $gallery = $video->VideoGallery();
150  return "/".$folder."/gallery_".$video->video_gallery_id."/".$video->video_file;
151  }
152  else
153  {
154  return "/action/video/stream?video_id=".$video->video_id;
155  }
156  }
157 
158  static function formatVideoURI($video)
159  {
161  }
162 
163  static function upgradeComponent($version)
164  {
165  $mgr = new VideoUpgradeManager();
166  $mgr->upgrade($version);
167  }
168 
169  private function getExecutablePath($exe)
170  {
171  $path = Settings::getValue("video", "ffmpeg_path");
172  $cmd = $path . DIRECTORY_SEPARATOR . $exe;
174  {
175  $cmd .= ".exe";
176  }
177 
178  return $cmd;
179  }
180 
181  private function execute($exe, $args)
182  {
183  if ($exe != "ffmpeg" && $exe != "ffprobe")
184  {
185  throw new Exception("Invalid FFMPEG executable");
186  }
187 
188  $cmd = $this->getExecutablePath($exe);
189 
190  if (!file_exists($cmd))
191  {
192  throw new FakoliException("FFMPEG executable not found");
193  }
194 
195  ob_start();
196  passthru("$cmd $args");
197  $out = ob_get_contents();
198  ob_end_clean();
199  return $out;
200  }
201 
202  function hasFFMPEG()
203  {
204  $path = Settings::getValue("video", "ffmpeg_path");
205 
206  if (!$path)
207  {
208  trace("FFMPEG: path not set", 3);
209  return false;
210  }
211 
212  $ffmpeg = $this->getExecutablePath("ffmpeg");
213  if (!file_exists($ffmpeg))
214  {
215  trace("FFMPEG: no executable at $ffmpeg", 3);
216  return false;
217  }
218 
219  $ffprobe = $this->getExecutablePath("ffprobe");
220  if (!file_exists($ffprobe))
221  {
222  trace("FFMPEG: no executable at $ffprobe", 3);
223  return false;
224  }
225 
226  trace("FFMPEG: executables detected in correct location", 3);
227 
228  return true;
229  }
230 
231  function open($file)
232  {
233  trace("Opening video '$file'", 3);
234 
235  if (!file_exists($file))
236  {
237  throw new FakoliException("File not found");
238  }
239 
240  $this->movieFile = $file;
241  $this->movieInfo = json_decode($this->execute("ffprobe", "-v quiet -print_format json -show_format -show_streams {$this->movieFile}"));
242  }
243 
244  function getFrameWidth()
245  {
246  foreach($this->movieInfo->streams as $stream)
247  {
248  if ($stream->codec_type == "video") return $stream->width;
249  }
250 
251  throw new FakoliException("Failed to locate video stream in file");
252  }
253 
254  function getFrameHeight()
255  {
256  foreach($this->movieInfo->streams as $stream)
257  {
258  if ($stream->codec_type == "video") return $stream->height;
259  }
260 
261  throw new FakoliException("Failed to locate video stream in file");
262  }
263 
264  function getFrameRate()
265  {
266  foreach($this->movieInfo->streams as $stream)
267  {
268  if ($stream->codec_type == "video") return $stream->r_frame_rate;
269  }
270 
271  throw new FakoliException("Failed to locate video stream in file");
272  }
273 
274  function getFrame($idx)
275  {
276  return $this->ffmpeg->getFrame($idx);
277  }
278 
279  function saveFrame($offset, $out)
280  {
281  if (!$this->movieFile) throw new FakoliException("Movie File not loaded");
282  $width = $this->getFrameWidth();
283  $height = $this->getFrameHeight();
284 
285  $this->execute("ffmpeg", "-ss {$offset} -i {$this->movieFile} -t 1 -s {$width}x{$height} -f image2 {$out}");
286  }
287 
288  function makeMultipleTwo($value)
289  {
290  return intval($value) >> 1 << 1;
291  }
292 
300  {
301  return "/action/video/thumbnail?video_id=$video_id&size=$size";
302  }
303 
305  {
306  return "/action/video/show?image_id=$video_id";
307  }
308 
321  function renderThumbnail($video_id, $size = 0, $width = 0, $height = 0)
322  {
323  global $config;
324 
325  $video = new Video($video_id);
326 
327  //AJG - default to natural size if not specified
328  if (!$size && !$width && !$height)
329  {
330  $width = $video->width;
331  $height = $video->height;
332  }
333 
334  $suffix = $size ? "$size" : ($width ? "{$width}w" : ($height ? "{$height}h" : ""));
335 
336  $dir = $video->VideoGallery()->getGalleryDirectory();
337 
338  $imageFile = $dir . DIRECTORY_SEPARATOR . $video->image_file;
339  $cacheFile = $dir . DIRECTORY_SEPARATOR . $video->video_id . "_" . $suffix .".png";
340  trace("renderThumbnail:: imageFile {$imageFile} and cacheDir {$cacheDir} and cacheFile {$cacheFile}", 3);
341 
342  if (!file_exists($cacheFile) || (filemtime($cacheFile) < filemtime($imageFile)))
343  {
344  $src = imagecreatefrompng($imageFile);
345 
346  // If the thumbnail hasn't been generated yet, or is out-of-date, create it.
347  $fullWidth = imagesx($src);
348  $fullHeight = imagesy($src);
349 
350  if ($size)
351  {
352  if ($fullWidth > $fullHeight)
353  {
354  $newWidth = $size;
355  $newHeight = intval(($fullHeight * $size) / $fullWidth);
356 
357  }
358  else
359  {
360  $newWidth = intval(($fullWidth * $size) / $fullHeight);
361  $newHeight = $size;
362  }
363  }
364  else if ($width)
365  {
366  $newWidth = $width;
367  $newHeight = intval(($fullHeight * $width) / $fullWidth);
368  }
369  else if ($height)
370  {
371  $newHeight = $height;
372  $newWidth = intval(($fullWidth * $height) / $fullHeight);
373  }
374  else
375  {
376  $newWidth = $fullWidth;
377  $newHeight = $fullHeight;
378  }
379 
380  trace("Rendering $cacheFile @ $newWidth x $newHeight", 3);
381 
382  $dst = imagecreatetruecolor($newWidth, $newHeight);
383  imagecopyresampled($dst, $src, 0, 0, 0, 0, $newWidth, $newHeight, $fullWidth, $fullHeight);
384 
385  if (file_exists($cacheFile))
386  {
387  // If a previous copy of the file already exists, remove it
388  trace("renderThumbnail:: unlinking cachefile", 3);
389  unlink($cacheFile);
390  }
391 
392  imagepng($dst, $cacheFile);
393 
394  imagedestroy($dst);
395  imagedestroy($src);
396  }
397 
398  Fakoli::sendFile($cacheFile);
399 
400  trace("renderThumbnail:: exiting", 3);
401  }
402 
403 
409  static function displayVideoGallery($identifier, &$continue)
410  {
411  try
412  {
413  $gallery = Query::create(VideoGallery, "WHERE identifier=:i")
414  ->bind(":i", $identifier)
415  ->executeSingle();
416 
417  $page = ComponentPage::findByIdentifier("video_gallery", "WHERE enabled=1");
418  $_GET["gallery_id"] = $gallery->video_gallery_id;
419 
420  $pageView = new ComponentPageView($page);
421 
422  $page_role = $page->role;
423 
424  if (!checkRole($page->role))
425  {
427  redirect("/login");
428  }
429 
430  echo $pageView->drawView();
431 
432  $continue = false;
433  }
434  catch(DataNotFoundException $e)
435  {
436 
437  }
438 
439  return $identifier;
440  }
441 
449  static function deleteUser($user)
450  {
451  $pk = $user->getPrimaryKey();
452  $user_id = $user->$pk;
453 
454  trace("Component video is deleting objects dependent on user_id {$user_id}", 3);
455 
456  $tx = new DataTransaction();
457 
458  try
459  {
460  $galleries = Query::create(VideoGallery, "WHERE owner_id=:owner_id")
461  ->bind(":owner_id", $user_id)
462  ->execute();
463 
464  if(count($galleries))
465  {
466  foreach($galleries as $gallery)
467  {
468  $gallery->joinTransaction($tx);
470  }
471  }
472 
473  $video = new Video();
474  $video->joinTransaction($tx);
475  $video->delete("WHERE user_id={$user_id}");
476 
477  $download = new VideoDownload();
478  $download->joinTransaction($tx);
479  $download->delete("WHERE user_id={$user_id}");
480  $tx->commit();
481  }
482  catch(Exception $e)
483  {
484  $tx->rollback();
485  throw $e;
486  }
487 
488  return $user;
489  }
490 
497  static function deleteGallery($gallery)
498  {
499  $tx = new DataTransaction();
500 
501  try
502  {
503  $videos = $gallery->Videos();
504 
505  if(count($videos))
506  {
507  $download = new VideoDownload();
508  foreach($videos as $video)
509  {
510  $download->joinTransaction($tx);
511  $video->joinTransaction($tx);
512  $download->delete("WHERE video_id={$video->video_id}");
513  $video->delete();
514  }
515  }
516  $tx->commit();
517  }
518  catch(Exception $e)
519  {
520  $tx->rollback();
521  throw $e;
522  }
523  }
524 
525  static function videoGalleryTabs($key)
526  {
527  $tabs = array(
528  "Gallery" => "/admin/video_gallery_form",
529  "Permissions" => "/admin/video_gallery_permissions",
530  "Videos" => "/admin/videos",
531  "Statistics" => "/admin/video_gallery_stats",
532  );
533 
534  $qs = ($key) ? "video_gallery_id=$key" : "";
535  return new TabBar("tabs", $tabs, $qs);
536  }
537 
542  static function enumerateItems($items)
543  {
544  $galleries = Query::create(VideoGallery, "ORDER BY gallery_name")->execute();
545 
546  $items["Video Galleries"] = $galleries;
547  return $items;
548  }
549 
551  {
552  $classes[] = Video;
553  return $classes;
554  }
555 
556 }?>
$user_id
$tabs
$page
Definition: help.inc:39
$out
Definition: page.inc:66
if(! $page) $path
Definition: page.inc:57
$src
Definition: page.inc:37
$folder
Definition: templates.inc:41
$dir
Definition: delete.inc:44
$file
Definition: delete.inc:47
$size
Definition: download.inc:47
static findByIdentifier($identifier, $constraint="")
ComponentPageView generates the page content for a component page, substituting page fields,...
FakoliException is the base exception class for all Fakoli errors.
Definition: core.inc:53
static detectWindowsOS()
Returns true if running on Windows, false otherwise.
Definition: core.inc:1455
static sendFile($resource)
Sends the contents of the specified file to the client.
Definition: core.inc:780
static using()
Import the datamodels, views and manifest for the specified component(s).
Definition: core.inc:116
static storeRedirectPage()
Store the page from which a user has been redirected when prompted to login or create an account.
Definition: login.inc:493
static getValue($component, $name)
Retrieve the value of the specified Setting.
Definition: settings.inc:104
static setDefaultValue($component, $name, $value, $field_type="String", $annotation="", $category="", $options="", $weight=0)
Sets the default value of the given component setting.
Definition: settings.inc:174
Definition: video.inc:42
static displayVideoGallery($identifier, &$continue)
Event handler to display an image gallery from the specified identifier.
static onInitialize()
static getVideoURI($video)
imageLink($video_id)
static getVideoFolder()
renderThumbnail($video_id, $size=0, $width=0, $height=0)
Renders the Image specified by the image_id at the specified size.
static registerTaxonomyClasses($classes)
static enumerateItems($items)
Enumerate the Image Gallery objects.
static setDefaults()
makeMultipleTwo($value)
static videoGalleryTabs($key)
static formatVideoURI($video)
static deleteUser($user)
Respond to fired event DeleteUser.
static upgradeComponent($version)
static deleteGallery($gallery)
When deleting a video gallery, we must also delete all videos linked to the gallery and any downloads...
saveFrame($offset, $out)
thumbnailLink($video_id, $size)
Returns the URI that can be used to access the specified thumbnail of a video at the given size.
global $user
$isResource
Definition: core.inc:1585
if($config["default_content_type"]) $isAction
Definition: core.inc:1584
$height
Definition: cover.inc:38
$width
Definition: cover.inc:37
global $config
Definition: import.inc:4
$styles
$galleries
$identifier
Definition: rss.inc:37
$useHTML5
Definition: video_form.inc:58
$video_id
Definition: video_form.inc:40
$video
Definition: video_form.inc:42
$videos
Definition: videos.inc:40