MythTV  0.26-pre
mirobridge_interpreter_2_0_3.py
Go to the documentation of this file.
00001 # Miro - an RSS based video player application
00002 # Copyright (C) 2005-2009 Participatory Culture Foundation
00003 #
00004 # This program is free software; you can redistribute it and/or modify
00005 # it under the terms of the GNU General Public License as published by
00006 # the Free Software Foundation; either version 2 of the License, or
00007 # (at your option) any later version.
00008 #
00009 # This program is distributed in the hope that it will be useful,
00010 # but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012 # GNU General Public License for more details.
00013 #
00014 # You should have received a copy of the GNU General Public License
00015 # along with this program; if not, write to the Free Software
00016 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
00017 #
00018 # In addition, as a special exception, the copyright holders give
00019 # permission to link the code of portions of this program with the OpenSSL
00020 # library.
00021 #
00022 # You must obey the GNU General Public License in all respects for all of
00023 # the code used other than OpenSSL. If you modify file(s) with this
00024 # exception, you may extend this exception to your version of the file(s),
00025 # but you are not obligated to do so. If you do not wish to do so, delete
00026 # this exception statement from your version. If you delete this exception
00027 # statement from all source files in the program, then also delete it here.
00028 ###########################################################################
00029 # The original source "...miro/frontends/cli/interpreter.py" 
00030 # was modified for the purposes of the MythTV script mirobridge.py 
00031 ###########################################################################
00032 
00033 import cmd
00034 import threading
00035 import time
00036 import Queue
00037 
00038 from miro import app
00039 from miro import dialogs
00040 from miro import eventloop
00041 from miro import folder
00042 from miro import indexes
00043 from miro import util
00044 from miro import views
00045 from miro.frontends.cli import clidialog
00046 from miro.plat import resources
00047 from miro import fileutil
00048 from miro import feed
00049 
00050 ## mirobridge.py import additions - All to get feed updates and auto downloads working
00051 import os, sys, subprocess, re, fnmatch, string
00052 import logging
00053 from miro.singleclick import parse_command_line_args
00054 from miro import moviedata
00055 from miro import autodler
00056 from miro import downloader
00057 from miro import iconcache
00058 from miro.clock import clock
00059 from miro import filetypes
00060 
00061 
00062 def run_in_event_loop(func):
00063     def decorated(*args, **kwargs):
00064         return_hack = []
00065         event = threading.Event()
00066         def runThenSet():
00067             try:
00068                 return_hack.append(func(*args, **kwargs))
00069             finally:
00070                 event.set()
00071         eventloop.addUrgentCall(runThenSet, 'run in event loop')
00072         event.wait()
00073         if return_hack:
00074             return return_hack[0]
00075     decorated.__doc__ = func.__doc__
00076     return decorated
00077 
00078 class FakeTab:
00079     def __init__(self, tab_type, tabTemplateBase):
00080         self.type = tab_type
00081         self.tabTemplateBase = tabTemplateBase
00082 
00083 class MiroInterpreter(cmd.Cmd):
00084     def __init__(self):
00085         cmd.Cmd.__init__(self)
00086         self.quit_flag = False
00087         self.tab = None
00088         self.init_database_objects()
00089 
00090     @run_in_event_loop
00091     def init_database_objects(self):
00092         self.channelTabs = util.getSingletonDDBObject(views.channelTabOrder)
00093         self.playlistTabs = util.getSingletonDDBObject(views.playlistTabOrder)
00094         self.tab_changed()
00095 
00096     def tab_changed(self):
00097         """Calculate the current prompt.  This method access database objects,
00098         so it should only be called from the backend event loop
00099         """
00100         if self.tab is None:
00101             self.prompt = "> "
00102             self.selection_type = None
00103         elif self.tab.type == 'feed':
00104             if isinstance(self.tab.obj, folder.ChannelFolder):
00105                 self.prompt = "channel folder: %s > " % self.tab.obj.get_title()
00106                 self.selection_type = 'channel-folder'
00107             else:
00108                 self.prompt = "channel: %s > " % self.tab.obj.get_title()
00109                 self.selection_type = 'feed'
00110         elif self.tab.type == 'playlist':
00111             self.prompt = "playlist: %s > " % self.tab.obj.get_title()
00112             self.selection_type = 'playlist'
00113         elif (self.tab.type == 'statictab' and
00114                 self.tab.tabTemplateBase == 'downloadtab'):
00115             self.prompt = "downloads > "
00116             self.selection_type = 'downloads'
00117         else:
00118             raise ValueError("Unknown tab type")
00119 
00120     def postcmd(self, stop, line):
00121         # HACK
00122         # If the last command results in a dialog, give it a little time to
00123         # pop up
00124         time.sleep(0.1)
00125         while True:
00126             try:
00127                 dialog = app.cli_events.dialog_queue.get_nowait()
00128             except Queue.Empty:
00129                 break
00130             clidialog.handle_dialog(dialog)
00131 
00132         return self.quit_flag
00133 
00134     def do_help(self, line):
00135         """help -- Lists commands and help."""
00136         commands = [m for m in dir(self) if m.startswith("do_")]
00137         for mem in commands:
00138             docstring = getattr(self, mem).__doc__
00139             print "    ", docstring
00140 
00141     def do_quit(self, line):
00142         """quit -- Quits Miro cli."""
00143         self.quit_flag = True
00144 
00145     @run_in_event_loop
00146     def do_feed(self, line):
00147         """feed <name> -- Selects a feed by name."""
00148         for tab in self.channelTabs.getView():
00149             if tab.obj.get_title() == line:
00150                 self.tab = tab
00151                 self.tab_changed()
00152                 return
00153         print "Error: %s not found" % line
00154 
00155     @run_in_event_loop
00156     def do_rmfeed(self, line):
00157         """rmfeed <name> -- Deletes a feed."""
00158         for tab in self.channelTabs.getView():
00159             if tab.obj.get_title() == line:
00160                 tab.obj.remove()
00161                 return
00162         print "Error: %s not found" % line
00163 
00164     @run_in_event_loop
00165     def complete_feed(self, text, line, begidx, endidx):
00166         return self.handle_tab_complete(text, self.channelTabs.getView())
00167 
00168     @run_in_event_loop
00169     def complete_rmfeed(self, text, line, begidx, endidx):
00170         return self.handle_tab_complete(text, self.channelTabs.getView())
00171 
00172     @run_in_event_loop
00173     def complete_playlist(self, text, line, begidx, endidx):
00174         return self.handle_tab_complete(text, self.playlistTabs.getView())
00175 
00176     def handle_tab_complete(self, text, view):
00177         text = text.lower()
00178         matches = []
00179         for tab in view:
00180             if tab.obj.get_title().lower().startswith(text):
00181                 matches.append(tab.obj.get_title())
00182         return matches
00183 
00184     def handle_item_complete(self, text, view, filterFunc=lambda i: True):
00185         text = text.lower()
00186         matches = []
00187         for item in view:
00188             if (item.get_title().lower().startswith(text) and
00189                     filterFunc(item)):
00190                 matches.append(item.get_title())
00191         return matches
00192 
00193 
00194     ###################################################################################
00195     #
00196     # Start of mythbridge specific routines
00197     #
00198     ###################################################################################
00199     @run_in_event_loop
00200     def do_mythtv_update_autodownload(self, line):
00201         """Update feeds and auto-download"""
00202         logging.info("Starting auto downloader...")
00203         autodler.start_downloader()
00204         feed.expire_items()
00205         starttime = clock()
00206         logging.timing("Icon clear: %.3f", clock() - starttime)
00207         logging.info("Starting video updates")
00208         moviedata.movieDataUpdater.startThread()
00209         parse_command_line_args()
00210         # autoupdate.check_for_updates()
00211         # Wait a bit before starting the downloader daemon.  It can cause a bunch
00212         # of disk/CPU load, so try to avoid it slowing other stuff down.
00213         eventloop.addTimeout(5, downloader.startupDownloader,
00214                 "start downloader daemon")
00215         # ditto for feed updates
00216         eventloop.addTimeout(30, feed.start_updates, "start feed updates")
00217         # ditto for clearing stale icon cache files, except it's the very lowest
00218         # priority
00219         eventloop.addTimeout(10, iconcache.clear_orphans, "clear orphans")
00220 
00221     def movie_data_program_info(self, movie_path, thumbnail_path):
00222         extractor_path = os.path.join(os.path.split(__file__)[0], "gst_extractor.py")
00223         return ((sys.executable, extractor_path, movie_path, thumbnail_path), None)
00224 
00225     @run_in_event_loop
00226     def do_mythtv_check_downloading(self, line):
00227         """Check if any items are being downloaded. Set True or False"""
00228         self.downloading = False
00229         downloadingItems = views.downloadingItems
00230         count = len(downloadingItems)
00231         for item in downloadingItems:
00232             logging.info(u"(%s - %s) video is downloading with (%0.0f%%) complete" % (item.get_channel_title(True).replace(u'/',u'-'), item.get_title().replace(u'/',u'-'), item.download_progress()))
00233         if not count:
00234             logging.info(u"No items downloading")
00235         if count:
00236             self.downloading = True
00237 
00238     @run_in_event_loop
00239     def do_mythtv_updatewatched(self, line):
00240         """Process MythTV update watched videos"""
00241         items = views.watchableItems
00242         for video in self.videofiles:
00243             for item in items:
00244                 if item.get_filename() == video:
00245                      break
00246             else:
00247                 logging.info(u"Item for Miro video (%s) not found, skipping" % video)
00248                 continue
00249             if self.simulation:
00250                 logging.info(u"Simulation: Item (%s - %s) marked as seen and watched" % (item.get_channel_title(True), item.get_title()))
00251             else:
00252                 item.markItemSeen(markOtherItems=False)
00253                 self.statistics[u'Miro_marked_watch_seen']+=1
00254                 logging.info(u"Item (%s - %s) marked as seen and watched" % (item.get_channel_title(True), item.get_title()))
00255 
00256     @run_in_event_loop
00257     def do_mythtv_getunwatched(self, line):
00258         """Process MythTV get all un-watched video details"""
00259         if self.verbose:
00260              print
00261              print u"Getting details on un-watched Miro videos"
00262 
00263         self.videofiles = []
00264         if len(views.watchableItems):
00265             if self.verbose:
00266                 print u"%-20s %-10s %s" % (u"State", u"Size", u"Name")
00267                 print u"-" * 70
00268             for item in views.watchableItems:
00269                 # Skip any audio file as MythTV Internal player may abort the MythTV Frontend on a MP3
00270                 if not item.isVideo:
00271                     continue
00272                 state = item.get_state()
00273                 if not state == u'newly-downloaded':
00274                     continue
00275                 # Skip any bittorrent video downloads for legal concerns
00276                 if filetypes.is_torrent_filename(item.getURL()):
00277                     continue
00278                 self.printItems(item)
00279                 self.videofiles.append(self._get_item_dict(item))
00280             if self.verbose:
00281                 print
00282         if not len(self.videofiles):
00283              logging.info(u"No un-watched Miro videos")
00284 
00285     @run_in_event_loop
00286     def do_mythtv_getwatched(self, line):
00287         """Process MythTV get all watched/saved video details"""
00288         if self.verbose:
00289            print
00290            print u"Getting details on watched/saved Miro videos"
00291         self.videofiles = []
00292         if len(views.watchableItems):
00293             if self.verbose:
00294                 print u"%-20s %-10s %s" % (u"State", u"Size", u"Name")
00295                 print "-" * 70
00296             for item in views.watchableItems:
00297                 # Skip any audio file as MythTV Internal player may abort the MythTV Frontend on a MP3
00298                 if not item.isVideo:
00299                     continue
00300                 state = item.get_state()
00301                 if state == u'newly-downloaded':
00302                     continue
00303                 # Skip any bittorrent video downloads for legal concerns
00304                 if filetypes.is_torrent_filename(item.getURL()):
00305                     continue
00306                 self.printItems(item)
00307                 self.videofiles.append(self._get_item_dict(item))
00308             if self.verbose:
00309                 print
00310         if not len(self.videofiles):
00311              logging.info(u"No watched/saved Miro videos")
00312 
00313     def printItems(self, item):
00314         if not self.verbose:
00315             return
00316         state = item.get_state()
00317         if state == u'downloading':
00318             state += u' (%0.0f%%)' % item.download_progress()
00319         print u"%-20s %-10s %s" % (state, item.get_size_for_display(),
00320             item.get_title())
00321     # end printItems()
00322 
00323     @run_in_event_loop
00324     def do_mythtv_item_remove(self, args):
00325         """Removes an item from Miro by file name or Channel and title"""
00326         for item in views.watchableItems:
00327              if isinstance(args, list):
00328                   if filter(self.is_not_punct_char, item.get_channel_title(True).lower()) == filter(self.is_not_punct_char, args[0].lower()) and (filter(self.is_not_punct_char, item.get_title().lower())).startswith(filter(self.is_not_punct_char, args[1].lower())):
00329                      break
00330              elif filter(self.is_not_punct_char, item.get_filename().lower()) == filter(self.is_not_punct_char, args.lower()):
00331                  break
00332         else:
00333             logging.info(u"No item named %s" % args)
00334             return
00335         if item.is_downloaded():
00336             if self.simulation:
00337                 logging.info(u"Simulation: Item (%s - %s) has been removed from Miro" % (item.get_channel_title(True), item.get_title()))
00338             else:
00339                 item.expire()
00340                 self.statistics[u'Miro_videos_deleted']+=1
00341                 logging.info(u'%s has been removed from Miro' % item.get_title())
00342         else:
00343             logging.info(u'%s is not downloaded' % item.get_title())
00344 
00345 
00346     def _get_item_dict(self, item):
00347         """Take an item and convert all elements into a dictionary
00348         return a dictionary of item elements
00349         """
00350         def compatibleGraphics(filename):
00351             if filename:
00352                 (dirName, fileName) = os.path.split(filename)
00353                 (fileBaseName, fileExtension)=os.path.splitext(fileName)
00354                 if not fileExtension[1:] in [u"png", u"jpg", u"bmp", u"gif"]:
00355                     return u''
00356                 else:
00357                     return filename
00358             else:
00359                 return u''
00360 
00361         def useImageMagick(screenshot):
00362             """ Using ImageMagick's utility 'identify'. Decide whether the screen shot is worth using.
00363             >>> useImageMagick('identify screenshot.jpg')
00364             >>> Example returned information "rose.jpg JPEG 640x480 DirectClass 87kb 0.050u 0:01"
00365             >>> u'' if the screenshot quality is too low
00366             >>> screenshot if the quality is good enough to use
00367             """
00368             if not self.imagemagick: # If imagemagick is not installed do not bother checking
00369                return u''
00370 
00371             width_height = re.compile(u'''^(.+?)[ ]\[?([0-9]+)x([0-9]+)[^\\/]*$''', re.UNICODE)
00372             p = subprocess.Popen(u'identify "%s"' % (screenshot), shell=True, bufsize=4096, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True)
00373 
00374             response = p.stdout.readline()
00375             if response:
00376                 match = width_height.match(response)
00377                 if match:
00378                     dummy, width, height = match.groups()
00379                     width, height = int(width), int(height)
00380                     if width >= 320:
00381                         return screenshot
00382                 return u''
00383             else:
00384                 return u''
00385             return screenshot
00386         # end useImageMagick()
00387 
00388         item_icon_filename = None
00389         channel_icon = None
00390         if item.getFeed():
00391             channel_icon = fileutil.expand_filename(item.getFeed().iconCache.get_filename())
00392 
00393         if item.iconCache and item.iconCache.filename:
00394             item_icon_filename = item.iconCache.filename
00395 
00396         # Conform to maximum length for MythTV database fields title and subtitle
00397         maximum_length = 128
00398         channel_title = item.get_channel_title(True).replace(u'/',u'-')
00399         if channel_title:
00400             if len(channel_title) > maximum_length:
00401                 channel_title = channel_title[:maximum_length]
00402             channel_title = channel_title.replace(u'"', u'')    # These characters mess with filenames
00403         title = item.get_title().replace(u'/',u'-')
00404         if title:
00405             if len(title) > maximum_length:
00406                 title = title[:maximum_length]
00407             title = title.replace(u'"', u'')    # These characters mess with filenames
00408             title = title.replace(u"'", u'')    # These characters mess with filenames
00409 
00410         item_dict =  {u'feed_id': item.feed_id, u'parent_id': item.parent_id, u'isContainerItem': item.isContainerItem, u'isVideo': item.isVideo, u'seen': item.seen, u'autoDownloaded': item.autoDownloaded, u'pendingManualDL': item.pendingManualDL, u'downloadedTime': item.downloadedTime, u'watchedTime': item.watchedTime, u'pendingReason': item.pendingReason, u'title': title,  u'expired': item.expired, u'keep': item.keep, u'videoFilename': item.get_filename(), u'eligibleForAutoDownload': item.eligibleForAutoDownload, u'duration': item.duration, u'screenshot': item.screenshot, u'resized_screenshots': item.resized_screenshots, u'resumeTime': item.resumeTime, u'channelTitle': channel_title, u'description': item.get_description(), u'size': item._get_size(), u'releasedate': item.get_release_date_obj(), u'length': item.get_duration_value(), u'channel_icon': channel_icon, u'item_icon': item_icon_filename, u'inetref': u'', u'season': 1, u'episode': 1,}
00411 
00412         if not item_dict[u'screenshot']:
00413             if item_dict[u'item_icon']:
00414                 item_dict[u'screenshot'] = useImageMagick(item_dict[u'item_icon'])
00415 
00416         for key in [u'screenshot', u'channel_icon', u'item_icon']:
00417             if item_dict[key]:
00418                 item_dict[key] = compatibleGraphics(item_dict[key])
00419             #if self.verbose:
00420             #    if item_dict[key]:
00421             #        print "item (%s - %s) %s (%s)" % (channel_title, title, key, item_dict[key])
00422             #    else:
00423             #        print "item (%s - %s) does NOT have a %s" % (channel_title, title, key)
00424 
00425         #print item_dict
00426         return item_dict
00427 
00428 
00429     # Two routines used for Channel title search and matching
00430     def is_punct_char(self, char):
00431         '''check if char is punctuation char
00432         return True if char is punctuation
00433         return False if char is not punctuation
00434         '''
00435         return char in string.punctuation
00436 
00437     def is_not_punct_char(self, char):
00438         '''check if char is not punctuation char
00439         return True if char is not punctuation
00440         return False if chaar is punctuation
00441         '''
00442         return not self.is_punct_char(char)
00443 
00444     ###################################################################################
00445     #
00446     # End of mythbridge specific routines
00447     #
00448     ###################################################################################
00449 
00450     @run_in_event_loop
00451     def do_feeds(self, line):
00452         """feeds -- Lists all feeds."""
00453         current_folder = None
00454         for tab in self.channelTabs.getView():
00455             if isinstance(tab.obj, folder.ChannelFolder):
00456                 current_folder = tab.obj
00457             elif tab.obj.getFolder() is not current_folder:
00458                 current_folder = None
00459             if current_folder is None:
00460                 print tab.obj.get_title()
00461             elif current_folder is tab.obj:
00462                 print "[Folder] %s" % tab.obj.get_title()
00463             else:
00464                 print " - %s" % tab.obj.get_title()
00465 
00466     @run_in_event_loop
00467     def do_play(self, line):
00468         """play <name> -- Plays an item by name in an external player."""
00469         if self.selection_type is None:
00470             print "Error: No feed/playlist selected"
00471             return
00472         item = self._find_item(line)
00473         if item is None:
00474             print "No item named %r" % line
00475             return
00476         if item.is_downloaded():
00477             resources.open_file(item.get_video_filename())
00478         else:
00479             print '%s is not downloaded' % item.get_title()
00480 
00481     @run_in_event_loop
00482     def do_playlists(self, line):
00483         """playlists -- Lists all playlists."""
00484         for tab in self.playlistTabs.getView():
00485             print tab.obj.get_title()
00486 
00487     @run_in_event_loop
00488     def do_playlist(self, line):
00489         """playlist <name> -- Selects a playlist."""
00490         for tab in self.playlistTabs.getView():
00491             if tab.obj.get_title() == line:
00492                 self.tab = tab
00493                 self.tab_changed()
00494                 return
00495         print "Error: %s not found" % line
00496 
00497     @run_in_event_loop
00498     def do_items(self, line):
00499         """items -- Lists the items in the feed/playlist/tab selected."""
00500         if self.selection_type is None:
00501             print "Error: No tab/feed/playlist selected"
00502             return
00503         elif self.selection_type == 'feed':
00504             feed = self.tab.obj
00505             view = feed.items.sort(feed.itemSort.sort)
00506             self.printout_item_list(view)
00507             view.unlink()
00508         elif self.selection_type == 'playlist':
00509             playlist = self.tab.obj
00510             self.printout_item_list(playlist.getView())
00511         elif self.selection_type == 'downloads':
00512             self.printout_item_list(views.downloadingItems, views.pausedItems)
00513         elif self.selection_type == 'channel-folder':
00514             folder = self.tab.obj
00515             allItems = views.items.filterWithIndex(
00516                     indexes.itemsByChannelFolder, folder)
00517             allItemsSorted = allItems.sort(folder.itemSort.sort)
00518             self.printout_item_list(allItemsSorted)
00519             allItemsSorted.unlink()
00520         else:
00521             raise ValueError("Unknown tab type")
00522 
00523     @run_in_event_loop
00524     def do_downloads(self, line):
00525         """downloads -- Selects the downloads tab."""
00526         self.tab = FakeTab("statictab", "downloadtab")
00527         self.tab_changed()
00528 
00529     def printout_item_list(self, *views):
00530         totalItems = 0
00531         for view in views:
00532             totalItems += len(view)
00533         if totalItems > 0:
00534             print "%-20s %-10s %s" % ("State", "Size", "Name")
00535             print "-" * 70
00536             for view in views:
00537                 for item in view:
00538                     state = item.get_state()
00539                     if state == 'downloading':
00540                         state += ' (%0.0f%%)' % item.download_progress()
00541                     print "%-20s %-10s %s" % (state, item.get_size_for_display(),
00542                             item.get_title())
00543             print
00544         else:
00545             print "No items"
00546 
00547     def _get_item_view(self):
00548         if self.selection_type == 'feed':
00549             feed = self.tab.obj
00550             return feed.items
00551         elif self.selection_type == 'playlist':
00552             playlist = self.tab.obj
00553             return playlist.getView()
00554         elif self.selection_type == 'downloads':
00555             return views.downloadingItems
00556         elif self.selection_type == 'channel-folder':
00557             folder = self.tab.obj
00558             return views.items.filterWithIndex(indexes.itemsByChannelFolder,
00559                     folder)
00560         else:
00561             raise ValueError("Unknown selection type")
00562 
00563 
00564     def _find_item(self, line):
00565         line = line.lower()
00566         for item in self._get_item_view():
00567             if item.get_title().lower() == line:
00568                 return item
00569 
00570     @run_in_event_loop
00571     def do_stop(self, line):
00572         """stop <name> -- Stops download by name."""
00573         if self.selection_type is None:
00574             print "Error: No feed/playlist selected"
00575             return
00576         item = self._find_item(line)
00577         if item is None:
00578             print "No item named %r" % line
00579             return
00580         if item.get_state() in ('downloading', 'paused'):
00581             item.expire()
00582         else:
00583             print '%s is not being downloaded' % item.get_title()
00584 
00585     @run_in_event_loop
00586     def complete_stop(self, text, line, begidx, endidx):
00587         return self.handle_item_complete(text, self._get_item_view(),
00588                 lambda i: i.get_state() in ('downloading', 'paused'))
00589 
00590     @run_in_event_loop
00591     def do_download(self, line):
00592         """download <name> -- Downloads an item by name in the feed/playlist selected."""
00593         if self.selection_type is None:
00594             print "Error: No feed/playlist selected"
00595             return
00596         item = self._find_item(line)
00597         if item is None:
00598             print "No item named %r" % line
00599             return
00600         if item.get_state() == 'downloading':
00601             print '%s is currently being downloaded' % item.get_title()
00602         elif item.is_downloaded():
00603             print '%s is already downloaded' % item.get_title()
00604         else:
00605             item.download()
00606 
00607     @run_in_event_loop
00608     def complete_download(self, text, line, begidx, endidx):
00609         return self.handle_item_complete(text, self._get_item_view(),
00610                 lambda i: i.is_downloadable())
00611 
00612     @run_in_event_loop
00613     def do_pause(self, line):
00614         """pause <name> -- Pauses a download by name."""
00615         if self.selection_type is None:
00616             print "Error: No feed/playlist selected"
00617             return
00618         item = self._find_item(line)
00619         if item is None:
00620             print "No item named %r" % line
00621             return
00622         if item.get_state() == 'downloading':
00623             item.pause()
00624         else:
00625             print '%s is not being downloaded' % item.get_title()
00626 
00627     @run_in_event_loop
00628     def complete_pause(self, text, line, begidx, endidx):
00629         return self.handle_item_complete(text, self._get_item_view(),
00630                 lambda i: i.get_state() == 'downloading')
00631 
00632     @run_in_event_loop
00633     def do_resume(self, line):
00634         """resume <name> -- Resumes a download by name."""
00635         if self.selection_type is None:
00636             print "Error: No feed/playlist selected"
00637             return
00638         item = self._find_item(line)
00639         if item is None:
00640             print "No item named %r" % line
00641             return
00642         if item.get_state() == 'paused':
00643             item.resume()
00644         else:
00645             print '%s is not a paused download' % item.get_title()
00646 
00647     @run_in_event_loop
00648     def complete_resume(self, text, line, begidx, endidx):
00649         return self.handle_item_complete(text, self._get_item_view(),
00650                 lambda i: i.get_state() == 'paused')
00651 
00652     @run_in_event_loop
00653     def do_rm(self, line):
00654         """rm <name> -- Removes an item by name in the feed/playlist selected."""
00655         if self.selection_type is None:
00656             print "Error: No feed/playlist selected"
00657             return
00658         item = self._find_item(line)
00659         if item is None:
00660             print "No item named %r" % line
00661             return
00662         if item.is_downloaded():
00663             item.expire()
00664         else:
00665             print '%s is not downloaded' % item.get_title()
00666 
00667     @run_in_event_loop
00668     def complete_rm(self, text, line, begidx, endidx):
00669         return self.handle_item_complete(text, self._get_item_view(),
00670                 lambda i: i.is_downloaded())
00671 
00672     @run_in_event_loop
00673     def do_testdialog(self, line):
00674         """testdialog -- Tests the cli dialog system."""
00675         d = dialogs.ChoiceDialog("Hello", "I am a test dialog",
00676                 dialogs.BUTTON_OK, dialogs.BUTTON_CANCEL)
00677         def callback(dialog):
00678             print "TEST CHOICE: %s" % dialog.choice
00679         d.run(callback)
00680 
00681     @run_in_event_loop
00682     def do_dumpdatabase(self, line):
00683         """dumpdatabase -- Dumps the database."""
00684         from miro import database
00685         print "Dumping database...."
00686         database.defaultDatabase.liveStorage.dumpDatabase(database.defaultDatabase)
00687         print "Done."
00688 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends