podget-0.6.9/0000700000175000017500000000000012067134051012403 5ustar lowkeylowkeypodget-0.6.9/Makefile0000644000175000017500000000111312064171777014066 0ustar lowkeylowkeyprefix ?= /usr/ all: podget podget.7.gz changelog.gz podget.7.gz: DOC/podget.7 gzip -9 < $< > DOC/$@ changelog.gz: Changelog gzip -9 < $< > $@ install: podget podget.7.gz mkdir -p $(DESTDIR)$(prefix)/bin/ install -m 755 podget $(DESTDIR)$(prefix)/bin/podget mkdir -p $(DESTDIR)$(prefix)/share/man/man7/ install -m 644 DOC/podget.7.gz $(DESTDIR)$(prefix)/share/man/man7/podget.7.gz mkdir -p $(DESTDIR)$(prefix)/share/doc/podget install -m 444 changelog.gz $(DESTDIR)$(prefix)/share/doc/podget/changelog.gz distclean: clean clean: rm -f DOC/podget.7.gz rm -f changelog.gz podget-0.6.9/COPYING0000644000175000017500000004376011742322256013467 0ustar lowkeylowkey This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. podget-0.6.9/Changelog0000644000175000017500000002143212067132322014230 0ustar lowkeylowkeypodget (0.6.9) unstable; urgency=low * Update to filename_formatfix to correct for "?referrer=rss" being added to all filenames downloaded from Vimcasts.org. Bug submitted by Andi (apoisel) on Dec 19, 2012. Thanks. * Update to use mktemp to create temporary file for playlist sorting. If mktemp is not available, the script falls back to trying to use tempfile. Bug submitted by Scheini-72 on Aug 20, 2012. Thanks. -- Dave Vehrs Thu, 27 Dec 2012 14:51:20 -0700 podget (0.6.8) unstable; urgency=low * Updates for Debian Package. * Fixed version string in podget to be 0.6.8 -- Dave Vehrs Sat, 2 Dec 2012 11:29:30 -0700 podget (0.6.7) unstable; urgency=low * Fixed Bug #3146457 reported on Sourceforge by oaolsen. Fixes urls with ampersands in them. Thanks. * Fixed Bug #3021524 reported on Sourceforge by thebreaker. Fixes running session detection. Thanks. * Fixed podgets verbosity with a patch submitted by Norman Rasmussen. And fixed my email address in the script. Thanks. -- Dave Vehrs Sat, 14 Apr 2012 11:28:55 -0700 podget (0.6.6) unstable; urgency=low * Fixed Bug #666149 reported by Elena Grandi via the Debian reporting system. Playlist names were being created with spaces that would break some lines. Added quotes to prevent. Thanks. -- Dave Vehrs Wed, 4 Apr 2012 15:47:55 -0700 podget (0.6.5) unstable; urgency=low * Added version reporting flags (-V or --version), so people can test to see what version they have installed. -- Dave Vehrs Wed, 4 Apr 2012 15:30:00 -0700 podget (0.6.4) unstable; urgency=low * Fixed enclosure bug. * Fixed error checking for successful wget run. -- Dave Vehrs Fri, 19 Nov 2011 10:44:00 -0700 podget (0.6.3) unstable; urgency=low * Fixed tempfile bug/typo. * Fixed -f and --force to redownload existing files, including fix to wget base options. -- Dave Vehrs Fri, 18 Nov 2011 11:29:02 -0700 podget (0.6.2) unstable; urgency=low * Added use of the tempfile command to the playlist sorting per Renalt Sabitov's idea and bug report (via Debian). -- Dave Vehrs Fri, 16 Sep 2011 18:00:19 -0700 podget (0.6.1) unstable; urgency=low * Various fixes for Debian Lintain reports. -- Dave Vehrs Wed, 14 Sep 2011 12:56:19 -0700 podget (0.6) unstable; urgency=low * Added date substitutions to the feed category per David Willmore's feature request (#1652606). Thanks. * Reversed order of filename character modifications and format fixing. * Fixed error reports to go to stderr. * Added more custom error/exit codes to simplify debugging. * Added another filename format fix for feeds from LBC Plus per MoonUnit's feature request (#1660764). Thanks. * Fixed playlist sorting to prevent a race condition in some implementations of the sort command as suggested by Cameron Paine. Thanks. * Fixed Indexfile creation for Podcast feeds that have the enclosure URL broken over multiple lines as suggested by Colin Carter. Thanks. * Modified Session file messaging to clearer report when old session files were removed. * Fixed -r (most recent) and -f (force) command line options to override the defaults stored in the user configuration files. Thanks to Jim C. for reporting the bug and providing a potential patch. * Wrapped eval statement on line 422 in quotes to try to fix an OBSD error. * Changed install command --mode flags to -m in the Makefile so that it would be OBSD compatible. * Changed #!/bin/bash to #!/usr/bin/env bash so that its more cross platform compatible. * Moved test for configuration directory to before creation of session file so it has a place to store the session file. * Added Filename Format fix for podcasts hosted on Catradio.cat per Oriol Rius's Bug Report (#1744705). Thanks. * Fixed "Filesystem paths including spaces crash the podget script" (Bug# 1889724) submitted by Anonymous. Thanks. * Fixed "Some filenames cause podget to fail" (Patch#1845815) submitted by Anonymous. Thanks. * Fixed error with playlist loading submitted by Markus. Thanks. * Added a filename format fix as suggested by Rob Sims via the Debian Bug Reporting System (http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=459949, Bug#459949). Thanks. * Added SCRIPTS directory for user submitted scripts. First script is from Steven Black and allows for easy addition of feeds from within Firefox. Thanks. * Added patch from Steven Black to fix problem handling single apostrophes in filenames. Thanks again. -- Dave Vehrs Sun, 25 Apr 2010 12:28:39 -0700 podget (0.5.8) unstable; urgency=low * General Cleanup. * Fixed Filename Format issue (From BBC World News Bulletin). Files download named filename.mp3?123456 and are fixed to filename123456.mp3 (Patch# 1615902 submitted by mojoandy). * Added support for creating ASX Playlists for Windows Media Player. (Patch# 1615902 submitted by mojoandy). * Changed application name from podget.sh to podget to comply with Debian Policies. * Stopped compressing the copyright file to comply with Debian Policies. * Updated Debians copyright file to point at /usr/share/common-licenses/GPL * Removed "DEBIAN" from installed changelog to comply with Debian policies. * Updated Generic COPYING file for non-Debian installs. * Fixed synopsis line in Debian Control file to comply with Debian Policies. * Updated Debian Control file to Standards-Version 3.7.2 from 3.6.2. -- Dave Vehrs Sat, 6 Jan 2007 18:52:39 -0700 podget (0.5.7) unstable; urgency=low * Patch for avail_space bug. Thanks to Andreas Ruppen for catching it. * Added function to check for running sessions using the same core configuration file. * Added function to check for remaining space on library partition and stop if it drops below a configured level. (both these ideas were inspired by Luc's suggestions) -- Dave Vehrs Tue, 23 May 2006 11:01:30 -0700 podget (0.5.6) unstable; urgency=low * Added simulate function to cleanup loop per marc_in_lux's request (MSG ID: 3717464). -- Dave Vehrs Mon, 8 May 2006 10:37:30 -0700 podget (0.5.5) unstable; urgency=low * Fixed bug in cleanup loop. -- Dave Vehrs Tue, 10 Apr 2006 09:23:00 -0700 podget (0.5.4) unstable; urgency=low * Added UTF-16 formated feed handling (Support Request# 1432993). -- Dave Vehrs Tue, 2 Mar 2006 11:41:16 -0700 podget (0.5.3) unstable; urgency=low * Fixed enclosure url parsing (Bug# 1414282). -- Dave Vehrs Tue, 24 Jan 2006 21:21:37 -0700 podget (0.5.2) unstable; urgency=low * Removed bit-torrent support. Rarely used and poorly implemented. For an example of a better implementation see Podracer (http://podracer.sf.net/). * Fixed cleanup_days bug (if commandline option was not set, it overrode the default with a null value). * Added support for importing servers from iTunes PCAST files. -- Dave Vehrs Wed, 17 Jan 2006 07:21:37 -0700 podget (0.5.1) unstable; urgency=low * Suppress "no enclosures" warning while running silent (Patch# 1395582, submitted by Kai Timmer). * Test for completed file fails on filename with special chars, replace grep with fgrep (Patch# 1396969 submitted by Kai Timmer). * Standardized verbosity testing (Patch# 1397756, Submitted by Johnny Ljunggern). * Cleaned up comment output, and established clearer levels (0-4). -- Dave Vehrs Wed, 4 Jan 2006 09:16:17 -0700 podget (0.5) unstable; urgency=low * Added sed hook to handle rss feeds that break the enclosure tag over multiple lines (LugRadio). * Initial import OPML list support from local file or download from http/ftp. * Applied patch submited by anonymous. (Bug 1380412 - unhandled environment variable). * Dropped test for interactive shell. * Fixed configuration file parsing bug (Bug 1386657). * Added filename cleanup code for FAT32 compatability (Feature Request 1378956). -- Dave Vehrs Sun, 20 Dec 2005 18:00:00 -0700 podget (0.4.1) unstable; urgency=low * Initial Debian package release. * Moved podcast directory from /usr/local/share to $HOME/ * Improved configuration installation and testing. * Modified cleanup loop to allow user specified number of days to retain files. * Fix for RSS feeds without sufficient newlines. * Initial man page. * Installation by makefile or debian package now. * Started changelog. * Initial Debian Packages (.deb) -- Dave Vehrs Sun, 6 Nov 2005 19:37:01 -0700 podget-0.6.9/DOC/0000700000175000017500000000000012067134051013010 5ustar lowkeylowkeypodget-0.6.9/DOC/podget.70000644000175000017500000001177711742322255014415 0ustar lowkeylowkey.\"Text automatically generated by txt2man .TH podget 7 .SH NAME \fBPodget \- Simple tool to automate downloading of podcasts. \fB .SH SYNOPSIS .nf .fam C \fBpodget\fP [\fIcommand\fP] \fBpodget\fP \fB-h\fP | \fB--help\fP .fam T .fi .fam T .fi .SH DESCRIPTION .SH OPTIONS .TP .B \fB-c\fP, \fB--config\fP Name of configuration file. .TP .B \fB-C\fP, \fB--cleanup\fP Skip downloading and only run cleanup loop. .TP .B \fB--cleanup_days\fP Cleanup files older than days. .TP .B \fB--cleanup_simulate\fP Simulate cleanup loop to see what files would be deleted. .TP .B \fB-d\fP, \fB--dir_config\fP Directory that configuration files are stored in. .TP .B \fB-f\fP, \fB--force\fP Force download of items from each feed even if they've already been downloaded. .TP .B \fB--import_opml\fP Import servers from OPML file or HTTP/FTP URL. .TP .B \fB--import_pcast\fP Import server from iTunes PCAST file or HTTP/FTP URL. .TP .B \fB-l\fP, \fB--library\fP Directory to store downloaded files in. .TP .B \fB-p\fP, \fB--playlist-asx\fP In addition to M3U playlists, create ASX playlists. .TP .B \fB-r\fP, \fB--recent\fP Download only the newest items from each feed. .TP .B \fB--serverlist\fP Use as serverlist instead of default. .TP .B \fB-s\fP, \fB--silent\fP Run silently (for cron jobs). .TP .B \fB-v\fP Set verbosity to level 1. .TP .B \fB--verbosity\fP Set verbosity level (0-3). .TP .B \fB-h\fP, \fB--help\fP Display help. .RE .PP .SH EXAMPLE CRON JOB Once \fBpodget\fP is running acceptably, its most useful if you run it from a cron job so that the new songs are available to play or load onto a portable player and you don't have to wait for them to download. .PP To edit your crontab, do: .PP .nf .fam C $ crontab \-e .fam T .fi Then insert one line (i for insert mode), like the following: .PP .nf .fam C 15 04 * * * /usr/bin/podget \-s .fam T .fi This will run \fBpodget\fP at 4:15 AM every day. .SH AUTOMATIC CLEANUP You can enabled automatic cleanup with every run by configuring it in your $HOME/.\fBpodget\fP/podgetrc file. Simply set the following options: .PP .nf .fam C # Autocleanup. # 0 == disabled # 1 == delete any old content cleanup=1 # Number of days to keep files. Cleanup will remove anything # older than this. cleanup_days=7 .fam T .fi However, some people prefer to run cleanup as a separate cron session. To do that, set the options in .podgetrc to: .PP .nf .fam C # Autocleanup. # 0 == disabled # 1 == delete any old content cleanup=0 # Number of days to keep files. Cleanup will remove anything # older than this. cleanup_days=7 .fam T .fi And add a cron job to run cleanup, like one of these examples: .PP .nf .fam C # Once a week on Sunday at 04:15AM 15 04 * * Sun /usr/bin/podget \-C .fam T .fi .SH SESSION CONTROL .TP .B Podget checks for already running sessions when it starts and exits if any are found. Each session requires its own core configuration file, so for multiple sessions simply provide a unique \fB-c\fP option for each. .SH EXAMPLE SERVER LIST By default, Podget uses $HOME/.\fBpodget\fP/serverlist for the default list of servers .TP .B to contact. However you can configure the name with the config_serverlist variable in your $HOME/.\fBpodget\fP/podgetrc file. .PP The default format is: .PP NOTES: .RS .IP 1. 4 URL Rules: A. Any spaces in the URL needs to be converted to %20 .IP 2. 4 Category Rules: .RS .TP .B A. Must be one word without spaces. .TP .B B. You may use underscores and dashes. .TP .B C. You can insert date substitions. .RS .TP .B %YY% == Year .TP .B %MM% == Month .TP .B %DD% == Day .RE .RE .IP 3. 4 Name Rules: .RS .TP .B A. If you are creating ASX playlists, make sure the feed name does not have any spaces in it. .TP .B B. You can leave the feed name blank, and files will be saved in the category directory. .RE .IP 4. 4 Disable the downloading of any feed by commenting it out with a #. .RE .PP Examples: .PP .nf .fam C http://www.podcastingnews.com/forum/links.php?func=show&id=214 IT In the Trenches http://www.lugradio.org/episodes.rss Linux LUG Radio http://thelinuxlink.net/tllts/tllts.rss Linux The Linux Link http://www.bbc.co.uk/radio4/history/inourtime/mp3/podcast.xml Philosophy BBC: In Our Time http://www.privacyfreaks.org/podcast.php privacy Privacy Freaks http://dl.chickencat.com/podcast.php privacy Digital Liberties Example with date substitution in the category and a blank feed name. http://downloads.bbc.co.uk/rmhttp/downloadtrial/worldservice/summary/rss.xml News-%YY%-%MM%-%DD% .fam T .fi HANDLING UTF-16 FEEDS .PP Some servers provide their feeds in UTF-16 format rather than the more common UTF-8. .PP To automatically convert these files, create a secondary serverlist at: .PP .nf .fam C $HOME/.podget/serverlist.utf16 .fam T .fi Remember to change the name of the serverlist to match what you set it to with config_serverlist if you changed it. .SH AUTHORS Dave Vehrs podget-0.6.9/DOC/podget.man.txt0000644000175000017500000001123711742322256015630 0ustar lowkeylowkeyNAME Podget \- Simple tool to automate downloading of podcasts. SYNOPSIS podget [command] podget -h | --help DESCRIPTION OPTIONS -c, --config Name of configuration file. -C, --cleanup Skip downloading and only run cleanup loop. --cleanup_days Cleanup files older than days. --cleanup_simulate Simulate cleanup loop to see what files would be deleted. -d, --dir_config Directory that configuration files are stored in. -f, --force Force download of items from each feed even if they've already been downloaded. --import_opml Import servers from OPML file or HTTP/FTP URL. --import_pcast Import server from iTunes PCAST file or HTTP/FTP URL. -l, --library Directory to store downloaded files in. -p, --playlist-asx In addition to M3U playlists, create ASX playlists. -r, --recent Download only the newest items from each feed. --serverlist Use as serverlist instead of default. -s, --silent Run silently (for cron jobs). -v Set verbosity to level 1. --verbosity Set verbosity level (0-3). -h, --help Display help. EXAMPLE CRON JOB Once podget is running acceptably, its most useful if you run it from a cron job so that the new songs are available to play or load onto a portable player and you don't have to wait for them to download. To edit your crontab, do: $ crontab \-e Then insert one line (i for insert mode), like the following: 15 04 * * * /usr/bin/podget \-s This will run podget at 4:15 AM every day. AUTOMATIC CLEANUP You can enabled automatic cleanup with every run by configuring it in your $HOME/.podget/podgetrc file. Simply set the following options: # Autocleanup. # 0 == disabled # 1 == delete any old content cleanup=1 # Number of days to keep files. Cleanup will remove anything # older than this. cleanup_days=7 However, some people prefer to run cleanup as a separate cron session. To do that, set the options in .podgetrc to: # Autocleanup. # 0 == disabled # 1 == delete any old content cleanup=0 # Number of days to keep files. Cleanup will remove anything # older than this. cleanup_days=7 And add a cron job to run cleanup, like one of these examples: # Once a week on Sunday at 04:15AM 15 04 * * Sun /usr/bin/podget \-C SESSION CONTROL Podget checks for already running sessions when it starts and exits if any are found. Each session requires its own core configuration file, so for multiple sessions simply provide a unique -c option for each. EXAMPLE SERVER LIST By default, Podget uses $HOME/.podget/serverlist for the default list of servers to contact. However you can configure the name with the config_serverlist variable in your $HOME/.podget/podgetrc file. The default format is: NOTES: 1. URL Rules: A. Any spaces in the URL needs to be converted to %20 2. Category Rules: A. Must be one word without spaces. B. You may use underscores and dashes. C. You can insert date substitions. %YY% == Year %MM% == Month %DD% == Day 3. Name Rules: A. If you are creating ASX playlists, make sure the feed name does not have any spaces in it. B. You can leave the feed name blank, and files will be saved in the category directory. 4. Disable the downloading of any feed by commenting it out with a #. Examples: http://www.podcastingnews.com/forum/links.php?func=show&id=214 IT In the Trenches http://www.lugradio.org/episodes.rss Linux LUG Radio http://thelinuxlink.net/tllts/tllts.rss Linux The Linux Link http://www.bbc.co.uk/radio4/history/inourtime/mp3/podcast.xml Philosophy BBC: In Our Time http://www.privacyfreaks.org/podcast.php privacy Privacy Freaks http://dl.chickencat.com/podcast.php privacy Digital Liberties Example with date substitution in the category and a blank feed name. http://downloads.bbc.co.uk/rmhttp/downloadtrial/worldservice/summary/rss.xml News-%YY%-%MM%-%DD% HANDLING UTF-16 FEEDS Some servers provide their feeds in UTF-16 format rather than the more common UTF-8. To automatically convert these files, create a secondary serverlist at: $HOME/.podget/serverlist.utf16 Remember to change the name of the serverlist to match what you set it to with config_serverlist if you changed it. AUTHORS Dave Vehrs podget-0.6.9/INSTALL0000644000175000017500000000051611742322255013454 0ustar lowkeylowkey Source Install: $ sudo make install Building and installing a Debian package: $ cd podget- $ fakeroot ./debian/rules binary $ cd ../ $ sudo dpkg -i podget_-all.deb After the package is installed, run it once to install default configuration files into $HOME/.podget, edit them as needed. podget-0.6.9/README0000644000175000017500000000117611742322255013306 0ustar lowkeylowkeyPodget is a simple podcast aggregator optimized for running as a scheduled background job (i.e. cron). It features support for downloading podcasts from RSS & XML feeds, for sorting the files into folders & categories, for importing URLs from iTunes PCAST files & OPML lists automatic M3U & ASX playlist creation, and automatic cleanup of old files. It also features automatic UTF-16 conversion for podcasts hosted on MS Windows servers. Run podget once to install the users configuration files to $HOME/.podget, and then customize them there. MAN PAGE: Build manpage with txt2man. Syntax: txt2man -s 7 -p podget.man.txt > podget.7 podget-0.6.9/SCRIPTS/0000700000175000017500000000000012006031011013513 5ustar lowkeylowkeypodget-0.6.9/SCRIPTS/podget-add-feed0000644000175000017500000006354111742322255016415 0ustar lowkeylowkey#!/bin/bash # Copyright (C) 2009 Steven Black # Contributed to podget on 2009-11-06 to be released under the same license # as podget (currently GPL) EMPTY_GROUP="Unknown" EMPTY_TITLE="Unknown" PODGET_DIR="$HOME/.podget" config_serverlist=`sed -r -n -e 's/^[ ]*config_serverlist[ ]*=[ ]*(["]?)(.*)\1[ ]*$/\2/p' "$PODGET_DIR/podgetrc"` dir_library=`sed -r -n -e 's/^[ ]*dir_library[ ]*=[ ]*(["]?)(.*)\1[ ]*$/\2/p' "$PODGET_DIR/podgetrc"` case "$config_serverlist" in /*) SERVERLIST="$config_serverlist" ;; *) SERVERLIST="$PODGET_DIR/$config_serverlist" ;; esac case "$dir_library" in /*) LIBRARY_DIR="$dir_library" ;; *) LIBRARY_DIR="$PODGET_DIR/$dir_library" ;; esac if [ ! -f "$SERVERLIST" ] ; then exit 0 fi CURL=`which curl` fake_title() { # Originally we faked a title from the URL. # Now we only do that if we can't get one via curl and sed FAKE_TITLE="" if [ -x "$CURL" ] ; then FAKE_TITLE=`"$CURL" -A "Mozilla/4.0" -f -L -s "$1" | sed -n -e '//{ s|^.*<title>\(.*\).*$|\1|Mp q }'` fi if [ -z "$FAKE_TITLE" ] ; then FAKE_TITLE=`sed -r \ -e 's;^https?://;;' -e 's;[.](com|org|net)/;/;' \ -e 's;^(www|web|feeds?[0-9]?)?[.];;' \ -e 's;/(bookfeeds|[sS]ite)/;/;g' -e 's;/[mM][pP]3_[fF]eed;;g' \ -e 's;[.]libsyn;;' -e 's;^(me|feedburner|librivox)/;;' \ -e 's;/feeds?/;/;' -e 's;[.](xml|html)$;;' \ -e 's;/feed$;;' \ -e 's;/[?]feed=[^/.]+;;' -e 's;/rss/?$;;' -e 's;/$;;' \ -e 's;%20; ;g' -e 's;%[0-9a-fA-F][0-9a-fA-F];;' -e 'y;/;_;' \ <<< "$1"` fi } # fake_title "$1" # echo $FAKE_TITLE # exit 0 unsetcompetition() { KDIALOG="" XDIALOG="" GTKDIALOG="" WHIPTAIL="" TCDIALOG="" DIALOG="" AUTOMODE=0 } checkdialog() { USRDIALOG="$1" unsetcompetition if [ "$USRDIALOG" = "auto" ] ; then AUTOMODE=1 return fi if [ ! -z "$USRDIALOG" ] ; then case "$USRDIALOG" in */*) ;; *) USRDIALOG=`which $USRDIALOG` ;; esac if [ ! -x "$USRDIALOG" ] ; then USRDIALOG="" ; fi fi if [ ! -z "$USRDIALOG" ] ; then case "$USRDIALOG" in */kdialog*) KDIALOG="$USRDIALOG" ;; */[xX]dialog*) XDIALOG="$USRDIALOG" ;; */zenity*) ZENITY="$USRDIALOG" ;; */gtkdialog*) GTKDIALOG="$USRDIALOG" ;; */whiptail*) WHIPTAIL="$USRDIALOG" ;; */tcdialog*) TCDIALOG="$USRDIALOG" ;; */sh|*/bash*) DIALOG="" ;; *) # treat as 'dialog' if [ ! -z "$DISPLAY" ] ; then if tty -s ; then # Normally 'dialog' will only be used on a real TTY. # They have a real TTY, so we're fine. true DIALOG="$USRDIALOG" else # Woah, no TTY. Perhaps we should treat this as Xdialog. XDIALOG="$USRDIALOG" fi else DIALOG="$USRDIALOG" fi ;; esac else if [ ! -z "$DISPLAY" ] ; then KDIALOG=`which kdialog` XDIALOG=`which Xdialog` GTKDIALOG=`which gtkdialog` # Note: We *never* try to use 'zenity' unless the user specified # it. It just doesn't work the way we want -- at all. fi if tty -s ; then WHIPTAIL=`which whiptail` TCDIALOG=`which tcdialog` DIALOG=`which dialog` fi fi } dialogcheck() { local DIALOGS=0 if [ "$AUTOMODE" = "1" ] ; then if [ ! -z "$1" ] ; then echo "Running in automatic mode." fi return 0 fi if [ ! -z "$DISPLAY" ] ; then if [ ! -z "$1" ] ; then echo "X dialog services:" fi fi if [ -x "$KDIALOG" ] ; then if [ ! -z "$1" ] ; then echo "kdialog: good" fi DIALOGS=1 fi if [ -x "$GTKDIALOG" ] ; then if [ ! -z "$1" ] ; then echo "gtkdialog: good" fi DIALOGS=1 fi if [ -x "$XDIALOG" ] ; then if [ ! -z "$1" ] ; then echo "xdialog: okay" echo "---------- NOTICE regarding the use of Xdialog ----------" echo "Xdialog does not have the capacity to load in an icon or " echo "pixmap. This means it'll be uglier than the competition." echo "-----------------------------------------------------------" echo "For a prettier display, install kdialog or gtkdialog." fi DIALOGS=1 fi if [ -x "$ZENITY" ] ; then DIALOGS=1 echo "zenity: BAD (and UNSUPPORTED)" echo "---------- WARNING regarding the use of Zenity ----------" echo "'zenity' is unsupported. 'zenity' has no support for yes/no or" echo "yes/no/cancel dialogs. It has no capacity to add a button and" echo "change labels, allowing any sort of usable yes/no/cancel system." echo "zenity does not let me repurpose ok/cancel dialogs. The return" echo "code for cancel is the same as abort, so I can't even make do" echo "with changing the behavior there." echo echo "The dialogs will be confusing and missing features." echo echo "This is why it is unsupported." echo echo "Please install kdialog, Xdialog, or gtkdialog or run it from" echo "a terminal." echo echo "Really, even the automatic mode works better than zenity mode." echo "---------------------------------------------------------" echo "zenity is unsupported. Use at your own risk." if [ -z "$1" ] ; then somedialog --error "'zenity' is unsupported. 'zenity' has no support for yes/no or yes/no/cancel dialogs. It has no capacity to add a button and change labels, allowing any sort of usable yes/no/cancel system. zenity does not let me repurpose ok/cancel dialogs. The return code for cancel is the same as abort, so I can't even make do with changing the behavior there.\n\nThe dialogs will be confusing and missing features.\n\nThis is why it is unsupported.\n\nPlease install kdialog, Xdialog, or gtkdialog or run it from a terminal.\n\nReally, even the automatic mode works better than zenity mode." fi fi if [ $DIALOGS = 0 ] ; then if [ ! -z "$1" ] ; then echo "none: okay (X dialogs unavailable)" echo "------------ NOTICE regarding automatic mode -------------" echo "Will be fully automatic unless run from a terminal." echo "----------------------------------------------------------" echo "Please consider installing kdialog, Xdialog, or gtkdialog." echo fi fi if tty -s ; then if [ ! -z "$1" ] ; then echo "TTY dialog services:" fi fi if [ -x "$DIALOG" ] ; then if [ ! -z "$1" ] ; then echo "dialog: good" fi fi if [ -x "$WHIPTAIL" ] ; then if [ ! -z "$1" ] ; then echo "whiptail: okay (not as pretty as dialog)" echo "--------- NOTICE regarding the use of whiptail ---------" echo "The line-wrapping in message dialogs is fairly crude." echo "This results in the message dialogs being uglier than" echo "dialog." echo "--------------------------------------------------------" echo "For a prettier display, install dialog." echo fi fi if [ -x "$TCDIALOG" ] ; then if [ ! -z "$1" ] ; then echo "tcdialog: BAD" echo "---------- WARNING regarding the use of tcdialog ----------" echo "tcdialog requires the row and column sizes be specified, so" echo "it produces some awful looking dialogs as we do not " echo "precompute these sizes." echo echo "We've also seen cases of the cursor being missing in the" echo "input dialog with tcdialog." echo "-----------------------------------------------------------" echo "Please consider installing dialog." echo fi fi if [ ! -z "$1" ] ; then echo "bash: okay (not as pretty as dialog)" fi } askquestions() { local MY_URL="$1" local MY_GROUP="${2:-$EMPTY_GROUP}" local MY_TITLE="$3" CUR_ABORT=0 fake_title "$MY_URL" MY_TITLE="${MY_TITLE:-${FAKE_TITLE:-$EMPTY_TITLE}}" somedialog --input "Title for $MY_URL" "$MY_TITLE" if [ $? -eq 1 ] ; then CUR_ABORT=1 ; return 1 ; fi CUR_TITLE="${SOMEDIALOG:-${FAKE_TITLE:-$EMPTY_TITLE}}" somedialog --input "Genre or group for $CUR_TITLE" "$MY_GROUP" if [ $? -eq 1 ] ; then CUR_ABORT=1 ; return 1 ; fi CUR_GROUP="${SOMEDIALOG:-$EMPTY_GROUP}" somedialog --yesNOcancel "Activate <$MY_URL> in $CUR_GROUP as '$CUR_TITLE'?" CUR_ACTIVATE="$?" if [ $CUR_ACTIVATE -eq 2 ] ; then CUR_ABORT=1 ; return 1 ; fi if [ -z "$CUR_GROUP" ] ; then CUR_GROUP="$EMPTY_GROUP" fi if [ -z "$CUR_TITLE" ] ; then CUR_TITLE="${FAKE_TITLE:-$EMPTY_TITLE}" fi # Because of the backticks, the \\ needs to be escaped to \\\\ CUR_GROUP=`sed -e 'y; /\\\\;___;' <<< "$CUR_GROUP"` CUR_TITLE=`sed -e 'y;/\\\\;__;' <<< "$CUR_TITLE"` return $CUR_ABORT } somedialog() { local TYPE="$1" shift local RET=0 if [ -x "$KDIALOG" ] ; then if [ "$TYPE" = "--yesNOcancel" ] ; then TYPE="--yesnocancel" ; fi if [ "$TYPE" = "--error" ] ; then "$KDIALOG" --error "$*" elif [ "$TYPE" = "--sorry" ] ; then "$KDIALOG" --sorry "$*" elif [ "$TYPE" = "--message" ] ; then "$KDIALOG" --msgbox "$*" elif [ "$TYPE" = "--yesno" ] ; then "$KDIALOG" --yesno "$*" RET="$?" elif [ "$TYPE" = "--yesnocancel" ] ; then "$KDIALOG" --yesnocancel "$*" RET="$?" elif [ "$TYPE" = "--input" ] ; then SOMEDIALOG=`"$KDIALOG" --inputbox "$1" "$2"` RET="$?" fi elif [ -x "$XDIALOG" ] ; then if [ "$TYPE" = "--yesNOcancel" ] ; then TYPE="--yesnocancel" ; fi if [ "$TYPE" = "--error" ] ; then "$XDIALOG" --stdout --msgbox "Error: $*" 0 0 elif [ "$TYPE" = "--sorry" ] ; then "$XDIALOG" --stdout --msgbox "Sorry: $*" 0 0 elif [ "$TYPE" = "--message" ] ; then "$XDIALOG" --stdout --msgbox "$*" 0 0 elif [ "$TYPE" = "--yesno" ] ; then "$XDIALOG" --stdout --yesno "$*" 0 0 RET="$?" if [ $RET -eq 255 ] ; then RET=1 ; fi elif [ "$TYPE" = "--yesnocancel" ] ; then "$XDIALOG" --stdout --yesno "$*" 0 0 RET="$?" if [ $RET -eq 255 ] ; then RET=2 ; fi elif [ "$TYPE" = "--input" ] ; then SOMEDIALOG=`"$XDIALOG" --stdout --inputbox "$1" 0 0 "$2"` RET="$?" if [ $RET -eq 255 ] ; then RET=1 ; fi fi elif [ -x "$GTKDIALOG" ] ; then if [ "$TYPE" = "--yesNOcancel" ] ; then TYPE="--yesnocancel" ; fi if [ "$TYPE" = "--error" ] ; then GTKMSG=`echo "$*" | sed -e 's//]/g'` export GTK_DIALOG=" " "$GTKDIALOG" --program GTK_DIALOG | grep -q 'EXIT="OK"' elif [ "$TYPE" = "--sorry" ] ; then GTKMSG=`echo "$*" | sed -e 's//]/g'` export GTK_DIALOG=" " "$GTKDIALOG" --program GTK_DIALOG | grep -q 'EXIT="OK"' elif [ "$TYPE" = "--message" ] ; then GTKMSG=`echo "$*" | sed -e 's//]/g'` export GTK_DIALOG=" " "$GTKDIALOG" --program GTK_DIALOG | grep -q 'EXIT="OK"' elif [ "$TYPE" = "--yesno" ] ; then GTKMSG=`echo "$*" | sed -e 's//]/g'` export GTK_DIALOG=" " GTK_EXIT=`"$GTKDIALOG" --program GTK_DIALOG | sed -n -e 's/^EXIT="\([^"]*\)"/\1/p'` case "$GTK_EXIT" in Yes) RET=0 ;; No) RET=1 ;; *) RET=1 ;; esac elif [ "$TYPE" = "--yesnocancel" ] ; then GTKMSG=`echo "$*" | sed -e 's//]/g'` export GTK_DIALOG=" " GTK_EXIT=`"$GTKDIALOG" --program GTK_DIALOG | sed -n -e 's/^EXIT="\([^"]*\)"/\1/p'` case "$GTK_EXIT" in Yes) RET=0 ;; No) RET=1 ;; *) RET=2 ;; esac elif [ "$TYPE" = "--input" ] ; then GTKMSG=`echo "$1" | sed -e 's//]/g'` GTKDEFAULT=`echo "$2" | sed -e 's//]/g'` # This isn't what is done in gtkdialog examples, however gtkdialog # does *no escaping* of the input, so you can provide input such # as '" ; rm /etc/passwd ; true "' and what you get back is: # SOMEDIALOG="" ; rm /etc/passwd ; true "" export GTK_DIALOG=" $GTKDEFAULTSOMEDIALOG " GTK_LINES=`"$GTKDIALOG" --program GTK_DIALOG` SOMEDIALOG=`sed -n -e 's/^SOMEDIALOG="\(.*\)"$/\1/p' <<< "$GTK_LINES"` GTK_EXIT=`sed -n -e 's/^EXIT="\([^"]*\)"/\1/p' <<< "$GTK_LINES"` case "$GTK_EXIT" in OK) RET=0 ;; *) SOMEDIALOG="" ; RET=1 ;; esac fi elif [ -x "$ZENITY" ] ; then # There is *NO CANCEL* option, so it is depricated # It also has no Yes/No, so the yesno questions come out confusing if [ "$TYPE" = "--yesNOcancel" ] ; then TYPE="--yesnocancel" ; fi if [ "$TYPE" = "--error" ] ; then GTKMSG=`echo "$*" | sed -e 's//]/g'` "$ZENITY" --error --text="$GTKMSG" elif [ "$TYPE" = "--sorry" ] ; then GTKMSG=`echo "$*" | sed -e 's//]/g'` "$ZENITY" --warning --text="$GTKMSG" elif [ "$TYPE" = "--message" ] ; then GTKMSG=`echo "$*" | sed -e 's//]/g'` "$ZENITY" --info --text="$GTKMSG" elif [ "$TYPE" = "--yesno" ] ; then GTKMSG=`echo "$*" | sed -e 's//]/g'` "$ZENITY" --question --text="$GTKMSG" RET="$?" elif [ "$TYPE" = "--yesnocancel" ] ; then GTKMSG=`echo "$*" | sed -e 's//]/g'` "$ZENITY" --question --text="$GTKMSG" RET="$?" elif [ "$TYPE" = "--input" ] ; then GTKMSG=`echo "$1" | sed -e 's//]/g'` GTKDEFAULT=`echo "$2" | sed -e 's//]/g'` SOMEDIALOG=`"$ZENITY" --entry --text="$GTKMSG" --entry-text="$GTKDEFAULT"` RET="$?" fi elif tty -s && [ $AUTOMODE = 0 ] ; then TTY_ABORT=0 # trap TTY_ABORT=1 SIGINT if [ -x "$DIALOG" ] ; then if [ "$TYPE" = "--yesNOcancel" ] ; then TYPE="--yesnocancel" ; fi if [ "$TYPE" = "--error" ] ; then "$DIALOG" --msgbox "Error: $*" 0 0 elif [ "$TYPE" = "--sorry " ] ; then "$DIALOG" --msgbox "Sorry: $*" 0 0 elif [ "$TYPE" = "--message" ] ; then "$DIALOG" --msgbox "$*" 0 0 elif [ "$TYPE" = "--yesno" ] ; then "$DIALOG" --yesno "$*" 0 0 if [ $RET -eq 255 ] ; then RET=1 ; fi elif [ "$TYPE" = "--yesnocancel" ] ; then "$DIALOG" --extra-button --ok-label Yes --extra-label No --no-label Cancel --yesno "$*" 0 0 if [ $RET -eq 1 ] ; then RET=2 elif [ $RET -eq 2 ] ; then RET=1 elif [ $RET -eq 255 ] ; then RET=2 fi elif [ "$TYPE" = "--input" ] ; then "$DIALOG" --inputbox "$1" 0 0 "$2" 2>"$TMPFILE" RET="$?" SOMEDIALOG=`cat "$TMPFILE"` if [ $RET -eq 255 ] ; then RET=1 ; fi fi elif [ -x "$WHIPTAIL" ] ; then # whiptail doesn't handle 0 0 as nicely as dialog, so it # isn't preferred. if [ "$TYPE" = "--yesNOcancel" ] ; then TYPE="--yesnocancel" ; fi if [ "$TYPE" = "--error" ] ; then "$WHIPTAIL" --msgbox "Error: $*" 0 0 elif [ "$TYPE" = "--sorry " ] ; then "$WHIPTAIL" --msgbox "Sorry: $*" 0 0 elif [ "$TYPE" = "--message" ] ; then "$WHIPTAIL" --msgbox "$*" 0 0 elif [ "$TYPE" = "--yesno" ] ; then "$WHIPTAIL" --yesno "$*" 0 0 RET="$?" if [ $RET -eq 255 ] ; then RET=1 ; fi elif [ "$TYPE" = "--yesnocancel" ] ; then "$WHIPTAIL" --yesno "$* (ESC to cancel)" 0 0 RET="$?" if [ $RET -eq 255 ] ; then RET=2 ; fi elif [ "$TYPE" = "--input" ] ; then "$WHIPTAIL" --inputbox "$1" 0 0 "$2" 2>"$TMPFILE" RET="$?" SOMEDIALOG=`cat "$TMPFILE"` if [ $RET -eq 255 ] ; then RET=1 ; fi fi elif [ -x "$TCDIALOG" ] ; then # tcdialog requires the height/width values # as such, it is depricated if [ "$TYPE" = "--yesNOcancel" ] ; then TYPE="--yesnocancel" ; fi if [ "$TYPE" = "--error" ] ; then "$TCDIALOG" --msgbox "Error: $*" 20 60 elif [ "$TYPE" = "--sorry " ] ; then "$TCDIALOG" --msgbox "Sorry: $*" 20 60 elif [ "$TYPE" = "--message" ] ; then "$TCDIALOG" --msgbox "$*" 20 60 elif [ "$TYPE" = "--yesno" ] ; then "$TCDIALOG" --yesno "$*" 20 60 RET="$?" if [ $RET -eq 255 ] ; then RET=1 ; fi elif [ "$TYPE" = "--yesnocancel" ] ; then "$TCDIALOG" --yesno "$* (ESC to cancel)" 20 60 RET="$?" if [ $RET -eq 255 ] ; then RET=2 ; fi elif [ "$TYPE" = "--input" ] ; then "$TCDIALOG" --inputbox "$1" 20 60 "$2" 2>"$TMPFILE" RET="$?" SOMEDIALOG=`cat "$TMPFILE"` if [ $RET -eq 255 ] ; then RET=1 ; fi fi else if [ "$TYPE" = "--error" ] ; then echo "Error: $*" read -p "Press to continue." RET RET=0 if [ $TTY_ABORT -eq 1 ] ; then RET=1 ; fi elif [ "$TYPE" = "--sorry " ] ; then echo "Sorry: $*" read -p "Press to continue." RET RET=0 if [ $TTY_ABORT -eq 1 ] ; then RET=1 ; fi elif [ "$TYPE" = "--message" ] ; then echo "Note: $*" read -p "Press to continue." RET RET=0 if [ $TTY_ABORT -eq 1 ] ; then RET=1 ; fi elif [ "$TYPE" = "--yesno" ] ; then read -r -e -p "$* (y/N)?" RET case "$RET" in [yY]|[yY][eE][sS]) RET=0 ;; *) RET=1 ;; esac if [ $TTY_ABORT -eq 1 ] ; then RET=1 ; fi elif [ "$TYPE" = "--yesNOcancel" ] ; then read -r -e -p "$* (yes/NO/cancel):" RET if [ -z "$RET" ] ; then RET="n" ; fi case "$RET" in [yY]|[yY][eE][sS]) RET=0 ;; [nN]|[nN][oO]) RET=1 ;; *) RET=2 ;; esac if [ $TTY_ABORT -eq 1 ] ; then RET=2 ; fi elif [ "$TYPE" = "--yesnocancel" ] ; then read -r -e -p "$* (yes/no/CANCEL):" RET case "$RET" in [yY]|[yY][eE][sS]) RET=0 ;; [nN]|[nN][oO]) RET=1 ;; *) RET=2 ;; esac if [ $TTY_ABORT -eq 1 ] ; then RET=2 ; fi elif [ "$TYPE" = "--input" ] ; then read -r -e -p "$1 ($2): " SOMEDIALOG if [ -z "$SOMEDIALOG" ] ; then SOMEDIALOG="$2" fi RET=$TTY_ABORT fi fi # trap - SIGINT else RET=0 if [ "$TYPE" = "--error" ] ; then echo "Error: $*" elif [ "$TYPE" = "--sorry " ] ; then echo "Sorry: $*" elif [ "$TYPE" = "--message" ] ; then echo "Message: $*" elif [ "$TYPE" = "--yesno" ] ; then echo "Yes/No: $* (defaults to NO)" RET=1 elif [ "$TYPE" = "--yesNOcancel" ] ; then echo "Yes/No/Cancel: $* (defaults to NO)" RET=1 elif [ "$TYPE" = "--yesnocancel" ] ; then echo "Yes/No/Cancel: $* (defaults to CANCEL)" RET=2 elif [ "$TYPE" = "--input" ] ; then echo "Input: $1 (defaults to $2)" SOMEDIALOG="$2" fi fi return $RET } showversion() { # Version history: # 0.1 - (yam655) first version # 0.1.1 (2009-05-08): # fix issue with feed://somewhere/... # genre/title dialog order swap (as seen in 0.2 line) # 0.2 - (mostly aborted) added ability to run podget afterwards # 0.3 - added inactivity tag support echo "podget-add-feed Version 0.3" } showhelp() { cat << FEOF Usage: $0 {Options} Options may include: -h | --help : show this help message -V | --version : show the version -c | --check : check dialog compatibility -a | --auto : run in non-interactive mode -d | --dialog : use a specific dialog program FEOF } checkdialog TMPFILE=`tempfile -p paf.` if [ -z "$TMPFILE" ] ; then TMPFILE=`mktemp -t addfeed.XXXXXXXXXX` ; fi if [ -z "$TMPFILE" ] ; then TMPFILE="/tmp/podget-add-feed.$$.`hostname`" ; fi while [ $# -gt 0 ] ; do case "$1" in --check|-c) dialogcheck -v exit 0 ;; --help|-h) showhelp exit 0 ;; --version|-V) showversion exit 0 ;; --auto|-a) unset DISPLAY DIALOG="auto" shift continue ;; --dialog|-d) shift checkdialog "$1" dialogcheck shift continue ;; esac URL="$1" shift CUR_GROUP="$EMPTY_GROUP" CUR_TITLE="" CUR_ACTIVATE=0 # Interestingly, the body of the single-quoted segments is getting # parsed. URL=`sed -e 's@^feed://@http://@' -e 's@^feed:\([a-z]\+:\)@\1@' \ -e 's/ /%20/g' -e 's/|/%7C/g' -e 's/\\\\/%5C/g' \ -e 's/\\$/%24/g' -e 's/"/%34/g' -e "s/'/%39/g" \ <<< "$URL"` case "$URL" in http:*) ;; https:*) ;; *) somedialog --error "Unknown protocol in URL: $URL" continue ;; esac FOUNDURL=`grep -e "^\(#[a-zA-Z0-9]* \)\?$URL " "$SERVERLIST"` FURL="" FGROUP="" FTITLE="" FACTIVE="inactive" FREASON="" if [ ! -z "$FOUNDURL" ] ; then case "$FOUNDURL" in \#*) read FACTIVE FURL FGROUP FTITLE <<< "$FOUNDURL" if [ "$FACTIVE" != "#" ] ; then FREASON=" (${FACTIVE:1})" fi FACTIVE="inactive" ;; *) read FURL FGROUP FTITLE <<< "$FOUNDURL" FACTIVE="active" ;; esac if ! somedialog --yesnocancel "Found <$FURL> in $FGROUP as \"$FTITLE\". It is $FACTIVE$FREASON. Change this?" ; then continue fi fi if ! askquestions "$URL" "$FGROUP" "$FTITLE" ; then continue fi ACTIVE="" if [ $CUR_ACTIVATE -ne 0 ] ; then ACTIVE='# ' fi if [ -z "$FOUNDURL" ] ; then echo "$ACTIVE$URL $CUR_GROUP $CUR_TITLE" >> "$SERVERLIST" else OLIBDIR="$LIBRARY_DIR/$FGROUP/$FTITLE" NLIBDIR="$LIBRARY_DIR/$CUR_GROUP/$CUR_TITLE" sed -e 's|^\(# \)\?'"$URL"' .*$|'"$ACTIVE$URL $CUR_GROUP $CUR_TITLE"'|g' "$SERVERLIST" > "$SERVERLIST".new mv "$SERVERLIST" "$SERVERLIST~" mv "$SERVERLIST".new "$SERVERLIST" if [ -d "$OLIBDIR" ] ; then if [ ! -d "$LIBRARY_DIR/$CUR_GROUP" ] ; then mkdir -p "$LIBRARY_DIR/$CUR_GROUP" fi if [ -d "$NLIBDIR" ] ; then somedialog --sorry "Directory $NLIBDIR already existed. We could not move $OLIBDIR. You will need to move the files by hand." else mv -f "$OLIBDIR" "$NLIBDIR" fi fi # if [ -z "$ACTIVE" -a "$FACTIVE" != "active" ] ; then # if somedialog --yesno "Run podget now?" ; then # podget # fi # fi fi done podget-0.6.9/SCRIPTS/podget-add-feed.README0000644000175000017500000000725411742322255017350 0ustar lowkeylowkeyFrom Steven Black's posting: This is a simple shell script I created to easily add RSS feeds from within Firefox, etc. The user-interface is handled through third-party tools, falling back to BASH built-ins if on a TTY and sane defaults if not on a TTY. If you run the script from an xterm with --check it will check for the available interfaces and print the details. Usable X-based dialog tools include: kdialog, gtkdialog, Xdialog. Xdialog won't look as good as either kdialog or gtkdialog. ("zenity" is not supported as it is missing core features required.) Usable TTY-based dialog tools include: dialog (preferred), whiptail (not as good-looking or full-functioning as dialog), BASH is also well-supported. As mentioned previously, there are sane defaults and with no available user-interface it works in "automated" mode. (--help produces arguments.) For the title, it uses 'curl' to do a quick-download of the feed to try to get the proper title. (It usually works.) If it can't be determined from the feed, it tries to mangle the URL to get something of a usable default. If even that fails, it falls back to "Unknown". It supports the genre/group, but at this time it doesn't present a list of the currently in-use options. It is on my list of things to add, but I've been so lax on adding support for it that I'm making it available before I add it. It also supports both active and inactive feeds. These are handled by commenting out the feed in the ~/.podget/serverlist file. If running in automated mode, the feeds are inactive with an "Unknown" genre/group. (If it is automated, it is expected that you will need to manually tweak the title and group/genre.) If you add an existing feed, you can edit the title, genre/group, and activate/deactivate the feed. If you change the title or genre/group of a feed, it will automatically move the underlying directory if possible. (It tries to move the directory, if the directory already exists, it does nothing.) Additionally, it supports "inactivity tags". These are simple one-word reasons for why a feed is deactivated. These are handled by adding the word directly to the comment character in the serverlist. I use it to mark when a podiobook is completely downloaded ("DONE"), when I've finished reading a completed podiobook ("READ"), and when I've abandoned a feed ("QUIT"). So I have things like the following in my serverlist: #### Scott Sigler http://feeds.feedburner.com/scottsigler Horror Scott Sigler #READ http://www.podiobooks.com/title/infected/feed Horror Scott Sigler, Infected There's no user-interface to set the inactivity tag, (it needs to be set directly in the serverlist file) but if you reference the URL again it will present it to you. If I run podget-add-feed with the first URL, it will present a dialog like: Found in Horror as "Scott Sigler". It is active. Change this? For the second URL, it would say: Found in Horror as "Scott Sigler, Infected". It is inactive (READ). Change this? If the inactivity tag were not defined, the whole " (...)" section would be missing, as you would expect. Oh, and when editing an existing feed it does not move the feed within the file. It's an in-place sed-based operation. This means that if I ran the command on either of the above-mentioned URLs, they would remain in the "Scott Sigler" section of my serverlist. It's a human-readable file, so it is expected that it would have human-centered structure. I have Firefox set to display the feed within the browser. I can then choose to add it as a text feed using some other software, or I can select "podget-add-feed" and it will allow me to set it up for podget. podget-0.6.9/SCRIPTS/README0000644000175000017500000000012711742322255014430 0ustar lowkeylowkeyThis directory is for user contributed scripts that extend Podget's functionality. podget-0.6.9/podget0000755000175000017500000012756212067131002013633 0ustar lowkeylowkey#!/usr/bin/env bash # ---------------------------------------------------------------------------------------------------------------------------------- # Filename: podget {{{ # Maintainer: Dave Vehrs # Created: 05 Mar 2005 09:35:44 PM # Last Modified: 27 Dec 2012 02:37:25 PM by Dave # Copyright: © 2005,2006,2007,2008,2009,2010,2011 Dave Vehrs # # This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public # License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any # later version. # # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied # warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more # details. # # You should have received a copy of the GNU General Public License along with this program; if not, write to the # Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA _OR_ download at copy at # http://www.gnu.org/licenses/licenses.html#TOCGPL # # Description: Podget is a simple bash script to automate the downloading and # organizing of podcast content. # Dependencies: bash, coreutils, grep, libc6 (for iconv), sed, tofrodos (unix2dos for ASX Playlists) and wget. # Installation: cp podget.sh /usr/local/bin # chmod 755 /usr/local/bin/podget.sh }}} # ---------------------------------------------------------------------------------------------------------------------------------- # Exit Codes {{{ # "Reserved" Exit codes # 1 General Error # 2 Misuse of shell built-ins # 126 Command invoked cannot execute # 127 Command not found # 128 Invalid argument to exit # 130 Script terminated by Control-C # "Our" Exit codes # Display Help (set to '0' because it is an valid exit condition, just not an error.) err_displayhelp=0 # Library directory not defined. err_libnotdef=50 # Library directory available space below limit err_liblowspace=51 # Libc6 not installed. Cannot convert UTF16 feeds. err_libc6notinstalled=60 # Another running session already exists. err_runningsession=70 # OPML import error. err_importopml=80 # }}} # ---------------------------------------------------------------------------------------------------------------------------------- # Help text and default file formats {{{ : << HELP_STEXT -c --config Name of configuration file. -C --cleanup Skip downloading and only run cleanup loop. --cleanup_simulate Skip downloading and simulate running cleanup loop. Display files to be deleted. --cleanup_days Number of days to retain files. Anything older will be removed. -d --dir_config Directory that configuration files are stored in. -f --force Force download of items from each feed even if they have already been downloaded. --import_opml Import servers from OPML file or HTTP/FTP URL. --import_pcast Import servers from iTunes PCAST file or HTTP/FTP URL. -l --library Directory to store downloaded files in. -p --playlist-asx In addition to the default M3U playlist, create an ASX Playlist. -r --recent Download only the newest items from each feed. --serverlist Serverlist to use. -s --silent Run silently (for cron jobs). --verbosity Set verbosity level (0-4). -v Set verbosity to level 1. -vv Set verbosity to level 2. -vvv Set verbosity to level 3. -vvvv Set verbosity to level 4. -h --help Display help. HELP_STEXT # }}} # ---------------------------------------------------------------------------------------------------------------------------------- # Defaults {{{ ######################################################################################################################### ## Do not configure here. Run podget once to install default user configuration files ($HOME/.podget) and edit those. ## ######################################################################################################################### # Set dir_cache, dir_install, dir_log, dir_temp in config file. dir_config="$HOME/.podget" config_core="podgetrc" config_serverlist="serverlist" # Default Verbosity # 0 == silent # 1 == Warning messages only. # 2 == Progress and Warning messages. # 3 == Debug, Progress and Warning messages. # 4 == All messages and wget set to maximum verbosity. verbosity=2 # Silent mode (for calling from cron jobs) # 0 == normal # 1 == suppress all messages silent=0 # Auto-Cleanup. # 0 == disabled # 1 == delete any old content cleanup=0 # Skip downloading and just run cleanup # 0 == disable cleanup_only=0 # Simulate cleanup cleanup_simulate=0 # Number of days to keep files. Cleanup will remove anything # older than this. cleanup_days=7 # Most Recent # 0 == download all new items. # 1+ == download only the most recent most_recent=0 # Force # 0 == Only download new material. # 1 == Force download all items even those you've downloaded before. force=0 # Install session. This gets called when script is first installed. install_session=0 # Fix filenames for FAT32 compatibility modify_filename=0 # Fix downloaded file names of format filename.mp3?1232456 to filename123456.mp3 filename_formatfix=1 # Filename Cleanup 3: Fix filenames from feeds downloaded from LBC Plus. filename_formatfix2=1 # Filename Cleanup 4: filename_formatfix3=1 # Filename Cleanup 5: filename_formatfix4=0 # Filename Cleanup 6: filename_formatfix5=1 # Stop downloads if available space drops below min_space=10000 # ASX Playlists for Windows Media Player # 0 == do not create # 1 == create asx_playlist=0 # }}} # ---------------------------------------------------------------------------------------------------------------------------------- # Text for default configuration files: {{{ : << TEXT_DEFAULT_CONFIG # Name of Server List configuration file config_serverlist=serverlist # Directory where to store downloaded files dir_library=@HOME@/POD # Directory to store logs in # dir_log=@HOME@/POD/LOG # Set logging files log_fail=errors log_comp=done # Build playlists (comment out or set to a blank string to accept default format: New-). playlist_namebase=New- # Date format for new playlist names date_format=+%m-%d-%Y # Wget base options # Commonly used options: # -c Continued interupted downloads # -nH No host directories (overrides .wgetrc defaults if necessary) # --proxy=off To disable proxy set by environmental variable http_proxy/ #wget_baseopts=-c --proxy=off wget_baseopts=-c -nH # Most Recent # 0 == download all new items. # 1+ == download only the most recent most_recent=0 # Force # 0 == Only download new material. # 1 == Force download all items even those you've downloaded before. force=0 # Autocleanup. # 0 == disabled # 1 == delete any old content cleanup=0 # Number of days to keep files. Cleanup will remove anything # older than this. cleanup_days=7 # Filename Cleanup: For FAT32 filename compatability (Feature Request #1378956) # Tested with the following characters: !@#$%^&*()_-+=||{[}]:;"'<,>.?/ # filename_badchars=!#$^&=+{}[]:;"'<>?|\ # Filename Replace Character: Character to use to replace any/all # bad characters found. filename_replacechar=_ # Filename Cleanup 2: Some RSS Feeds (like the BBC World News Bulletin) # download files with names like filename.mp3?1234567. Enable this mode # to fix the format to filename1234567.mp3. # 0 == disabled # 1 == enabled (default) filename_formatfix=1 # Filename Cleanup 3: Filenames of feeds hosted by LBC Plus corrupted. # Fixed per MoonUnit's feature request (#1660764) # # Takes an URL that looks like: http://lbc.audioagain.com/shared/audio/stream.mp3?guid=2007-03/14<...snip> # a7766e8ad2748269fd347eaee2b2e3f8&source=podcast.php&channel_id=88 # # Which normally creates a file named: a7766e8ad2748269fd347eaee2b2e3f8&source=podcast.php&channel_id=88 # # This fix extracts the date of the episode and changes the filename to 2007-03-14.mp3 # 0 == disabled # 1 == enabled (default) filename_formatfix2=1 # Filename Cleanup 4: Filenames of feeds hosted by CatRadio.cat need fixing. # Fixed per Oriol Rius's Bug Report (#1744705) # # Downloaded filenames look like: 1189153569775.mp3?programa=El+mat%ED+de+Catalunya+R%E0dio&podcast=y # This fix removes everything after the .mp3 # # 0 == disabled # 1 == enabled (default) filename_formatfix3=1 # Filename Cleanup 5: When the filename is part of the URL and the actual filename stays the same for # all items listed. # # Download URLs look like: http://feeds.theonion.com/~r/theonion/radionews/~5/213589629/podcast_redirect.mp3 # Where 213589629 is the unique filename. # # 0 == disabled # 1 == enabled (default) filename_formatfix4=1 # Filename Cleanup 6: Remove "?referrer=rss" from the end of filenames as included in some feeds like # those from Vimcasts.org. Setup to work for MP3, M4V, OGG and OGV files. # # Feed URLs: http://vimcasts.org/feeds/ogg # http://vimcasts.org/feeds/quicktime # # In the feed, enclosure URLs look like: http://media.vimcasts.org/videos/1/show_invisibles.ogv?referrer=rss # # 0 == disabled # 1 == enabled (default) filename_formatfix5=1 # Stop downloading if available space on the partition drops below value (in KB) # default: 614400 (600MB) min_space=614400 # ASX Playlists for Windows Media Player # 0 == do not create # 1 == create asx_playlist=0 TEXT_DEFAULT_CONFIG : << TEXT_DEFAULT_SERVERLIST # Default Server List for podget # # FORMAT: # # NOTES: # 1. URL Rules: # A. Any spaces in the URL needs to be converted to %20 # 2. Category Rules: # A. Must be one word without spaces. # B. You may use underscores and dashes. # C. You can insert date substitions. # %YY% == Year # %MM% == Month # %DD% == Day # 3. Name Rules: # A. If you are creating ASX playlists, make sure the feed name does not # have any spaces in it and the filename cannot be blank. # B. You can leave the feed name blank, and files will be saved in the # category directory. # 4. Disable the downloading of any feed by commenting it out with a #. # # Find more servers at: http://www.ipodder.org/directory/4/podcasts # # Examples: # http://www.lugradio.org/episodes.rss Linux LUG Radio # # Example with date substitution in the category and a blank feed name: # http://downloads.bbc.co.uk/rmhttp/downloadtrial/worldservice/summary/rss.xml News-%YY%-%MM%-%DD% # http://thelinuxlink.net/tllts/tllts.rss LINUX The Linux Link TEXT_DEFAULT_SERVERLIST : << TEXT_ASX_BEGINNING TEXT_ASX_BEGINNING : << TEXT_ASX_END TEXT_ASX_END # }}} # ---------------------------------------------------------------------------------------------------------------------------------- # Functions {{{ function display_shelp { echo; echo "Usage $0 [options]"; echo sed --silent -e '/HELP_STEXT$/,/^HELP_STEXT/p' "$0" | sed -e '/HELP_STEXT/d' } # }}} # ---------------------------------------------------------------------------------------------------------------------------------- # Version (Update with changes!) {{{ version=0.6.9 repver=0 # }}} # ---------------------------------------------------------------------------------------------------------------------------------- # Parse command line {{{ unset cmdl_library while [ $# -ge 1 ] ; do case $1 in -c | --config ) config_core=$2 ; shift ; shift ;; -C | --cleanup ) cleanup_only=1 ; cleanup=1 ; shift ;; --cleanup_days ) cleanup_days_cmdl=$2 ; shift ; shift ;; --cleanup_simulate ) cleanup_sim_cmdl=1 ; cleanup_only=1 ; cleanup=1 ; shift ;; -d | --dir_config ) dir_config=$2 ; shift ; shift ;; -f | --force ) cmdl_force=1 ; shift ;; --import_opml ) import_opml=$2 ; shift ; shift ;; --import_pcast ) import_pcast=$2 ; shift ; shift ;; -l | --library ) cmdl_library=$2 ; shift ; shift ;; -p | --playlist-asx ) cmdl_asx=1 ; shift ;; -r | --recent ) cmdl_most_recent=$2 ; shift ; shift ;; --serverlist ) cmdl_serverlist=$2 ; shift ; shift ;; -s | --silent ) silent=1 ; verbosity=0 ; shift ;; -V | --version ) verbosity=2 ; repver=1 ; shift ;; -v ) verbosity=1 ; shift ;; -vv ) verbosity=2 ; shift ;; -vvv ) verbosity=3 ; shift ;; -vvvv ) verbosity=4 ; shift ;; --verbosity ) verbosity=$2 ; shift ; shift ;; * ) display_shelp ; exit ${err_displayhelp} ;; esac done if [ -n "$cmdl_serverlist" ] ; then config_serverlist=$cmdl_serverlist fi if [ $verbosity -ge 2 ] ; then echo "podget" fi if [ ${repver} -eq 1 ]; then echo "Version: ${version}" exit 0 fi if [ $verbosity -ge 3 ] ; then echo "Parsing Config file." echo -e "Config directory:\t\t${dir_config}" echo -e "Config file:\t\t${config_core}" fi # for testing #echo "Verbosity: ${verbosity}" #exit 0 # }}} # ---------------------------------------------------------------------------------------------------------------------------------- # Test for existing configuration, {{{ # if its not found create it. # Test for config dir. If missing install it. if [ ! -d "${dir_config}" ] ; then echo " First Run. Installing user configuration files." echo " Configuration directory not found. Creating "${dir_config}"" mkdir "${dir_config}" sed --silent -e '/TEXT_DEFAULT_CONFIG$/,/^TEXT_DEFAULT_CONFIG/p' "$0" | sed -e '/TEXT_DEFAULT_CONFIG/d' | sed -e "s|@HOME@|${HOME}|" > "${dir_config}"/${config_core} sed --silent -e '/TEXT_DEFAULT_SERVERLIST$/,/^TEXT_DEFAULT_SERVERLIST/p' "$0" | sed -e '/TEXT_DEFAULT_SERVERLIST/d' > "${dir_config}"/${config_serverlist} install_session=1 fi # }}} # ---------------------------------------------------------------------------------------------------------------------------------- # Test for another session. {{{ test_session=0 for file in $(ls -1 "${dir_config}"/session.[0-9]* 2>/dev/null) ; do if [ $verbosity -ge 2 ] ; then echo "Session file found: ${file}" fi test_session=1 while read line ; do testindex=$(expr "$line" : "\sfile:") if [ "A${testindex}" != "A" ]; then test_file=$(echo $line | sed -n -e 's/^[^:]\+:\s\(.*\)$/\1/p') if [ "$test_file" == "$config_core" ] ; then session_pid=$(echo ${file} | sed -n -e 's/^.\+\.\([0-9]*\)$/\1/p') if [ $verbosity -ge 2 ] ; then echo " Testing PID ${session_pid} to determine if its still running." fi if ps -p ${session_pid} &> /dev/null ; then echo "Another session with config file ${config_core} found running. Killing session." 1>&2 exit ${err_runningsession} else if [ $verbosity -ge 2 ] ; then echo " Session PID ${session_pid} is not running, removing lock file" fi rm -f ${file} fi fi fi done < $file done if [ $verbosity -ge 2 ] ; then if [ $test_session -gt 0 ] ; then echo -e "\nOld Session file(s) found and removed. Creating new one." else echo -e "\nSession file not found. Creating." fi fi echo -e "Config file: $config_core" > "${dir_config}"/session.$$ # }}} # ---------------------------------------------------------------------------------------------------------------------------------- # Configuration {{{ # Parse config file while read line ; do if [ $verbosity -ge 3 ] ; then echo -e "\nConfig line --> $line" fi testindex=$(expr "$line" : "\(^[ ]*#\)") if [ "A${testindex}" != "A" ]; then if [ $verbosity -ge 4 ] ; then echo "Discarding comment." fi continue fi if [[ $(expr "$line" : ".*=") > 0 ]]; then if [ $verbosity -ge 3 ] ; then echo " Found config line." fi var2set=$(echo $line | sed -n -e 's/^\([^=]\+\)=.*$/\1/p') set2var=$(echo $line | sed -n -e 's/^[^=]\+=\(.*\)$/\1/p') eval "export ${var2set}='${set2var}'" fi done < "${dir_config}"/$config_core if [ $install_session -gt 0 ] ; then echo " Downloading a single item from each default server to test configuration." echo most_recent=1 verbosity=3 fi if [ -n "$cmdl_library" ] ; then dir_library=$cmdl_library fi if [ $verbosity -ge 3 ] ; then echo "LIBRARY DIR: $dir_library" fi if [ -z $dir_library ] ; then echo "ERROR - Library directory not defined." 1>&2 exit $err_libnotdef fi if [ ! -z $cleanup_days_cmdl ] ; then cleanup_days=$cleanup_days_cmdl fi if [ ! -z $cleanup_sim_cmdl ] ; then cleanup_simulate=$cleanup_sim_cmdl fi if [ -z $dir_log ] ; then dir_log=$dir_library/.LOG fi if [ -n "$cmdl_asx" ] ; then asx_playlist=$cmdl_asx fi if [ ${verbosity} -ge 3 ]; then echo "WGet options: ${wget_baseopts}" fi if [ -n "$cmdl_force" ] ; then force=$cmdl_force wget_baseopts=$(echo $wget_baseopts | sed -e 's/-c //') if [ ${verbosity} -ge 3 ]; then echo "WGet options: ${wget_baseopts}" fi fi if [ -n "$cmdl_most_recent" ] ; then most_recent=$cmdl_most_recent fi if [ $verbosity -le 1 ] ; then wget_options="-q $wget_baseopts" elif [ $verbosity -eq 2 ] ; then wget_options="-nv $wget_baseopts" elif [ $verbosity -eq 3 ] ; then wget_options="$wget_baseopts --progress=dot:mega" else wget_options="$wget_baseopts --progress=bar" fi if [ ! -z $filename_badchars ] ; then # insert a space between all characters. filename_badchars=$(echo ${filename_badchars} | sed -e 's/\([^ ]\)/\1 /g' -e 's/[*]/\\*/g') if [ $verbosity -eq 3 ] ; then echo -e "\nFilename Bad Characters: ${filename_badchars}" echo "Filename Replace Character: ${filename_replacechar}" fi modify_filename=1 fi # }}} # ---------------------------------------------------------------------------------------------------------------------------------- # Loop over servers on list {{{ if [ $cleanup_only -eq 0 ] && [ -z $import_opml ] && [ -z $import_pcast ] ; then if [ $verbosity -ge 3 ] ; then echo -e "\nMain loop." echo "SERVER LIST FILE: $config_serverlist" echo "WGET OPTIONS: $wget_options" fi mkdir -p $dir_log touch $dir_log/$log_fail $dir_log/$log_comp if [ -n "$playlist_namebase" ] ; then playlist_name="$playlist_namebase$(date $date_format).m3u" else playlist_name="New-$(date $date_format).m3u" fi counter=2 while [[ -e ${dir_library}/${playlist_name} ]] ; do playlist_name="$playlist_namebase$(date $date_format).r$counter.m3u" counter=$((counter+1)) done if [ ${verbosity} -ge 3 ]; then echo "Playlist name: ${playlist_name}" fi # UTF-8/16 handling for filetype in utf8 utf16 ; do if [ $verbosity -ge 3 ] ; then echo case $filetype in 'utf8') echo "UTF-8 Loop running." ;; 'utf16') echo "UTF-16 Loop running." ;; esac fi case $filetype in 'utf8') current_serverlist=""${dir_config}"/${config_serverlist}" ;; 'utf16') iconv_binary=$(which iconv) if [ -z $iconv_binary ] ; then echo "Can't find iconv binary, is libc6 installed?" 1>&2 echo "Exiting UTF16 loop, unable to convert file to UTF8" 1>&2 exit $err_libc6notinstalled fi current_serverlist=""${dir_config}"/${config_serverlist}.utf16" ;; *) echo "Unknown Filetype: $filetype" esac if [ ! -f $current_serverlist ] ; then if [ $verbosity -ge 3 ] ; then echo "No config file found, exiting loop." fi continue fi while read feed_url feed_category feed_name ; do if [ $verbosity -ge 3 ] ; then echo -e "\nServer/Feed URL --> $feed_url" fi if [[ $(expr index "# " "$feed_url") > 0 ]] && [[ $(expr index "# " "$feed_url") < 2 ]] || [[ $feed_url == "" ]] ; then if [ $verbosity -ge 3 ] ; then echo " Discarding line (comment or blank line)." fi continue fi feed_category=$(echo ${feed_category} | sed -e "s#%YY%#`date +%Y`#" -e "s#%MM%#`date +%m`#" -e "s#%DD%#`date +%d`#" ) if [ $verbosity -ge 2 ] ; then echo -e "\n-------------------------------------------------" echo -e "Category: $feed_category\t\tName: $feed_name" echo "Downloading feed index from $feed_url" fi case $filetype in 'utf8') indexfile=$(wget $wget_options -O - $feed_url | sed -e 's/\r/\n/g' -e 's/<\([^/]\)/\n<\1/g' | \ sed -n -e :a -e 's/.*&2 exit ${err_liblowspace} fi # Set base value for filename format fixes and character substitutions. mod_filename=${url_filename} # Fix improperly formated filenames (fixes filename.mp3?123456 to filename123456.mp3) if [ ${filename_formatfix} -gt 0 ] ; then if [ $(expr "${mod_filename}" : ".*\.mp3..*$") -gt 0 ] ; then mod_filename=$(echo ${mod_filename} | sed 's/\.mp3\(.*\)/\1.mp3/') if [ $verbosity -ge 3 ] ; then echo "FILENAME FORMAT FIXED: $mod_filename" fi fi fi # Filename format fix for podcasts hosted on http://lbc.audioagain.com. if [ ${filename_formatfix2} -gt 0 ] ; then if [ $(expr "${mod_filename}" : "[0-9a-Z]\+[&]amp;source=podcast.php[&]amp;channel_id=[0-9]\+\$") -gt 0 ] ; then mod_filename=$(echo ${url} | sed 's/.*stream.mp3[?]guid=\([0-9]\+\)-\([0-9]\+\)\/\([0-9]\+\)\/.*/\1-\2-\3.mp3/') if [ $verbosity -ge 3 ] ; then echo "FILENAME FORMAT(2) FIXED: $mod_filename" fi fi fi # Filename format fix for podcasts hosted on http://www.catradio.cat if [ ${filename_formatfix3} -gt 0 ] ; then if [ $(expr "${mod_filename}" : "[0-9]\+\.mp3\?[\\?]programa=[0-9a-Z+=%&;]*\$") -gt 0 ] ; then mod_filename=$(echo ${mod_filename} | sed 's/\(.*\)\.mp3\(.*\)/\1\.mp3/g') if [ $verbosity -ge 3 ] ; then echo "FILENAME FORMAT(3) FIXED: $mod_filename" fi fi fi # Fix case where filename part of URI is constant if [ ${filename_formatfix4} -gt 0 ] ; then if [ -z "$mod_filename" ] ; then mod_filename=${url_filename} fi mod_prefix="${url_base%%/}" mod_prefix="${mod_prefix##*/}" mod_filename="${mod_prefix##*/}_${mod_filename}" if [ $verbosity -ge 3 ] ; then echo "FILENAME DUPLICATE FIXED: $mod_filename" fi fi # Remove "?referrer=rss" from filename as included with some feeds like Vimcasts.org if [ ${filename_formatfix5} -gt 0 ] ; then if [ $(expr "${mod_filename}" : "[-0-9a-Z_]\+\.[gmopv34]\+[?]referrer=rss") -gt 0 ] ; then mod_filename=$(echo ${mod_filename} | sed -r 's/([-A-Za-z0-9_].[ogmpv34])?referrer=rss/\1/g' | sed 's/[?]//') if [ $verbosity -ge 3 ] ; then echo "FILENAME FORMAT(5) FIXED: $mod_filename" fi fi fi # Test for filename modifications. if [ ${modify_filename} -gt 0 ] ; then for character in ${filename_badchars} ; do eval "mod_filename=$(echo \"$mod_filename\" | sed -e s/[\\${character}]/${filename_replacechar}/g)" done if [ $verbosity -ge 3 ] ; then echo "MODIFIED FILENAME: $mod_filename" fi fi mkdir -p "$dir_library/$feed_category/$feed_name" dtest=$(fgrep $url $dir_log/$log_comp) if [ -z "${dtest}" ] || [ ${force} -ne 0 ] ; then if [ ${verbosity} -ge 2 ] ; then if [ $modify_filename -gt 0 ] || [ $filename_formatfix -gt 0 ] || [ $filename_formatfix2 -gt 0 ]; then echo -e "Downloading $mod_filename from $url_base" else echo -e "Downloading $url_filename from $url_base" fi fi if [ -n "$mod_filename" ] ; then wget $wget_options -O "$dir_library/$feed_category/$feed_name/$mod_filename" $url else wget $wget_options -P "$dir_library/$feed_category/$feed_name/" $url fi # Suggested fix from https://sourceforge.net/tracker/?func=detail&aid=2170298&group_id=133382&atid=727035 if [ $? -eq 0 ] ; then echo $url >> $dir_log/$log_comp if [ -n "$playlist_name" ] ; then if [ -n "$mod_filename" ] ; then echo "${feed_category}/${feed_name}/${mod_filename}" >> "${dir_library}/${playlist_name}" if [ ${verbosity} -ge 2 ]; then echo "Adding ${feed_category}/${feed_name}/${mod_filename} to ${dir_library}/${playlist_name}" fi else echo "${feed_category}/${feed_name}/${url_filename}" >> "${dir_library}/${playlist_name}" if [ ${verbosity} -ge 2 ]; then echo "Adding ${feed_category}/${feed_name}/${url_filename} to ${dir_library}/${playlist_name}" fi fi fi else echo $url >> $dir_log/$log_fail fi else if [ $verbosity -ge 2 ] ; then if [ $verbosity -ge 3 ] ; then echo ; fi if [ $modify_filename -gt 0 ] || [ $filename_formatfix -gt 0 ] || [ $filename_formatfix2 -gt 0 ]; then echo "Already downloaded ${mod_filename}." else echo "Already downloaded ${url_filename}." fi fi fi done if [ $most_recent -ne 0 -a $install_session -eq 0 ] ; then for url in $fullindexfile do dtest=$(fgrep $url $dir_log/$log_comp) if [ -z "${dtest}" ] ; then url_filename=$(echo $url | sed -e 's/.*\/\([^\/]\+\)/\1/' -e 's/%20/ /g') if [ $verbosity -ge 2 ] ; then if [ -n "$mod_filename" ] ; then echo "Marking as already downloaded $mod_filename." else echo "Marking as already downloaded $url_filename." fi fi echo $url >> $dir_log/$log_comp fi done fi else if [ $verbosity -ge 1 ] ; then echo " No enclosures in feed: $feed_url" fi echo $feed_url >> $dir_log/$log_fail fi done < $current_serverlist done # Sort new playlist if [ -e "$dir_library/$playlist_name" ] ; then realPlaylistName="$dir_library/$playlist_name" # Sort Playlist unset tempPlaylistName tempPlaylistName=$(mktemp 2>/dev/null) # Test if 'mktemp' was able to create the temporary file name, # if not use 'tempfile' if [ -z "${tempPlaylistName}" ]; then tempPlaylistName=$(tempfile 2>/dev/null) fi # Test if temporary file name is set, if no skip sorting. if [ -z "${tempPlaylistName}" ]; then echo "Error: Niether mktemp or tempfile found. Unable to sort playlist." else cp -p "$realPlaylistName" "$tempPlaylistName" && sort -o "$realPlaylistName" "$tempPlaylistName" && rm "$tempPlaylistName" fi unset realPlaylistName tempPlaylistName # Create ASX Playlist if [ ${asx_playlist} -gt 0 ] ; then asx_location="\\SD Card\\POD\\" asx_playlist_name=`basename ${dir_library}/${playlist_name} .m3u`.asx sed --silent -e '/TEXT_ASX_BEGINNING$/,/^TEXT_ASX_BEGINNING/p' "$0" | sed -e '/TEXT_ASX_BEGINNING/d' > ${dir_library}/${asx_playlist_name} while read line ; do fixed_entry=$(echo ${line} | sed 's/\//\\/g') echo ' ' >> ${dir_library}/${asx_playlist_name} echo " " >> ${dir_library}/${asx_playlist_name} echo " " >> ${dir_library}/${asx_playlist_name} echo ' ' >> ${dir_library}/${asx_playlist_name} done < ${dir_library}/${playlist_name} sed --silent -e '/TEXT_ASX_END$/,/^TEXT_ASX_END/p' "$0" | sed -e '/TEXT_ASX_END/d' >> ${dir_library}/${asx_playlist_name} unix2dos -d ${dir_library}/${asx_playlist_name} fi fi fi # }}} # ---------------------------------------------------------------------------------------------------------------------------------- # Cleanup loop {{{ if [ -z $import_opml ] && [ -z $import_pcast ] ; then if [ $cleanup -ne 0 ] || [ $cleanup_only -ne 0 ] ; then if [ $verbosity -ge 2 ] ; then if [ $cleanup_simulate -gt 0 ] ; then echo "Simulating cleanup, the following files will be removed when you run cleanup." else echo -e "\n-------------------------------------------------\nCleanup old tracks." fi fi filelist=$(find $dir_library/ -maxdepth 1 -type f -name "*.m3u" -mtime +${cleanup_days}) for file in $filelist ; do if [ $verbosity -ge 2 ] ; then echo "Deleting tracks from $file:" fi while read line ; do if [ $cleanup_simulate -gt 0 ] ; then echo "File: $dir_library/$line" else if [ $verbosity -ge 2 ] ; then rm -v "$dir_library/$line" else rm -f "$dir_library/$line" fi fi done < $file if [ $cleanup_simulate -gt 0 ] ; then echo "Removing playlist: $file" else if [ $verbosity -eq 0 ] ; then rm -f "$file" else rm -fv "$file" fi fi done fi fi # }}} # ---------------------------------------------------------------------------------------------------------------------------------- # OPML import loop: {{{ if [ ! -z $import_opml ] ; then if [ $verbosity -ge 2 ] ; then echo -e "\nImport servers from OPML file: $import_opml" fi new_category="OPML_Import_$(date ${date_format})" if [[ $import_opml == http:* ]] || [[ $import_opml == ftp:* ]] ; then if [ $verbosity -ge 2 ] ; then echo "Getting opml list." fi opml_list=$(wget ${wget_options} -O - ${import_opml}) else opml_list=$(cat ${import_opml}) fi new_list=$(echo ${opml_list} | sed -e 's/\(\/>\)/\1\n/g' | sed -e :a -n -e 's/]\+\)\/>/\1/Ip;/\\|]//g") new_url=$(echo $data | sed -n -e 's/.*[xml]*url="\([^"]\+\)".*/\1/Ip' | sed -e 's/ /%20/g') if [ $verbosity -ge 3 ] ; then echo "LABEL: ${new_label}" echo "URL: ${new_url}" fi test=$(grep ${new_url} "${dir_config}"/${config_serverlist}) if [ -z $test ] ; then echo "${new_url} ${new_category} ${new_label}" >> "${dir_config}"/${config_serverlist} elif [ $verbosity -ge 2 ] ; then echo "Feed ${new_label} is already in the serverlist" fi done IFS=$OLD_IFS else if [ $verbosity -ge 2 ] ; then echo " OPML Import Error $import_opml" 1>&2 fi echo OPML Import Error: $import_opml >> $dir_log/$log_fail exit ${err_importopml} fi fi # }}} # ---------------------------------------------------------------------------------------------------------------------------------- # PCAST import: {{{ if [ ! -z $import_pcast ] ; then if [ $verbosity -ge 2 ] ; then echo -e "\nImport server from PCAST file: $import_pcast" fi if [[ $import_pcast == http:* ]] || [[ $import_pcast == ftp:* ]] ; then if [ $verbosity -ge 2 ] ; then echo "Getting pcast file." fi pcast_data=$(wget ${wget_options} -O - ${import_pcast}) else pcast_data=$(cat ${import_pcast}) fi new_link=$(echo ${pcast_data} | sed -n -e 's/.*\(href\|url\)="\([^"]\+\)".*/\2/Ip' | sed -e 's/ /%20/g') new_category=$(echo ${pcast_data} | sed -n -e 's/.*\([^<]\+\)<.*/\1/Ip' | sed -e 's/ /_/g;s/\"/\&/g;s/\&/\&/g') new_title=$(echo ${pcast_data} | sed -n -e 's/.*\([^<]\+\)<.*/\1/Ip') if [ $verbosity -ge 2 ] ; then echo "LINK: ${new_link}" echo "CATEGORY: ${new_category}" echo "TITLE: ${new_title}" fi test=$(grep ${new_link} "${dir_config}"/${config_serverlist}) if [ -z "$test" ] ; then echo "${new_link} ${new_category} ${new_title}" >> "${dir_config}"/${config_serverlist} elif [ $verbosity -ge 2 ] ; then echo "Feed ${new_title} is already in the serverlist" fi fi # }}} # ---------------------------------------------------------------------------------------------------------------------------------- # Close session and clean up: {{{ if [ $verbosity -ge 2 ] ; then echo -e "\nClosing session and removing lock file." fi rm -f "${dir_config}"/session.$$ # }}} # ---------------------------------------------------------------------------------------------------------------------------------- # Notes: {{{ # 1. Best viewed in Vim (http://vim.sf.net) with the AutoFold plugin and the # Relaxedgreen colorscheme (vimscripts #925 and #791). # 2. Known Bug: If the same filename is downloaded for multiple items on a # single feed, wgets continue fuction will cause them to append or error. # }}} # ---------------------------------------------------------------------------------------------------------------------------------- # vim:tw=132:ts=4:sw=4 ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������