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
28 Usage: gpo [COMMAND] [params...]
30 - Subscription management -
32 subscribe URL [TITLE] Subscribe to a new feed at URL (as TITLE)
33 rename URL TITLE Rename feed at URL to TITLE
34 unsubscribe URL Unsubscribe from feed at URL
35 enable URL Enable feed updates for the feed at URL
36 disable URL Disable feed updates for the feed at URL
38 info URL Show information about feed at URL
39 list List all subscribed podcasts
40 update [URL] Check for new episodes (all or only at URL)
42 - Episode management -
44 download [URL] Download new episodes (all or only from URL)
45 pending [URL] List new episodes (all or only from URL)
46 episodes [URL] List episodes (all or only from URL)
50 sync Synchronize downloaded episodes to device
51 youtube [URL] Resolve the YouTube URL to a download URL
60 gpodder_script
= sys
.argv
[0]
61 if os
.path
.islink(gpodder_script
):
62 gpodder_script
= os
.readlink(gpodder_script
)
63 gpodder_dir
= os
.path
.join(os
.path
.dirname(gpodder_script
), '..')
64 prefix
= os
.path
.abspath(os
.path
.normpath(gpodder_dir
))
66 src_dir
= os
.path
.join(prefix
, 'src')
67 data_dir
= os
.path
.join(prefix
, 'data')
69 if os
.path
.exists(src_dir
) and os
.path
.exists(data_dir
) and \
70 not prefix
.startswith('/usr'):
71 # Run gPodder from local source folder (not installed)
72 sys
.path
.insert(0, src_dir
)
78 # Use only the gPodder API here, so this serves both as an example
79 # and as a motivation to provide all functionality in the API :)
80 from gpodder
import api
83 return '\033[91m' + x
+ '\033[0m'
86 return '\033[92m' + x
+ '\033[0m'
89 return '\033[94m' + x
+ '\033[0m'
91 class gPodderCli(object):
95 self
.client
= api
.PodcastClient()
96 self
._current
_action
= ''
98 def _start_action(self
, msg
, *args
):
100 if len(line
) > self
.COLUMNS
-7:
101 line
= line
[:self
.COLUMNS
-7-3] + '...'
103 line
= line
+ (' '*(self
.COLUMNS
-7-len(line
)))
104 self
._current
_action
= line
105 sys
.stdout
.write(line
)
108 def _update_action(self
, progress
):
109 progress
= '%3.0f%%' % (progress
*100.,)
110 result
= '['+inblue(progress
)+']'
111 sys
.stdout
.write('\r' + self
._current
_action
+ result
)
114 def _finish_action(self
, success
=True):
115 result
= '['+ingreen('DONE')+']' if success
else '['+inred('FAIL')+']'
116 print '\r' + self
._current
_action
+ result
117 self
._current
_action
= ''
119 # -------------------------------------------------------------------
121 def subscribe(self
, url
, title
=None):
122 if self
.client
.get_podcast(url
) is not None:
123 self
._info
(_('You are already subscribed to %s.' % url
))
126 if self
.client
.create_podcast(url
, title
) is None:
127 self
._error
(_('Cannot download feed for %s.') % url
)
132 self
._info
(_('Successfully added %s.' % url
))
135 def rename(self
, url
, title
):
136 podcast
= self
.client
.get_podcast(url
)
139 self
._error
(_('You are not subscribed to %s.') % url
)
141 old_title
= podcast
.title
142 podcast
.rename(title
)
144 self
._info
(_('Renamed %s to %s.') % (old_title
, title
))
148 def unsubscribe(self
, url
):
149 podcast
= self
.client
.get_podcast(url
)
152 self
._error
(_('You are not subscribed to %s.') % url
)
156 self
._error
(_('Unsubscribed from %s.') % url
)
160 def _episodesList(self
, podcast
):
161 def status_str(episode
):
164 if episode
.is_downloaded
:
166 if episode
.is_deleted
:
171 episodes
= ('%3d. %s %s' % (i
+1, status_str(e
), e
.title
) for i
, e
in enumerate(podcast
.get_episodes()))
175 podcast
= self
.client
.get_podcast(url
)
178 self
._error
(_('You are not subscribed to %s.') % url
)
180 title
, url
, status
= podcast
.title
, podcast
.url
, podcast
.feed_update_status_msg()
181 episodes
= self
._episodesList
(podcast
)
182 episodes
= '\n '.join(episodes
)
183 print >>sys
.stdout
, """
186 Feed update is %(status)s
194 def episodes(self
, url
=None):
195 for podcast
in self
.client
.get_podcasts():
196 podcast_printed
= False
197 if url
is None or podcast
.url
== url
:
198 episodes
= self
._episodesList
(podcast
)
199 episodes
= '\n '.join(episodes
)
200 print >>sys
.stdout
, """
203 """ % (podcast
.url
, episodes
)
207 for podcast
in self
.client
.get_podcasts():
212 def update(self
, url
=None):
213 for podcast
in self
.client
.get_podcasts():
214 if url
is None and podcast
.update_enabled():
215 self
._start
_action
('Updating %s', podcast
.title
)
217 self
._finish
_action
()
218 elif podcast
.url
== url
:
219 # Don't need to check for update_enabled()
220 self
._start
_action
('Updating %s', podcast
.title
)
222 self
._finish
_action
()
226 def pending(self
, url
=None):
228 for podcast
in self
.client
.get_podcasts():
229 podcast_printed
= False
230 if url
is None or podcast
.url
== url
:
231 for episode
in podcast
.get_episodes():
233 if not podcast_printed
:
235 podcast_printed
= True
236 print ' ', episode
.title
239 print count
, 'episodes pending.'
242 def download(self
, url
=None):
244 for podcast
in self
.client
.get_podcasts():
245 podcast_printed
= False
246 if url
is None or podcast
.url
== url
:
247 for episode
in podcast
.get_episodes():
249 if not podcast_printed
:
250 print inblue(podcast
.title
)
251 podcast_printed
= True
252 self
._start
_action
('Downloading %s', episode
.title
)
253 episode
.download(self
._update
_action
)
254 self
._finish
_action
()
257 print count
, 'episodes downloaded.'
260 def disable(self
, url
):
261 podcast
= self
.client
.get_podcast(url
)
264 self
._error
(_('You are not subscribed to %s.') % url
)
268 self
._error
(_('Disabling feed update from %s.') % url
)
272 def enable(self
, url
):
273 podcast
= self
.client
.get_podcast(url
)
276 self
._error
(_('You are not subscribed to %s.') % url
)
280 self
._error
(_('Enabling feed update from %s.') % url
)
285 self
.client
.synchronize_device()
287 def youtube(self
, url
):
288 yurl
= self
.client
.youtube_url_resolver(url
)
292 # -------------------------------------------------------------------
294 def _error(self
, *args
):
295 print >>sys
.stderr
, inred(' '.join(args
))
297 def _info(self
, *args
):
298 print >>sys
.stdout
, ' '.join(args
)
300 def _checkargs(self
, func
, command_line
):
301 args
, varargs
, keywords
, defaults
= inspect
.getargspec(func
)
302 args
.pop(0) # Remove "self" from args
303 defaults
= defaults
or ()
304 minarg
, maxarg
= len(args
)-len(defaults
), len(args
)
306 if len(command_line
) < minarg
or len(command_line
) > maxarg
:
307 self
._error
('Wrong argument count for %s.' % func
.__name
__)
310 return func(*command_line
)
313 def _parse(self
, command_line
):
317 command
= command_line
.pop(0)
318 if command
.startswith('_'):
319 self
._error
(_('This command is not available.'))
322 for name
, func
in inspect
.getmembers(self
):
323 if inspect
.ismethod(func
) and name
== command
:
324 return self
._checkargs
(func
, command_line
)
326 self
._error
(_('The requested function is not available.'))
331 s
= re
.sub(r
' .{27}', lambda m
: inblue(m
.group(0)), s
)
332 s
= re
.sub(r
' - .*', lambda m
: ingreen(m
.group(0)), s
)
335 if __name__
== '__main__':
337 cli
._parse
(sys
.argv
[1:]) or sys
.stderr
.write(stylize(__doc__
))