Set release details for 2.2.20
[clive.git] / lib / clive / App.pm
blobcb3e8ed62cd7f8179fe8279c48201cc84e52bdff
1 # -*- coding: ascii -*-
2 ###########################################################################
3 # clive, command line video extraction utility.
5 # Copyright 2009 Toni Gundogdu.
7 # This file is part of clive.
9 # clive is free software: you can redistribute it and/or modify it under
10 # the terms of the GNU General Public License as published by the Free
11 # Software Foundation, either version 3 of the License, or (at your option)
12 # any later version.
14 # clive is distributed in the hope that it will be useful, but WITHOUT ANY
15 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16 # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
17 # details.
19 # You should have received a copy of the GNU General Public License along
20 # with this program. If not, see <http://www.gnu.org/licenses/>.
21 ###########################################################################
22 package clive::App;
24 use warnings;
25 use strict;
27 use base 'Class::Singleton';
29 use clive::Log;
30 use clive::Error qw(
31 CLIVE_OK
32 CLIVE_NOTHINGTODO
33 CLIVE_NOSUPPORT
34 CLIVE_READ
36 use clive::Config;
37 use clive::Curl;
38 use clive::Cache;
39 use clive::Exec;
41 sub new {
42 my $class = shift;
43 return bless( {}, $class );
46 sub main {
47 my $self = shift;
49 clive::Config->instance->init;
50 clive::Curl->instance->init;
51 clive::Log->instance->init;
52 clive::Cache->instance->init;
53 clive::Exec->instance->init;
55 _parseInput();
57 exit( clive::Log->instance->returnCode );
60 sub _parseInput {
61 my $self = shift;
63 $self->{queue} = [];
65 my $config = clive::Config->instance->config;
67 # Read from recall file.
68 if ( $config->{recall} and -e $config->{recall_file} ) {
69 if ( open( my $fh, "<", $config->{recall_file} ) ) {
70 _parseLine( $self, $_ ) while (<$fh>);
71 close($fh);
73 else {
74 clive::Log->instance->errn( CLIVE_READ,
75 "$config->{recall_file}: $!" );
79 # From cache (grep).
80 if ( $config->{cache_grep} ) {
81 my $cache = clive::Cache->instance;
82 foreach ( @{ $cache->grepQueue } ) {
83 push( @{ $self->{queue} }, $_ );
87 # Use argv.
88 _parseLine( $self, $_ ) foreach (@ARGV);
90 # Default to STDIN.
91 if ( scalar( @{ $self->{queue} } ) == 0
92 && scalar( @ARGV == 0 ) )
94 _parseLine( $self, $_ ) while (<STDIN>);
97 my $cache = clive::Cache->instance;
98 my $curl = clive::Curl->instance;
99 my $log = clive::Log->instance;
101 require clive::HostFactory;
102 require clive::Video;
104 foreach ( @{ $self->{queue} } ) {
105 my $host = clive::HostFactory->new($_);
106 if ($host) {
107 my $props = clive::Video->new;
108 $props->page_link($_);
110 my $rc = 0;
112 # Read from cache.
113 if ( $cache->enabled() && $config->{cache_read} ) {
114 $rc = $cache->read( \$props );
115 if ( $rc == 1 && $props->video_format() ne $config->{format} )
118 # Cache: video format != requested format -> re-fetch.
119 $rc = 0;
123 # Cache failed or record did not exist. Fetch the video page.
124 if ( $rc == 0 ) {
125 my $content;
127 $rc = $curl->fetchToMem( $props->page_link, \$content );
129 if ( $rc == 0 ) {
131 $props->page_title( \$content );
132 $rc = $host->parsePage( \$content, \$props );
134 if ( $rc == 0 ) {
135 $rc = $curl->queryFileLength( \$props );
136 if ( $rc == 0 ) {
137 $props->video_format( $config->{format} );
138 $cache->write( \$props );
144 # Cache record found.
145 else {
146 $log->out("cache $_ ...done\n");
147 $rc = 0;
150 # If everything went OK so far, proceed to extract etc.
151 if ( $rc == 0 ) {
153 push( @{ $self->{passed_queue} }, $_ );
155 $props->formatOutputFilename;
157 if ( $props->nothing_todo ) {
158 $log->err( CLIVE_NOTHINGTODO,
159 "file already retrieved; nothing todo" );
160 clive::Exec->instance->queue( \$props );
161 next;
164 if ( $config->{no_extract} ) {
165 $props->printVideo;
167 elsif ( $config->{emit_csv} ) {
168 $props->emitCSV;
170 elsif ( $config->{stream_pass} ) {
171 clive::Exec->instance->passStream( \$props );
173 else {
174 if ( $config->{print_fname} ) {
175 $props->printVideo;
177 if ( $curl->fetchToFile( \$props ) == 0 ) {
178 clive::Exec->instance->queue( \$props );
182 elsif ( $rc == 0xff ) {
184 # Purpose: dump (cctv) video segment links to terminal.
185 # a.k.a. "One link leads to many video segment links."
186 # a.k.a. "Something we never had even thought of."
187 # We skip quite a few steps with this chunk of joy.
190 else {
191 $log->err( CLIVE_NOSUPPORT, "no support: $_" );
195 clive::Exec->instance->runExec;
197 # Update recall file.
198 if ( $log->returnCode == CLIVE_OK ) {
199 if ( open( my $fh, ">", $config->{recall_file} ) ) {
200 print( $fh "$_\n" ) foreach ( @{ $self->{passed_queue} } );
201 close($fh);
203 else {
204 $log->errn( CLIVE_READ, "$config->{recall_file}: $!" );
209 sub _parseLine {
210 my ( $self, $ln ) = @_;
212 return if $ln =~ /^$/;
213 return if $ln =~ /^#/;
215 chomp $ln;
217 $ln = "http://$ln"
218 if $ln !~ m{^[a-z]+://}i;
220 # Youtube: youtube-nocookie.com -> youtube.com.
221 $ln =~ s/-nocookie//;
223 # Translate host specific embedded link to video page link.
224 $ln =~ s!/v/!/watch?v=!i; # youtube
225 $ln =~ s!googleplayer.swf!videoplay!i; # googlevideo
226 $ln =~ s!/pl/!/videos/!i; # sevenload
227 $ln =~ s!/e/!/view?i=!i; # liveleak
229 if ($ln =~ /\/swf\//) { # dailymotion
230 $ln =~ s!/video/!/!;
231 $ln =~ s!/swf/!/video/!;
234 # Lastfm demystifier.
235 if ( $ln =~ /last\.fm/ ) {
236 $ln =~ /\+1\-(.+)/;
237 if ( !$1 ) {
238 clive::Log->instance->err( CLIVE_NOSUPPORT, "no support: $ln" );
239 return;
241 $ln = "http://youtube.com/watch?v=$1";
244 push( @{ $self->{queue} }, $ln );
247 sub _printHosts {
248 require clive::HostFactory;
249 clive::HostFactory->dumpHosts();
250 exit(CLIVE_OK);
255 # There must be some kinda way out of here.