1#!/usr/bin/env perl 2 3use Net::UPnP::ControlPoint; 4use Net::UPnP::AV::MediaServer; 5 6use Shell qw(curl ffmpeg); 7 8#curl('--version'); 9#ffmpeg('-version'); 10 11#------------------------------ 12# program info 13#------------------------------ 14 15$program_name = 'DLNA Media Sever 2 Vodcast'; 16$copy_right = 'Copyright (c) 2005 Satoshi Konno'; 17$script_name = 'dms2vodcast.pl'; 18$script_version = '1.0.3'; 19 20#------------------------------ 21# global variables 22#------------------------------ 23 24@dms_content_list = (); 25 26#------------------------------ 27# command option 28#------------------------------ 29 30$rss_file_name = ""; 31$base_directory = "./"; 32$rss_base_url= "http://localhost"; 33$rss_description = "CyberGarage Vodcast"; 34$rss_language = ""; 35$rss_link= ""; 36$rss_title = "CyberGarage"; 37$requested_count = 0; 38$mp4_format = 'ipod'; 39$title_regexp = ""; 40 41@command_opt = ( 42['-b', '--base-url', '<url>', 'Set the base url in the item link property of the output RSS file'], 43['-B', '--base-directory', '<url>', 'Set the base directory to output the RSS file and the MPEG4 files'], 44['-d', '--rss-description', '<description>', 'Set the description tag in the output RSS file'], 45['-g', '--rss-language', '<language>', 'Set the language tag in the output RSS file'], 46['-h', '--help', '', 'This is help text.'], 47['-l', '--rss-link', '<link>', 'Set the link tag in the output RSS file'], 48['-r', '--requested-count', '<url>', 'Set the max request count to the media server contents'], 49['-t', '--rss-title', '<file>', 'Set the title tag in the output RSS file'], 50['-f', '--mp4-format', '<ipod | psp>', 'Set the MPEG4 format'], 51['-s', '--search-title', '<regular expression>', 'Set the regular expression of the content titles by UTF-8'], 52); 53 54sub is_command_option { 55 ($opt) = @_; 56 for ($n=0; $n<@command_opt; $n++) { 57 if ($opt eq $command_opt[$n][0] || $opt eq $command_opt[$n][1]) { 58 return $n; 59 } 60 } 61 return -1; 62} 63 64#------------------------------ 65# main (pase command line) 66#------------------------------ 67 68for ($i=0; $i<(@ARGV); $i++) { 69 $opt = $ARGV[$i]; 70 $opt_num = is_command_option($opt); 71 $opt_short_name = ''; 72 if ($opt_num < 0) { 73 if ($opt =~ m/^-/) { 74 print "$script_name : option $opt is unknown\n"; 75 print "$script_name : try \'$script_name --help\' for more information \n"; 76 exit 1; 77 } 78 } 79 else { 80 $opt_short_name = $command_opt[$opt_num][0]; 81 } 82 if ($opt_short_name eq '-h') { 83 print "Usage : $script_name [options...] <output RSS file name>\n"; 84 print "Options : \n"; 85 $max_opt_output_len = 0; 86 for ($n=0; $n<@command_opt; $n++) { 87 $opt_output_len = length("$command_opt[$n][0]\/$command_opt[$n][1] $command_opt[$n][2]"); 88 if ($max_opt_output_len <= $opt_output_len) { 89 $max_opt_output_len = $opt_output_len; 90 } 91 } 92 for ($n=0; $n<@command_opt; $n++) { 93 $opt_output_str = "$command_opt[$n][0]\/$command_opt[$n][1] $command_opt[$n][2]"; 94 print $opt_output_str; 95 for ($j=0; $j<($max_opt_output_len-length($opt_output_str)); $j++) { 96 print " "; 97 } 98 print " $command_opt[$n][3]\n"; 99 } 100 exit 1; 101 } elsif ($opt_short_name eq '-b') { 102 $rss_base_url = $ARGV[++$i]; 103 } elsif ($opt_short_name eq '-B') { 104 $base_directory = $ARGV[++$i]; 105 } elsif ($opt_short_name eq '-d') { 106 $rss_description = $ARGV[++$i]; 107 } elsif ($opt_short_name eq '-g') { 108 $rss_language = $ARGV[++$i]; 109 } elsif ($opt_short_name eq '-l') { 110 $rss_link = $ARGV[++$i]; 111 } elsif ($opt_short_name eq '-r') { 112 $requested_count = $ARGV[++$i]; 113 } elsif ($opt_short_name eq '-t') { 114 $rss_title = $ARGV[++$i]; 115 } elsif ($opt_short_name eq '-f') { 116 $mp4_format = $ARGV[++$i]; 117 if ($mp4_format ne 'ipod' && $mp4_format ne 'psp') { 118 print "Unkown MPEG4 format : $mp4_format !!\n"; 119 exit 1; 120 } 121 } elsif ($opt_short_name eq '-s') { 122 $title_regexp = $ARGV[++$i]; 123 } else { 124 $rss_file_name = $opt; 125 } 126} 127 128if (length($rss_file_name) <= 0) { 129 print "$script_name : Must specify a output RSS file name\n"; 130 print "$script_name : try \'$script_name --help\' for more information \n"; 131 exit 1 ; 132} 133 134print "$program_name (v$script_version), $copy_right\n"; 135print "Output RSS file name = $rss_file_name\n"; 136print " title : $rss_title\n"; 137print " description : $rss_description\n"; 138print " language : $rss_language\n"; 139print " base url : $rss_base_url\n"; 140print " base directory : $base_directory\n"; 141print " requested_count : $requested_count\n"; 142print " mp4_format : $mp4_format\n"; 143print " search regexp : $title_regexp\n"; 144 145#------------------------------ 146# main 147#------------------------------ 148 149my $obj = Net::UPnP::ControlPoint->new(); 150 151$retry_cnt = 0; 152@dev_list = (); 153while (@dev_list <= 0 || $retry_cnt > 5) { 154# @dev_list = $obj->search(st =>'urn:schemas-upnp-org:device:MediaServer:1', mx => 10); 155 @dev_list = $obj->search(st =>'upnp:rootdevice', mx => 3); 156 $retry_cnt++; 157} 158 159$devNum= 0; 160foreach $dev (@dev_list) { 161 $device_type = $dev->getdevicetype(); 162 if ($device_type ne 'urn:schemas-upnp-org:device:MediaServer:1') { 163 next; 164 } 165 unless ($dev->getservicebyname('urn:schemas-upnp-org:service:ContentDirectory:1')) { 166 next; 167 } 168 print "[$devNum] : " . $dev->getfriendlyname() . "\n"; 169 $mediaServer = Net::UPnP::AV::MediaServer->new(); 170 $mediaServer->setdevice($dev); 171 #@content_list = $mediaServer->getcontentlist(ObjectID => 0, RequestedCount => $requested_count); 172 @content_list = $mediaServer->getcontentlist(ObjectID => 0); 173 #print "content_list = @content_list\n"; 174 foreach $content (@content_list) { 175 parse_content_directory($mediaServer, $content); 176 } 177 $devNum++; 178} 179 180#------------------------------ 181# Output RSS file 182#------------------------------ 183 184if (@dms_content_list <= 0) { 185 print "Couldn't find video contents !!\n"; 186 exit 1; 187} 188 189$output_rss_filename = $base_directory . $rss_file_name; 190 191open(RSS_FILE, ">$output_rss_filename") || die "Couldn't open the specifed output file($output_rss_filename)\n"; 192 193$rss_header = <<"RSS_HEADER"; 194<?xml version="1.0" encoding="utf-8"?> 195<rss xmlns:itunes="http://www.itunes.com/DTDs/Podcast-1.0.dtd" version="2.0"> 196<channel> 197<title>$rss_title</title> 198<language>$rss_language</language> 199<description>$rss_description</description> 200<link>$rss_link</link> 201RSS_HEADER 202print RSS_FILE $rss_header; 203 204foreach $content (@dms_content_list){ 205 $title = $content->{'title'}; 206 $fname = $content->{'file_name'}; 207 $fsize = $content->{'file_size'}; 208 209$mp4_link = $rss_base_url . $fname; 210$mp4_item = <<"RSS_MP4_ITEM"; 211<item> 212<title>$title</title> 213<guid isPermalink="false">$mp4_link</guid> 214<enclosure url="$mp4_link" length="$fsize" type="video/mp4" /> 215</item> 216RSS_MP4_ITEM 217 print RSS_FILE $mp4_item; 218} 219 220$rss_footer = <<"RSS_FOOTER"; 221</channel> 222</rss> 223RSS_FOOTER 224print RSS_FILE $rss_footer; 225 226 close(RSS_FILE); 227 228$rss_outputed_items = @dms_content_list; 229print "Outputed $rss_outputed_items RSS items to $output_rss_filename\n"; 230 231#------------------------------ 232# parse_content_directory 233#------------------------------ 234 235sub parse_content_directory { 236 ($mediaServer, $content) = @_; 237 my $objid = $content->getid(); 238 239 if ($content->isitem()) { 240 my $title = $content->gettitle(); 241 my $mime = $content->getcontenttype(); 242 if ( ($mime =~ m/video/) && ( (length($title_regexp) == 0) || ($title =~ m/$title_regexp/) ) ) { 243 my $dms_content_count = @dms_content_list; 244 if ($requested_count == 0 || $dms_content_count < $requested_count) { 245 my $mp4_content = mpeg2tompeg4($mediaServer, $content); 246 if (defined($mp4_content)) { 247 push(@dms_content_list, $mp4_content); 248 } 249 } 250 } 251 } 252 253 unless ($content->iscontainer()) { 254 return; 255 } 256 257 my @child_content_list = $mediaServer->getcontentlist(ObjectID => $objid ); 258 259 if (@child_content_list <= 0) { 260 return; 261 } 262 263 foreach my $child_content (@child_content_list) { 264 parse_content_directory($mediaServer, $child_content); 265 } 266} 267 268#------------------------------ 269# mpeg2tompeg4 270#------------------------------ 271 272sub mpeg2tompeg4 { 273 ($mediaServer, $content) = @_; 274 my $objid = $content->getid(); 275 my $title = $content->gettitle(); 276 my $url = $content->geturl(); 277 278 print "[$objid] $title ($url)\n"; 279 280 my $dev = $mediaServer->getdevice(); 281 my $dev_friendlyname = $dev->getfriendlyname(); 282 my $dev_udn = $dev->getudn(); 283 $dev_udn =~ s/:/-/g; 284 285 my $filename_body = $dev_friendlyname . "_" . $dev_udn . "_" . $objid; 286 $filename_body =~ s/ //g; 287 $filename_body =~ s/\//-/g; 288 289 my $mpeg2_file_name = $filename_body . ".mpeg"; 290 my $mpeg4_file_name = $filename_body . "_" . $mp4_format . ".m4v"; 291 my $output_mpeg4_file_name = $base_directory . $mpeg4_file_name; 292 293 if (!(-e $output_mpeg4_file_name)) { 294 $curl_opt = "\"$url\" -o \"$mpeg2_file_name\""; 295 print "curl $curl_opt\n"; 296 curl($curl_opt); 297 298 if ($mp4_format eq 'psp') { 299 $ffmpeg_opt = "-y -i \"$mpeg2_file_name\" -bitexact -fixaspect -s 320x240 -r 29.97 -b 768 -ar 24000 -ab 32 -f psp \"$output_mpeg4_file_name\""; 300 } 301 else { 302 $ffmpeg_opt = "-y -i \"$mpeg2_file_name\" -bitexact -fixaspect -s 320x240 -r 29.97 -b 850 -acodec aac -ac 2 -ar 44100 -ab 64 -f mp4 \"$output_mpeg4_file_name\""; 303 } 304 305 print "ffmpeg $ffmpeg_opt\n"; 306 ffmpeg($ffmpeg_opt); 307 308 unlink($mpeg2_file_name); 309 } 310 311 if (!(-e $output_mpeg4_file_name)) { 312 return undef; 313 } 314 315 my $mpeg4_file_size = -s $output_mpeg4_file_name; 316 317 if ($mpeg4_file_size <= 0) { 318 return undef; 319 } 320 321 my %info = ( 322 'objid' => $objid, 323 'title' => $title, 324 'file_name' => $mpeg4_file_name, 325 'file_size' => $mpeg4_file_size, 326 ); 327 328 return \%info; 329} 330 331exit 0; 332 333