Beispiel #1
0
    def request_update(self, iter, task=None):
        if task is None:
            # Ongoing update request from UI - get task from model
            task = self.get_value(iter, self.C_TASK)
        else:
            # Initial update request - update non-changing fields
            self.set(iter,
                    self.C_TASK, task,
                    self.C_URL, task.url)

        if task.status == task.FAILED:
            status_message = '%s: %s' % (\
                    task.STATUS_MESSAGE[task.status], \
                    task.error_message)
        elif task.status == task.DOWNLOADING:
            status_message = '%s (%s, %s/s)' % (\
                    task.STATUS_MESSAGE[task.status], \
                    util.format_filesize(task.total_size), \
                    util.format_filesize(task.speed))
        else:
            status_message = '%s (%s)' % (\
                    task.STATUS_MESSAGE[task.status], \
                    util.format_filesize(task.total_size))

        self.set(iter,
                self.C_NAME, self._format_message(task.markup_name, \
                    status_message, task.markup_podcast_name),
                self.C_PROGRESS, 100.*task.progress,
                self.C_PROGRESS_TEXT, '%.0f%%' % (task.progress*100.,),
                self.C_ICON_NAME, self._status_ids[task.status])
Beispiel #2
0
    def add_track(self, episode):
        self.notify('status', _('Adding %s...') % episode.title)
        filename = str(self.convert_track(episode))
        logger.info("sending %s (%s).", filename, episode.title)

        try:
            # verify free space
            needed = util.calculate_size(filename)
            free = self.get_free_space()
            if needed > free:
                logger.error('Not enough space on device %s: %s available, but '
                             'need at least %s',
                             self.get_name(),
                             util.format_filesize(free),
                             util.format_filesize(needed))
                self.cancelled = True
                return False

            # fill metadata
            metadata = pymtp.LIBMTP_Track()
            metadata.title = str(episode.title)
            metadata.artist = str(episode.channel.title)
            metadata.album = str(episode.channel.title)
            metadata.genre = "podcast"
            metadata.date = self.__date_to_mtp(episode.published)
            metadata.duration = get_track_length(str(filename))

            folder_name = ''
            if episode.mimetype.startswith('audio/') and self._config.mtp_audio_folder:
                folder_name = self._config.mtp_audio_folder
            if episode.mimetype.startswith('video/') and self._config.mtp_video_folder:
                folder_name = self._config.mtp_video_folder
            if episode.mimetype.startswith('image/') and self._config.mtp_image_folder:
                folder_name = self._config.mtp_image_folder

            if folder_name != '' and self._config.mtp_podcast_folders:
                folder_name += os.path.sep + str(episode.channel.title)

            # log('Target MTP folder: %s' % folder_name)

            if folder_name == '':
                folder_id = 0
            else:
                folder_id = self.__MTPDevice.mkdir(folder_name)

            # send the file
            to_file = util.sanitize_filename(metadata.title) + episode.extension()
            self.__MTPDevice.send_track_from_file(filename, to_file,
                    metadata, folder_id, callback=self.__callback)
            if gpodder.user_hooks is not None:
                gpodder.user_hooks.on_file_copied_to_mtp(self, filename, to_file)
        except:
            logger.error('unable to add episode %s', episode.title)
            return False

        return True
Beispiel #3
0
    def calculate_total_size(self):
        if self.size_attribute is not None:
            (total_size, count) = (0, 0)
            for episode in self.get_selected_episodes():
                try:
                    total_size += int(getattr(episode, self.size_attribute))
                    count += 1
                except:
                    pass

            text = []
            if count == 0:
                text.append(_("Nothing selected"))
            text.append(N_("%(count)d episode", "%(count)d episodes", count) % {"count": count})
            if total_size > 0:
                text.append(_("size: %s") % util.format_filesize(total_size))
            self.labelTotalSize.set_text(", ".join(text))
            self.btnOK.set_sensitive(count > 0)
            self.btnRemoveAction.set_sensitive(count > 0)
            if count > 0:
                self.btnCancel.set_label(gtk.STOCK_CANCEL)
            else:
                self.btnCancel.set_label(gtk.STOCK_CLOSE)
        else:
            self.btnOK.set_sensitive(False)
            self.btnRemoveAction.set_sensitive(False)
            for index, row in enumerate(self.model):
                if self.model.get_value(row.iter, self.COLUMN_TOGGLE) == True:
                    self.btnOK.set_sensitive(True)
                    self.btnRemoveAction.set_sensitive(True)
                    break
            self.labelTotalSize.set_text("")
Beispiel #4
0
 def on_download_status_progress(self):
     if self.task:
         self.download_progress.set_fraction(self.task.progress)
         self.download_progress.set_text('%s: %d%% (%s/s)' % ( \
                 self.task.STATUS_MESSAGE[self.task.status], \
                 100.*self.task.progress, \
                 util.format_filesize(self.task.speed)))
Beispiel #5
0
 def on_download_status_progress(self):
     if self.task:
         self.download_progress.set_fraction(self.task.progress)
         self.download_progress.set_text('%s: %d%% (%s/s)' % ( \
                 self.task.STATUS_MESSAGE[self.task.status], \
                 100.*self.task.progress, \
                 util.format_filesize(self.task.speed)))
Beispiel #6
0
    def update(self, episode):
        self.scrolled_window.get_vadjustment().set_value(0)

        self.define_colors()

        if episode.has_website_link():
            self._base_uri = episode.link
        else:
            self._base_uri = episode.channel.url

        # for incomplete base URI (e.g. http://919.noagendanotes.com)
        baseURI = urlparse(self._base_uri)
        if baseURI.path == '':
            self._base_uri += '/'
        self._loaded = False

        stylesheet = self.get_stylesheet()
        if stylesheet:
            self.manager.add_style_sheet(stylesheet)
        heading = '<h3>%s</h3>' % html.escape(episode.title)
        subheading = _('from %s') % html.escape(episode.channel.title)
        details = '<small>%s</small>' % html.escape(self.details_fmt % {
            'date': util.format_date(episode.published),
            'size': util.format_filesize(episode.file_size, digits=1)
            if episode.file_size > 0 else "-",
            'duration': episode.get_play_info_string()})
        header_html = _('<div id="gpodder-title">\n%(heading)s\n<p>%(subheading)s</p>\n<p>%(details)s</p></div>\n') \
            % dict(heading=heading, subheading=subheading, details=details)
        # uncomment to prevent background override in html shownotes
        # self.manager.remove_all_style_sheets ()
        logger.debug("base uri: %s (chan:%s)", self._base_uri, episode.channel.url)
        self.html_view.load_html(header_html + episode.html_description(), self._base_uri)
        # uncomment to show web inspector
        # self.html_view.get_inspector().show()
        self.episode = episode
