File Coverage

blib/lib/Video/DVDRip/FilterList.pm
Criterion Covered Total %
statement 42 298 14.0
branch 0 114 0.0
condition 0 48 0.0
subroutine 14 66 21.2
pod 0 9 0.0
total 56 535 10.4


line stmt bran cond sub pod time code
1             # $Id: FilterList.pm 2287 2007-03-17 16:53:44Z joern $
2              
3             #-----------------------------------------------------------------------
4             # Copyright (C) 2001-2006 Jörn Reder .
5             # All Rights Reserved. See file COPYRIGHT for details.
6             #
7             # This module is part of Video::DVDRip, which is free software; you can
8             # redistribute it and/or modify it under the same terms as Perl itself.
9             #-----------------------------------------------------------------------
10              
11             package Video::DVDRip::FilterList;
12 1     1   8 use Locale::TextDomain qw (video.dvdrip);
  1         2  
  1         21  
13              
14 1     1   508 use base Video::DVDRip::Base;
  1         2  
  1         174  
15              
16 1     1   7 use Carp;
  1         3  
  1         140  
17 1     1   6 use strict;
  1         2  
  1         61  
18 1     1   6 use Data::Dumper;
  1         2  
  1         59  
19 1     1   10 use FileHandle;
  1         1  
  1         19  
20              
21 1     1   1145 use Video::DVDRip::CPAN::Scanf;
  1         3  
  1         1552  
