Exemple #1
0
 def move_to_directory(self, directory):
     check_f(directory)
     if self.channelName:
         channel_name = filter_directory_name(self.channelName)
         # bug 10769: shutil and windows has problems with long
         # filenames, so we clip the directory name.
         if len(channel_name) > 80:
             channel_name = channel_name[:80]
         directory = os.path.join(directory, channel_name)
         if not os.path.exists(directory):
             try:
                 fileutil.makedirs(directory)
             except (SystemExit, KeyboardInterrupt):
                 raise
             except:
                 pass
     newfilename = os.path.join(directory, self.shortFilename)
     if newfilename == self.filename:
         return
     newfilename, fp = next_free_filename(newfilename)
     def callback():
         self.filename = newfilename
         self.update_client()
     fileutil.migrate_file(self.filename, newfilename, callback)
     fp.close()
Exemple #2
0
    def add_items(self, item_infos):
        for info in item_infos:
            if self.stopping:
                self._check_finished()
                return
            if self.device.database.item_exists(info):
                continue # don't recopy stuff
            conversion = self.conversion_for_info(info)
            if not conversion:
                continue
            if info.file_type == 'audio':
                target_folder = self.audio_target_folder
            elif info.file_type == 'video':
                target_folder = self.video_target_folder
            else:
                continue
            self.total += 1
            if conversion == 'copy':
                final_path = os.path.join(target_folder,
                                          os.path.basename(
                        info.video_path))
                if os.path.exists(final_path):
                    logging.debug('%r exists, getting a new one precopy',
                                  final_path)
                    final_path, fp = next_free_filename(final_path)
                self.copy_file(info, final_path)
            else:
                self.start_conversion(conversion,
                                      info,
                                      target_folder)

        self._check_finished()
Exemple #3
0
    def pick_initial_filename(self, suffix=".part", torrent=False):
        """Pick a path to download to based on self.shortFilename.

        This method sets self.filename, as well as creates any leading
        paths needed to start downloading there.

        If the torrent flag is true, then the filename we're working
        with is utf-8 and shouldn't be transformed in any way.

        If the torrent flag is false, then the filename we're working
        with is ascii and needs to be transformed into something sane.
        (default)
        """
        download_dir = os.path.join(app.config.get(prefs.MOVIES_DIRECTORY),
                                    'Incomplete Downloads')
        # Create the download directory if it doesn't already exist.
        if not os.path.exists(download_dir):
            fileutil.makedirs(download_dir)
        filename = self.shortFilename + suffix
        if not torrent:
            # this is an ascii filename and needs to be fixed
            filename = clean_filename(filename)
        self.filename, fp = next_free_filename(
                                os.path.join(download_dir, filename))
        # We can close this object now the file's been created, I guess.
        # This will linger in the filesystem namespace, caller is responsible
        # for cleaning this up (which I think it does).
        fp.close()
Exemple #4
0
 def dumpDatabase(self):
     output = next_free_filename(os.path.join(app.config.get(prefs.SUPPORT_DIRECTORY), "database-dump.xml"))
     def indent(level):
         output.write('    ' * level)
     def output_object(table_name, values):
         indent(1)
         if 'id' in values:
             output.write('<%s id="%s">\n' % (table_name, values['id']))
         else:
             output.write('<%s>\n' % (table_name,))
         for key, value in values.items():
             if key == 'id':
                 continue
             indent(2)
             output.write('<%s>' % (key,))
             if isinstance (value, unicode):
                 output.write (value.encode('ascii', 'xmlcharrefreplace'))
             else:
                 output.write (str(value))
             output.write ('</%s>\n' % (key,))
         indent(1)
         output.write ('</%s>\n' % (table_name))
     output.write ('<?xml version="1.0"?>\n')
     output.write ('<database schema="%d">\n' % (self._schema_version,))
     for schema in self._object_schemas:
         self.cursor.execute("SELECT * FROM %s" % schema.table_name)
         column_names = [d[0] for d in self.cursor.description]
         for row in self.cursor:
             output_object(schema.table_name.replace('_', '-'),
                     dict(zip(column_names, row)))
     output.write ('</database>\n')
     output.close()
