Fix translation format strings
[gpodder.git] / bin / gpo
blob176736267b721bc70d756ffce395196ab7cf31be
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
5 # gPodder - A media aggregator and podcast client
6 # Copyright (c) 2005-2010 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)
40 update URL Refresh the feed at URL
42 Episode management
43 ------------------
45 download [URL] Download all new (=pending) episodes; if
46 URL is an episode URL, download only one
47 episode; if it's a channel URL, download
48 all pending episodes from that channel
49 pending [URL] Show episodes that are marked as new; if
50 URL is given, show only pending downloads
51 from the channel at this URL
52 queue URL Add episode at URL to pending episodes
53 skip URL Remove episode at URL from pending episodes
55 details URL Show information about episode at URL
56 episodes [URL] Show a list of all episodes; if URL is given
57 it should be a channel URL and only episodes
58 from that channel will be displayed
60 delete URL Delete the downloaded episode at URL
62 disable URL Mark the feed as disabled
63 enable URL Mark the feed as enabled
65 Portable device synchronization
66 -------------------------------
68 device Show information about your device
69 sync Synchronize downloaded episodes to device
71 Helper commands
72 ---------------
74 youtube resolve [URL] Resolve the YouTube URL to a download URL
75 youtube download [URL] Download a video from YouTube via its URL
77 """
79 import sys
80 import os
81 import inspect
83 gpodder_script = sys.argv[0]
84 if os.path.islink(gpodder_script):
85 gpodder_script = os.readlink(gpodder_script)
86 gpodder_dir = os.path.join(os.path.dirname(gpodder_script), '..')
87 prefix = os.path.abspath(os.path.normpath(gpodder_dir))
89 src_dir = os.path.join(prefix, 'src')
90 data_dir = os.path.join(prefix, 'data')
92 if os.path.exists(src_dir) and os.path.exists(data_dir) and \
93 not prefix.startswith('/usr'):
94 # Run gPodder from local source folder (not installed)
95 sys.path.insert(0, src_dir)
98 import gpodder
99 _ = gpodder.gettext
101 # Use only the gPodder API here, so this serves both as an example
102 # and as a motivation to provide all functionality in the API :)
103 from gpodder import api
107 class gPodderCli(object):
108 def __init__(self):
109 self.client = api.PodcastClient()
111 # -------------------------------------------------------------------
113 def subscribe(self, url, title=None):
114 if self.client.get_podcast(url) is not None:
115 self._info(_('You are already subscribed to %s.' % url))
116 return True
118 if self.client.create_podcast(url, title) is None:
119 self._error(_('Cannot download feed for %s.') % url)
120 return True
122 self.client.finish()
124 self._info(_('Successfully added %s.' % url))
125 return True
127 def rename(self, url, title):
128 podcast = self.client.get_podcast(url)
130 if podcast is None:
131 self._error(_('You are not subscribed to %s.') % url)
132 else:
133 old_title = podcast.title
134 podcast.rename(title)
135 self.client.finish()
136 self._info(_('Renamed %s to %s.') % (old_title, title))
138 return True
140 def unsubscribe(self, url):
141 podcast = self.client.get_podcast(url)
143 if podcast is None:
144 self._error(_('You are not subscribed to %s.') % url)
145 else:
146 podcast.delete()
147 self.client.finish()
148 self._error(_('Unsubscribed from %s.') % url)
150 return True
152 def _episodesList(self, podcast):
153 def status_str(episode):
154 if episode.is_new:
155 return ' * '
156 if episode.is_downloaded:
157 return ' ▉ '
158 if episode.is_deleted:
159 return ' ░ '
161 return ' '
163 episodes = ('%3d. %s %s' % (i+1, status_str(e), e.title) for i, e in enumerate(podcast.get_episodes()))
164 return episodes
166 def info(self, url):
167 podcast = self.client.get_podcast(url)
169 if podcast is None:
170 self._error(_('You are not subscribed to %s.') % url)
171 else:
172 title, url, status = podcast.title, podcast.url, podcast.feed_update_status_msg()
173 episodes = self._episodesList(podcast)
174 episodes = '\n '.join(episodes)
175 print >>sys.stdout, """
176 Title: %(title)s
177 URL: %(url)s
178 Feed update is %(status)s
180 Episodes:
181 %(episodes)s
182 """ % locals()
184 return True
186 def episodes(self, url=None):
187 for podcast in self.client.get_podcasts():
188 podcast_printed = False
189 if url is None or podcast.url == url:
190 episodes = self._episodesList(podcast)
191 episodes = '\n '.join(episodes)
192 print >>sys.stdout, """
193 Episodes from %s:
195 """ % (podcast.url, episodes)
196 return True
198 def list(self):
199 for podcast in self.client.get_podcasts():
200 print podcast.url
202 return True
204 def update(self, url=None):
205 for podcast in self.client.get_podcasts():
206 if url is None and podcast.update_enabled():
207 print 'Updating', podcast.title
208 podcast.update()
209 print 'Done.'
210 elif podcast.url == url:
211 # Don't need to check for update_enabled()
212 print 'Updating', podcast.title
213 podcast.update()
214 print 'Done.'
216 return True
218 def pending(self, url=None):
219 count = 0
220 for podcast in self.client.get_podcasts():
221 podcast_printed = False
222 if url is None or podcast.url == url:
223 for episode in podcast.get_episodes():
224 if episode.is_new:
225 if not podcast_printed:
226 print podcast.title
227 podcast_printed = True
228 print ' ', episode.title
229 count += 1
231 print count, 'episodes pending.'
232 return True
234 def download(self, url=None):
235 count = 0
236 for podcast in self.client.get_podcasts():
237 podcast_printed = False
238 if url is None or podcast.url == url:
239 for episode in podcast.get_episodes():
240 if episode.is_new:
241 if not podcast_printed:
242 print podcast.title
243 podcast_printed = True
244 print ' ', episode.title
245 episode.download()
246 count += 1
248 print count, 'episodes downloaded.'
249 return True
251 def disable(self, url):
252 podcast = self.client.get_podcast(url)
254 if podcast is None:
255 self._error(_('You are not subscribed to %s.') % url)
256 else:
257 podcast.disable()
258 self.client.finish()
259 self._error(_('Disabling feed update from %s.') % url)
261 return True
263 def enable(self, url):
264 podcast = self.client.get_podcast(url)
266 if podcast is None:
267 self._error(_('You are not subscribed to %s.') % url)
268 else:
269 podcast.enable()
270 self.client.finish()
271 self._error(_('Enabling feed update from %s.') % url)
273 return True
277 def sync(self):
278 self.client.synchronize_device()
281 def youtube_resolve(self, url):
282 yurl = self.client.youtube_url_resolver(url)
283 print "Youtube URL %s resolved to %s" % (url, yurl)
284 return True
286 # -------------------------------------------------------------------
288 def _error(self, *args):
289 print >>sys.stderr, ' '.join(args)
291 def _info(self, *args):
292 print >>sys.stdout, ' '.join(args)
294 def _checkargs(self, func, command_line):
295 args, varargs, keywords, defaults = inspect.getargspec(func)
296 args.pop(0) # Remove "self" from args
297 defaults = defaults or ()
298 minarg, maxarg = len(args)-len(defaults), len(args)
300 if len(command_line) < minarg or len(command_line) > maxarg:
301 self._error('Wrong argument count for %s.' % func.__name__)
302 return False
304 return func(*command_line)
307 def _parse(self, command_line):
308 if not command_line:
309 return False
311 command = command_line.pop(0)
312 if command.startswith('_'):
313 self._error(_('This command is not available.'))
314 return False
316 for name, func in inspect.getmembers(self):
317 if inspect.ismethod(func) and name == command:
318 return self._checkargs(func, command_line)
320 self._error(_('The requested function is not available.'))
321 return False
324 if __name__ == '__main__':
325 cli = gPodderCli()
326 cli._parse(sys.argv[1:]) or sys.stderr.write(__doc__)