Beispiel #7
0
    def calculate_total_size( self):
        if self.size_attribute is not None:
            (total_size, count) = (0, 0)
            for episode in self.get_selected_episodes():
                try:
                    total_size += int(getattr( episode, self.size_attribute))
                    count += 1
                except:
                    log( 'Cannot get size for %s', episode.title, sender = self)

            text = []
            if count == 0: 
                text.append(_('Nothing selected'))
            else:
                text.append(N_('%(count)d episode', '%(count)d episodes', count) % {'count':count})
            if total_size > 0: 
                text.append(_('size: %s') % util.format_filesize(total_size))
            self.labelTotalSize.set_text(', '.join(text))
            self.btnOK.set_sensitive(count>0)
            self.btnRemoveAction.set_sensitive(count>0)
        else:
            selection = self.treeviewEpisodes.get_selection()
            selected_rows = selection.count_selected_rows()
            self.btnOK.set_sensitive(selected_rows > 0)
            self.btnRemoveAction.set_sensitive(selected_rows > 0)
            self.labelTotalSize.set_text('')
Beispiel #8
0
 def update(self, episode):
     heading = episode.title
     subheading = _('from %s') % (episode.channel.title)
     details = self.details_fmt % (
         util.format_date(episode.published),
         util.format_filesize(episode.file_size, digits=1)
         if episode.file_size > 0 else "-", episode.get_play_info_string())
     self.define_colors()
     hyperlinks = [(0, None)]
     self.text_buffer.set_text('')
     self.text_buffer.insert_with_tags_by_name(
         self.text_buffer.get_end_iter(), heading, 'heading')
     self.text_buffer.insert_at_cursor('\n')
     self.text_buffer.insert_with_tags_by_name(
         self.text_buffer.get_end_iter(), subheading, 'subheading')
     self.text_buffer.insert_at_cursor('\n')
     self.text_buffer.insert_with_tags_by_name(
         self.text_buffer.get_end_iter(), details, 'details')
     self.text_buffer.insert_at_cursor('\n\n')
     for target, text in util.extract_hyperlinked_text(
             episode.description_html or episode.description):
         hyperlinks.append((self.text_buffer.get_char_count(), target))
         if target:
             self.text_buffer.insert_with_tags_by_name(
                 self.text_buffer.get_end_iter(), text, 'hyperlink')
         else:
             self.text_buffer.insert(self.text_buffer.get_end_iter(), text)
     hyperlinks.append((self.text_buffer.get_char_count(), None))
     self.hyperlinks = [
         (start, end, url)
         for (start, url), (end, _) in zip(hyperlinks, hyperlinks[1:])
         if url
     ]
     self.text_buffer.place_cursor(self.text_buffer.get_start_iter())
Beispiel #9
0
    def calculate_total_size(self):
        if self.size_attribute is not None:
            (total_size, count) = (0, 0)
            for episode in self.get_selected_episodes():
                try:
                    total_size += int(getattr(episode, self.size_attribute))
                    count += 1
                except:
                    pass

            text = []
            if count == 0:
                text.append(_('Nothing selected'))
            text.append(
                N_('%(count)d episode', '%(count)d episodes', count) %
                {'count': count})
            if total_size > 0:
                text.append(_('size: %s') % util.format_filesize(total_size))
            self.labelTotalSize.set_text(', '.join(text))
            self.btnOK.set_sensitive(count > 0)
            self.btnRemoveAction.set_sensitive(count > 0)
            if count > 0:
                self.btnCancel.set_label(gtk.STOCK_CANCEL)
            else:
                self.btnCancel.set_label(gtk.STOCK_CLOSE)
        else:
            self.btnOK.set_sensitive(False)
            self.btnRemoveAction.set_sensitive(False)
            for index, row in enumerate(self.model):
                if self.model.get_value(row.iter, self.COLUMN_TOGGLE) == True:
                    self.btnOK.set_sensitive(True)
                    self.btnRemoveAction.set_sensitive(True)
                    break
            self.labelTotalSize.set_text('')
    def calculate_total_size( self):
        if self.size_attribute is not None:
            (total_size, count) = (0, 0)
            for episode in self.get_selected_episodes():
                try:
                    total_size += int(getattr( episode, self.size_attribute))
                    count += 1
                except:
                    log( 'Cannot get size for %s', episode.title, sender = self)

            text = []
            if count == 0:
                text.append(_('Nothing selected'))
            else:
                text.append(N_('%(count)d episode', '%(count)d episodes', count) % {'count':count})

            if total_size > 0:
                text.append(_('size: %s') % util.format_filesize(total_size))
            self.labelTotalSize.set_text(', '.join(text))
            self.btnOK.set_sensitive(count>0)
            self.btnRemoveAction.set_sensitive(count>0)
            if count > 0:
                self.btnCancel.set_label(gtk.STOCK_CANCEL)
            else:
                self.btnCancel.set_label(gtk.STOCK_CLOSE)
        else:
            self.btnOK.set_sensitive(False)
            self.btnRemoveAction.set_sensitive(False)
            for index, row in enumerate(self.model):
                if self.model.get_value(row.iter, self.COLUMN_TOGGLE) == True:
                    self.btnOK.set_sensitive(True)
                    self.btnRemoveAction.set_sensitive(True)
                    break
            self.labelTotalSize.set_text('')
Beispiel #11
0
    def calculate_total_size( self):
        if self.size_attribute is not None:
            (total_size, count) = (0, 0)
            for episode in self.get_selected_episodes():
                try:
                    total_size += int(getattr( episode, self.size_attribute))
                    count += 1
                except:
                    log( 'Cannot get size for %s', episode.title, sender = self)

            text = []
            if count == 0: 
                text.append(_('Nothing selected'))
            else:
                text.append(N_('%d episode', '%d episodes', count) % count)
            if total_size > 0: 
                text.append(_('size: %s') % util.format_filesize(total_size))
            self.labelTotalSize.set_text(', '.join(text))
            self.btnOK.set_sensitive(count>0)
            self.btnRemoveAction.set_sensitive(count>0)
        else:
            selection = self.treeviewEpisodes.get_selection()
            selected_rows = selection.count_selected_rows()
            self.btnOK.set_sensitive(selected_rows > 0)
            self.btnRemoveAction.set_sensitive(selected_rows > 0)
            self.labelTotalSize.set_text('')