22              
23             my $DEBUG = 0;
24              
25             my $FILTER_LIST;
26             my %FILTER_SELECTION_CB = (
27             logo => sub {
28             my %par = @_;
29             my ( $x1, $y1, $x2, $y2, $filter_setting )
30             = @par{ 'x1', 'y1', 'x2', 'y2', 'filter_setting' };
31              
32             $filter_setting->set_value(
33             option_name => 'pos',
34             idx => 0,
35             value => $x1,
36             );
37              
38             $filter_setting->set_value(
39             option_name => 'pos',
40             idx => 1,
41             value => $y1,
42             );
43              
44             1;
45             },
46             logoaway => sub {
47             my %par = @_;
48             my ( $x1, $y1, $x2, $y2, $filter_setting )
49             = @par{ 'x1', 'y1', 'x2', 'y2', 'filter_setting' };
50              
51             $filter_setting->set_value(
52             option_name => 'pos',
53             idx => 0,
54             value => $x1,
55             );
56              
57             $filter_setting->set_value(
58             option_name => 'pos',
59             idx => 1,
60             value => $y1,
61             );
62              
63             $filter_setting->set_value(
64             option_name => 'size',
65             idx => 0,
66             value => $x2 - $x1,
67             );
68              
69             $filter_setting->set_value(
70             option_name => 'size',
71             idx => 1,
72             value => $y2 - $y1,
73             );
74              
75             1;
76             },
77             mask => sub {
78             my %par = @_;
79             my ( $x1, $y1, $x2, $y2, $filter_setting )
80             = @par{ 'x1', 'y1', 'x2', 'y2', 'filter_setting' };
81              
82             $filter_setting->set_value(
83             option_name => 'lefttop',
84             idx => 0,
85             value => $x1,
86             );
87              
88             $filter_setting->set_value(
89             option_name => 'lefttop',
90             idx => 1,
91             value => $y1,
92             );
93              
94             $filter_setting->set_value(
95             option_name => 'rightbot',
96             idx => 0,
97             value => $x2,
98             );
99              
100             $filter_setting->set_value(
101             option_name => 'rightbot',
102             idx => 1,
103             value => $y2,
104             );
105              
106             1;
107             },
108             );
109              
110 0     0 0   sub filters { shift->{filters} }
111 0     0 0   sub set_filters { shift->{filters} = $_[1] }
112              
113             sub get_filter_list {
114 0     0 0   my $class = shift;
115              
116             # cache instance per process
117 0 0         return $FILTER_LIST if $FILTER_LIST;
118              
119 0           my $dir = "$ENV{HOME}/.dvdrip";
120 0           my $filename = "$dir/tc_filter_list";
121              
122 0 0 0       mkdir $dir, 0755 or die "can't create $dir" if not -d $dir;
123              
124 0           my $transcode_modpath = qx[ tcmodinfo -p 2>/dev/null ];
125 0           chomp $transcode_modpath;
126              
127 0 0         $DEBUG && print STDERR "transcode module path: $transcode_modpath\n";
128              
129             # empty list if tcmodinfo not available
130 0 0         return $FILTER_LIST = $class->new() if not $transcode_modpath;
131              
132 0           my $filter_mtime = ( stat($filename) )[9];
133 0           my $transcode_mtime = ( stat($transcode_modpath) )[9];
134 0           my $FilterList_mtime = (
135             stat(
136             $class->search_perl_inc(
137             rel_path => "Video/DVDRip/FilterList.pm"
138             )
139             )
140             )[9];
141              
142             # create new list of no file avaiable or if file
143             # is older than transcode's modpath, or if dvd::rip's
144             # FilterList module is newer.
145 0 0 0       if ( not -f $filename
      0        
146             or $filter_mtime < $transcode_mtime
147             or $filter_mtime < $FilterList_mtime ) {
148 0           $FILTER_LIST = $class->new();
149 0           $FILTER_LIST->scan( modpath => $transcode_modpath );
150 0           $FILTER_LIST->save( filename => $filename );
151 0           return $FILTER_LIST;
152             }
153              
154 0           return $FILTER_LIST = $class->load( filename => $filename );
155             }
156              
157             sub new {
158 0     0 0   my $class = shift;
159              
160 0           my $self = { filters => {}, };
161              
162 0           return bless $self, $class;
163             }
164              
165             sub load {
166 0     0 0   my $class = shift;
167 0           my %par = @_;
168 0           my ($filename) = @par{'filename'};
169              
170 0           my $fh = FileHandle->new;
171 0 0         open( $fh, $filename ) or croak "can't read $filename";
172 0           my $data = join( '', <$fh> );
173 0           close $fh;
174              
175 0           my $filter_list;
176 0           eval($data);
177 0 0         croak "can't load $filename. Perl error: $@" if $@;
178              
179 0           return bless $filter_list, $class;
180             }
181              
182             sub save {
183 0     0 0   my $self = shift;
184 0           my %par = @_;
185 0           my ($filename) = @par{'filename'};
186              
187 0           my $data_sref = $self->get_save_data;
188              
189 0           my $fh = FileHandle->new;
190              
191 0 0         open( $fh, "> $filename" ) or confess "can't write $filename";
192 0           print $fh q{# $Id: FilterList.pm 2287 2007-03-17 16:53:44Z joern $},
193             "\n";
194 0           print $fh
195             "# This file was generated by Video::DVDRip Version $Video::DVDRip::VERSION\n\n";
196              
197 0           print $fh ${$data_sref};
  0            
198 0           close $fh;
199              
200 0           1;
201             }
202              
203             sub get_save_data {
204 0     0 0   my $self = shift;
205              
206 0           my $dd = Data::Dumper->new( [$self], ['filter_list'] );
207 0           $dd->Indent(1);
208 0           $dd->Purity(1);
209 0           my $data = $dd->Dump;
210              
211 0           return \$data;
212             }
213              
214             sub scan {
215 0     0 0   my $self = shift;
216 0           my %par = @_;
217 0           my ($modpath) = @par{'modpath'};
218              
219 0           print STDERR
220             "[filterlist] (re)scanning transcode's module path $modpath...\n";
221              
222 0           my @filter_names = grep !/^(pv|preview)$/,
223 0           map {m!/filter_([^/]+)\.so$!} glob("$modpath/filter_*");
224              
225 0           my %filters;
226 0           foreach my $filter_name (@filter_names) {
227 0           my $filter
228             = Video::DVDRip::Filter->new( filter_name => $filter_name );
229 0 0 0       next if !$filter || !$filter->capabilities;
230 0           $filters{$filter_name} = $filter;
231             }
232              
233 0           $self->set_filters( \%filters );
234              
235 0           1;
236             }
237              
238             sub get_filter {
239 0     0 0   my $self = shift;
240 0           my %par = @_;
241 0           my ($filter_name) = @par{'filter_name'};
242              
243 0 0         $self = $self->get_filter_list if not ref $self;
244              
245 0 0         croak "Filter '$filter_name' unknown"
246             if not exists $self->filters->{$filter_name};
247              
248 0           return $self->filters->{$filter_name};
249             }
250              
251             package Video::DVDRip::Filter;
252 1     1   9 use Locale::TextDomain qw (video.dvdrip);
  1         2  
  1         10  
253              
254 1     1   512 use Carp;
  1         2  
  1         89  
255 1     1   16558 use Text::Wrap;
  1         4445  
  1         1854  
256              
257 0     0     sub filter_name { shift->{filter_name} }
258 0     0     sub desc { shift->{desc} }
259 0     0     sub version { shift->{version} }
260 0     0     sub author { shift->{author} }
261 0     0     sub capabilities { shift->{capabilities} }
262 0     0     sub frames_needed { shift->{frames_needed} }
263 0     0     sub options { shift->{options} }
264 0     0     sub options_by_name { shift->{options_by_name} }
265              
266 0     0     sub can_video { shift->capabilities =~ /V/ }
267 0     0     sub can_audio { shift->capabilities =~ /A/ }
268 0     0     sub can_rgb { shift->capabilities =~ /R/ }
269 0     0     sub can_yuv { shift->capabilities =~ /Y/ }
270 0     0     sub can_multiple { shift->capabilities =~ /M/ }
271              
272 0     0     sub is_pre { shift->capabilities =~ /E/ }
273 0     0     sub is_post { shift->capabilities =~ /O/ }
274 0 0   0     sub is_pre_post { $_[0]->is_pre and $_[0]->is_post }
275              
276             sub new {
277 0     0     my $class = shift;
278 0           my %par = @_;
279 0           my ($filter_name) = @par{'filter_name'};
280              
281 0 0         $DEBUG && print STDERR "Scan: tcmodinfo -i $filter_name ... ";
282              
283 0           my $config;
284 0           eval {
285 0     0     local $SIG{ALRM} = sub { die "alarm" };
  0            
286 0           alarm 2;
287 0           $config = qx[ tcmodinfo -i $filter_name 2>/dev/null ];
288 0           alarm 0;
289             };
290              
291 0 0         if ( $@ ) {
292 0 0         $DEBUG && print STDERR "TIMEOUT\n";
293 0           return;
294             }
295            
296 0 0         $DEBUG && print STDERR "OK\n------\n$config\n------\n";
297              
298 0           my $line;
299 0           my ( %options, @options );
300              
301 0           my ( $desc, $version, $author, $capabilities, $frames_needed );
302 0           my $in_config = 0;
303              
304 0           while ( $config =~ /(.*)/g ) {
305 0           $line = $1;
306 0 0         if ( not $in_config ) {
307 0 0         next if $line !~ /^START/;
308 0           $in_config = 1;
309             }
310 0 0         next if $line !~ /^"/;
311 0 0         if ( not $desc ) {
312 0           my @csv_fields = ( $line =~ /"([^"]+)"/g );
313 0           shift @csv_fields;
314 0           $desc = shift @csv_fields;
315 0           $version = shift @csv_fields;
316 0           $author = shift @csv_fields;
317 0           $capabilities = shift @csv_fields;
318 0           $frames_needed = shift @csv_fields;
319 0           next;
320             }
321              
322 0           my $option = Video::DVDRip::FilterOption->new(
323             config => $line,
324             filter_name => $filter_name,
325             );
326 0 0         return if $option->option_name !~ /^\w+$/;
327 0           $options{ $option->option_name } = $option;
328 0           push @options, $option;
329             }
330              
331 0 0         $capabilities =~ s/O/E/ if $filter_name eq 'logoaway';
332              
333 0           my $self = {
334             filter_name => $filter_name,
335             desc => $desc,
336             version => $version,
337             author => $author,
338             capabilities => $capabilities,
339             frames_needed => $frames_needed,
340             options => \@options,
341             options_by_name => \%options,
342             };
343              
344 0           return bless $self, $class;
345             }
346              
347             sub get_option {
348 0     0     my $self = shift;
349 0           my %par = @_;
350 0           my ($option_name) = @par{'option_name'};
351              
352 0 0         croak "Option '$option_name' unknown for filter '".$self->filter_name."'"
353             if not exists $self->options_by_name->{$option_name};
354              
355 0           return $self->options_by_name->{$option_name};
356             }
357              
358             sub get_info {
359 0     0     my $self = shift;
360              
361 0           $Text::Wrap::columns = 32;
362              
363 0           my @info = (
364             [ "Name", wrap( "", "", $self->filter_name ), ],
365             [ "Desc", wrap( "", "", $self->desc ), ],
366             [ "Version", wrap( "", "", $self->version ), ],
367             [ "Author(s)", wrap( "", "", $self->author ), ],
368             );
369              
370 0           my $info;
371 0 0         $info .= "Video, " if $self->can_video;
372 0 0         $info .= "Audio, " if $self->can_audio;
373 0           $info =~ s/, $//;
374              
375 0           push @info, [ "Type", $info ];
376              
377 0           $info = "";
378 0 0         $info .= "RGB, " if $self->can_rgb;
379 0 0         $info .= "YUV, " if $self->can_yuv;
380 0           $info =~ s/, $//;
381              
382 0           push @info, [ "Color", $info ];
383              
384 0           $info = "";
385 0 0         $info .= "PRE, " if $self->is_pre;
386 0 0         $info .= "POST, " if $self->is_post;
387 0           $info =~ s/, $//;
388 0   0       $info ||= "unknown";
389              
390 0           push @info, [ "Pre/Post", $info ];
391 0 0         push @info, [ "Multiple", ( $self->can_multiple ? "Yes" : "No" ) ];
392              
393 0           return \@info;
394             }
395              
396             sub av_type {
397 0     0     my $self = shift;
398              
399 0           my $info = "";
400 0 0         $info .= __("Video").", " if $self->can_video;
401 0 0         $info .= __("Audio").", " if $self->can_audio;
402 0           $info =~ s/, $//;
403              
404 0           return $info;
405             }
406              
407             sub colorspace_type {
408 0     0     my $self = shift;
409              
410 0 0         return "--" if !$self->can_video;
411            
412 0           my $info = "";
413 0 0         $info .= "RGB, " if $self->can_rgb;
414 0 0         $info .= "YUV, " if $self->can_yuv;
415 0           $info =~ s/, $//;
416              
417 0           return $info;
418             }
419              
420             sub pre_post_type {
421 0     0     my $self = shift;
422              
423 0           my $info = "";
424 0 0         $info .= "PRE, " if $self->is_pre;
425 0 0         $info .= "POST, " if $self->is_post;
426 0           $info =~ s/, $//;
427 0   0       $info ||= "unknown";
428              
429 0           return $info;
430             }
431              
432             sub multiple_type {
433 0     0     my $self = shift;
434 0 0         return $self->can_multiple ? __"Yes" : __"No";
435             }
436              
437             sub get_selection_cb {
438 0     0     my $self = shift;
439              
440 0           return $FILTER_SELECTION_CB{ $self->filter_name };
441             }
442              
443             sub get_dummy_instance {
444 0     0     my $self = shift;
445 0           return Video::DVDRip::FilterSettingsInstance->new (
446             id => -1,
447             filter_name => $self->filter_name
448             );
449             }
450              
451             package Video::DVDRip::FilterOption;
452 1     1   16 use Locale::TextDomain qw (video.dvdrip);
  1         9  
  1         11  