Exemple #5
0
    def migrate(self, directory):
        if app.download_state_manager.get_download(self.dlid):
            c = command.MigrateDownloadCommand(RemoteDownloader.dldaemon,
                                               self.dlid, directory)
            c.send()
        else:
            # downloader doesn't have our dlid.  Move the file ourself.
            short_filename = self.status.get("shortFilename")
            if not short_filename:
                logging.warning(
                    "can't migrate download; no shortfilename!  URL was %s",
                    self.url)
                return
            filename = self.status.get("filename")
            if not filename:
                logging.warning(
                    "can't migrate download; no filename!  URL was %s",
                    self.url)
                return
            if fileutil.exists(filename):
                if self.status.get('channelName', None) is not None:
                    channelName = filter_directory_name(
                        self.status['channelName'])
                    directory = os.path.join(directory, channelName)
                if not os.path.exists(directory):
                    try:
                        fileutil.makedirs(directory)
                    except OSError:
                        # FIXME - what about permission issues?
                        pass
                newfilename = os.path.join(directory, short_filename)
                if newfilename == filename:
                    return
                # create a file or directory to serve as a placeholder before
                # we start to migrate.  This helps ensure that the destination
                # we're migrating too is not already taken.
                try:
                    is_dir = fileutil.isdir(filename)
                    if is_dir:
                        newfilename = next_free_directory(newfilename)
                        fp = None
                    else:
                        newfilename, fp = next_free_filename(newfilename)
                        fp.close()
                except ValueError:
                    func = ('next_free_directory'
                            if is_dir else 'next_free_filename')
                    logging.warn('migrate: %s failed.  candidate = %r', func,
                                 newfilename)
                else:

                    def callback():
                        self.status['filename'] = newfilename
                        self.signal_change(needs_signal_item=False)
                        self._file_migrated(filename)

                    fileutil.migrate_file(filename, newfilename, callback)
        for i in self.item_list:
            i.migrate_children(directory)
Exemple #6
0
    def _add_item(self, final_path, item_info):
        dirname, basename = os.path.split(final_path)
        _, extension = os.path.splitext(basename)
        new_basename = "%s%s" % (unicode_to_filename(item_info.name,
                                                     self.device.mount),
                                 extension)
        new_path = os.path.join(dirname, new_basename)
        if os.path.exists(new_path):
            logging.debug('final destination %r exists, making a new one',
                          new_path)
            new_path, fp = next_free_filename(new_path)

        def callback():
            if not os.path.exists(new_path):
                return # copy failed, just give up

            device_item = item.DeviceItem(
                device=self.device,
                file_type=item_info.file_type,
                video_path=new_path[len(self.device.mount):],
                title=item_info.name,
                feed_name=item_info.feed_name,
                feed_url=item_info.feed_url,
                description=item_info.description,
                release_date=time.mktime(item_info.release_date.timetuple()),
                duration=(item_info.duration and item_info.duration * 1000 or
                          None),
                permalink=item_info.permalink,
                commentslink=item_info.commentslink,
                payment_link=item_info.payment_link,
                screenshot=item_info.thumbnail,
                thumbnail_url=item_info.thumbnail_url,
                file_format=item_info.file_format,
                license=item_info.license,
                url=item_info.file_url,
                media_type_checked=item_info.media_type_checked,
                mime_type=item_info.mime_type,
                creation_time=time.mktime(item_info.date_added.timetuple()),
                title_tag=item_info.title_tag,
                artist=item_info.artist,
                album=item_info.album,
                track=item_info.track,
                year=item_info.year,
                genre=item_info.genre,
                metadata_version=item_info.metadata_version,
                mdp_state=item_info.mdp_state,
                auto_sync=getattr(item_info, 'auto_sync', False)
                )
            device_item._migrate_thumbnail()
            database = self.device.database
            database.setdefault(device_item.file_type, {})
            database[device_item.file_type][device_item.id] = \
                device_item.to_dict()
            database.emit('item-added', device_item)
            self.device.remaining -= device_item.size

        fileutil.migrate_file(final_path, new_path, callback)
Exemple #7
0
 def migrate(self, directory):
     if app.download_state_manager.get_download(self.dlid):
         c = command.MigrateDownloadCommand(RemoteDownloader.dldaemon,
                                            self.dlid, directory)
         c.send()
     else:
         # downloader doesn't have our dlid.  Move the file ourself.
         short_filename = self.short_filename
         if not short_filename:
             logging.warning(
                 "can't migrate download; no shortfilename!  URL was %s",
                 self.url)
             return
         filename = self.filename
         if not filename:
             logging.warning(
                 "can't migrate download; no filename!  URL was %s",
                 self.url)
             return
         if fileutil.exists(filename):
             if self.channel_name is not None:
                 channel_name = filter_directory_name(self.channel_name)
                 directory = os.path.join(directory, channel_name)
             if not os.path.exists(directory):
                 try:
                     fileutil.makedirs(directory)
                 except OSError:
                     # FIXME - what about permission issues?
                     pass
             newfilename = os.path.join(directory, short_filename)
             if newfilename == filename:
                 return
             # create a file or directory to serve as a placeholder before
             # we start to migrate.  This helps ensure that the destination
             # we're migrating too is not already taken.
             try:
                 is_dir = fileutil.isdir(filename)
                 if is_dir:
                     newfilename = next_free_directory(newfilename)
                     fp = None
                 else:
                     newfilename, fp = next_free_filename(newfilename)
                     fp.close()
             except ValueError:
                 func = ('next_free_directory' if is_dir
                         else 'next_free_filename')
                 logging.warn('migrate: %s failed.  candidate = %r',
                              func, newfilename)
             else:
                 def callback():
                     self.filename = newfilename
                     self.signal_change(needs_signal_item=False)
                     self._file_migrated(filename)
                 fileutil.migrate_file(filename, newfilename, callback)
     for i in self.item_list:
         i.migrate_children(directory)
Exemple #8
0
    def test_next_free_filename(self):
        # make a bunch of files that we should skip over
        for name in ('foo', 'foo.1', 'foo.2', 'bar.jpg', 'bar.1.jpg'):
            path = os.path.join(self.tempdir, name)
            open(path, 'wt').write("FAKE FILE")

        path1 = os.path.join(self.tempdir, 'foo')
        # test we find the a nonexistent file
        returned_path, fp = download_utils.next_free_filename(path1)
        self.assertEquals(returned_path, os.path.join(self.tempdir, 'foo.3'))
        # test that we create the file
        self.assert_(os.path.exists(returned_path))

        # try with an extension
        path2 = os.path.join(self.tempdir, 'bar.jpg')
        returned_path, fp = download_utils.next_free_filename(path2)
        self.assertEquals(returned_path, os.path.join(self.tempdir,
            'bar.2.jpg'))
        self.assert_(os.path.exists(returned_path))
Exemple #9
0
def _make_screenshot_path(source_path, image_directory):
    """Get a unique path to put a screenshot at

    This function creates a unique path to put a screenshot file at.

    :param source_path: path to the input video file
    :param image_directory: directory to put screenshots in
    """
    filename = os.path.basename(source_path) + ".png"
    path = os.path.join(image_directory, filename)
    # we have to use next_free_filename_no_create() here because we are
    # passing the file path to the movie data process, so keeping the file
    # open is not an option.  We'll just have to live with the race condition
    return download_utils.next_free_filename(path)
Exemple #10
0
 def migrate(self, directory):
     if _downloads.has_key(self.dlid):
         c = command.MigrateDownloadCommand(RemoteDownloader.dldaemon,
                                            self.dlid, directory)
         c.send()
     else:
         # downloader doesn't have our dlid.  Move the file ourself.
         short_filename = self.status.get("shortFilename")
         if not short_filename:
             logging.warning(
                 "can't migrate download; no shortfilename!  URL was %s",
                 self.url)
             return
         filename = self.status.get("filename")
         if not filename:
             logging.warning(
                 "can't migrate download; no filename!  URL was %s",
                 self.url)
             return
         if fileutil.exists(filename):
             if self.status.get('channelName', None) is not None:
                 channelName = filter_directory_name(self.status['channelName'])
                 directory = os.path.join(directory, channelName)
             if not os.path.exists(directory):
                 try:
                     fileutil.makedirs(directory)
                 except OSError:
                     # FIXME - what about permission issues?
                     pass
             newfilename = os.path.join(directory, short_filename)
             if newfilename == filename:
                 return
             # create a file or directory to serve as a placeholder before
             # we start to migrate.  This helps ensure that the destination
             # we're migrating too is not already taken.
             if fileutil.isdir(filename):
                 newfilename = next_free_directory(newfilename)
                 fp = None
             else:
                 newfilename, fp = next_free_filename(newfilename)
             def callback():
                 self.status['filename'] = newfilename
                 self.signal_change(needs_signal_item=False)
                 self._file_migrated(filename)
             fileutil.migrate_file(filename, newfilename, callback)
             if fp is not None:
                 fp.close() # clean up if we called next_free_filename()
     for i in self.item_list:
         i.migrate_children(directory)
