rewrite description, minor tweaks to manual.
[umph.git] / bin / umph
blob852ae45f057e41457bd22b29d50be11c37df441f
1 #!/usr/bin/perl
2 # -*- coding: ascii -*-
5 # Copyright (C) 2010 Toni Gundogdu <legatvs@gmail.com>.
7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
21 use warnings;
22 use strict;
24 binmode( STDOUT, ":utf8" );
25 binmode( STDERR, ":utf8" );
27 use Getopt::ArgvFile( home => 1, startupFilename => [qw(.umphrc)] );
28 use Getopt::Long qw(:config bundling);
30 my $VERSION = "0.1.1";
31 my %config;
32 my @entries;
33 my $done = 0;
35 init();
36 main();
37 exit 0;
39 sub init {
40 GetOptions(
41 \%config,
42 'interactive|i',
43 'version|v' => \&print_version,
44 'help|h' => \&print_help,
45 ) or exit(1);
48 sub print_version {
49 print
50 "umph $VERSION. Copyright (C) 2010 Toni Gundogdu. License: GNU GPL version 3
51 or later This is free software; see the source for copying conditions. There
52 is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR
53 PURPOSE.
55 exit(0);
58 sub print_help {
59 require Pod::Usage;
60 Pod::Usage::pod2usage( -exitstatus => 0, -verbose => 1 );
63 sub main {
65 print_help if scalar @ARGV == 0;
67 my $req_body = "http://gdata.youtube.com/feeds/api/playlists/";
69 my $url = $ARGV[0];
71 if ( $url =~ /^http:/i ) {
73 # Looks like an URL.
74 if ( $url =~ /view_play_list\?p=(.*?)$/i ) {
75 $url = "$req_body$1";
78 else {
80 # Assume playlist id.
81 $url = "$req_body$url";
83 $url .= "?v=2";
85 print STDERR "Fetch $url ...\n";
87 require XML::DOM;
89 my $p = new XML::DOM::Parser;
90 my $doc = $p->parsefile($url);
91 my $root = $doc->getDocumentElement;
93 print STDERR ":: Parse playlist for video entries ...\n";
95 for my $entry ( $root->getElementsByTagName("entry") ) {
97 my $title = to_item( $entry, "title" )->getFirstChild->getNodeValue;
99 my $link
100 = to_item( $entry, "link" )->getAttributeNode("href")->getValue;
102 my %data = ( title => $title, url => $link, selected => 1 );
103 push @entries, \%data;
106 $doc->dispose;
108 print STDERR "::: Found " . scalar @entries . " video entries\n";
110 prompt() if $config{interactive};
112 foreach (@entries) {
113 print "$_->{url}\n" if $_->{selected} || !$config{interactive};
117 sub to_item {
118 my ( $entry, $name ) = @_;
119 return $entry->getElementsByTagName($name)->item(0);
122 sub prompt {
123 my $p = "(umph) ";
124 my %cmds = (
125 'h' => \&help,
126 'q' => \&quit,
127 'l' => \&list,
128 'd' => \&dump,
129 'a' => \&select_all,
130 'n' => \&select_none,
131 'r' => \&revert_selection,
133 print STDERR "Enter prompt\nNote: all videos selected by default\n";
134 print STDERR "Type \"help\" to get a list of commands\n";
135 while ( !$done ) {
136 print STDERR $p;
137 my $ln = <STDIN>;
138 next if !$ln;
139 chomp $ln;
140 if ( $ln =~ /(\d+)/ ) {
141 toggle_number($1);
143 else {
144 next if $ln !~ /(\w)/;
145 $cmds{$1}() if defined $cmds{$1};
150 sub toggle_number {
151 my $i = (shift) - 1;
152 if ( $i >= 0 && exists $entries[$i] ) {
153 $entries[$i]->{selected} = !$entries[$i]->{selected};
154 list();
156 else {
157 printf STDERR "error: out of range\n";
161 sub help {
162 print STDERR "
163 Commands:
164 help .. this
165 list .. display playlist entries (numbered, *=selected)
166 all .. select all videos
167 none .. select none
168 revert .. revert selection
169 (number) .. toggle (select, unselect) video, see list output
171 dump .. dump selected video urls to stdout and exit
172 quit .. terminate program
174 Command name abbreviations are allowed, e.g. \"a\" instead of \"all\".
178 sub quit {
179 exit 0;
182 sub list {
183 my $i = 0;
184 foreach (@entries) {
185 printf STDERR "%2d: $_->{title}%s\n", ++$i, $_->{selected} ? "*" : "";
189 sub dump {
190 $done = 1;
193 sub select_all {
194 $_->{selected} = 1 foreach @entries;
195 list();
198 sub select_none {
199 $_->{selected} = 0 foreach @entries;
200 list();
203 sub revert_selection {
204 $_->{selected} = !$_->{selected} foreach @entries;
205 list();
208 __END__
210 =head1 NAME
212 umph - Youtube video playlist parser for cclive and alike tools
214 =head1 SYNOPSIS
216 umph [options] [URL|PLAYLIST_ID]
218 =head1 DESCRIPTION
220 umph is a command line tool for parsing Youtube playlists. It prints
221 playlist video links, each separated with a newline, to the standard
222 output stream, while everything else gets printed to the standard
223 error stream. umph can be run with a simple interactive prompt to
224 select the printed video links.
226 =head1 OPTIONS
228 -h, --help print help and exit
229 -v, --version print version and exit
230 -i, --interactive be interactive, default is no
232 =head1 OPTION DESCRIPTIONS
234 =over 4
236 =item B<-h, --help>
238 Print help and exit.
240 =item B<-v, --version>
242 Print version and exit.
244 =item B<-i, --interactive>
246 Be interactive, default is no. Implies the interactive prompt which
247 can be used select the printed video links.
249 =back
251 =head1 EXAMPLES
253 =over 4
255 =item umph "http://www.youtube.com/view_play_list?p=AAF3A1D0CA1E304F"
257 =item umph AAF3A1D0CA1E304F
259 Both fetch and parse the same playlist (identified as "AAF3A1D0CA1E304F").
261 =item umph AAF3A1D0CA1E304F | cclive -f mp4
263 Passes the parsed video links to cclive.
265 =back
267 =head1 EXIT STATUS
269 Exits 0 on success, otherwise 1.
271 =head1 FILES
273 =over 4
275 =item $HOME/.umphrc, for example:
277 echo "--interactive" >> ~/.umphrc
279 =back
281 =head1 OTHER
283 umph depends on XML::DOM which uses LWP::UserAgent to retrieve
284 the playlist data. Note that LWP::UserAgent reads http_proxy
285 environment setting.
287 Project:
288 http://umph.googlecode.com/
290 Development repository:
291 git clone git://repo.or.cz/umph.git
293 =head1 SEE ALSO
295 C<cclive(1)> C<clive(1)>
297 =head1 AUTHOR
299 Toni Gundogdu <legatvs@gmail.com>
301 =cut