def add_track(self, episode,reporthook=None): self.notify('status', _('Adding %s') % episode.title.decode('utf-8', 'ignore')) # get the folder on the device folder = self.get_episode_folder_on_device(episode) filename = episode.local_filename(create=False) # The file has to exist, if we ought to transfer it, and therefore, # local_filename(create=False) must never return None as filename assert filename is not None from_file = util.sanitize_encoding(filename) # get the filename that will be used on the device to_file = self.get_episode_file_on_device(episode) to_file = util.sanitize_encoding(os.path.join(folder, to_file)) if not os.path.exists(folder): try: os.makedirs(folder) except: logger.error('Cannot create folder on MP3 player: %s', folder) return False if not os.path.exists(to_file): logger.info('Copying %s => %s', os.path.basename(from_file), to_file.decode(util.encoding)) self.copy_file_progress(from_file, to_file, reporthook) return True
def add_track(self, episode, reporthook=None): self.notify('status', _('Adding %s') % episode.title.decode('utf-8', 'ignore')) if self._config.device_sync.one_folder_per_podcast: # Add channel title as subfolder folder = episode.channel.title # Clean up the folder name for use on limited devices folder = util.sanitize_filename( folder, self._config.device_sync.max_filename_length) folder = os.path.join(self.destination, folder) else: folder = self.destination folder = util.sanitize_encoding(folder) filename = episode.local_filename(create=False) # The file has to exist, if we ought to transfer it, and therefore, # local_filename(create=False) must never return None as filename assert filename is not None from_file = util.sanitize_encoding(filename) filename_base = util.sanitize_filename( episode.sync_filename( self._config.device_sync.custom_sync_name_enabled, self._config.device_sync.custom_sync_name), self._config.device_sync.max_filename_length) to_file = filename_base + os.path.splitext(from_file)[1].lower() # dirty workaround: on bad (empty) episode titles, # we simply use the from_file basename # (please, podcast authors, FIX YOUR RSS FEEDS!) if os.path.splitext(to_file)[0] == '': to_file = os.path.basename(from_file) to_file = util.sanitize_encoding(os.path.join(folder, to_file)) if not os.path.exists(folder): try: os.makedirs(folder) except: logger.error('Cannot create folder on MP3 player: %s', folder) return False if not os.path.exists(to_file): logger.info('Copying %s => %s', os.path.basename(from_file), to_file.decode(util.encoding)) self.copy_file_progress(from_file, to_file, reporthook) return True
def add_track(self, episode,reporthook=None): self.notify('status', _('Adding %s') % episode.title.decode('utf-8', 'ignore')) if self._config.device_sync.one_folder_per_podcast: # Add channel title as subfolder folder = episode.channel.title # Clean up the folder name for use on limited devices folder = util.sanitize_filename(folder, self._config.device_sync.max_filename_length) folder = os.path.join(self.destination, folder) else: folder = self.destination folder = util.sanitize_encoding(folder) filename = episode.local_filename(create=False) # The file has to exist, if we ought to transfer it, and therefore, # local_filename(create=False) must never return None as filename assert filename is not None from_file = util.sanitize_encoding(filename) filename_base = util.sanitize_filename(episode.sync_filename( self._config.device_sync.custom_sync_name_enabled, self._config.device_sync.custom_sync_name), self._config.device_sync.max_filename_length) to_file = filename_base + os.path.splitext(from_file)[1].lower() # dirty workaround: on bad (empty) episode titles, # we simply use the from_file basename # (please, podcast authors, FIX YOUR RSS FEEDS!) if os.path.splitext(to_file)[0] == '': to_file = os.path.basename(from_file) to_file = util.sanitize_encoding(os.path.join(folder, to_file)) if not os.path.exists(folder): try: os.makedirs(folder) except: logger.error('Cannot create folder on MP3 player: %s', folder) return False if not os.path.exists(to_file): logger.info('Copying %s => %s', os.path.basename(from_file), to_file.decode(util.encoding)) self.copy_file_progress(from_file, to_file, reporthook) return True
def get_save_dir(self, force_new=False): if self.download_folder is None or force_new: # we must change the folder name, because it has not been set manually fn_template = util.sanitize_filename(self.title, self.MAX_FOLDERNAME_LENGTH) if not fn_template: fn_template = util.sanitize_filename(self.url, self.MAX_FOLDERNAME_LENGTH) # Find a unique folder name for this podcast download_folder = self.find_unique_folder_name(fn_template) # Try removing the download folder if it has been created previously if self.download_folder is not None: folder = os.path.join(gpodder.downloads, self.download_folder) try: os.rmdir(folder) except OSError: logger.info("Old download folder is kept for %s", self.url) logger.info("Updating download_folder of %s to %s", self.url, download_folder) self.download_folder = download_folder self.save() save_dir = os.path.join(gpodder.downloads, self.download_folder) # Avoid encoding errors for OS-specific functions (bug 1570) save_dir = util.sanitize_encoding(save_dir) # Create save_dir if it does not yet exist if not util.make_directory(save_dir): logger.error("Could not create save_dir: %s", save_dir) return save_dir
def make_filename(self, current_filename, title, sortdate, podcast_title): dirname = os.path.dirname(current_filename) filename = os.path.basename(current_filename) basename, ext = os.path.splitext(filename) new_basename = [] new_basename.append(util.sanitize_encoding(title) + ext) if self.config.add_podcast_title: new_basename.insert(0, podcast_title) if self.config.add_sortdate: new_basename.insert(0, sortdate) new_basename = ' - '.join(new_basename) # On Windows, force ASCII encoding for filenames (bug 1724) new_basename = util.sanitize_filename(new_basename, use_ascii=gpodder.ui.win32) new_filename = os.path.join(dirname, new_basename) if new_filename == current_filename: return current_filename for filename in util.generate_names(new_filename): # Avoid filename collisions if not os.path.exists(filename): return filename
def __init__(self, config, download_status_model, download_queue_manager): Device.__init__(self, config) self.destination = util.sanitize_encoding( self._config.device_sync.device_folder) self.buffer_size = 1024 * 1024 # 1 MiB self.download_status_model = download_status_model self.download_queue_manager = download_queue_manager
def rename_file(current_filename, title): dirname = os.path.dirname(current_filename) filename = os.path.basename(current_filename) basename, ext = os.path.splitext(filename) new_filename = util.sanitize_encoding(title) + ext return os.path.join(dirname, new_filename)
def add_track(self, episode, reporthook=None): self.notify('status', _('Adding %s') % episode.title.decode('utf-8', 'ignore')) # get the folder on the device folder = self.get_episode_folder_on_device(episode) filename = episode.local_filename(create=False) # The file has to exist, if we ought to transfer it, and therefore, # local_filename(create=False) must never return None as filename assert filename is not None from_file = util.sanitize_encoding(filename) # verify free space needed = util.calculate_size(from_file) free = self.get_free_space() if free == -1: logger.warn('Cannot determine free disk space on device') elif needed > free: d = { 'path': self.destination, 'free': util.format_filesize(free), 'need': util.format_filesize(needed) } message = _( 'Not enough space in %(path)s: %(free)s available, but need at least %(need)s' ) raise SyncFailedException(message % d) # get the filename that will be used on the device to_file = self.get_episode_file_on_device(episode) to_file = util.sanitize_encoding(os.path.join(folder, to_file)) if not os.path.exists(folder): try: os.makedirs(folder) except: logger.error('Cannot create folder on MP3 player: %s', folder) return False if not os.path.exists(to_file): logger.info('Copying %s => %s', os.path.basename(from_file), to_file.decode(util.encoding)) self.copy_file_progress(from_file, to_file, reporthook) return True
def add_track(self, episode): self.notify('status', _('Adding %s') % episode.title.decode('utf-8', 'ignore')) if self._config.fssync_channel_subfolders: # Add channel title as subfolder folder = episode.channel.title # Clean up the folder name for use on limited devices folder = util.sanitize_filename(folder, self._config.mp3_player_max_filename_length) folder = os.path.join(self.destination, folder) else: folder = self.destination from_file = util.sanitize_encoding(self.convert_track(episode)) filename_base = util.sanitize_filename(episode.sync_filename(self._config.custom_sync_name_enabled, self._config.custom_sync_name), self._config.mp3_player_max_filename_length) to_file = filename_base + os.path.splitext(from_file)[1].lower() # dirty workaround: on bad (empty) episode titles, # we simply use the from_file basename # (please, podcast authors, FIX YOUR RSS FEEDS!) if os.path.splitext(to_file)[0] == '': to_file = os.path.basename(from_file) to_file = os.path.join(folder, to_file) if not os.path.exists(folder): try: os.makedirs(folder) except: log('Cannot create folder on MP3 player: %s', folder, sender=self) return False if self._config.mp3_player_use_scrobbler_log and not episode.is_played: # FIXME: This misses some things when channel.title<>album tag which is what # the scrobbling entity will be using. if [episode.channel.title, episode.title] in self.scrobbler_log: log('Marking "%s" from "%s" as played', episode.title, episode.channel.title, sender=self) episode.mark(is_played=True) if self._config.rockbox_copy_coverart and not os.path.exists(os.path.join(folder, 'cover.bmp')): log('Creating Rockbox album art for "%s"', episode.channel.title, sender=self) self.copy_player_cover_art(folder, from_file, \ 'cover.bmp', 'BMP', self._config.rockbox_coverart_size) if self._config.custom_player_copy_coverart \ and not os.path.exists(os.path.join(folder, \ self._config.custom_player_coverart_name)): log('Creating custom player album art for "%s"', episode.channel.title, sender=self) self.copy_player_cover_art(folder, from_file, \ self._config.custom_player_coverart_name, \ self._config.custom_player_coverart_format, \ self._config.custom_player_coverart_size) if not os.path.exists(to_file): log('Copying %s => %s', os.path.basename(from_file), to_file.decode(util.encoding), sender=self) return self.copy_file_progress(from_file, to_file) return True
def __init__(self, config, download_status_model, download_queue_manager): Device.__init__(self, config) self.destination = util.sanitize_encoding(self._config.device_sync.device_folder) self.buffer_size = 1024*1024 # 1 MiB self.download_status_model = download_status_model self.download_queue_manager = download_queue_manager
def __init__(self, config, playlist_name): self._config=config self.linebreak = '\r\n' self.playlist_file=util.sanitize_filename(playlist_name + '.m3u') self.playlist_folder = os.path.join(self._config.device_sync.device_folder, self._config.device_sync.playlists.folder) self.mountpoint = util.find_mount_point(util.sanitize_encoding(self.playlist_folder)) if self.mountpoint == '/': self.mountpoint = self.playlist_folder logger.warning('MP3 player resides on / - using %s as MP3 player root', self.mountpoint) self.playlist_absolute_filename=os.path.join(self.playlist_folder, self.playlist_file)
def add_track(self, episode,reporthook=None): self.notify('status', _('Adding %s') % episode.title.decode('utf-8', 'ignore')) # get the folder on the device folder = self.get_episode_folder_on_device(episode) filename = episode.local_filename(create=False) # The file has to exist, if we ought to transfer it, and therefore, # local_filename(create=False) must never return None as filename assert filename is not None from_file = util.sanitize_encoding(filename) # verify free space needed = util.calculate_size(from_file) free = self.get_free_space() if free == -1: logger.warn('Cannot determine free disk space on device') elif needed > free: d = {'path': self.destination, 'free': util.format_filesize(free), 'need': util.format_filesize(needed)} message =_('Not enough space in %(path)s: %(free)s available, but need at least %(need)s') raise SyncFailedException(message % d) # get the filename that will be used on the device to_file = self.get_episode_file_on_device(episode) to_file = util.sanitize_encoding(os.path.join(folder, to_file)) if not os.path.exists(folder): try: os.makedirs(folder) except: logger.error('Cannot create folder on MP3 player: %s', folder) return False if not os.path.exists(to_file): logger.info('Copying %s => %s', os.path.basename(from_file), to_file.decode(util.encoding)) self.copy_file_progress(from_file, to_file, reporthook) return True
def get_episode_folder_on_device(self, episode): if self._config.device_sync.one_folder_per_podcast: # Add channel title as subfolder folder = episode.channel.title # Clean up the folder name for use on limited devices folder = util.sanitize_filename(folder, self._config.device_sync.max_filename_length) folder = os.path.join(self.destination, folder) else: folder = self.destination return util.sanitize_encoding(folder)
def get_episode_folder_on_device(self, episode): if self._config.device_sync.one_folder_per_podcast: # Add channel title as subfolder folder = episode.channel.title # Clean up the folder name for use on limited devices folder = util.sanitize_filename( folder, self._config.device_sync.max_filename_length) folder = os.path.join(self.destination, folder) else: folder = self.destination return util.sanitize_encoding(folder)
def get_thing_info(self, payment_url): """Get information about a Thing on Flattr Return a tuple (flattrs, flattred): flattrs ... The number of Flattrs this thing received flattred ... True if this user already flattred this thing """ if not self._config.token: return (0, False) quote_url = urllib.quote_plus(util.sanitize_encoding(payment_url)) url = self.THING_INFO_URL_TEMPLATE % {'url': quote_url} data = self.request(url) return (int(data.get('flattrs', 0)), bool(data.get('flattred', False)))
def make_filename(self, current_filename, title): dirname = os.path.dirname(current_filename) filename = os.path.basename(current_filename) basename, ext = os.path.splitext(filename) new_basename = util.sanitize_encoding(title) + ext new_filename = os.path.join(dirname, new_basename) if new_filename == current_filename: return current_filename for filename in util.generate_names(new_filename): # Avoid filename collisions if not os.path.exists(filename): return filename
def on_episode_downloaded(self, episode): current_filename = episode.local_filename(False) dirname = os.path.dirname(current_filename) filename = os.path.basename(current_filename) basename, ext = os.path.splitext(filename) new_filename = util.sanitize_encoding(episode.title) + ext print 'Renaming:', filename, '->', new_filename destination_filename = os.path.join(dirname, new_filename) os.rename(current_filename, destination_filename) episode.filename = new_filename episode.save()
def make_filename(self, current_filename, title): dirname = os.path.dirname(current_filename) filename = os.path.basename(current_filename) basename, ext = os.path.splitext(filename) new_basename = util.sanitize_encoding(title) + ext new_basename = util.sanitize_filename(new_basename) new_filename = os.path.join(dirname, new_basename) if new_filename == current_filename: return current_filename for filename in util.generate_names(new_filename): # Avoid filename collisions if not os.path.exists(filename): return filename
def _convert_mp4(self, episode, from_file): """Convert MP4 file to rockbox mpg file""" # generate new filename and check if the file already exists to_file = self._get_rockbox_filename(from_file) if to_file is None: return None if os.path.isfile(to_file): return to_file logger.info("Converting: %s", from_file) gpodder.user_extensions.on_notification_show("Converting", episode.title) # calculationg the new screen resolution info = kaa.metadata.parse(from_file) resolution = self._calc_resolution(info.video[0].width, info.video[0].height, self.container.config.device_width, self.container.config.device_height) if resolution is None: logger.error("Error calculating the new screen resolution") return None convert_command = FFMPEG_CMD % { 'from': from_file, 'to': to_file, 'width': str(resolution[0]), 'height': str(resolution[1]), 'options': self.container.config.ffmpeg_options } # Prior to Python 2.7.3, this module (shlex) did not support Unicode input. convert_command = util.sanitize_encoding(convert_command) process = subprocess.Popen(shlex.split(convert_command), stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = process.communicate() if process.returncode != 0: logger.error(stderr) return None gpodder.user_extensions.on_notification_show("Converting finished", episode.title) return to_file
def _convert_mp4(self, episode, from_file): """Convert MP4 file to rockbox mpg file""" # generate new filename and check if the file already exists to_file = self._get_rockbox_filename(from_file) if to_file is None: return None if os.path.isfile(to_file): return to_file logger.info("Converting: %s", from_file) gpodder.user_extensions.on_notification_show("Converting", episode) # calculationg the new screen resolution info = kaa.metadata.parse(from_file) resolution = self._calc_resolution( info.video[0].width, info.video[0].height, self.container.config.device_width, self.container.config.device_height ) if resolution is None: logger.error("Error calculating the new screen resolution") return None convert_command = FFMPEG_CMD % { 'from': from_file, 'to': to_file, 'width': str(resolution[0]), 'height': str(resolution[1]), 'options': self.container.config.ffmpeg_options } # Prior to Python 2.7.3, this module (shlex) did not support Unicode input. convert_command = util.sanitize_encoding(convert_command) process = subprocess.Popen(shlex.split(convert_command), stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = process.communicate() if process.returncode != 0: logger.error(stderr) return None gpodder.user_extensions.on_notification_show("Converting finished", episode) return to_file
def get_episode_file_on_device(self, episode): # get the local file from_file = util.sanitize_encoding(episode.local_filename(create=False)) # get the formated base name filename_base = util.sanitize_filename(episode.sync_filename( self._config.device_sync.custom_sync_name_enabled, self._config.device_sync.custom_sync_name), self._config.device_sync.max_filename_length) # add the file extension to_file = filename_base + os.path.splitext(from_file)[1].lower() # dirty workaround: on bad (empty) episode titles, # we simply use the from_file basename # (please, podcast authors, FIX YOUR RSS FEEDS!) if os.path.splitext(to_file)[0] == '': to_file = os.path.basename(from_file) return to_file
def make_filename(self, current_filename, title): dirname = os.path.dirname(current_filename) filename = os.path.basename(current_filename) basename, ext = os.path.splitext(filename) new_basename = util.sanitize_encoding(title) + ext # On Windows, force ASCII encoding for filenames (bug 1724) new_basename = util.sanitize_filename(new_basename, use_ascii=gpodder.ui.win32) new_filename = os.path.join(dirname, new_basename) if new_filename == current_filename: return current_filename for filename in util.generate_names(new_filename): # Avoid filename collisions if not os.path.exists(filename): return filename
def make_filename(self, current_filename, title, sortdate = ''): dirname = os.path.dirname(current_filename) filename = os.path.basename(current_filename) basename, ext = os.path.splitext(filename) new_basename = sortdate + util.sanitize_encoding(title) + ext # On Windows, force ASCII encoding for filenames (bug 1724) new_basename = util.sanitize_filename(new_basename, use_ascii=gpodder.ui.win32) new_filename = os.path.join(dirname, new_basename) if new_filename == current_filename: return current_filename for filename in util.generate_names(new_filename): # Avoid filename collisions if not os.path.exists(filename): return filename
def get_save_dir(self, force_new=False): if self.download_folder is None or force_new: # we must change the folder name, because it has not been set manually fn_template = util.sanitize_filename(self.title, self.MAX_FOLDERNAME_LENGTH) if not fn_template: fn_template = util.sanitize_filename( self.url, self.MAX_FOLDERNAME_LENGTH) # Find a unique folder name for this podcast download_folder = self.find_unique_folder_name(fn_template) # Try removing the download folder if it has been created previously if self.download_folder is not None: folder = os.path.join(gpodder.downloads, self.download_folder) try: os.rmdir(folder) except OSError: logger.info('Old download folder is kept for %s', self.url) logger.info('Updating download_folder of %s to %s', self.url, download_folder) self.download_folder = download_folder self.save() save_dir = os.path.join(gpodder.downloads, self.download_folder) # Avoid encoding errors for OS-specific functions (bug 1570) save_dir = util.sanitize_encoding(save_dir) # Create save_dir if it does not yet exist if not util.make_directory(save_dir): logger.error('Could not create save_dir: %s', save_dir) return save_dir
def local_filename(self, create, force_update=False, check_only=False, template=None, return_wanted_filename=False): """Get (and possibly generate) the local saving filename Pass create=True if you want this function to generate a new filename if none exists. You only want to do this when planning to create/download the file after calling this function. Normally, you should pass create=False. This will only create a filename when the file already exists from a previous version of gPodder (where we used md5 filenames). If the file does not exist (and the filename also does not exist), this function will return None. If you pass force_update=True to this function, it will try to find a new (better) filename and move the current file if this is the case. This is useful if (during the download) you get more information about the file, e.g. the mimetype and you want to include this information in the file name generation process. If check_only=True is passed to this function, it will never try to rename the file, even if would be a good idea. Use this if you only want to check if a file exists. If "template" is specified, it should be a filename that is to be used as a template for generating the "real" filename. The generated filename is stored in the database for future access. If return_wanted_filename is True, the filename will not be written to the database, but simply returned by this function (for use by the "import external downloads" feature). """ if self.download_filename is None and (check_only or not create): return None ext = self.extension(may_call_local_filename=False).encode("utf-8", "ignore") if not check_only and (force_update or not self.download_filename): # Avoid and catch gPodder bug 1440 and similar situations if template == "": logger.warn("Empty template. Report this podcast URL %s", self.channel.url) template = None # Try to find a new filename for the current file if template is not None: # If template is specified, trust the template's extension episode_filename, ext = os.path.splitext(template) else: episode_filename, _ = util.filename_from_url(self.url) fn_template = util.sanitize_filename(episode_filename, self.MAX_FILENAME_LENGTH) if "redirect" in fn_template and template is None: # This looks like a redirection URL - force URL resolving! logger.warn("Looks like a redirection to me: %s", self.url) url = util.get_real_url(self.channel.authenticate_url(self.url)) logger.info("Redirection resolved to: %s", url) episode_filename, _ = util.filename_from_url(url) fn_template = util.sanitize_filename(episode_filename, self.MAX_FILENAME_LENGTH) # Use title for YouTube, Vimeo and Soundcloud downloads if ( youtube.is_video_link(self.url) or vimeo.is_video_link(self.url) or escapist_videos.is_video_link(self.url) or fn_template == "stream" ): sanitized = util.sanitize_filename(self.title, self.MAX_FILENAME_LENGTH) if sanitized: fn_template = sanitized # If the basename is empty, use the md5 hexdigest of the URL if not fn_template or fn_template.startswith("redirect."): logger.error("Report this feed: Podcast %s, episode %s", self.channel.url, self.url) fn_template = hashlib.md5(self.url).hexdigest() # Find a unique filename for this episode wanted_filename = self.find_unique_file_name(fn_template, ext) if return_wanted_filename: # return the calculated filename without updating the database return wanted_filename # The old file exists, but we have decided to want a different filename if self.download_filename and wanted_filename != self.download_filename: # there might be an old download folder crawling around - move it! new_file_name = os.path.join(self.channel.save_dir, wanted_filename) old_file_name = os.path.join(self.channel.save_dir, self.download_filename) if os.path.exists(old_file_name) and not os.path.exists(new_file_name): logger.info("Renaming %s => %s", old_file_name, new_file_name) os.rename(old_file_name, new_file_name) elif force_update and not os.path.exists(old_file_name): # When we call force_update, the file might not yet exist when we # call it from the downloading code before saving the file logger.info("Choosing new filename: %s", new_file_name) else: logger.warn("%s exists or %s does not", new_file_name, old_file_name) logger.info('Updating filename of %s to "%s".', self.url, wanted_filename) elif self.download_filename is None: logger.info("Setting download filename: %s", wanted_filename) self.download_filename = wanted_filename self.save() return os.path.join( util.sanitize_encoding(self.channel.save_dir), util.sanitize_encoding(self.download_filename) )
def add_track(self, episode): self.notify('status', _('Adding %s') % episode.title.decode('utf-8', 'ignore')) if self._config.fssync_channel_subfolders: # Add channel title as subfolder folder = episode.channel.title # Clean up the folder name for use on limited devices folder = util.sanitize_filename(folder, self._config.mp3_player_max_filename_length) folder = os.path.join(self.destination, folder) else: folder = self.destination folder = util.sanitize_encoding(folder) from_file = util.sanitize_encoding(self.convert_track(episode)) filename_base = util.sanitize_filename(episode.sync_filename(self._config.custom_sync_name_enabled, self._config.custom_sync_name), self._config.mp3_player_max_filename_length) to_file = filename_base + os.path.splitext(from_file)[1].lower() # dirty workaround: on bad (empty) episode titles, # we simply use the from_file basename # (please, podcast authors, FIX YOUR RSS FEEDS!) if os.path.splitext(to_file)[0] == '': to_file = os.path.basename(from_file) to_file = util.sanitize_encoding(os.path.join(folder, to_file)) if not os.path.exists(folder): try: os.makedirs(folder) except: log('Cannot create folder on MP3 player: %s', folder, sender=self) return False if self._config.mp3_player_use_scrobbler_log and not episode.is_played: # FIXME: This misses some things when channel.title<>album tag which is what # the scrobbling entity will be using. if [episode.channel.title, episode.title] in self.scrobbler_log: log('Marking "%s" from "%s" as played', episode.title, episode.channel.title, sender=self) episode.mark(is_played=True) if self._config.rockbox_copy_coverart and not os.path.exists(os.path.join(folder, 'cover.bmp')): log('Creating Rockbox album art for "%s"', episode.channel.title, sender=self) self.copy_player_cover_art(folder, from_file, \ 'cover.bmp', 'BMP', self._config.rockbox_coverart_size) if self._config.custom_player_copy_coverart \ and not os.path.exists(os.path.join(folder, \ self._config.custom_player_coverart_name)): log('Creating custom player album art for "%s"', episode.channel.title, sender=self) self.copy_player_cover_art(folder, from_file, \ self._config.custom_player_coverart_name, \ self._config.custom_player_coverart_format, \ self._config.custom_player_coverart_size) if not os.path.exists(to_file): log('Copying %s => %s', os.path.basename(from_file), to_file.decode(util.encoding), sender=self) copied = self.copy_file_progress(from_file, to_file) if copied and gpodder.user_hooks is not None: gpodder.user_hooks.on_file_copied_to_filesystem(self, from_file, to_file) return copied return True
def local_filename(self, create, force_update=False, check_only=False, template=None, return_wanted_filename=False): """Get (and possibly generate) the local saving filename Pass create=True if you want this function to generate a new filename if none exists. You only want to do this when planning to create/download the file after calling this function. Normally, you should pass create=False. This will only create a filename when the file already exists from a previous version of gPodder (where we used md5 filenames). If the file does not exist (and the filename also does not exist), this function will return None. If you pass force_update=True to this function, it will try to find a new (better) filename and move the current file if this is the case. This is useful if (during the download) you get more information about the file, e.g. the mimetype and you want to include this information in the file name generation process. If check_only=True is passed to this function, it will never try to rename the file, even if would be a good idea. Use this if you only want to check if a file exists. If "template" is specified, it should be a filename that is to be used as a template for generating the "real" filename. The generated filename is stored in the database for future access. If return_wanted_filename is True, the filename will not be written to the database, but simply returned by this function (for use by the "import external downloads" feature). """ if self.download_filename is None and (check_only or not create): return None ext = self.extension(may_call_local_filename=False).encode( 'utf-8', 'ignore') if not check_only and (force_update or not self.download_filename): # Avoid and catch gPodder bug 1440 and similar situations if template == '': logger.warn('Empty template. Report this podcast URL %s', self.channel.url) template = None # Try to find a new filename for the current file if template is not None: # If template is specified, trust the template's extension episode_filename, ext = os.path.splitext(template) else: episode_filename, _ = util.filename_from_url(self.url) fn_template = util.sanitize_filename(episode_filename, self.MAX_FILENAME_LENGTH) if 'redirect' in fn_template and template is None: # This looks like a redirection URL - force URL resolving! logger.warn('Looks like a redirection to me: %s', self.url) url = util.get_real_url(self.channel.authenticate_url( self.url)) logger.info('Redirection resolved to: %s', url) episode_filename, _ = util.filename_from_url(url) fn_template = util.sanitize_filename(episode_filename, self.MAX_FILENAME_LENGTH) # Use title for YouTube, Vimeo and Soundcloud downloads if (youtube.is_video_link(self.url) or vimeo.is_video_link(self.url) or escapist_videos.is_video_link(self.url) or fn_template == 'stream'): sanitized = util.sanitize_filename(self.title, self.MAX_FILENAME_LENGTH) if sanitized: fn_template = sanitized # If the basename is empty, use the md5 hexdigest of the URL if not fn_template or fn_template.startswith('redirect.'): logger.error('Report this feed: Podcast %s, episode %s', self.channel.url, self.url) fn_template = hashlib.md5(self.url).hexdigest() # Find a unique filename for this episode wanted_filename = self.find_unique_file_name(fn_template, ext) if return_wanted_filename: # return the calculated filename without updating the database return wanted_filename # The old file exists, but we have decided to want a different filename if self.download_filename and wanted_filename != self.download_filename: # there might be an old download folder crawling around - move it! new_file_name = os.path.join(self.channel.save_dir, wanted_filename) old_file_name = os.path.join(self.channel.save_dir, self.download_filename) if os.path.exists( old_file_name) and not os.path.exists(new_file_name): logger.info('Renaming %s => %s', old_file_name, new_file_name) os.rename(old_file_name, new_file_name) elif force_update and not os.path.exists(old_file_name): # When we call force_update, the file might not yet exist when we # call it from the downloading code before saving the file logger.info('Choosing new filename: %s', new_file_name) else: logger.warn('%s exists or %s does not', new_file_name, old_file_name) logger.info('Updating filename of %s to "%s".', self.url, wanted_filename) elif self.download_filename is None: logger.info('Setting download filename: %s', wanted_filename) self.download_filename = wanted_filename self.save() return os.path.join(util.sanitize_encoding(self.channel.save_dir), util.sanitize_encoding(self.download_filename))