Exemple #11
0
 def migrate(self, directory):
     if _downloads.has_key(self.dlid):
         c = command.MigrateDownloadCommand(RemoteDownloader.dldaemon,
                                            self.dlid, directory)
         c.send()
     else:
         # downloader doesn't have our dlid.  Move the file ourself.
         short_filename = self.status.get("shortFilename")
         if not short_filename:
             logging.warning(
                 "can't migrate download; no shortfilename!  URL was %s",
                 self.url)
             return
         filename = self.status.get("filename")
         if not filename:
             logging.warning(
                 "can't migrate download; no filename!  URL was %s",
                 self.url)
             return
         if fileutil.exists(filename):
             if self.status.get('channelName', None) is not None:
                 channelName = filter_directory_name(self.status['channelName'])
                 directory = os.path.join(directory, channelName)
             if not os.path.exists(directory):
                 try:
                     fileutil.makedirs(directory)
                 except OSError:
                     # FIXME - what about permission issues?
                     pass
             newfilename = os.path.join(directory, short_filename)
             if newfilename == filename:
                 return
             newfilename, fp = next_free_filename(newfilename)
             def callback():
                 self.status['filename'] = newfilename
                 self.signal_change(needs_signal_item=False)
                 self._file_migrated(filename)
             fileutil.migrate_file(filename, newfilename, callback)
             fp.close()
     for i in self.item_list:
         i.migrate_children(directory)
Exemple #12
0
    def _process_message_queue(self):
        try:
            msg = self.message_queue.get_nowait()
        except Queue.Empty:
            return

        if msg['message'] == 'get_tasks_list':
            self._notify_tasks_list()

        elif msg['message'] == 'cancel':
            try:
                task = self._lookup_task(msg['key'])
            except KeyError:
                logging.warn("Couldn't find task for key %s", msg['key'])
                return
            if task.is_pending():
                task_list = self.pending_tasks
            elif task.is_running():
                task_list = self.running_tasks
            else:
                task_list = self.finished_tasks
            try:
                task_list.remove(task)
            except ValueError:
                logging.warn("Task not in list: %s", msg['key'])
            else:
                self._notify_task_removed(task)
                self._notify_tasks_count()
            task.interrupt()

        elif msg['message'] == 'clear_all_finished':
            for task in self.finished_tasks:
                self._notify_task_removed(task)
            self.finished_tasks = []
            self._notify_tasks_count()

        elif msg['message'] == 'clear_finished':
            try:
                task = self._lookup_task(msg['key'])
            except KeyError:
                logging.warn("Couldn't find task for key %s", msg['key'])
                return
            self.finished_tasks.remove(task)
            self._notify_task_removed(task)
            self._notify_tasks_count()

        elif msg['message'] == 'cancel_all':
            self._terminate()

        elif msg['message'] == 'stage_conversion':
            try:
                task = self._lookup_task(msg['key'])
            except KeyError:
                logging.warn("Couldn't find task for key %s", msg['key'])
                return
            source = task.temp_output_path
            destination, fp = next_free_filename(task.final_output_path)
            source_info = task.item_info
            conversion_name = task.get_display_name()
            if os.path.exists(source):
                self._move_finished_file(source, destination)
                if task.create_item:
                    _create_item_for_conversion(destination,
                                                source_info,
                                                conversion_name)
                if not task.temp_output_path.endswith('.tmp'):  # temp dir
                    clean_up(task.temp_output_path,
                             file_and_directory=True)
            else:
                task.error = _("Reason unknown--check log")
                self._notify_tasks_count()
            fp.close()
            self.emit('task-staged', task)