Beispiel #12
0
    def request_update(self, iter, task=None):
        if task is None:
            # Ongoing update request from UI - get task from model
            task = self.get_value(iter, self.C_TASK)
        else:
            # Initial update request - update non-changing fields
            self.set(iter, self.C_TASK, task, self.C_URL, task.url)

        if task.status == task.FAILED:
            status_message = "%s: %s" % (task.STATUS_MESSAGE[task.status], task.error_message)
        elif task.status == task.DOWNLOADING:
            status_message = "%s (%.0f%%, %s/s)" % (
                task.STATUS_MESSAGE[task.status],
                task.progress * 100,
                util.format_filesize(task.speed),
            )
        else:
            status_message = task.STATUS_MESSAGE[task.status]

        if task.progress > 0 and task.progress < 1:
            current = util.format_filesize(task.progress * task.total_size, digits=1)
            total = util.format_filesize(task.total_size, digits=1)

            # Remove unit from current if same as in total
            # (does: "12 MiB / 24 MiB" => "12 / 24 MiB")
            current = current.split()
            if current[-1] == total.split()[-1]:
                current.pop()
            current = " ".join(current)

            progress_message = " / ".join((current, total))
        elif task.total_size > 0:
            progress_message = util.format_filesize(task.total_size, digits=1)
        else:
            progress_message = "unknown size"

        self.set(
            iter,
            self.C_NAME,
            self._format_message(task.episode.title, status_message, task.episode.channel.title),
            self.C_PROGRESS,
            100.0 * task.progress,
            self.C_PROGRESS_TEXT,
            progress_message,
            self.C_ICON_NAME,
            self._status_ids[task.status],
        )
Beispiel #13
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
Beispiel #14
0
    def request_update(self, iter, task=None):
        if task is None:
            # Ongoing update request from UI - get task from model
            task = self.get_value(iter, self.C_TASK)
        else:
            # Initial update request - update non-changing fields
            self.set(iter,
                    self.C_TASK, task,
                    self.C_URL, task.url)

        if task.status == task.FAILED:
            status_message = '%s: %s' % (
                    task.STATUS_MESSAGE[task.status],
                    task.error_message)
        elif task.status == task.DOWNLOADING:
            status_message = '%s (%.0f%%, %s/s)' % (
                    task.STATUS_MESSAGE[task.status],
                    task.progress * 100,
                    util.format_filesize(task.speed))
        else:
            status_message = task.STATUS_MESSAGE[task.status]

        if task.progress > 0 and task.progress < 1:
            current = util.format_filesize(task.progress * task.total_size, digits=1)
            total = util.format_filesize(task.total_size, digits=1)

            # Remove unit from current if same as in total
            # (does: "12 MiB / 24 MiB" => "12 / 24 MiB")
            current = current.split()
            if current[-1] == total.split()[-1]:
                current.pop()
            current = ' '.join(current)

            progress_message = ' / '.join((current, total))
        elif task.total_size > 0:
            progress_message = util.format_filesize(task.total_size,
                    digits=1)
        else:
            progress_message = ('unknown size')

        self.set(iter,
                self.C_NAME, self._format_message(task.episode.title,
                    status_message, task.episode.channel.title),
                self.C_PROGRESS, 100. * task.progress,
                self.C_PROGRESS_TEXT, progress_message,
                self.C_ICON_NAME, self._status_ids[task.status])
Beispiel #15
0
 def on_download_status_progress(self):
     # We receive this from the main window every time the progress
     # for our episode has changed (but only when this window is visible)
     if self.task:
         self.download_progress.set_fraction(self.task.progress)
         self.download_progress.set_text('%s: %d%% (%s/s)' % ( \
                 self.task.STATUS_MESSAGE[self.task.status], \
                 100.*self.task.progress, \
                 util.format_filesize(self.task.speed)))
Beispiel #16
0
 def on_download_status_progress(self):
     # We receive this from the main window every time the progress
     # for our episode has changed (but only when this window is visible)
     if self.task:
         self.download_progress.set_fraction(self.task.progress)
         self.download_progress.set_text('%s: %d%% (%s/s)' % ( \
                 self.task.STATUS_MESSAGE[self.task.status], \
                 100.*self.task.progress, \
                 util.format_filesize(self.task.speed)))
Beispiel #17
0
 def markup_new_episodes(self):
     if self.file_size > 0:
         length_str = '%s; ' % util.format_filesize(self.file_size)
     else:
         length_str = ''
     return ('<b>%s</b>\n<small>%s' + _('released %s') + '; ' +
             _('from %s') + '</small>') % (
                 html.escape(re.sub('\s+', ' ', self.title)),
                 html.escape(length_str), html.escape(self.pubdate_prop),
                 html.escape(re.sub('\s+', ' ', self.channel.title)))
Beispiel #18
0
 def on_download_status_progress(self):
     if self.task:
         self.download_progress.set_fraction(self.task.progress)
         self.download_progress.set_text(
             "%s: %d%% (%s/s)"
             % (
                 self.task.STATUS_MESSAGE[self.task.status],
                 100.0 * self.task.progress,
                 util.format_filesize(self.task.speed),
             )
         )
Beispiel #19
0
 def markup_new_episodes(self):
     if self.file_size > 0:
         length_str = '%s; ' % util.format_filesize(self.file_size)
     else:
         length_str = ''
     return ('<b>%s</b>\n<small>%s' + _('released %s') +
             '; ' + _('from %s') + '</small>') % (
             cgi.escape(re.sub('\s+', ' ', self.title)),
             cgi.escape(length_str),
             cgi.escape(self.pubdate_prop),
             cgi.escape(re.sub('\s+', ' ', self.channel.title)))
Beispiel #20
0
    def __init__(self, title, length, modified, **kwargs):
        self.title = title
        self.length = length
        self.filesize = util.format_filesize(length)
        self.modified = modified

        # Set some (possible) keyword arguments to default values
        self.playcount = 0
        self.podcast = None

        # Convert keyword arguments to object attributes
        self.__dict__.update(kwargs)
Beispiel #21
0
    def __init__(self, title, length, modified, **kwargs):
        self.title = title
        self.length = length
        self.filesize = util.format_filesize(length)
        self.modified = modified

        # Set some (possible) keyword arguments to default values
        self.playcount = 0
        self.podcast = None
        self.released = None

        # Convert keyword arguments to object attributes
        self.__dict__.update(kwargs)
