Exemple #1
0
    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
Exemple #2
0
    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
Exemple #3
0
    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
Exemple #4
0
    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
Exemple #5
0
    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
Exemple #6
0
 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)
Exemple #8
0
    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
Exemple #9
0
    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
Exemple #10
0
 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
Exemple #11
0
 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)
Exemple #12
0
    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
Exemple #13
0
    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)
Exemple #14
0
    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)
Exemple #15
0
    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)))
Exemple #16
0
    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
Exemple #17
0
    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 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()
Exemple #19
0
    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
Exemple #20
0
    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 _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
Exemple #23
0
    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
Exemple #25
0
    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
Exemple #26
0
    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
Exemple #27
0
    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
Exemple #28
0
    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)
        )
Exemple #29
0
    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
Exemple #30
0
    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))