discourse-legacysite-perl/site/slowtwitch.com/cgi-bin/articles/admin/Plugins/ConvertVideo.pm
2024-06-17 21:49:12 +10:00

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;