Beispiel #22
0
    def add_track(self, task, reporthook=None):
        episode = task.episode
        self.notify('status', _('Adding %s') % episode.title)

        # 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 = 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 = folder.get_child(to_file)

        util.make_directory(folder)

        if not to_file.query_exists():
            logger.info('Copying %s => %s',
                    os.path.basename(from_file),
                    to_file.get_uri())
            from_file = Gio.File.new_for_path(from_file)
            try:
                def hookconvert(current_bytes, total_bytes, user_data):
                    return reporthook(current_bytes, 1, total_bytes)
                from_file.copy(to_file, Gio.FileCopyFlags.OVERWRITE, task.cancellable, hookconvert, None)
            except GLib.Error as err:
                if err.matches(Gio.io_error_quark(), Gio.IOErrorEnum.CANCELLED):
                    raise SyncCancelledException()
                logger.error('Error copying %s to %s: %s', from_file.get_uri(), to_file.get_uri(), err.message)
                d = {'from_file': from_file.get_uri(), 'to_file': to_file.get_uri(), 'message': err.message}
                self.errors.append(_('Error copying %(from_file)s to %(to_file)s: %(message)s') % d)
                return False

        return True
Beispiel #23
0
 def markup_delete_episodes(self):
     if self.total_time and self.current_position:
         played_string = self.get_play_info_string()
     elif not self.is_new:
         played_string = _('played')
     else:
         played_string = _('unplayed')
     downloaded_string = self.get_age_string()
     if not downloaded_string:
         downloaded_string = _('today')
     return ('<b>%s</b>\n<small>%s; %s; ' + _('downloaded %s') + '; ' +
             _('from %s') + '</small>') % (
                 html.escape(self.title),
                 html.escape(util.format_filesize(self.file_size)),
                 html.escape(played_string), html.escape(downloaded_string),
                 html.escape(self.channel.title))
Beispiel #24
0
 def markup_delete_episodes(self):
     if self.total_time and self.current_position:
         played_string = self.get_play_info_string()
     elif not self.is_new:
         played_string = _('played')
     else:
         played_string = _('unplayed')
     downloaded_string = self.get_age_string()
     if not downloaded_string:
         downloaded_string = _('today')
     return ('<b>%s</b>\n<small>%s; %s; ' + _('downloaded %s') +
             '; ' + _('from %s') + '</small>') % (
             cgi.escape(self.title),
             cgi.escape(util.format_filesize(self.file_size)),
             cgi.escape(played_string),
             cgi.escape(downloaded_string),
             cgi.escape(self.channel.title))
Beispiel #25
0
        def check_free_space():
            # "Will we add this episode to the device?"
            def will_add(episode):
                # If already on-device, it won't take up any space
                if device.episode_on_device(episode):
                    return False

                # Might not be synced if it's played already
                if not force_played and \
                        self._config.only_sync_not_played and \
                        episode.is_played:
                    return False

                # In all other cases, we expect the episode to be
                # synchronized to the device, so "answer" positive
                return True

            # "What is the file size of this episode?"
            def file_size(episode):
                filename = episode.local_filename(create=False)
                if filename is None:
                    return 0
                return util.calculate_size(str(filename))

            # Calculate total size of sync and free space on device
            total_size = sum(file_size(e) for e in episodes if will_add(e))
            free_space = max(device.get_free_space(), 0)

            if total_size > free_space:
                title = _('Not enough space left on device')
                message = _('You need to free up %s.\nDo you want to continue?') \
                                % (util.format_filesize(total_size-free_space),)
                if not self.show_confirmation(message, title):
                    device.cancel()
                    device.close()
                    return

            # Finally start the synchronization process
            gPodderSyncProgress(self.parent_window, device=device)

            def sync_thread_func():
                device.add_tracks(episodes, force_played=force_played)
                device.close()

            threading.Thread(target=sync_thread_func).start()
Beispiel #26
0
        def check_free_space():
            # "Will we add this episode to the device?"
            def will_add(episode):
                # If already on-device, it won't take up any space
                if device.episode_on_device(episode):
                    return False

                # Might not be synced if it's played already
                if (not force_played
                        and self._config.device_sync.skip_played_episodes):
                    return False

                # In all other cases, we expect the episode to be
                # synchronized to the device, so "answer" positive
                return True

            # "What is the file size of this episode?"
            def file_size(episode):
                filename = episode.local_filename(create=False)
                if filename is None:
                    return 0
                return util.calculate_size(str(filename))

            # Calculate total size of sync and free space on device
            total_size = sum(file_size(e) for e in episodes if will_add(e))
            free_space = max(device.get_free_space(), 0)

            if total_size > free_space:
                title = _('Not enough space left on device')
                message = (_(
                    'Additional free space required: %(required_space)s\nDo you want to continue?'
                ) % {
                    'required_space':
                    util.format_filesize(total_size - free_space)
                })
                if not self.show_confirmation(message, title):
                    device.cancel()
                    device.close()
                    return

            # Finally start the synchronization process
            @util.run_in_background
            def sync_thread_func():
                self.enable_download_list_update()
                device.add_sync_tasks(episodes, force_played=force_played)
Beispiel #27
0
        def check_free_space():
            # "Will we add this episode to the device?"
            def will_add(episode):
                # If already on-device, it won't take up any space
                if device.episode_on_device(episode):
                    return False

                # Might not be synced if it's played already
                if not force_played and \
                        self._config.only_sync_not_played and \
                        episode.is_played:
                    return False

                # In all other cases, we expect the episode to be
                # synchronized to the device, so "answer" positive
                return True

            # "What is the file size of this episode?"
            def file_size(episode):
                filename = episode.local_filename(create=False)
                if filename is None:
                    return 0
                return util.calculate_size(str(filename))

            # Calculate total size of sync and free space on device
            total_size = sum(file_size(e) for e in episodes if will_add(e))
            free_space = max(device.get_free_space(), 0)

            if total_size > free_space:
                title = _('Not enough space left on device')
                message = _('You need to free up %s.\nDo you want to continue?') \
                                % (util.format_filesize(total_size-free_space),)
                if not self.show_confirmation(message, title):
                    device.cancel()
                    device.close()
                    return

            # Finally start the synchronization process
            gPodderSyncProgress(self.parent_window, device=device)
            def sync_thread_func():
                device.add_tracks(episodes, force_played=force_played)
                device.close()
            threading.Thread(target=sync_thread_func).start()
