369 lines
12 KiB
Perl
369 lines
12 KiB
Perl
# ==================================================================
|
|
# Plugins::ConvertVideo - Auto Generated Program Module
|
|
#
|
|
# Plugins::ConvertVideo
|
|
# Author : Virginia Lo
|
|
# Version : 1.1
|
|
# Updated : Wed Feb 21 16:05:27 2007
|
|
#
|
|
# ==================================================================
|
|
#
|
|
|
|
package Plugins::ConvertVideo;
|
|
|
|
# ==================================================================
|
|
|
|
use strict;
|
|
use GT::Base;
|
|
use GT::Plugins qw/STOP CONTINUE/;
|
|
use Links qw/$CFG $IN $DB/;
|
|
use Links::Plugins;
|
|
use vars qw/$WIDTH $HEIGHT $LINKID $CONVERTBOX $CHECKCONVERTBOX/;
|
|
|
|
# Inherit from base class for debug and error methods
|
|
@Plugins::ConvertVideo::ISA = qw(GT::Base);
|
|
|
|
# Your code begins here! Good Luck!
|
|
|
|
# PLUGIN HOOKS
|
|
# ===================================================================
|
|
|
|
sub validate_link_pre {
|
|
# -------------------------------------------------------------------
|
|
# This subroutine will get called whenever the hook 'validate_link'
|
|
# is run. You should call GT::Plugins->action (STOP) if you don't
|
|
# want the regular code to run, otherwise the code will continue as
|
|
# normal.
|
|
#
|
|
my ($link) = @_;
|
|
$link = convert_video($link);
|
|
return $link;
|
|
}
|
|
|
|
sub pre_form_link {
|
|
# -------------------------------------------------------------------
|
|
#
|
|
my $opts = shift;
|
|
if ($opts->{mode} =~ /([add|modify])_form/) {
|
|
$CONVERTBOX = 1;
|
|
|
|
if ($1 eq 'add') {
|
|
$CHECKCONVERTBOX = 1;
|
|
}
|
|
}
|
|
return $opts;
|
|
}
|
|
|
|
sub post_form_link {
|
|
# -------------------------------------------------------------------
|
|
#
|
|
my @args = @_;
|
|
if (($CONVERTBOX and $IN->param('db') eq 'Links')
|
|
|| $IN->param('action') eq 'link_add_form'
|
|
|| $IN->param('action') eq 'link_modify_form')
|
|
{
|
|
my $checked = '';
|
|
if ($IN->param('action') eq 'link_add_form' || $CHECKCONVERTBOX) {
|
|
$checked = ' checked';
|
|
}
|
|
|
|
my $font = 'face="Tahoma,Arial,Helvetica" size="2"';
|
|
$args[0] .=
|
|
qq~<p><table border=1 cellpadding=0 bgcolor="#FFFFFF" cellspacing=0 width="500"><tr><td>
|
|
<table border=0 bgcolor="#FFFFFF" width="500"><tr>
|
|
<td width="20%" valign="top"><font $font>Converting Video(s)?</td>
|
|
<td width="80%"><font $font><input type="checkbox" name="admin_convert_video" value="1"$checked/> YES </td>
|
|
</tr></table>
|
|
</td></tr></table>~;
|
|
}
|
|
return @args;
|
|
}
|
|
|
|
sub modify_link_pre {
|
|
# -------------------------------------------------------------------
|
|
# This subroutine will get called whenever the hook 'modify_link'
|
|
# is run. You should call GT::Plugins->action (STOP) if you don't
|
|
# want the regular code to run, otherwise the code will continue as
|
|
# normal.
|
|
#
|
|
my @args = @_;
|
|
my $link = $args[0];
|
|
$LINKID = $link->{ID};
|
|
return @args;
|
|
}
|
|
|
|
sub modify_link_post {
|
|
# -------------------------------------------------------------------
|
|
#
|
|
my $ret = shift;
|
|
if ($ret and $LINKID and $IN->param('admin_convert_video')) {
|
|
my $linkdb = $DB->table('Links');
|
|
my $link = convert_video($linkdb->get($LINKID));
|
|
my $res = $linkdb->update($link, { ID => $LINKID });
|
|
if (!$res) {
|
|
die "Can't modify link #$LINKID: $GT::SQL::error\n";
|
|
}
|
|
}
|
|
return $ret;
|
|
}
|
|
|
|
sub add_link_post {
|
|
# -------------------------------------------------------------------
|
|
#
|
|
$LINKID = shift;
|
|
if ($LINKID) {
|
|
&modify_link_post(1);
|
|
}
|
|
return $LINKID;
|
|
}
|
|
|
|
sub convert_video {
|
|
# ------------------------------------------------------------------
|
|
# Grab video details
|
|
my ($link) = @_;
|
|
|
|
my $linkid = $link->{ID};
|
|
my $result = { ID => $linkid };
|
|
|
|
my $linksdb = $DB->table('Links');
|
|
my $cfg = Links::Plugins::get_plugin_user_cfg('ConvertVideo');
|
|
|
|
my $vf_field = $cfg->{video_file_field};
|
|
my $ff_field = $cfg->{flash_file_field};
|
|
my $thumb = $cfg->{thumbnail_file_field};
|
|
my $image = $cfg->{image_file_field};
|
|
my $url_field = $cfg->{video_url_field};
|
|
|
|
if ($cfg->{flash_dimension} =~ /\s*(\d+)x(\d+)\s*/) {
|
|
($WIDTH, $HEIGHT) = ($1, $2);
|
|
}
|
|
|
|
my $url = $link->{$url_field};
|
|
if ($url and $url ne '' and $url ne 'http://') {
|
|
my $fh = $linksdb->file_info($image, $linkid) or return $result;
|
|
|
|
require Plugins::SlideShow;
|
|
|
|
# save the Image file (if required)
|
|
my $fname = Plugins::SlideShow::get_filename("$fh");
|
|
my $efname = GT::CGI::escape($fname);
|
|
|
|
my $main_fpath = $CFG->{admin_root_path} . "/tmp/work-$efname";
|
|
if ($main_fpath ne "$fh") {
|
|
open IMG, ">$main_fpath" or return throw_error( $! );
|
|
binmode IMG;
|
|
print IMG <$fh>;
|
|
close IMG;
|
|
}
|
|
|
|
my $quality = 75;
|
|
|
|
my $image_path = $CFG->{admin_root_path} . "/tmp/image-$efname";
|
|
Plugins::SlideShow::resize_image($main_fpath, $image_path, $WIDTH, $HEIGHT, $quality);
|
|
$result->{$image} = GT::SQL::File->open($image_path);
|
|
|
|
if ($cfg->{thumbnail_size} =~ /\s*(\d+)x(\d+)\s*/) {
|
|
my ($thumb_width, $thumb_height) = ($1, $2);
|
|
my $thumb_path = $CFG->{admin_root_path} . "/tmp/thumbnail-$efname";
|
|
Plugins::SlideShow::resize_image($main_fpath, $thumb_path, $thumb_width, $thumb_height, $quality);
|
|
$result->{$thumb} = GT::SQL::File->open($thumb_path);
|
|
}
|
|
return $result;
|
|
}
|
|
|
|
my $fh = $linksdb->file_info($vf_field, $linkid) or return $result;
|
|
|
|
my $full_path = "$fh";
|
|
my $video = {};
|
|
|
|
my $filename = $full_path;
|
|
$filename =~ s/(.+)\/\d-([^\/]+)\.$/$2/g;
|
|
|
|
my $flv_file_path =
|
|
$CFG->{admin_root_path} . "/tmp/" . $linkid . ".flv";
|
|
my $thumb_path =
|
|
$CFG->{admin_root_path} . "/tmp/" . "thumbnail-" . $linkid . ".png";
|
|
my $image_path =
|
|
$CFG->{admin_root_path} . "/tmp/" . "image-" . $linkid . ".png";
|
|
|
|
my $buf = `ffmpeg -i "$full_path" 2>&1`;
|
|
|
|
if ($buf =~ /Video:\s.+\s+(\d+)x(\d+),?.*\s+(\d+)\s+(?:fps|tb)/) {
|
|
$video->{width} = $1;
|
|
$video->{height} = $2;
|
|
$video->{fps} = $3;
|
|
|
|
if ($buf =~ /Duration:\s+(\d+):(\d+):(\d+).+bitrate:\s+(\d+)\s+kb\/s/)
|
|
{
|
|
$video->{duration} = $1 * 3600 + $2 * 60 + $3;
|
|
$video->{bitrate} = $4;
|
|
}
|
|
|
|
} else {
|
|
warn "Couldn't get video info";
|
|
return $result;
|
|
}
|
|
|
|
# Figure out any scaling we might need to do
|
|
if ($video->{width} > $WIDTH and $video->{height} > $HEIGHT) {
|
|
|
|
# Choose the larger dimension to scale down
|
|
if ($video->{width} / $WIDTH > $video->{height} / $HEIGHT) {
|
|
$video->{out_height} =
|
|
int($video->{height} * $WIDTH / $video->{width});
|
|
$video->{out_width} = $WIDTH;
|
|
} else {
|
|
$video->{out_width} =
|
|
int($video->{width} * $HEIGHT / $video->{height});
|
|
$video->{out_height} = $HEIGHT;
|
|
}
|
|
} elsif ($video->{width} > $WIDTH) {
|
|
$video->{out_height} = int($HEIGHT * $WIDTH / $video->{width});
|
|
$video->{out_width} = $WIDTH;
|
|
} elsif ($video->{height} > $HEIGHT) {
|
|
$video->{out_height} = $HEIGHT;
|
|
$video->{out_width} = int($video->{height} * $WIDTH / $HEIGHT);
|
|
}
|
|
|
|
# Source dimensions are smaller than output
|
|
else {
|
|
$video->{out_height} = $video->{height};
|
|
$video->{out_width} = $video->{width};
|
|
}
|
|
|
|
$video->{out_height}++ if $video->{out_height} % 2;
|
|
$video->{out_width}++ if $video->{out_width} % 2;
|
|
|
|
$video->{out_padtop} = int(($HEIGHT - $video->{out_height}) / 2);
|
|
$video->{out_padtop}-- if $video->{out_padtop} % 2;
|
|
$video->{out_padbottom} =
|
|
$HEIGHT - $video->{out_height} - $video->{out_padtop};
|
|
|
|
$video->{out_padleft} = int(($WIDTH - $video->{out_width}) / 2);
|
|
$video->{out_padleft}-- if $video->{out_padleft} % 2;
|
|
$video->{out_padright} =
|
|
$WIDTH - $video->{out_width} - $video->{out_padleft};
|
|
|
|
# Encode the video
|
|
system(
|
|
"ffmpeg",
|
|
'-i', "$full_path",
|
|
'-y', # overwrite
|
|
# video options
|
|
'-vf', "scale=$video->{out_width}:$video->{out_height},pad='$WIDTH:$HEIGHT:$video->{out_padleft}:$video->{out_padtop}'",
|
|
|
|
# audio options
|
|
'-ar', 22050, # audio sampling rate (Hz)
|
|
'-ab', 64, # audio bitrate (kb/s)
|
|
'-ac', 2, # audio channels
|
|
# encoding options
|
|
# '-b', 100000, # video bitrate (b/s)
|
|
'-qscale',
|
|
$cfg->{flash_quality} || 6, # quality scale (1 [best] - 31 [worst])
|
|
# watermark
|
|
# '-vhook', '/usr/lib/vhook/watermark.so -f admin/water3.gif',
|
|
# output
|
|
"$flv_file_path",
|
|
);
|
|
|
|
=tag
|
|
# video options
|
|
'-r', $video->{fps}, # frame rate
|
|
'-s', "$video->{out_width2}x$video->{out_height2}", # frame size
|
|
'-padtop', $video->{out_padtop},
|
|
'-padbottom', $video->{out_padbottom},
|
|
'-padleft', $video->{out_padleft},
|
|
'-padright', $video->{out_padright},
|
|
=cut
|
|
|
|
# -----------------------------------------------------------------------------------------
|
|
# -----------------------------------------------------------------------------------------
|
|
|
|
# Generate a thumbnail (a quarter of the way through)
|
|
my $when = int($video->{duration} * 0.25);
|
|
my $hours = int($when / 3600);
|
|
my $mins = int($when / 60) - $hours * 60;
|
|
my $secs = $when - $hours * 3600 - $mins * 60;
|
|
$when = sprintf("%.2d:%.2d:%.2d", $hours, $mins, $secs);
|
|
|
|
if ($cfg->{thumbnail_size} and $cfg->{thumbnail_file_field}) {
|
|
system(
|
|
"ffmpeg",
|
|
'-i', "$flv_file_path",
|
|
'-y', # overwrite
|
|
'-vframes', 1, # record 1 frame
|
|
'-ss', $when,
|
|
'-an', # no audio
|
|
'-vcodec', 'png',
|
|
'-f', 'rawvideo',
|
|
'-s', $cfg->{thumbnail_size},
|
|
"$thumb_path",
|
|
);
|
|
}
|
|
|
|
system(
|
|
"ffmpeg",
|
|
'-i', "$flv_file_path",
|
|
'-y', # overwrite
|
|
'-vframes', 1, # record 1 frame
|
|
'-ss', $when,
|
|
'-an', # no audio
|
|
'-vcodec', 'png',
|
|
'-f', 'rawvideo',
|
|
'-s', "${WIDTH}x$HEIGHT", # frame size
|
|
"$image_path",
|
|
);
|
|
|
|
$result->{$ff_field} = GT::SQL::File->open($flv_file_path);
|
|
$result->{$image} = GT::SQL::File->open($image_path);
|
|
$result->{$thumb} = GT::SQL::File->open($thumb_path)
|
|
if ($cfg->{thumbnail_file_field} and $thumb_path);
|
|
|
|
return $result;
|
|
|
|
}
|
|
|
|
sub get_file_path {
|
|
# ---------------------------------------------------------------------------
|
|
# return file path of a file column
|
|
#
|
|
my $cfg = Links::Plugins::get_plugin_user_cfg('ConvertVideo');
|
|
my $id = shift;
|
|
my $field_name = shift || "flash_file_field";
|
|
my $field = $cfg->{$field_name};
|
|
my $wantpath = shift || 0;
|
|
my $fh = $DB->table('Links')->file_info($field, $id);
|
|
return { $field_name . "_path" => '' } if (!$fh);
|
|
my $fdir = $fh->File_Directory();
|
|
my $full_path = "$fh";
|
|
my $rel_path = $full_path;
|
|
$rel_path =~ s,$fdir,,;
|
|
$rel_path =~ s,%,%25,g;
|
|
|
|
if ($wantpath) {
|
|
return $rel_path;
|
|
}
|
|
return { $field_name . "_path" => $cfg->{video_url} . $rel_path };
|
|
}
|
|
|
|
sub get_flash_dimension {
|
|
my $cfg = Links::Plugins::get_plugin_user_cfg('ConvertVideo');
|
|
my $width = 320;
|
|
my $height = 240;
|
|
if ($cfg->{flash_dimension} =~ /\s*(\d+)x(\d+)\s*/) {
|
|
($width, $height) = ($1, $2);
|
|
}
|
|
|
|
return { 'video_width' => $width, 'video_height' => $height };
|
|
}
|
|
|
|
sub get_video_max_size {
|
|
my $cfg = Links::Plugins::get_plugin_user_cfg('ConvertVideo');
|
|
my $field_name = $cfg->{video_file_field};
|
|
my %cols = $DB->table('Links')->_file_cols();
|
|
return $cols{$field_name}->{file_max_size};
|
|
}
|
|
|
|
# Always end with a 1.
|
|
1;
|