Exemple #13
0
    def update_icon_cache(self, url, info):
        self.dbItem.confirm_db_thread()

        if self.removed:
            app.icon_cache_updater.update_finished()
            return

        needs_save = False
        needsChange = False

        if info == None or (info['status'] != 304 and info['status'] != 200):
            self.error_callback(url, "bad response")
            return
        try:
            # Our cache is good.  Hooray!
            if info['status'] == 304:
                return

            needsChange = True

            # We have to update it, and if we can't write to the file, we
            # should pick a new filename.
            if ((self.filename and
                 not fileutil.access(self.filename, os.R_OK | os.W_OK))):
                self.filename = None

            cachedir = app.config.get(prefs.ICON_CACHE_DIRECTORY)
            try:
                fileutil.makedirs(cachedir)
            except OSError:
                pass

            try:
                # Write to a temp file.
                if self.filename:
                    tmp_filename = self.filename + ".part"
                else:
                    tmp_filename = os.path.join(cachedir, info["filename"]) + ".part"
                tmp_filename, output = next_free_filename(tmp_filename)
                output.write(info["body"])
                output.close()
            except IOError:
                self.remove_file(tmp_filename)
                return
            except ValueError:
                logging.warn('update_icon_cache: next_free_filename failed '
                             '#1, candidate = %r', tmp_filename)
                return

            filename = unicode(info["filename"])
            filename = unicode_to_filename(filename, cachedir)
            filename = os.path.join(cachedir, filename)
            needs_save = True
            try:
                filename, fp = next_free_filename(filename)
            except ValueError:
                logging.warn('update_icon_cache: next_free_filename failed '
                             '#2, candidate = %r', filename)
                return

            if self.filename:
                filename = self.filename
                self.filename = None
                self.remove_file(filename)

            # we need to move the file here--so we close the file
            # pointer and then move the file.
            fp.close()
            try:
                self.remove_file(filename)
                fileutil.rename(tmp_filename, filename)
            except (IOError, OSError):
                logging.exception("iconcache: fileutil.move failed")
                filename = None

            self.filename = filename

            etag = unicodify(info.get("etag"))
            modified = unicodify(info.get("modified"))

            if self.etag != etag:
                needs_save = True
                self.etag = etag
            if self.modified != modified:
                needs_save = True
                self.modified = modified
            if self.url != url:
                needs_save = True
                self.url = url
        finally:
            if needsChange:
                self.icon_changed(needs_save=needs_save)
            self.updating = False
            if self.needsUpdate:
                self.needsUpdate = False
                self.request_update(True)
            app.icon_cache_updater.update_finished()
Exemple #14
0
    def _process_message_queue(self):
        try:
            msg = self.message_queue.get_nowait()
        except Queue.Empty:
            return

        if msg['message'] == 'get_tasks_list':
            self._notify_tasks_list()

        elif msg['message'] == 'cancel':
            try:
                task = self._lookup_task(msg['key'])
            except KeyError:
                logging.warn("Couldn't find task for key %s", msg['key'])
                return
            if task.is_pending():
                task_list = self.pending_tasks
            elif task.is_running():
                task_list = self.running_tasks
            else:
                task_list = self.finished_tasks
            try:
                task_list.remove(task)
            except ValueError:
                logging.warn("Task not in list: %s", msg['key'])
            else:
                self._notify_task_removed(task)
                self._notify_tasks_count()
            task.interrupt()

        elif msg['message'] == 'clear_all_finished':
            for task in self.finished_tasks:
                self._notify_task_removed(task)
            self.finished_tasks = []
            self._notify_tasks_count()

        elif msg['message'] == 'clear_finished':
            try:
                task = self._lookup_task(msg['key'])
            except KeyError:
                logging.warn("Couldn't find task for key %s", msg['key'])
                return
            self.finished_tasks.remove(task)
            self._notify_task_removed(task)
            self._notify_tasks_count()

        elif msg['message'] == 'cancel_all':
            self._terminate()

        elif msg['message'] == 'stage_conversion':
            try:
                task = self._lookup_task(msg['key'])
            except KeyError:
                logging.warn("Couldn't find task for key %s", msg['key'])
                return
            source = task.temp_output_path
            try:
                destination, fp = next_free_filename(task.final_output_path)
                fp.close()
            except ValueError:
                logging.warn(
                    '_process_message_queue: '
                    'next_free_filename failed.  Candidate = %r',
                    task.final_output_path)
                return
            source_info = task.item_info
            conversion_name = task.get_display_name()
            if os.path.exists(source):
                self._move_finished_file(source, destination)
                if task.create_item:
                    _create_item_for_conversion(destination, source_info,
                                                conversion_name)
                if not task.temp_output_path.endswith('.tmp'):  # temp dir
                    clean_up(task.temp_output_path, file_and_directory=True)
            else:
                task.error = _("Reason unknown--check log")
                self._notify_tasks_count()
            self.emit('task-staged', task)
