00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029 import cmd
00030 import threading
00031 import time
00032 import Queue
00033
00034 from miro import app
00035 from miro import dialogs
00036 from miro import eventloop
00037 from miro import item
00038 from miro import folder
00039 from miro import util
00040 from miro import tabs
00041 from miro.frontends.cli import clidialog
00042 from miro.plat import resources
00043
00044
00045 import os, sys, subprocess, re, fnmatch, string
00046 import logging
00047 from miro import moviedata
00048 from miro import commandline
00049 from miro import autodler
00050 from miro import downloader
00051 from miro import iconcache
00052 from miro.clock import clock
00053 from miro import feed
00054 from miro.commandline import parse_command_line_args
00055 from miro import fileutil
00056 from miro import autoupdate
00057 from miro import startup
00058 from miro import filetypes
00059 from miro import messages
00060 from miro import config
00061 from miro import prefs
00062
00063 def run_in_event_loop(func):
00064 def decorated(*args, **kwargs):
00065 return_hack = []
00066 event = threading.Event()
00067 def runThenSet():
00068 try:
00069 return_hack.append(func(*args, **kwargs))
00070 finally:
00071 event.set()
00072 eventloop.add_urgent_call(runThenSet, 'run in event loop')
00073 event.wait()
00074 if return_hack:
00075 return return_hack[0]
00076 decorated.__doc__ = func.__doc__
00077 return decorated
00078
00079 class FakeTab:
00080 def __init__(self, tab_type, tab_template_base):
00081 self.type = tab_type
00082 self.tab_template_base = tab_template_base
00083
00084 class MiroInterpreter(cmd.Cmd):
00085 def __init__(self):
00086 cmd.Cmd.__init__(self)
00087 self.quit_flag = False
00088 self.tab = None
00089 self.init_database_objects()
00090
00091 def emptyline(self):
00092 print "Type \"help\" for help."
00093
00094 @run_in_event_loop
00095 def init_database_objects(self):
00096 self.video_feed_tabs = tabs.TabOrder.video_feed_order()
00097 self.audio_feed_tabs = tabs.TabOrder.audio_feed_order()
00098 self.playlist_tabs = tabs.TabOrder.playlist_order()
00099 self.tab_changed()
00100
00101 def tab_changed(self):
00102 """Calculate the current prompt. This method access database objects,
00103 so it should only be called from the backend event loop
00104 """
00105 if self.tab is None:
00106 self.prompt = "> "
00107 self.selection_type = None
00108
00109 elif self.tab.type == 'feed':
00110 if isinstance(self.tab, folder.ChannelFolder):
00111 self.prompt = "channel folder: %s > " % self.tab.get_title()
00112 self.selection_type = 'channel-folder'
00113 else:
00114 self.prompt = "channel: %s > " % self.tab.get_title()
00115 self.selection_type = 'feed'
00116
00117 elif self.tab.type == 'playlist':
00118 self.prompt = "playlist: %s > " % self.tab.get_title()
00119 self.selection_type = 'playlist'
00120
00121 elif (self.tab.type == 'statictab' and
00122 self.tab.tab_template_base == 'downloadtab'):
00123 self.prompt = "downloads > "
00124 self.selection_type = 'downloads'
00125 else:
00126 raise ValueError("Unknown tab type")
00127
00128 def postcmd(self, stop, line):
00129
00130
00131
00132 time.sleep(0.1)
00133 while True:
00134 try:
00135 dialog = app.cli_events.dialog_queue.get_nowait()
00136 except Queue.Empty:
00137 break
00138 clidialog.handle_dialog(dialog)
00139
00140 return self.quit_flag
00141
00142 def do_help(self, line):
00143 """help -- Lists commands and help."""
00144 commands = [m for m in dir(self) if m.startswith("do_")]
00145 for mem in commands:
00146 docstring = getattr(self, mem).__doc__
00147 print " ", docstring.strip()
00148
00149 def do_quit(self, line):
00150 """quit -- Quits Miro cli."""
00151 self.quit_flag = True
00152
00153 @run_in_event_loop
00154 def do_feed(self, line):
00155 """feed <name> -- Selects a feed by name."""
00156 for tab in self.video_feed_tabs.get_all_tabs():
00157 if tab.get_title() == line:
00158 self.tab = tab
00159 self.tab.type = "feed"
00160 self.tab_changed()
00161 return
00162 for tab in self.audio_feed_tabs.get_all_tabs():
00163 if tab.get_title() == line:
00164 self.tab = tab
00165 self.tab.type = "feed"
00166 self.tab_changed()
00167 return
00168 print "Error: %s not found." % line
00169
00170 @run_in_event_loop
00171 def do_rmfeed(self, line):
00172 """rmfeed <name> -- Deletes a feed."""
00173 for tab in self.video_feed_tabs.get_all_tabs():
00174 if tab.get_title() == line:
00175 tab.remove()
00176 return
00177 for tab in self.audio_feed_tabs.get_all_tabs():
00178 if tab.get_title() == line:
00179 tab.remove()
00180 return
00181 print "Error: %s not found." % line
00182
00183 @run_in_event_loop
00184 def complete_feed(self, text, line, begidx, endidx):
00185 return self.handle_tab_complete(text, list(self.video_feed_tabs.get_all_tabs()) + list(self.audio_feed_tabs.get_all_tabs()))
00186
00187 @run_in_event_loop
00188 def complete_rmfeed(self, text, line, begidx, endidx):
00189 return self.handle_tab_complete(text, list(self.video_feed_tabs.get_all_tabs()) + list(self.audio_feed_tabs.get_all_tabs()))
00190
00191 @run_in_event_loop
00192 def complete_playlist(self, text, line, begidx, endidx):
00193 return self.handle_tab_complete(text, self.playlist_tabs.get_all_tabs())
00194
00195 def handle_tab_complete(self, text, view_items):
00196 text = text.lower()
00197 matches = []
00198 for tab in view_items:
00199 if tab.get_title().lower().startswith(text):
00200 matches.append(tab.get_title())
00201 return matches
00202
00203 def handle_item_complete(self, text, view, filterFunc=lambda i: True):
00204 text = text.lower()
00205 matches = []
00206 for item in view:
00207 if (item.get_title().lower().startswith(text) and
00208 filterFunc(item)):
00209 matches.append(item.get_title())
00210 return matches
00211
00212
00213
00214
00215
00216
00217 @run_in_event_loop
00218 def do_mythtv_update_autodownload(self, line):
00219 """Update feeds and auto-download"""
00220 logging.info("Starting auto downloader...")
00221 autodler.start_downloader()
00222 feed.expire_items()
00223 logging.info("Starting video data updates")
00224
00225 moviedata.movie_data_updater.start_thread()
00226 commandline.startup()
00227
00228
00229
00230
00231 eventloop.add_timeout(5, downloader.startup_downloader,
00232 "start downloader daemon")
00233
00234 eventloop.add_timeout(30, feed.start_updates, "start feed updates")
00235
00236
00237 eventloop.add_timeout(10, clear_icon_cache_orphans, "clear orphans")
00238
00239
00240 def movie_data_program_info(self, movie_path, thumbnail_path):
00241 extractor_path = os.path.join(os.path.split(__file__)[0], "gst_extractor.py")
00242 return ((sys.executable, extractor_path, movie_path, thumbnail_path), None)
00243
00244 @run_in_event_loop
00245 def do_mythtv_check_downloading(self, line):
00246 """Check if any items are being downloaded. Set True or False"""
00247 self.downloading = False
00248
00249 downloadingItems = item.Item.only_downloading_view()
00250 count = downloadingItems.count()
00251 for it in downloadingItems:
00252 logging.info(u"(%s - %s) video is downloading with (%0.0f%%) complete" % (it.get_channel_title(True).replace(u'/',u'-'), it.get_title().replace(u'/',u'-'), it.download_progress()))
00253 if not count:
00254 logging.info(u"No items downloading")
00255 if count:
00256 self.downloading = True
00257
00258 @run_in_event_loop
00259 def do_mythtv_import_opml(self, filename):
00260 """Import an OPML file"""
00261 try:
00262 messages.ImportFeeds(filename).send_to_backend()
00263 logging.info(u"Import of OPML file (%s) sent to Miro" % (filename))
00264 except Exception, e:
00265 logging.info(u"Import of OPML file (%s) failed, error (%s)" % (filename, e))
00266
00267 @run_in_event_loop
00268 def do_mythtv_updatewatched(self, line):
00269 """Process MythTV update watched videos"""
00270 items = item.Item.downloaded_view()
00271 for video in self.videofiles:
00272 for it in items:
00273 if it.get_filename() == video:
00274 break
00275 else:
00276 logging.info(u"Item for Miro video (%s) not found, skipping" % video)
00277 continue
00278 if self.simulation:
00279 logging.info(u"Simulation: Item (%s - %s) marked as seen and watched" % (it.get_channel_title(True), it.get_title()))
00280 else:
00281 it.mark_item_seen(mark_other_items=True)
00282 self.statistics[u'Miro_marked_watch_seen']+=1
00283 logging.info(u"Item (%s - %s) marked as seen and watched" % (it.get_channel_title(True), it.get_title()))
00284
00285 @run_in_event_loop
00286 def do_mythtv_getunwatched(self, line):
00287 """Process MythTV get all un-watched video details"""
00288 if self.verbose:
00289 print
00290 print u"Getting details on un-watched Miro videos"
00291 self.videofiles = []
00292 if item.Item.unwatched_downloaded_items().count():
00293 if self.verbose:
00294 print u"%-20s %-10s %s" % (u"State", u"Size", u"Name")
00295 print u"-" * 70
00296 for it in item.Item.unwatched_downloaded_items():
00297
00298
00299
00300 state = it.get_state()
00301 if not state == u'newly-downloaded':
00302 continue
00303
00304 if hasattr(it.get_parent(), u'url'):
00305 if filetypes.is_torrent_filename(it.get_parent().url):
00306 continue
00307 self.printItems(it)
00308 self.videofiles.append(self._get_item_dict(it))
00309 if self.verbose:
00310 print
00311 if not len(self.videofiles):
00312 logging.info(u"No un-watched Miro videos")
00313
00314 @run_in_event_loop
00315 def do_mythtv_getwatched(self, line):
00316 """Process MythTV get all watched/saved video details"""
00317 if self.verbose:
00318 print
00319 print u"Getting details on watched/saved Miro videos"
00320 self.videofiles = []
00321 if item.Item.downloaded_view().count():
00322 if self.verbose:
00323 print u"%-20s %-10s %s" % (u"State", u"Size", u"Name")
00324 print u"-" * 70
00325 for it in item.Item.downloaded_view():
00326 state = it.get_state()
00327 if state == u'newly-downloaded':
00328 continue
00329
00330 if hasattr(it.get_parent(), u'url'):
00331 if filetypes.is_torrent_filename(it.get_parent().url):
00332 continue
00333 self.printItems(it)
00334 self.videofiles.append(self._get_item_dict(it))
00335 if self.verbose:
00336 print
00337 if not len(self.videofiles):
00338 logging.info(u"No watched/saved Miro videos")
00339
00340
00341
00342
00343
00344
00345
00346
00347
00348
00349
00350
00351
00352
00353
00354
00355
00356
00357
00358
00359
00360
00361
00362
00363
00364
00365
00366
00367
00368
00369
00370
00371 def printItems(self, it):
00372 if not self.verbose:
00373 return
00374 state = it.get_state()
00375 if state == u'downloading':
00376 state += u' (%0.0f%%)' % it.download_progress()
00377 print u"%-20s %-10s %s" % (state, it.get_size_for_display(),
00378 it.get_title())
00379
00380
00381 @run_in_event_loop
00382 def do_mythtv_item_remove(self, args):
00383 """Removes an item from Miro by file name or Channel and title"""
00384 for it in item.Item.downloaded_view():
00385 if isinstance(args, list):
00386 if not args[0] or not args[1]:
00387 continue
00388 if filter(self.is_not_punct_char, it.get_channel_title(True).lower()) == filter(self.is_not_punct_char, args[0].lower()) and (filter(self.is_not_punct_char, it.get_title().lower())).startswith(filter(self.is_not_punct_char, args[1].lower())):
00389 break
00390 elif args:
00391 if filter(self.is_not_punct_char, it.get_filename().lower()) == filter(self.is_not_punct_char, args.lower()):
00392 break
00393 else:
00394 logging.info(u"No item named %s" % args)
00395 return
00396 if it.is_downloaded():
00397 if self.simulation:
00398 logging.info(u"Simulation: Item (%s - %s) has been removed from Miro" % (it.get_channel_title(True), it.get_title()))
00399 else:
00400 it.expire()
00401 self.statistics[u'Miro_videos_deleted']+=1
00402 logging.info(u'%s has been removed from Miro' % it.get_title())
00403 else:
00404 logging.info(u'%s is not downloaded' % it.get_title())
00405
00406 def _get_item_dict(self, it):
00407 """Take an item and convert all elements into a dictionary
00408 return a dictionary of item elements
00409 """
00410 def compatibleGraphics(filename):
00411 if filename:
00412 (dirName, fileName) = os.path.split(filename)
00413 (fileBaseName, fileExtension)=os.path.splitext(fileName)
00414 if not fileExtension[1:] in [u"png", u"jpg", u"bmp", u"gif"]:
00415 return u''
00416 else:
00417 return filename
00418 else:
00419 return u''
00420
00421 def useImageMagick(screenshot):
00422 """ Using ImageMagick's utility 'identify'. Decide whether the screen shot is worth using.
00423 >>> useImageMagick('identify screenshot.jpg')
00424 >>> Example returned information "rose.jpg JPEG 640x480 DirectClass 87kb 0.050u 0:01"
00425 >>> u'' if the screenshot quality is too low
00426 >>> screenshot if the quality is good enough to use
00427 """
00428 if not self.imagemagick:
00429 return u''
00430
00431 width_height = re.compile(u'''^(.+?)[ ]\[?([0-9]+)x([0-9]+)[^\\/]*$''', re.UNICODE)
00432 p = subprocess.Popen(u'identify "%s"' % (screenshot), shell=True, bufsize=4096, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True)
00433
00434 response = p.stdout.readline()
00435 if response:
00436 match = width_height.match(response)
00437 if match:
00438 dummy, width, height = match.groups()
00439 width, height = int(width), int(height)
00440 if width >= 320:
00441 return screenshot
00442 return u''
00443 else:
00444 return u''
00445 return screenshot
00446
00447
00448 item_icon_filename = it.icon_cache.filename
00449 channel_icon = it.get_feed().icon_cache.get_filename()
00450
00451
00452 maximum_length = 128
00453 channel_title = it.get_channel_title(True).replace(u'/',u'-')
00454 if channel_title:
00455 if len(channel_title) > maximum_length:
00456 channel_title = channel_title[:maximum_length]
00457 channel_title = channel_title.replace(u'"', u'')
00458 title = it.get_title().replace(u'/',u'-')
00459 if title:
00460 if len(title) > maximum_length:
00461 title = title[:maximum_length]
00462 title = title.replace(u'"', u'')
00463 title = title.replace(u"'", u'')
00464
00465 item_dict = {u'feed_id': it.feed_id, u'parent_id': it.parent_id, u'isContainerItem': it.isContainerItem, u'seen': it.seen, u'autoDownloaded': it.autoDownloaded, u'pendingManualDL': it.pendingManualDL, u'downloadedTime': it.downloadedTime, u'watchedTime': it.watchedTime, u'pendingReason': it.pendingReason, u'title': title, u'expired': it.expired, u'keep': it.keep, u'videoFilename': it.get_filename(), u'eligibleForAutoDownload': it.eligibleForAutoDownload, u'duration': it.duration, u'screenshot': it.screenshot, u'resumeTime': it.resumeTime, u'channelTitle': channel_title, u'description': it.get_description(), u'size': it._get_size(), u'releasedate': it.get_release_date_obj(), u'length': it.get_duration_value(), u'channel_icon': channel_icon, u'item_icon': item_icon_filename, u'inetref': u'', u'season': 1, u'episode': 1,}
00466
00467 if not item_dict[u'screenshot']:
00468 if item_dict[u'item_icon']:
00469 item_dict[u'screenshot'] = useImageMagick(item_dict[u'item_icon'])
00470
00471 for key in [u'screenshot', u'channel_icon', u'item_icon']:
00472 if item_dict[key]:
00473 item_dict[key] = compatibleGraphics(item_dict[key])
00474
00475
00476
00477
00478
00479
00480
00481 return item_dict
00482
00483
00484 def is_punct_char(self, char):
00485 '''check if char is punctuation char
00486 return True if char is punctuation
00487 return False if char is not punctuation
00488 '''
00489 return char in string.punctuation
00490
00491 def is_not_punct_char(self, char):
00492 '''check if char is not punctuation char
00493 return True if char is not punctuation
00494 return False if chaar is punctuation
00495 '''
00496 return not self.is_punct_char(char)
00497
00498
00499
00500
00501
00502
00503
00504
00505 def _print_feeds(self, feeds):
00506 current_folder = None
00507 for tab in feeds:
00508 if isinstance(tab, folder.ChannelFolder):
00509 current_folder = tab
00510 elif tab.get_folder() is not current_folder:
00511 current_folder = None
00512 if current_folder is None:
00513 print " * " + tab.get_title()
00514 elif current_folder is tab:
00515 print " * [Folder] %s" % tab.get_title()
00516 else:
00517 print " * - %s" % tab.get_title()
00518
00519 @run_in_event_loop
00520 def do_feeds(self, line):
00521 """feeds -- Lists all feeds."""
00522 print "VIDEO FEEDS"
00523 self._print_feeds(self.video_feed_tabs.get_all_tabs())
00524 print "AUDIO FEEDS"
00525 self._print_feeds(self.audio_feed_tabs.get_all_tabs())
00526
00527 @run_in_event_loop
00528 def do_play(self, line):
00529 """play <name> -- Plays an item by name in an external player."""
00530 if self.selection_type is None:
00531 print "Error: No feed/playlist selected."
00532 return
00533 item = self._find_item(line)
00534 if item is None:
00535 print "No item named %r" % line
00536 return
00537 if item.is_downloaded():
00538 resources.open_file(item.get_video_filename())
00539 else:
00540 print '%s is not downloaded' % item.get_title()
00541
00542 @run_in_event_loop
00543 def do_playlists(self, line):
00544 """playlists -- Lists all playlists."""
00545 for tab in self.playlistTabs.getView():
00546 print tab.obj.get_title()
00547
00548 @run_in_event_loop
00549 def do_playlist(self, line):
00550 """playlist <name> -- Selects a playlist."""
00551 for tab in self.playlistTabs.getView():
00552 if tab.obj.get_title() == line:
00553 self.tab = tab
00554 self.tab_changed()
00555 return
00556 print "Error: %s not found." % line
00557
00558 @run_in_event_loop
00559 def do_items(self, line):
00560 """items -- Lists the items in the feed/playlist/tab selected."""
00561 if self.selection_type is None:
00562 print "Error: No tab/feed/playlist selected."
00563 return
00564 elif self.selection_type == 'feed':
00565 feed = self.tab
00566 view = feed.items
00567 self.printout_item_list(view)
00568 elif self.selection_type == 'playlist':
00569 playlist = self.tab.obj
00570 self.printout_item_list(playlist.getView())
00571 elif self.selection_type == 'downloads':
00572 self.printout_item_list(item.Item.downloading_view(),
00573 item.Item.paused_view())
00574 elif self.selection_type == 'channel-folder':
00575 folder = self.tab.obj
00576 allItems = views.items.filterWithIndex(
00577 indexes.itemsByChannelFolder, folder)
00578 allItemsSorted = allItems.sort(folder.itemSort.sort)
00579 self.printout_item_list(allItemsSorted)
00580 allItemsSorted.unlink()
00581 else:
00582 raise ValueError("Unknown tab type")
00583
00584 @run_in_event_loop
00585 def do_downloads(self, line):
00586 """downloads -- Selects the downloads tab."""
00587 self.tab = FakeTab("statictab", "downloadtab")
00588 self.tab_changed()
00589
00590 def printout_item_list(self, *views):
00591 totalItems = 0
00592 for view in views:
00593 totalItems += view.count()
00594 if totalItems > 0:
00595 print "%-20s %-10s %s" % ("State", "Size", "Name")
00596 print "-" * 70
00597 for view in views:
00598 for item in view:
00599 state = item.get_state()
00600 if state == 'downloading':
00601 state += ' (%0.0f%%)' % item.download_progress()
00602 print "%-20s %-10s %s" % (state, item.get_size_for_display(),
00603 item.get_title())
00604 print
00605 else:
00606 print "No items"
00607
00608 def _get_item_view(self):
00609 if self.selection_type == 'feed':
00610 return item.Item.visible_feed_view(self.tab.id)
00611 elif self.selection_type == 'playlist':
00612 return item.Item.playlist_view(self.tab.id)
00613 elif self.selection_type == 'downloads':
00614 return item.Item.downloading_view()
00615 elif self.selection_type == 'channel-folder':
00616 folder = self.tab
00617 return item.Item.visible_folder_view(folder.id)
00618 else:
00619 raise ValueError("Unknown selection type")
00620
00621 def _find_item(self, line):
00622 line = line.lower()
00623 for item in self._get_item_view():
00624 if item.get_title().lower() == line:
00625 return item
00626
00627 @run_in_event_loop
00628 def do_stop(self, line):
00629 """stop <name> -- Stops download by name."""
00630 if self.selection_type is None:
00631 print "Error: No feed/playlist selected."
00632 return
00633 item = self._find_item(line)
00634 if item is None:
00635 print "No item named %r" % line
00636 return
00637 if item.get_state() in ('downloading', 'paused'):
00638 item.expire()
00639 else:
00640 print '%s is not being downloaded' % item.get_title()
00641
00642 @run_in_event_loop
00643 def complete_stop(self, text, line, begidx, endidx):
00644 return self.handle_item_complete(text, self._get_item_view(),
00645 lambda i: i.get_state() in ('downloading', 'paused'))
00646
00647 @run_in_event_loop
00648 def do_download(self, line):
00649 """download <name> -- Downloads an item by name in the feed/playlist selected."""
00650 if self.selection_type is None:
00651 print "Error: No feed/playlist selected."
00652 return
00653 item = self._find_item(line)
00654 if item is None:
00655 print "No item named %r" % line
00656 return
00657 if item.get_state() == 'downloading':
00658 print '%s is currently being downloaded' % item.get_title()
00659 elif item.is_downloaded():
00660 print '%s is already downloaded' % item.get_title()
00661 else:
00662 item.download()
00663
00664 @run_in_event_loop
00665 def complete_download(self, text, line, begidx, endidx):
00666 return self.handle_item_complete(text, self._get_item_view(),
00667 lambda i: i.is_downloadable())
00668
00669 @run_in_event_loop
00670 def do_pause(self, line):
00671 """pause <name> -- Pauses a download by name."""
00672 if self.selection_type is None:
00673 print "Error: No feed/playlist selected."
00674 return
00675 item = self._find_item(line)
00676 if item is None:
00677 print "No item named %r" % line
00678 return
00679 if item.get_state() == 'downloading':
00680 item.pause()
00681 else:
00682 print '%s is not being downloaded' % item.get_title()
00683
00684 @run_in_event_loop
00685 def complete_pause(self, text, line, begidx, endidx):
00686 return self.handle_item_complete(text, self._get_item_view(),
00687 lambda i: i.get_state() == 'downloading')
00688
00689 @run_in_event_loop
00690 def do_resume(self, line):
00691 """resume <name> -- Resumes a download by name."""
00692 if self.selection_type is None:
00693 print "Error: No feed/playlist selected."
00694 return
00695 item = self._find_item(line)
00696 if item is None:
00697 print "No item named %r" % line
00698 return
00699 if item.get_state() == 'paused':
00700 item.resume()
00701 else:
00702 print '%s is not a paused download' % item.get_title()
00703
00704 @run_in_event_loop
00705 def complete_resume(self, text, line, begidx, endidx):
00706 return self.handle_item_complete(text, self._get_item_view(),
00707 lambda i: i.get_state() == 'paused')
00708
00709 @run_in_event_loop
00710 def do_rm(self, line):
00711 """rm <name> -- Removes an item by name in the feed/playlist selected."""
00712 if self.selection_type is None:
00713 print "Error: No feed/playlist selected."
00714 return
00715 item = self._find_item(line)
00716 if item is None:
00717 print "No item named %r" % line
00718 return
00719 if item.is_downloaded():
00720 item.expire()
00721 else:
00722 print '%s is not downloaded' % item.get_title()
00723
00724 @run_in_event_loop
00725 def complete_rm(self, text, line, begidx, endidx):
00726 return self.handle_item_complete(text, self._get_item_view(),
00727 lambda i: i.is_downloaded())
00728
00729 @run_in_event_loop
00730 def do_testdialog(self, line):
00731 """testdialog -- Tests the cli dialog system."""
00732 d = dialogs.ChoiceDialog("Hello", "I am a test dialog",
00733 dialogs.BUTTON_OK, dialogs.BUTTON_CANCEL)
00734 def callback(dialog):
00735 print "TEST CHOICE: %s" % dialog.choice
00736 d.run(callback)
00737
00738 @run_in_event_loop
00739 def do_dumpdatabase(self, line):
00740 """dumpdatabase -- Dumps the database."""
00741 from miro import database
00742 print "Dumping database...."
00743 database.defaultDatabase.liveStorage.dumpDatabase(database.defaultDatabase)
00744 print "Done."
00745
00746
00747
00748
00749
00750
00751
00752 @eventloop.idle_iterator
00753 def clear_icon_cache_orphans():
00754
00755
00756 removed_objs = []
00757 for ic in iconcache.IconCache.orphaned_view():
00758 logging.warn("No object for IconCache: %s. Discarding", ic)
00759 ic.remove()
00760 removed_objs.append(str(ic.url))
00761 if removed_objs:
00762 databaselog.info("Removed IconCache objects without an associated "
00763 "db object: %s", ','.join(removed_objs))
00764 yield None
00765
00766
00767
00768
00769 cachedir = fileutil.expand_filename(config.get(prefs.ICON_CACHE_DIRECTORY))
00770 if not os.path.isdir(cachedir):
00771 return
00772
00773 existingFiles = [os.path.normcase(os.path.join(cachedir, f))
00774 for f in os.listdir(cachedir)]
00775 yield None
00776
00777 knownIcons = iconcache.IconCache.all_filenames()
00778 yield None
00779
00780 knownIcons = [ os.path.normcase(fileutil.expand_filename(path)) for path in
00781 knownIcons]
00782 yield None
00783
00784 for filename in existingFiles:
00785 if (os.path.exists(filename)
00786 and os.path.basename(filename)[0] != '.'
00787 and os.path.basename(filename) != 'extracted'
00788 and not filename in knownIcons):
00789 try:
00790 os.remove(filename)
00791 except OSError:
00792 pass
00793 yield None
00794
00795
00796
00797
00798