|
From: Chilts <and...@us...> - 2005-05-13 12:59:56
|
Update of /cvsroot/pollpod/pollpod/src In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv4322/src Modified Files: pollpod.pl Log Message: - added ability for program to first read /etc/pollpod.ini, then ~/.pollpod.ini, then an ability to override with new command line options - added sample.ini file for help and slight changes in pollpod.ini - a lot of re-arrangment to be nicer Index: pollpod.pl =================================================================== RCS file: /cvsroot/pollpod/pollpod/src/pollpod.pl,v retrieving revision 1.3 retrieving revision 1.4 diff -u -d -r1.3 -r1.4 --- pollpod.pl 8 May 2005 10:32:36 -0000 1.3 +++ pollpod.pl 13 May 2005 12:59:44 -0000 1.4 @@ -27,89 +27,166 @@ use IO::File; use LWP::Simple; use XML::Simple; +use Getopt::Mixed "nextOption"; -my $cfg_file = "$ENV{HOME}/.pollpod.ini"; +my @options = qw( PodCastSaveDir + SaveLatestOnly + TestOnly + CatchUpAll + MakeDirs + FieldForFilename + NormaliseFilenames + PrecedeFilenamesWithDate + MakeDateLinks + DateDir + CreateM3UPlayList + M3UPlayListFile + SaveRssXmlFeeds + DumpParsedXml ); + +my $optionDesc = qq{s=s save-dir>s + l latest>l + t test-only>t + c catchup>c + m make-dirs>m + f=s filename>f + n normalise>n + p date-precede>p + d date-links>d + b=s date-dir>b + u create-m3u>u + e=s m3u-playlist-file>e + save-rssxml-feeds + dump-parsed-xml + config=s + h help>h +}; + +my $cmdLineToCfg = { + s => 'PodCastSaveDir', + l => 'SaveLatestOnly', + t => 'TestOnly', + c => 'CatchUpAll', + m => 'MakeDirs', + f => 'FieldForFilename', + n => 'NormaliseFilenames', + p => 'PrecedeFilenamesWithDate', + d => 'MakeDateLinks', + b => 'DateDir', + u => 'CreateM3UPlayList', + e => 'M3UPlayListFile', + 'save-rssxml-feeds' => 'SaveRssXmlFeeds', + 'dump-parsed-xml' => 'DumpParsedXml', + 'config' => 'ConfigFile', + h => 'help', +}; ## ---------------------------------------------------------------------------- MAIN: { - line('='); + my ($defaultCfg, $userCfg, $cmdLineCfg); - my ($data, $cfg); - $cfg = Config::IniFiles->new( -file => $cfg_file ); + # read the default config file, the command line, and the user (specified) config + $defaultCfg = readIniFileOptions('/etc/pollpod.ini'); + $cmdLineCfg = readCmdLineOptions(); + if ( defined $cmdLineCfg->{ConfigFile} ) { + $userCfg = readIniFileOptions($cmdLineCfg->{ConfigFile}); + } else { + $userCfg = readIniFileOptions("$ENV{HOME}/.pollpod.ini"); + } - $data = { - feeds => [], - }; - title('Reading in Options'); - readIniOptions($data, $cfg); + # see if they require help + if ( exists $cmdLineCfg->{help} ) { + usage(); + exit 2; + } + + # hierarchically save all the options + my $cfg = {}; + %$cfg = (%$defaultCfg, %$userCfg, %$cmdLineCfg); + + #print Data::Dumper->Dump( [$defaultCfg, $userCfg, $cmdLineCfg], + # ['default', 'user', 'cmdline'] ); + + # check the validity of the overall options + line('='); title('Validating Options'); - unless ( validateOptions($data) ) { - exit 0; + my $msg = validateOptions( $cfg ); + if ( defined $msg ) { + print STDERR $msg; + usage(); + exit 2; } + + # set up the main data structures for use in the program + my $data = { + feeds => [], + opts => $cfg, + }; + title('Reading in Feeds'); - readIniFeeds($data, $cfg); + readIniFeeds($data, "$ENV{HOME}/.pollpod.ini"); - # print Data::Dumper->Dumper([$data], ['data']); + # print Data::Dumper->Dump([$data], ['data']); title('Checking Save Directories'); checkSaveDirs($data) - if $data->{Options}{MakeDirs} eq 'yes'; + if $data->{opts}{MakeDirs} eq 'yes'; title('Fetching All Feeds'); fetchAllFeeds($data); title('Saving RSS XML Feeds'); saveRssXmlFeeds($data) - if $data->{Options}{SaveRssXmlFeeds} eq 'yes'; + if $data->{opts}{SaveRssXmlFeeds} eq 'yes'; title('Parsing Feeds'); parseFeeds($data); title('Dumping Parsed XML'); dumpParsedXml($data) - if $data->{Options}{DumpParsedXml} eq 'yes'; + if $data->{opts}{DumpParsedXml} eq 'yes'; title('Extracting Items'); extractItemsWithPodCasts($data); title('Extracting filenames'); - extractFilename($data, $data->{Options}{FieldForFilename}); + extractFilename($data, $data->{opts}{FieldForFilename}); title('Normalising Filenames'); normaliseFilenames($data) - if $data->{Options}{NormaliseFilenames} eq 'yes'; + if $data->{opts}{NormaliseFilenames} eq 'yes'; title('Preceeding Filenames with Date'); precedeFilenamesWithDate($data) - if $data->{Options}{PrecedeFilenamesWithDate} eq 'yes'; + if $data->{opts}{PrecedeFilenamesWithDate} eq 'yes'; title('Catching Up PodCasts'); catchUpAll($data) - if $data->{Options}{CatchUpAll} eq 'yes'; + if $data->{opts}{CatchUpAll} eq 'yes'; title('Skip Downloaded'); skipDownloaded($data); title('Save Latest Only'); saveLatestOnly($data) - if $data->{Options}{SaveLatestOnly} eq 'yes'; + if $data->{opts}{SaveLatestOnly} eq 'yes'; title('Testing Only'); testOnly($data) - if $data->{Options}{TestOnly} eq 'yes'; + if $data->{opts}{TestOnly} eq 'yes'; title('Fetching PodCasts'); fetchPodCasts($data); title('Making Date Links'); makeDateLinks($data) - if $data->{Options}{MakeDateLinks} eq 'yes'; + if $data->{opts}{MakeDateLinks} eq 'yes'; title('Creating Play List (m3u)'); createM3UPlayList($data) - if $data->{Options}{CreateM3UPlayList} eq 'yes'; + if $data->{opts}{CreateM3UPlayList} eq 'yes'; title('Finished'); fmsg('Exiting', 'Byeeee...!'); @@ -117,26 +194,94 @@ } ## ---------------------------------------------------------------------------- +# subs + +sub usage +{ + print <<EOF +Usage: $0 [options...] +Options: + -s,save-dir the main directory to save podcasts too + -l,--latest save only the most recent podast from each feed + -t,--test do not save anything (podcasts, catchup files) + -c,--catchup catchup all feeds and all podcasts + -m,--make-dirs create required directories when running + -f,--filename <name> field to create the filenames from ('url' or 'title') + -n,--normalise normalise filenames so spaces and slashes become spaces + -p,--date-precede precede all saved filenames with the date + -d,--date-links create dirs and link from date/YYYYMMDD directory + -b,--date-dir <dir> the directory to save the date directories and links in + -u,--create-m3u creates a file of type 'm3u' in the main dir + -e,--m3u-playlist-file name of the file in the above m3u discussion + --save-rssxml-feeds save the feeds in the main directory as 'feed.xml' + --dump-parsed-xml save the parsed feeds in the main directory as 'feed.dmp' + --config specify a config file to use instead of ~/.pollpod.ini + -h,--help this help screen + +Copyright Andrew Chilton (see http://www.chilts.org/software/) +EOF +} + +## ---------------------------------------------------------------------------- # processes +sub readIniFileOptions +{ + my ($filename) = @_; + + return {} unless -f $filename; + + my $cfg = {}; + + my $config; + $config = eval { Config::IniFiles->new( -file => $filename ); }; + if ( $@ ) { + warn "Couldn't open config file: $@"; + return {}; + } + + foreach ( @options ) { + my $value = $config->val('Options', $_); + $cfg->{$_} = $value if defined $value; + } + + return $cfg; +} + +sub readCmdLineOptions +{ + my $cmdLineCfg = {}; + Getopt::Mixed::init($optionDesc); + while (my ($option, $value, $pretty) = nextOption()) { + $cmdLineCfg->{$option} = $value || 'yes'; + } + Getopt::Mixed::cleanup(); + + my $cfg = {}; + foreach ( keys %$cmdLineCfg ) { + $cfg->{$cmdLineToCfg->{$_}} = $cmdLineCfg->{$_}; + } + + return $cfg; +} + sub readIniOptions { my ($data, $cfg) = @_; - $data->{Options}{PodCastSaveDir} = tilde($cfg->val('Options', 'PodCastSaveDir')); - $data->{Options}{MakeDateLinks} = $cfg->val('Options', 'MakeDateLinks'); - $data->{Options}{TestOnly} = $cfg->val('Options', 'TestOnly'); - $data->{Options}{SaveRssXmlFeeds} = $cfg->val('Options', 'SaveRssXmlFeeds'); - $data->{Options}{DumpParsedXml} = $cfg->val('Options', 'DumpParsedXml'); - $data->{Options}{CatchUpAll} = $cfg->val('Options', 'CatchUpAll'); - $data->{Options}{MakeDirs} = $cfg->val('Options', 'MakeDirs'); - $data->{Options}{Feed} = $cfg->val('Options', 'Feed'); - $data->{Options}{NormaliseFilenames} = $cfg->val('Options', 'NormaliseFilenames'); - $data->{Options}{SaveLatestOnly} = $cfg->val('Options', 'SaveLatestOnly'); - $data->{Options}{PrecedeFilenamesWithDate} = $cfg->val('Options', 'PrecedeFilenamesWithDate'); - $data->{Options}{FieldForFilename} = $cfg->val('Options', 'FieldForFilename'); - $data->{Options}{DateBaseDir} = tilde($cfg->val('Options', 'DateBaseDir')); - $data->{Options}{CreateM3UPlayList} = $cfg->val('Options', 'CreateM3UPlayList'); - $data->{Options}{M3UPlayListFile} = tilde($cfg->val('Options', 'M3UPlayListFile')); + $data->{opts}{PodCastSaveDir} = tilde($cfg->val('Options', 'PodCastSaveDir')); + $data->{opts}{SaveRssXmlFeeds} = $cfg->val('Options', 'SaveRssXmlFeeds'); + $data->{opts}{DumpParsedXml} = $cfg->val('Options', 'DumpParsedXml'); + $data->{opts}{SaveLatestOnly} = $cfg->val('Options', 'SaveLatestOnly'); + $data->{opts}{TestOnly} = $cfg->val('Options', 'TestOnly'); + $data->{opts}{CatchUpAll} = $cfg->val('Options', 'CatchUpAll'); + $data->{opts}{MakeDirs} = $cfg->val('Options', 'MakeDirs'); + $data->{opts}{NormaliseFilenames} = $cfg->val('Options', 'NormaliseFilenames'); + $data->{opts}{PrecedeFilenamesWithDate} = $cfg->val('Options', 'PrecedeFilenamesWithDate'); + $data->{opts}{FieldForFilename} = $cfg->val('Options', 'FieldForFilename'); + $data->{opts}{MakeDateLinks} = $cfg->val('Options', 'MakeDateLinks'); + $data->{opts}{DateDir} = tilde($cfg->val('Options', 'DateDir')); + $data->{opts}{CreateM3UPlayList} = $cfg->val('Options', 'CreateM3UPlayList'); + $data->{opts}{M3UPlayListFile} = tilde($cfg->val('Options', 'M3UPlayListFile')); fmsg('Options', "Read in all the options"); } @@ -144,18 +289,21 @@ sub validateOptions { my ($data) = @_; - # ??? fmsg('Options', "All validated"); - return 1; + return; } sub readIniFeeds { - my ($data, $cfg) = @_; + my ($data, $inifile) = @_; + + return {} unless -f $inifile; my $error=0; my $ok=0; + my $cfg = eval { Config::IniFiles->new( -file => $inifile ); }; + my @feeds = $cfg->val('Feeds', 'Feed'); foreach my $feedline ( @feeds ) { # IniFiles BUG? get 1 undefined element even if no feeds in ini file @@ -169,7 +317,7 @@ } push @{$data->{feeds}}, { feedName => $feedName, - fullDir => "$data->{Options}{PodCastSaveDir}/$feedName", + fullDir => "$data->{opts}{PodCastSaveDir}/$feedName", url => $feed, }; @@ -349,8 +497,6 @@ next; } - # print Data::Dumper->Dump([$item], ['item']); - # save the item we have left to the podcasts push @{$feed->{podcasts}}, { title => $item->{title}, @@ -449,10 +595,10 @@ my $no_dir=0; my $ok=0; - if ( check_dir_exists($data->{Options}{PodCastSaveDir}) ) { - tmsg('SaveDirExists', $data->{Options}{PodCastSaveDir}); + if ( check_dir_exists($data->{opts}{PodCastSaveDir}) ) { + tmsg('SaveDirExists', $data->{opts}{PodCastSaveDir}); } else { - tmsg('NoSaveDir', " $data->{Options}{PodCastSaveDir} - couldn't create"); + tmsg('NoSaveDir', " $data->{opts}{PodCastSaveDir} - couldn't create"); foreach my $feed ( @{$data->{feeds}} ) { $feed->{done} = 1; } @@ -662,18 +808,18 @@ return; } - if ( check_dir_exists($data->{Options}{DateBaseDir}) ) { - tmsg('DateDirExists', "$data->{Options}{DateBaseDir}"); + if ( check_dir_exists($data->{opts}{DateDir}) ) { + tmsg('DateDirExists', "$data->{opts}{DateDir}"); } else { - tmsg('NoDateDir', " $data->{Options}{DateBaseDir} - couldn't create"); + tmsg('NoDateDir', " $data->{opts}{DateDir} - couldn't create"); return; } my $date = get_date(); - if ( check_dir_exists("$data->{Options}{DateBaseDir}/$date") ) { - tmsg('DateDirExists', "$data->{Options}{DateBaseDir}/$date"); + if ( check_dir_exists("$data->{opts}{DateDir}/$date") ) { + tmsg('DateDirExists', "$data->{opts}{DateDir}/$date"); } else { - tmsg('NoDateDir', " $data->{Options}{DateBaseDir}/$date - couldn't create"); + tmsg('NoDateDir', " $data->{opts}{DateDir}/$date - couldn't create"); return; } @@ -688,15 +834,15 @@ next if $podcast->{done}; # see if there already is a symlink - if ( -l "$data->{Options}{DateBaseDir}/$date/$podcast->{filename}" ) { + if ( -l "$data->{opts}{DateDir}/$date/$podcast->{filename}" ) { tmsg( 'ExistsAlready', - "$data->{Options}{DateBaseDir}/$date/$podcast->{filename}" ); + "$data->{opts}{DateDir}/$date/$podcast->{filename}" ); $exists++; next; } # link to downloaded file - unless ( symlink "$feed->{fullDir}/$podcast->{filename}", "$data->{Options}{DateBaseDir}/$date/$podcast->{filename}" ) { + unless ( symlink "$feed->{fullDir}/$podcast->{filename}", "$data->{opts}{DateDir}/$date/$podcast->{filename}" ) { tmsg( 'Failed', "Linking $date/ -> $podcast->{filename}" ); $failed++; @@ -719,7 +865,7 @@ { my ($data) = @_; - my $filename = $data->{Options}{M3UPlayListFile}; + my $filename = $data->{opts}{M3UPlayListFile}; unless ( open(PLAYLIST, ">$filename") ) { tmsg( 'Failed', "Can't open file $filename for writing: $!" ); @@ -755,21 +901,13 @@ sub title { my ($title) = @_; - print "--- $title ---", ('-' x (71-length($title))); - print "\n"; + print "--- $title ---", '-' x (71-length($title)), "\n"; } sub line { my ($char) = @_; - print ($char x 79); - print "\n"; -} - -sub dump_feed -{ - my ($ref) = @_; - print Data::Dumper->Dump([$ref], ['ref']); + print $char x 79, "\n"; } sub check_dir_exists |