Exemple #15
0
    def update_icon_cache(self, url, info):
        self.dbItem.confirm_db_thread()

        if self.removed:
            icon_cache_updater.update_finished()
            return

        needs_save = False
        needsChange = False

        if info == None or (info['status'] != 304 and info['status'] != 200):
            self.error_callback(url, "bad response")
            return
        try:
            # Our cache is good.  Hooray!
            if info['status'] == 304:
                return

            needsChange = True

            # We have to update it, and if we can't write to the file, we
            # should pick a new filename.
            if ((self.filename and
                 not fileutil.access(self.filename, os.R_OK | os.W_OK))):
                self.filename = None

            cachedir = app.config.get(prefs.ICON_CACHE_DIRECTORY)
            try:
                fileutil.makedirs(cachedir)
            except OSError:
                pass

            try:
                # Write to a temp file.
                if self.filename:
                    tmp_filename = self.filename + ".part"
                else:
                    tmp_filename = os.path.join(cachedir, info["filename"]) + ".part"
                tmp_filename, output = next_free_filename(tmp_filename)
                output.write(info["body"])
                output.close()
            except IOError:
                self.remove_file(tmp_filename)
                return
            except ValueError:
                logging.warn('update_icon_cache: next_free_filename failed '
                             '#1, candidate = %r', tmp_filename)
                return

            filename = unicode(info["filename"])
            filename = unicode_to_filename(filename, cachedir)
            filename = os.path.join(cachedir, filename)
            needs_save = True
            try:
                filename, fp = next_free_filename(filename)
            except ValueError:
                logging.warn('update_icon_cache: next_free_filename failed '
                             '#2, candidate = %r', filename)
                return

            if self.filename:
                filename = self.filename
                self.filename = None
                self.remove_file(filename)

            # we need to move the file here--so we close the file
            # pointer and then move the file.
            fp.close()
            try:
                self.remove_file(filename)
                fileutil.rename(tmp_filename, filename)
            except (IOError, OSError):
                logging.exception("iconcache: fileutil.move failed")
                filename = None

            self.filename = filename

            etag = unicodify(info.get("etag"))
            modified = unicodify(info.get("modified"))

            if self.etag != etag:
                needs_save = True
                self.etag = etag
            if self.modified != modified:
                needs_save = True
                self.modified = modified
            if self.url != url:
                needs_save = True
                self.url = url
        finally:
            if needsChange:
                self.icon_changed(needs_save=needs_save)
            self.updating = False
            if self.needsUpdate:
                self.needsUpdate = False
                self.request_update(True)
            icon_cache_updater.update_finished()
Exemple #16
0
    def update_icon_cache(self, url, info):
        self.dbItem.confirm_db_thread()

        if self.removed:
            iconCacheUpdater.update_finished()
            return

        needs_save = False
        needsChange = False

        if info == None or (info['status'] != 304 and info['status'] != 200):
            self.error_callback(url, "bad response")
            return
        try:
            # Our cache is good.  Hooray!
            if info['status'] == 304:
                return

            needsChange = True

            # We have to update it, and if we can't write to the file, we
            # should pick a new filename.
            if self.filename and not fileutil.access(self.filename, os.R_OK | os.W_OK):
                self.filename = None

            cachedir = app.config.get(prefs.ICON_CACHE_DIRECTORY)
            try:
                fileutil.makedirs(cachedir)
            except OSError:
                pass

            try:
                # Write to a temp file.
                if self.filename:
                    tmp_filename = self.filename + ".part"
                else:
                    tmp_filename = os.path.join(cachedir, info["filename"]) + ".part"

                tmp_filename, output = next_free_filename(tmp_filename)
                output.write(info["body"])
                output.close()
            except IOError:
                self.remove_file(tmp_filename)
                return

            if self.filename:
                self.remove_file(self.filename)

            # Create a new filename always to avoid browser caching in case a
            # file changes.
            # Add a random unique id
            parts = unicodify(info["filename"]).split('.')
            uid = u"%08d" % random.randint(0, 99999999)
            if len(parts) == 1:
                parts.append(uid)
            else:
                parts[-1:-1] = [uid]
            self.filename = u'.'.join(parts)
            self.filename = unicode_to_filename(self.filename, cachedir)
            self.filename = os.path.join(cachedir, self.filename)
            self.filename, fp = next_free_filename(self.filename)
            needs_save = True

            try:
                fileutil.rename(tmp_filename, self.filename)
            except OSError:
                self.filename = None
                needs_save = True
            fp.close()

            etag = unicodify(info.get("etag"))
            modified = unicodify(info.get("modified"))

            if self.etag != etag:
                needs_save = True
                self.etag = etag
            if self.modified != modified:
                needs_save = True
                self.modified = modified
            if self.url != url:
                needs_save = True
                self.url = url
        finally:
            if needsChange:
                self.icon_changed(needs_save=needs_save)
            self.updating = False
            if self.needsUpdate:
                self.needsUpdate = False
                self.request_update(True)
            iconCacheUpdater.update_finished()