453              
454 1     1   277 use Carp;
  1         2  
  1         78  
455 1     1   5 use Text::Wrap;
  1         2  
  1         707  
456              
457 0     0     sub option_name { shift->{option_name} }
458 0     0     sub desc { shift->{desc} }
459 0     0     sub format { shift->{format} }
460 0     0     sub fields { shift->{fields} }
461 0     0     sub switch { shift->{switch} }
462              
463             sub new {
464 0     0     my $class = shift;
465 0           my %par = @_;
466 0           my ( $config, $filter_name ) = @par{ 'config', 'filter_name' };
467              
468 0           my @csv_fields = ( $config =~ /"([^"]*)"/g );
469              
470 0           my $name = shift @csv_fields;
471 0           my $desc = shift @csv_fields;
472 0           my $format = shift @csv_fields;
473 0           my $default = shift @csv_fields;
474              
475 0           my $switch;
476 0 0         if ( $format eq '' ) {
    0          
477              
478             # on/off only, no value
479 0           push @csv_fields, "0", "1";
480 0           $format = "%B";
481 0           $switch = 1;
482             }
483             elsif ( $format eq '%s' ) {
484 0           push @csv_fields, "", "";
485             }
486              
487             # cpaudio reports '%c' - stupid, %c scans ASCII code
488 0 0         $format = '%s' if $format eq '%c';
489              
490             # logoaway reports '%2x' - stupid, we get spaces this way
491 0           $format =~ s/\%2x/\%02x/g;
492              
493 0           my $scan_format = $format;
494 0           $scan_format =~ s/\%\%//g; # eliminate quoted %
495 0           my $default_format = $format;
496 0           $default_format =~ s/\%\%//g; # eliminate quoted %
497              
498 0           my @field_formats = ( $scan_format =~ /\%(.)/g );
499             my @default_values
500 0           = Video::DVDRip::CPAN::Scanf::sscanf( $default_format, $default );
501              
502 0           my @fields;
503 0           while (@csv_fields) {
504 0           my $range_from = shift @csv_fields;
505 0           my $range_to = shift @csv_fields;
506 0           my $type = shift @field_formats;
507              
508 0           push @fields,
509             Video::DVDRip::FilterOptionField->new(
510             default => shift @default_values,
511             range_from => $range_from,
512             range_to => $range_to,
513             fractional => ( $type eq 'f' ),
514             text => ( $type eq 's' ),
515             );
516             }
517              
518 0 0         print "WARNING: [$filter_name] Option $name has fields left!\n"
519             if @default_values;
520              
521 0           my $self = {
522             option_name => $name,
523             desc => $desc,
524             format => $format,
525             fields => \@fields,
526             switch => $switch,
527             };
528              
529 0           return bless $self, $class;
530             }
531              
532             sub get_wrapped_desc {
533 0     0     my $self = shift;
534              
535 0           local($Text::Wrap::columns) = 24;
536              
537 0           return join( "\n", wrap( "", "", $self->desc ) );
538             }
539              
540             package Video::DVDRip::FilterOptionField;
541 1     1   9 use Locale::TextDomain qw (video.dvdrip);
  1         1  
  1         5  
