Fix attach_to_status_icon bug (bug 860)
[gpodder.git] / bin / gpo
blob52a151e80df11a9bbace96bb75b90acecbe37398
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)
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 # Use only the gPodder API here, so this serves both as an example
98 # and as a motivation to provide all functionality in the API :)
99 from gpodder import api
103 class gPodderCli(object):
104 def __init__(self):
105 self.client = api.PodcastClient()
107 # -------------------------------------------------------------------
109 def subscribe(self, url, title=None):
110 if self.client.get_podcast(url) is not None:
111 self._info(_('You are already subscribed to %s.' % url))
112 return True
114 if self.client.create_podcast(url, title) is None:
115 self._error(_('Cannot download feed for %s.') % url)
116 return True
118 self.client.finish()
120 self._info(_('Successfully added %s.' % url))
121 return True
123 def rename(self, url, title):
124 podcast = self.client.get_podcast(url)
126 if podcast is None:
127 self._error(_('You are not subscribed to %s.') % url)
128 else:
129 old_title = podcast.title
130 podcast.rename(title)
131 self.client.finish()
132 self._info(_('Renamed %s to %s.') % (old_title, title))
134 return True
136 def unsubscribe(self, url):
137 podcast = self.client.get_podcast(url)
139 if podcast is None:
140 self._error(_('You are not subscribed to %s.') % url)
141 else:
142 podcast.delete()
143 self.client.finish()
144 self._error(_('Unsubscribed from %s.') % url)
146 return True
148 def info(self, url):
149 podcast = self.client.get_podcast(url)
151 if podcast is None:
152 self._error(_('You are not subscribed to %s.') % url)
153 else:
154 title, url = podcast.title, podcast.url
155 def status_str(episode):
156 if episode.is_new:
157 return ' * '
158 if episode.is_downloaded:
159 return ' ▉ '
160 if episode.is_deleted:
161 return ' ░ '
163 return ' '
165 episodes = ('%3d. %s %s' % (i+1, status_str(e), e.title) for i, e in enumerate(podcast.get_episodes()))
166 episodes = '\n '.join(episodes)
167 print >>sys.stdout, """
168 Title: %(title)s
169 URL: %(url)s
171 Episodes:
172 %(episodes)s
173 """ % locals()
175 return True
177 def list(self):
178 for podcast in self.client.get_podcasts():
179 print podcast.url
181 return True
183 def update(self):
184 for podcast in self.client.get_podcasts():
185 print 'Updating', podcast.title
186 podcast.update()
187 print 'Done.'
189 return True
191 def pending(self):
192 count = 0
193 for podcast in self.client.get_podcasts():
194 podcast_printed = False
195 for episode in podcast.get_episodes():
196 if episode.is_new:
197 if not podcast_printed:
198 print podcast.title
199 podcast_printed = True
200 print ' ', episode.title
201 count += 1
203 print count, 'episodes pending.'
204 return True
206 def download(self):
207 count = 0
208 for podcast in self.client.get_podcasts():
209 podcast_printed = False
210 for episode in podcast.get_episodes():
211 if episode.is_new:
212 if not podcast_printed:
213 print podcast.title
214 podcast_printed = True
215 print ' ', episode.title
216 episode.download()
217 count += 1
219 print count, 'episodes downloaded.'
220 return True
222 def sync(self):
223 self.client.synchronize_device()
224 return True
226 # -------------------------------------------------------------------
228 def _error(self, *args):
229 print >>sys.stderr, ' '.join(args)
231 def _info(self, *args):
232 print >>sys.stdout, ' '.join(args)
234 def _checkargs(self, func, command_line):
235 args, varargs, keywords, defaults = inspect.getargspec(func)
236 args.pop(0) # Remove "self" from args
237 defaults = defaults or ()
238 minarg, maxarg = len(args)-len(defaults), len(args)
240 if len(command_line) < minarg or len(command_line) > maxarg:
241 self._error('Wrong argument count for %s.' % func.__name__)
242 return False
244 return func(*command_line)
247 def _parse(self, command_line):
248 if not command_line:
249 return False
251 command = command_line.pop(0)
252 if command.startswith('_'):
253 self._error(_('This command is not available.'))
254 return False
256 for name, func in inspect.getmembers(self):
257 if inspect.ismethod(func) and name == command:
258 return self._checkargs(func, command_line)
260 return False
263 if __name__ == '__main__':
264 cli = gPodderCli()
265 cli._parse(sys.argv[1:]) or sys.stderr.write(__doc__)