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