542              
543 0     0     sub default { shift->{default} }
544 0     0     sub range_from { shift->{range_from} }
545 0     0     sub range_to { shift->{range_to} }
546 0     0     sub fractional { shift->{fractional} }
547 0     0     sub switch { shift->{switch} }
548 0     0     sub checkbox { shift->{checkbox} }
549 0     0     sub combo { shift->{combo} }
550 0     0     sub text { shift->{text} }
551              
552             #-----------------------------------------------------------
553             # checkbox vs. switch
554             # ===================
555             #
556             # Both are checkboxes on the GUI, but the internal
557             # parameter code generation differs:
558             #
559             # switch: the parameter has no option value. It's there or
560             # it's not there.
561             #
562             # checkbox: the parameter has either 0 or 1 as option value.
563             #-----------------------------------------------------------
564              
565             sub new {
566 0     0     my $class = shift;
567 0           my %par = @_;
568 0           my ($default, $range_from, $range_to, $fractional, $switch) =
569             @par{'default','range_from','range_to','fractional','switch'};
570 0           my ($text) =
571             @par{'text'};
572              
573 0           my ( $checkbox, $combo );
574              
575 0 0 0       $range_to = undef
576             if $range_to eq 'oo'
577             or $range_to < $range_from;
578              
579 0 0 0       $range_from = -99999999
580             if $range_from eq ''
581             or $range_from =~ /\D/;
582              
583 0 0 0       $range_to = 99999999
584             if $range_to eq ''
585             or $range_to =~ /\D/;
586              
587 0 0 0       if ( not $fractional and $range_from !~ /\D/ and $range_to !~ /\D/ ) {
      0        
588 0 0 0       if ( $range_from == 0 and $range_to == 1 ) {
    0 0        
      0        
589 0           $checkbox = 1;
590             }
591             elsif ( $range_to ne ''
592             and $range_from ne ''
593             and $range_to - $range_from < 20 ) {
594 0           $combo = 1;
595             }
596             }
597              
598 0           my $self = {
599             default => $default,
600             range_from => $range_from,
601             range_to => $range_to,
602             fractional => $fractional,
603             switch => $switch,
604             checkbox => $checkbox,
605             combo => $combo,
606             text => $text,
607             };
608              
609 0           return bless $self, $class;
610             }
611              
612             sub get_range_text {
613 0     0     my $self = shift;
614              
615 0 0 0       return "Default: " . ( $self->default ? "on" : "off" )
    0          
616             if $self->checkbox
617             or $self->switch;
618 0 0         return "Default: " . $self->default if $self->text;
619              
620 0 0         my $frac = $self->fractional ? " (fractional)" : "";
621              
622 0           my $range_from = $self->range_from;
623 0           my $range_to = $self->range_to;
624              
625 0           foreach my $range ( $range_from, $range_to ) {
626 0 0 0       $range = "WIDTH" if $range eq 'W' or $range eq 'width';
627 0 0 0       $range = "HEIGHT" if $range eq 'H' or $range eq 'height';
628             }
629              
630 0 0         $range_from = "-oo" if $range_from == -99999999;
631 0 0         $range_to = "oo" if $range_to == 99999999;
632              
633 0           my $default = $self->default;
634 0 0         $default = "" if $default eq '';
635              
636 0           my $info = "Valid values$frac: $range_from .. $range_to "
637             . "(Default: $default)";
638              
639 0           return $info;
640             }
641              
642             1;