Improve the style of "missing" episode icons
[gpodder.git] / bin / gpo
blob3e995b7cbb40bfeb04fef42db54542644d26b4a1
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
5 # gPodder - A media aggregator and podcast client
6 # Copyright (c) 2005-2009 Thomas Perl and the gPodder Team
8 # gPodder is free software; you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation; either version 3 of the License, or
11 # (at your option) any later version.
13 # gPodder is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with this program. If not, see <http://www.gnu.org/licenses/>.
23 # gpo - A better command-line interface to gPodder using the gPodder API
24 # by Thomas Perl <thp@gpodder.org>; 2009-05-07
27 """
28 Usage: gpo [COMMAND] [params...]
30 Subscription management
31 -----------------------
33 subscribe URL [TITLE] Subscribe to a new feed at URL (as TITLE)
34 rename URL TITLE Rename feed at URL to TITLE
35 unsubscribe URL Unsubscribe from feed at URL
37 info URL Show information about feed at URL
38 list List all subscribed podcasts
39 update Refresh all feeds (check for new episodes)
41 Episode management
42 ------------------
44 download [URL] Download all new (=pending) episodes; if
45 URL is an episode URL, download only one
46 episode; if it's a channel URL, download
47 all pending episodes from that channel
48 pending [URL] Show episodes that are marked as new; if
49 URL is given, show only pending downloads
50 from the channel at this URL
51 queue URL Add episode at URL to pending episodes
52 skip URL Remove episode at URL from pending episodes
54 details URL Show information about episode at URL
55 episodes [URL] Show a list of all episodes; if URL is given
56 it should be a channel URL and only episodes
57 from that channel will be displayed
59 delete URL Delete the downloaded episode at URL
61 Portable device synchronization
62 -------------------------------
64 device Show information about your device
65 sync Synchronize downloaded episodes to device
67 Helper commands
68 ---------------
70 youtube resolve [URL] Resolve the YouTube URL to a download URL
71 youtube download [URL] Download a video from YouTube via its URL
73 """
75 import sys
76 import os
77 import inspect
79 gpodder_script = sys.argv[0]
80 if os.path.islink(gpodder_script):
81 gpodder_script = os.readlink(gpodder_script)
82 gpodder_dir = os.path.join(os.path.dirname(gpodder_script), '..')
83 prefix = os.path.abspath(os.path.normpath(gpodder_dir))
85 src_dir = os.path.join(prefix, 'src')
86 data_dir = os.path.join(prefix, 'data')
88 if os.path.exists(src_dir) and os.path.exists(data_dir) and \
89 not prefix.startswith('/usr'):
90 # Run gPodder from local source folder (not installed)
91 sys.path.insert(0, src_dir)
94 import gpodder
95 _ = gpodder.gettext
97 # This is the command-line interface to gPodder
98 gpodder.interface = gpodder.CLI
100 # Use only the gPodder API here, so this serves both as an example
101 # and as a motivation to provide all functionality in the API :)
102 from gpodder import api
106 class gPodderCli(object):
107 def __init__(self):
108 self.client = api.PodcastClient()
110 # -------------------------------------------------------------------
112 def subscribe(self, url, title=None):
113 if self.client.get_podcast(url) is not None:
114 self._info(_('You are already subscribed to %s.' % url))
115 return True
117 if self.client.create_podcast(url, title) is None:
118 self._error(_('Cannot download feed for %s.') % url)
119 return True
121 self.client.finish()
123 self._info(_('Successfully added %s.' % url))
124 return True
126 def rename(self, url, title):
127 podcast = self.client.get_podcast(url)
129 if podcast is None:
130 self._error(_('You are not subscribed to %s.') % url)
131 else:
132 old_title = podcast.title
133 podcast.rename(title)
134 self.client.finish()
135 self._info(_('Renamed %s to %s.') % (old_title, title))
137 return True
139 def unsubscribe(self, url):
140 podcast = self.client.get_podcast(url)
142 if podcast is None:
143 self._error(_('You are not subscribed to %s.') % url)
144 else:
145 podcast.delete()
146 self.client.finish()
147 self._error(_('Unsubscribed from %s.') % url)
149 return True
151 def info(self, url):
152 podcast = self.client.get_podcast(url)
154 if podcast is None:
155 self._error(_('You are not subscribed to %s.') % url)
156 else:
157 title, url = podcast.title, podcast.url
158 def status_str(episode):
159 if episode.is_new:
160 return ' * '
161 if episode.is_downloaded:
162 return ' ▉ '
163 if episode.is_deleted:
164 return ' ░ '
166 return ' '
168 episodes = ('%3d. %s %s' % (i+1, status_str(e), e.title) for i, e in enumerate(podcast.get_episodes()))
169 episodes = '\n '.join(episodes)
170 print >>sys.stdout, """
171 Title: %(title)s
172 URL: %(url)s
174 Episodes:
175 %(episodes)s
176 """ % locals()
178 return True
180 def list(self):
181 for podcast in self.client.get_podcasts():
182 print podcast.url
184 return True
186 def update(self):
187 for podcast in self.client.get_podcasts():
188 print 'Updating', podcast.title
189 podcast.update()
190 print 'Done.'
192 return True
194 def pending(self):
195 count = 0
196 for podcast in self.client.get_podcasts():
197 podcast_printed = False
198 for episode in podcast.get_episodes():
199 if episode.is_new:
200 if not podcast_printed:
201 print podcast.title
202 podcast_printed = True
203 print ' ', episode.title
204 count += 1
206 print count, 'episodes pending.'
207 return True
209 def download(self):
210 count = 0
211 for podcast in self.client.get_podcasts():
212 podcast_printed = False
213 for episode in podcast.get_episodes():
214 if episode.is_new:
215 if not podcast_printed:
216 print podcast.title
217 podcast_printed = True
218 print ' ', episode.title
219 episode.download()
220 count += 1
222 print count, 'episodes downloaded.'
223 return True
225 def sync(self):
226 self.client.synchronize_device()
227 return True
229 # -------------------------------------------------------------------
231 def _error(self, *args):
232 print >>sys.stderr, ' '.join(args)
234 def _info(self, *args):
235 print >>sys.stdout, ' '.join(args)
237 def _checkargs(self, func, command_line):
238 args, varargs, keywords, defaults = inspect.getargspec(func)
239 args.pop(0) # Remove "self" from args
240 defaults = defaults or ()
241 minarg, maxarg = len(args)-len(defaults), len(args)
243 if len(command_line) < minarg or len(command_line) > maxarg:
244 self._error('Wrong argument count for %s.' % func.__name__)
245 return False
247 return func(*command_line)
250 def _parse(self, command_line):
251 if not command_line:
252 return False
254 command = command_line.pop(0)
255 if command.startswith('_'):
256 self._error(_('This command is not available.'))
257 return False
259 for name, func in inspect.getmembers(self):
260 if inspect.ismethod(func) and name == command:
261 return self._checkargs(func, command_line)
263 return False
266 if __name__ == '__main__':
267 cli = gPodderCli()
268 cli._parse(sys.argv[1:]) or sys.stderr.write(__doc__)