Beispiel #28
0
        def check_free_space():
            # "Will we add this episode to the device?"
            def will_add(episode):
                # If already on-device, it won't take up any space
                if device.episode_on_device(episode):
                    return False

                # Might not be synced if it's played already
                if (not force_played and
                        self._config.device_sync.skip_played_episodes):
                    return False

                # In all other cases, we expect the episode to be
                # synchronized to the device, so "answer" positive
                return True

            # "What is the file size of this episode?"
            def file_size(episode):
                filename = episode.local_filename(create=False)
                if filename is None:
                    return 0
                return util.calculate_size(str(filename))

            # Calculate total size of sync and free space on device
            total_size = sum(file_size(e) for e in episodes if will_add(e))
            free_space = max(device.get_free_space(), 0)

            if total_size > free_space:
                title = _('Not enough space left on device')
                message = (_('Additional free space required: %(required_space)s\nDo you want to continue?') %
               {'required_space': util.format_filesize(total_size - free_space)})
                if not self.show_confirmation(message, title):
                    device.cancel()
                    device.close()
                    return

            # Finally start the synchronization process
            @util.run_in_background
            def sync_thread_func():
                self.enable_download_list_update()
                device.add_sync_tasks(episodes, force_played=force_played)
Beispiel #29
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
Beispiel #30
0
    def update(self, episode):
        self.scrolled_window.get_vadjustment().set_value(0)

        heading = episode.title
        subheading = _('from %s') % (episode.channel.title)
        details = self.details_fmt % {
            'date': util.format_date(episode.published),
            'size': util.format_filesize(episode.file_size, digits=1)
            if episode.file_size > 0 else "-",
            'duration': episode.get_play_info_string()}
        self.define_colors()
        hyperlinks = [(0, None)]
        self.text_buffer.set_text('')
        if episode.link:
            hyperlinks.append((self.text_buffer.get_char_count(), episode.link))
        self.text_buffer.insert_with_tags_by_name(self.text_buffer.get_end_iter(), heading, 'heading')
        if episode.link:
            hyperlinks.append((self.text_buffer.get_char_count(), None))
        self.text_buffer.insert_at_cursor('\n')
        self.text_buffer.insert_with_tags_by_name(self.text_buffer.get_end_iter(), subheading, 'subheading')
        self.text_buffer.insert_at_cursor('\n')
        self.text_buffer.insert_with_tags_by_name(self.text_buffer.get_end_iter(), details, 'details')
        self.text_buffer.insert_at_cursor('\n\n')
        for target, text in util.extract_hyperlinked_text(episode.html_description()):
            hyperlinks.append((self.text_buffer.get_char_count(), target))
            if target:
                self.text_buffer.insert_with_tags_by_name(
                    self.text_buffer.get_end_iter(), text, 'hyperlink')
            else:
                self.text_buffer.insert(
                    self.text_buffer.get_end_iter(), text)
        hyperlinks.append((self.text_buffer.get_char_count(), None))
        self.hyperlinks = [(start, end, url) for (start, url), (end, _) in zip(hyperlinks, hyperlinks[1:]) if url]
        self.text_buffer.place_cursor(self.text_buffer.get_start_iter())

        if self.populate_popup_id is not None:
            self.text_view.disconnect(self.populate_popup_id)
        self.populate_popup_id = self.text_view.connect('populate-popup', self.on_populate_popup)
        self.episode = episode
Beispiel #31
0
 def _format_filesize(self, episode):
     if episode.length > 0:
         return util.format_filesize(episode.length, 1)
     else:
         return None
Beispiel #32
0
 def _filesize(self):
     if self._episode.file_size:
         return util.format_filesize(self._episode.file_size)
     else:
         return ''
Beispiel #33
0
 def format_filesize(self, bytesize, digits=2):
     return util.format_filesize(bytesize, self.config.use_si_units, digits)
Beispiel #34
0
        def check_free_space():
            # "Will we add this episode to the device?"
            def will_add(episode):
                # If already on-device, it won't take up any space
                if device.episode_on_device(episode):
                    return False

                # Might not be synced if it's played already
                if (not force_played and
                        self._config.device_sync.skip_played_episodes):
                    return False

                # In all other cases, we expect the episode to be
                # synchronized to the device, so "answer" positive
                return True

            # "What is the file size of this episode?"
            def file_size(episode):
                filename = episode.local_filename(create=False)
                if filename is None:
                    return 0
                return util.calculate_size(str(filename))

            # Calculate total size of sync and free space on device
            total_size = sum(file_size(e) for e in episodes if will_add(e))
            free_space = max(device.get_free_space(), 0)

            if total_size > free_space:
                title = _('Not enough space left on device')
                message = (_('Additional free space required: %(required_space)s\nDo you want to continue?') %
               {'required_space': util.format_filesize(total_size - free_space)})
                if not self.show_confirmation(message, title):
                    device.cancel()
                    device.close()
                    return

            #enable updating of UI
            self.enable_download_list_update()

            #Update device playlists
            #General approach is as follows:

            #When a episode is downloaded and synched, it is added to the
            #standard playlist for that podcast which is then written to
            #the device.

            #After the user has played that episode on their device, they
            #can delete that episode from their device.

            #At the next sync, gPodder will then compare the standard
            #podcast-specific playlists on the device (as written by
            #gPodder during the last sync), with the episodes on the
            #device.If there is an episode referenced in the playlist
            #that is no longer on the device, gPodder will assume that
            #the episode has already been synced and subsequently deleted
            #from the device, and will hence mark that episode as deleted
            #in gPodder. If there are no playlists, nothing is deleted.

            #At the next sync, the playlists will be refreshed based on
            #the downloaded, undeleted episodes in gPodder, and the
            #cycle begins again...

            def resume_sync(episode_urls, channel_urls,progress):
                if progress is not None:
                    progress.on_finished()

                #rest of sync process should continue here
                self.commit_changes_to_database()
                for current_channel in self.channels:
                    #only sync those channels marked for syncing
                    if (self._config.device_sync.device_type=='filesystem' and current_channel.sync_to_mp3_player and self._config.device_sync.playlists.create):

                        #get playlist object
                        playlist=gPodderDevicePlaylist(self._config,
                                                       current_channel.title)
                        #need to refresh episode list so that
                        #deleted episodes aren't included in playlists
                        episodes_for_playlist=sorted(current_channel.get_episodes(gpodder.STATE_DOWNLOADED),
                                                     key=lambda ep: ep.published)
                        #don't add played episodes to playlist if skip_played_episodes is True
                        if self._config.device_sync.skip_played_episodes:
                            episodes_for_playlist=filter(lambda ep: ep.is_new, episodes_for_playlist)
                        playlist.write_m3u(episodes_for_playlist)

                #enable updating of UI
                self.enable_download_list_update()
                
                if (self._config.device_sync.device_type=='filesystem' and self._config.device_sync.playlists.create):                 
                    title = _('Update successful')
                    message = _('The playlist on your MP3 player has been updated.')
                    self.notification(message, title, widget=self.preferences_widget)

                # Finally start the synchronization process
                @util.run_in_background
                def sync_thread_func():
                    device.add_sync_tasks(episodes, force_played=force_played,
                            done_callback=self.enable_download_list_update)

                return

            if self._config.device_sync.playlists.create:
                try:
                    episodes_to_delete=[]
                    if self._config.device_sync.playlists.two_way_sync:
                        for current_channel in self.channels:
                            #only include channels that are included in the sync
                            if current_channel.sync_to_mp3_player:
                                #get playlist object
                                playlist=gPodderDevicePlaylist(self._config, current_channel.title)
                                #get episodes to be written to playlist
                                episodes_for_playlist=sorted(current_channel.get_episodes(gpodder.STATE_DOWNLOADED),
                                                             key=lambda ep: ep.published)
                                episode_keys=map(playlist.get_absolute_filename_for_playlist,
                                                 episodes_for_playlist)

                                episode_dict=dict(zip(episode_keys, episodes_for_playlist))

                                #then get episodes in playlist (if it exists) already on device
                                episodes_in_playlists = playlist.read_m3u()
                                #if playlist doesn't exist (yet) episodes_in_playlist will be empty
                                if episodes_in_playlists:
                                    for episode_filename in episodes_in_playlists:

                                        if not(os.path.exists(os.path.join(playlist.mountpoint,
                                                                           episode_filename))):
                                            #episode was synced but no longer on device
                                            #i.e. must have been deleted by user, so delete from gpodder
                                            try:
                                                episodes_to_delete.append(episode_dict[episode_filename])
                                            except KeyError, ioe:
                                                logger.warn('Episode %s, removed from device has already been deleted from gpodder',
                                                            episode_filename)


                    #delete all episodes from gpodder (will prompt user)

                    #not using playlists to delete
                    def auto_delete_callback(episodes):

                        if not episodes:
                            #episodes were deleted on device
                            #but user decided not to delete them from gpodder
                            #so jump straight to sync
                            logger.info ('Starting sync - no episodes selected for deletion')
                            resume_sync([],[],None)
                        else:
                            #episodes need to be deleted from gpodder
                            for episode_to_delete in episodes:
                                logger.info("Deleting episode %s",
                                               episode_to_delete.title)

                            logger.info ('Will start sync - after deleting episodes')
                            self.delete_episode_list(episodes,False,
                                                     True,resume_sync)

                        return

                    if episodes_to_delete:
                        columns = (
                            ('markup_delete_episodes', None, None, _('Episode')),
                        )

                        gPodderEpisodeSelector(self.parent_window,
                            title = _('Episodes have been deleted on device'),
                            instructions = 'Select the episodes you want to delete:',
                            episodes = episodes_to_delete,
                            selected = [True,]*len(episodes_to_delete), columns = columns,
                            callback = auto_delete_callback,
                            _config=self._config)
                    else:
                        logger.warning("Starting sync - no episodes to delete")
                        resume_sync([],[],None)

                except IOError, ioe:
                    title =  _('Error writing playlist files')
                    message = _(str(ioe))
                    self.notification(message, title, widget=self.preferences_widget)
Beispiel #35
0
 def _format_filesize(self, episode):
     if episode.file_size > 0:
         return util.format_filesize(episode.file_size, digits=1)
     else:
         return None
Beispiel #36
0
    def add_track(self, episode, reporthook=None):
        self.notify('status', _('Adding %s') % episode.title)
        # episode.description
        # episode.description_html
        # file = episode.local_filename(create=False)
        # episode.link
        # episode.title
        # cover
        #

        #bb =io.BytesIO(episode.channel.cover)

        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 = 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_name = to_file
        to_file = os.path.join(folder, to_file)

        video_file = to_file.replace(".mp3", "").replace(".m4a", "") + ".avi"
        cmd_file = to_file.replace(".mp3", "").replace(".m4a", "") + ".ps1"

        image_file = to_file.replace(".mp3", "").replace(".m4a", "") + ".png"

        jpg_file = episode.channel.cover_file + ".jpg"
        if not os.path.exists(image_file):
            img = Image.open(jpg_file)
            draw = ImageDraw.Draw(img)
            font = ImageFont.truetype(
                "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 72)
            draw.text((0, 0), episode.title, (255, 255, 255), font=font)
            font = ImageFont.truetype(
                "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 42)
            yy = draw.multiline_text((0, 120),
                                     episode.description, (255, 255, 255),
                                     font=font)
            img.save(image_file)  # write the image

        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)
            self.copy_file_progress(from_file, to_file, reporthook)

            #ffmpeg -i "$1" -acodec libfaac -ac 2 -ab 128k -s 480x320
            #-vcodec libx264 -vpre libx264-hq -vpre libx264-ipod640 -b 768k -bt 512k -aspect 3:2 -threads 0 -f mp4 $1.mp4
            cmd = [
                'ffmpeg', '-framerate', '2', '-loop', '1', '-i', image_file,
                "-i", to_file, "-qmin", "3", "-qmax", "5", "-ac", "2", "-ab",
                "128k", "-b:v", "512k", "-aspect", "3:2", "-threads", "4",
                "-s", "480x320", "-ar", "16000", "-b:a", "44K", "-vbr",
                "constrained", "-vf", "scale=iw/4:ih/4", "-profile:v",
                "baseline", "-preset", "ultrafast", "-movflags", "+faststart",
                "-crf", "16", "-c:v", "libx264", "-c:a", "libmp3lame",
                "-reset_timestamps", "1", "-tune", "stillimage", "-pix_fmt",
                "yuv420p", "-shortest", video_file
                #"'{}'".format(video_file)
            ]
            #ffmpeg -i test.mpg -c:v libx264  test.avi
            cmd = subprocess.list2cmdline(cmd)

        if not os.path.exists(cmd_file):
            with open(cmd_file, "w") as fo:
                fo.write(cmd.replace(folder + "/", ""))

        if not os.path.exists(video_file):
            check_call(shlex.split(cmd))

            # now upload
            #loop = asyncio.get_event_loop()
            #loop.run_until_complete(run_bcupload(video_file, image_file, episode))
            #loop.close()

        return True
Beispiel #37
0
    def add_track(self, episode):
        self.notify('status', _('Adding %s...') % episode.title)
        filename = str(self.convert_track(episode))
        logger.info("sending %s (%s).", filename, episode.title)

        try:
            # verify free space
            needed = util.calculate_size(filename)
            free = self.get_free_space()
            if needed > free:
                logger.error(
                    'Not enough space on device %s: %s available, but need at least %s',
                    self.get_name(), util.format_filesize(free),
                    util.format_filesize(needed))
                self.cancelled = True
                return False

            # fill metadata
            metadata = pymtp.LIBMTP_Track()
            metadata.title = str(episode.title)
            metadata.artist = str(episode.channel.title)
            metadata.album = str(episode.channel.title)
            metadata.genre = "podcast"
            metadata.date = self.__date_to_mtp(episode.published)
            metadata.duration = get_track_length(str(filename))

            folder_name = ''
            if episode.mimetype.startswith(
                    'audio/') and self._config.mtp_audio_folder:
                folder_name = self._config.mtp_audio_folder
            if episode.mimetype.startswith(
                    'video/') and self._config.mtp_video_folder:
                folder_name = self._config.mtp_video_folder
            if episode.mimetype.startswith(
                    'image/') and self._config.mtp_image_folder:
                folder_name = self._config.mtp_image_folder

            if folder_name != '' and self._config.mtp_podcast_folders:
                folder_name += os.path.sep + str(episode.channel.title)

            # log('Target MTP folder: %s' % folder_name)

            if folder_name == '':
                folder_id = 0
            else:
                folder_id = self.__MTPDevice.mkdir(folder_name)

            # send the file
            to_file = util.sanitize_filename(
                metadata.title) + episode.extension()
            self.__MTPDevice.send_track_from_file(filename,
                                                  to_file,
                                                  metadata,
                                                  folder_id,
                                                  callback=self.__callback)
            if gpodder.user_hooks is not None:
                gpodder.user_hooks.on_file_copied_to_mtp(
                    self, filename, to_file)
        except:
            logger.error('unable to add episode %s', episode.title)
            return False

        return True
Beispiel #38
0
 def _format_filesize(self, episode):
     if episode.length > 0:
         return util.format_filesize(episode.length, 1)
     else:
         return None
Beispiel #39
0
 def get_filesize_string(self):
     return util.format_filesize(self.length)
Beispiel #40
0
 def _filesize(self):
     if self._episode.file_size:
         return util.format_filesize(self._episode.file_size)
     else:
         return ''
Beispiel #41
0
 def _format_filesize(self, episode):
     if episode.file_size > 0:
         return util.format_filesize(episode.file_size, digits=1)
     else:
         return None
Beispiel #42
0
        def check_free_space():
            # "Will we add this episode to the device?"
            def will_add(episode):
                # If already on-device, it won't take up any space
                if device.episode_on_device(episode):
                    return False

                # Might not be synced if it's played already
                if (not force_played and
                        self._config.device_sync.skip_played_episodes):
                    return False

                # In all other cases, we expect the episode to be
                # synchronized to the device, so "answer" positive
                return True

            # "What is the file size of this episode?"
            def file_size(episode):
                filename = episode.local_filename(create=False)
                if filename is None:
                    return 0
                return util.calculate_size(str(filename))

            # Calculate total size of sync and free space on device
            total_size = sum(file_size(e) for e in episodes if will_add(e))
            free_space = max(device.get_free_space(), 0)

            if total_size > free_space:
                title = _('Not enough space left on device')
                message = (_('Additional free space required: %(required_space)s\nDo you want to continue?') %
               {'required_space': util.format_filesize(total_size - free_space)})
                if not self.show_confirmation(message, title):
                    device.cancel()
                    device.close()
                    return

            #enable updating of UI
            self.enable_download_list_update()

            #Update device playlists
            #General approach is as follows:

            #When a episode is downloaded and synched, it is added to the
            #standard playlist for that podcast which is then written to
            #the device.

            #After the user has played that episode on their device, they
            #can delete that episode from their device.

            #At the next sync, gPodder will then compare the standard
            #podcast-specific playlists on the device (as written by
            #gPodder during the last sync), with the episodes on the
            #device.If there is an episode referenced in the playlist
            #that is no longer on the device, gPodder will assume that
            #the episode has already been synced and subsequently deleted
            #from the device, and will hence mark that episode as deleted
            #in gPodder. If there are no playlists, nothing is deleted.

            #At the next sync, the playlists will be refreshed based on
            #the downloaded, undeleted episodes in gPodder, and the
            #cycle begins again...

            def resume_sync(episode_urls, channel_urls,progress):
                if progress is not None:
                    progress.on_finished()

                #rest of sync process should continue here
                self.commit_changes_to_database()
                for current_channel in self.channels:
                    #only sync those channels marked for syncing
                    if (self._config.device_sync.device_type=='filesystem' and current_channel.sync_to_mp3_player and self._config.device_sync.playlists.create):

                        #get playlist object
                        playlist=gPodderDevicePlaylist(self._config,
                                                       current_channel.title)
                        #need to refresh episode list so that
                        #deleted episodes aren't included in playlists
                        episodes_for_playlist=sorted(current_channel.get_episodes(gpodder.STATE_DOWNLOADED),
                                                     key=lambda ep: ep.published)
                        #don't add played episodes to playlist if skip_played_episodes is True
                        if self._config.device_sync.skip_played_episodes:
                            episodes_for_playlist=[ep for ep in episodes_for_playlist if ep.is_new]
                        playlist.write_m3u(episodes_for_playlist)

                #enable updating of UI
                self.enable_download_list_update()

                if (self._config.device_sync.device_type=='filesystem' and self._config.device_sync.playlists.create):
                    title = _('Update successful')
                    message = _('The playlist on your MP3 player has been updated.')
                    self.notification(message, title)

                # Finally start the synchronization process
                @util.run_in_background
                def sync_thread_func():
                    device.add_sync_tasks(episodes, force_played=force_played,
                            done_callback=self.enable_download_list_update)

                return

            if self._config.device_sync.playlists.create:
                try:
                    episodes_to_delete=[]
                    if self._config.device_sync.playlists.two_way_sync:
                        for current_channel in self.channels:
                            #only include channels that are included in the sync
                            if current_channel.sync_to_mp3_player:
                                #get playlist object
                                playlist=gPodderDevicePlaylist(self._config, current_channel.title)
                                #get episodes to be written to playlist
                                episodes_for_playlist=sorted(current_channel.get_episodes(gpodder.STATE_DOWNLOADED),
                                                             key=lambda ep: ep.published)
                                episode_keys=list(map(playlist.get_absolute_filename_for_playlist,
                                                 episodes_for_playlist))

                                episode_dict=dict(list(zip(episode_keys, episodes_for_playlist)))

                                #then get episodes in playlist (if it exists) already on device
                                episodes_in_playlists = playlist.read_m3u()
                                #if playlist doesn't exist (yet) episodes_in_playlist will be empty
                                if episodes_in_playlists:
                                    for episode_filename in episodes_in_playlists:

                                        if not(os.path.exists(os.path.join(playlist.mountpoint,
                                                                           episode_filename))):
                                            #episode was synced but no longer on device
                                            #i.e. must have been deleted by user, so delete from gpodder
                                            try:
                                                episodes_to_delete.append(episode_dict[episode_filename])
                                            except KeyError as ioe:
                                                logger.warn('Episode %s, removed from device has already been deleted from gpodder',
                                                            episode_filename)
                    #delete all episodes from gpodder (will prompt user)

                    #not using playlists to delete
                    def auto_delete_callback(episodes):

                        if not episodes:
                            #episodes were deleted on device
                            #but user decided not to delete them from gpodder
                            #so jump straight to sync
                            logger.info('Starting sync - no episodes selected for deletion')
                            resume_sync([],[],None)
                        else:
                            #episodes need to be deleted from gpodder
                            for episode_to_delete in episodes:
                                logger.info("Deleting episode %s",
                                               episode_to_delete.title)

                            logger.info('Will start sync - after deleting episodes')
                            self.delete_episode_list(episodes,False,
                                                     True,resume_sync)

                        return

                    if episodes_to_delete:
                        columns = (
                            ('markup_delete_episodes', None, None, _('Episode')),
                        )

                        gPodderEpisodeSelector(self.parent_window,
                            title = _('Episodes have been deleted on device'),
                            instructions = 'Select the episodes you want to delete:',
                            episodes = episodes_to_delete,
                            selected = [True,] * len(episodes_to_delete), columns = columns,
                            callback = auto_delete_callback,
                            _config=self._config)
                    else:
                        logger.warning("Starting sync - no episodes to delete")
                        resume_sync([],[],None)

                except IOError as ioe:
                    title = _('Error writing playlist files')
                    message = _(str(ioe))
                    self.notification(message, title)
            else:
                logger.info('Not creating playlists - starting sync')
                resume_sync([],[],None)
Beispiel #43
0
    def add_track(self, episode):
        self.notify('status', _('Adding %s...') % episode.title)
        filename = str(self.convert_track(episode))
        log("sending " + filename + " (" + episode.title + ").", sender=self)

        try:
            # verify free space
            needed = util.calculate_size(filename)
            free = self.get_free_space()
            if needed > free:
                log('Not enough space on device %s: %s available, but need at least %s', self.get_name(), util.format_filesize(free), util.format_filesize(needed), sender=self)
                self.cancelled = True
                return False

            # fill metadata
            metadata = pymtp.LIBMTP_Track()
            metadata.title = str(episode.title)
            metadata.artist = str(episode.channel.title)
            metadata.album = str(episode.channel.title)
            metadata.genre = "podcast"
            metadata.date = self.__date_to_mtp(episode.pubDate)
            metadata.duration = get_track_length(str(filename))

            # send the file
            self.__MTPDevice.send_track_from_file(filename,
                    util.sanitize_filename(metadata.title)+episode.extension(),
                    metadata, 0, callback=self.__callback)
        except:
            log('unable to add episode %s', episode.title, sender=self, traceback=True)
            return False

        return True
Beispiel #44
0
    def add_track(self, episode):
        self.notify('status', _('Adding %s') % episode.title)
        for track in gpod.sw_get_playlist_tracks(self.podcasts_playlist):
            if episode.url == track.podcasturl:
                if track.playcount > 0:
                    gl.history_mark_played(track.podcasturl)
                # Mark as played on iPod if played locally (and set podcast flags)
                self.set_podcast_flags(track)
                return True

        original_filename=str(episode.local_filename())
        local_filename=original_filename

        # Reserve 10 MiB for iTunesDB writing (to be on the safe side)
        RESERVED_FOR_ITDB=1024*1024*10
        space_for_track=util.get_free_disk_space(self.mountpoint) - RESERVED_FOR_ITDB
        needed=util.calculate_size(local_filename)

        if needed > space_for_track:
            log('Not enough space on %s: %s available, but need at least %s', self.mountpoint, util.format_filesize(space_for_track), util.format_filesize(needed), sender=self)
            self.errors.append( _('Error copying %s: Not enough free disk space on %s') % (episode.title, self.mountpoint))
            self.cancelled=True
            return False

        (fn, extension)=os.path.splitext(original_filename)
        if libconverter.converters.has_converter(extension):
            log('Converting: %s', original_filename, sender=self)
            callback_status=lambda percentage: self.notify('sub-progress', int(percentage))
            local_filename=libconverter.converters.convert(original_filename, callback=callback_status)

            if not libtagupdate.update_metadata_on_file(local_filename, title=episode.title, artist=episode.channel.title):
                log('Could not set metadata on converted file %s', local_filename, sender=self)

            if local_filename is None:
                log('Cannot convert %s', original_filename, sender=self)
                return False
            else:
                local_filename=str(local_filename)

        (fn, extension)=os.path.splitext(local_filename)
        if extension.lower().endswith('ogg'):
            log('Cannot copy .ogg files to iPod.', sender=self)
            return False

        track=gpod.itdb_track_new()
        
        # Add release time to track if pubDate is parseable
        ipod_date=email.Utils.parsedate(episode.pubDate)
        if ipod_date is not None:
            try:
                # libgpod>= 0.5.x uses a new timestamp format
                track.time_released=gpod.itdb_time_host_to_mac(int(time.mktime(ipod_date)))
            except:
                # old (pre-0.5.x) libgpod versions expect mactime, so
                # we're going to manually build a good mactime timestamp here :)
                #
                # + 2082844800 for unixtime => mactime (1970 => 1904)
                track.time_released=int(time.mktime(ipod_date) + 2082844800)
        
        track.title=str(episode.title)
        track.album=str(episode.channel.title)
        track.artist=str(episode.channel.title)
        track.description=str(episode.description)

        track.podcasturl=str(episode.url)
        track.podcastrss=str(episode.channel.url)

        track.tracklen=get_track_length(local_filename)
        track.size=os.path.getsize(local_filename)

        if episode.file_type() == 'audio':
            track.filetype='mp3'
            track.mediatype=0x00000004
        elif episode.file_type() == 'video':
            track.filetype='m4v'
            track.mediatype=0x00000006

        self.set_podcast_flags(track)
        self.set_cover_art(track, local_filename)

        gpod.itdb_track_add(self.itdb, track, -1)
        gpod.itdb_playlist_add_track(self.podcasts_playlist, track, -1)
        gpod.itdb_cp_track_to_ipod( track, local_filename, None)

        # If the file has been converted, delete the temporary file here
        if local_filename != original_filename:
            util.delete_file(local_filename)

        return True