def __init__( self): log('Creating gPodderLib()', sender=self) if gpodder.interface == gpodder.MAEMO: gpodder_dir='/media/mmc2/gpodder/' self.osso_c=osso.Context('gpodder_osso_sender', '1.0', False) else: gpodder_dir=os.path.expanduser('~/.config/gpodder/') util.make_directory( gpodder_dir) self.tempdir=gpodder_dir self.feed_cache_file=os.path.join(gpodder_dir, 'feedcache.pickle.db') self.channel_settings_file=os.path.join(gpodder_dir, 'channelsettings.pickle.db') self.episode_metainfo_file=os.path.join(gpodder_dir, 'episodemetainfo.pickle.db') self.channel_opml_file=os.path.join(gpodder_dir, 'channels.opml') self.channel_xml_file=os.path.join(gpodder_dir, 'channels.xml') if os.path.exists(self.channel_xml_file) and not os.path.exists(self.channel_opml_file): log('Trying to migrate channel list (channels.xml => channels.opml)', sender=self) self.migrate_channels_xml() self.config=config.Config( os.path.join( gpodder_dir, 'gpodder.conf')) util.make_directory(self.config.bittorrent_dir) # We need to make a seamless upgrade, so by default the video player is not specified # so the first time this application is run it will detect this and set it to the same # as the audio player. This keeps gPodder functionality identical to that prior to the # upgrade. The user can then set a specific video player if they so wish. if self.config.videoplayer == 'unspecified': self.config.videoplayer=self.config.player self.__download_history=HistoryStore( os.path.join( gpodder_dir, 'download-history.txt')) self.__playback_history=HistoryStore( os.path.join( gpodder_dir, 'playback-history.txt')) self.__locked_history=HistoryStore( os.path.join( gpodder_dir, 'lock-history.txt'))
def migrate_channels_xml(self): """Migrate old (gPodder < 0.9.5) channels.xml to channels.opml This function does a one-time conversion of the old channels.xml file format to the new (supported by 0.9.5, the default on 0.10.0) channels.opml format. """ def channels_xml_iter(filename='channels.xml'): for e in xml.dom.minidom.parse(filename).getElementsByTagName('url'): yield ''.join(n.data for n in e.childNodes if n.nodeType==n.TEXT_NODE) def create_outline(doc, url): outline=doc.createElement('outline') for w in (('title', ''), ('text', ''), ('xmlUrl', url), ('type', 'rss')): outline.setAttribute(*w) return outline def export_opml(urls, filename='channels.opml'): doc=xml.dom.minidom.Document() opml=doc.createElement('opml') opml.setAttribute('version', '1.1') doc.appendChild(opml) body=doc.createElement('body') for url in urls: body.appendChild(create_outline(doc, url)) opml.appendChild(body) open(filename,'w').write(doc.toxml(encoding='utf-8')) try: export_opml(channels_xml_iter(self.channel_xml_file), self.channel_opml_file) shutil.move(self.channel_xml_file, self.channel_xml_file+'.converted') log('Successfully converted channels.xml to channels.opml', sender=self) except: log('Cannot convert old channels.xml to channels.opml', traceback=True, sender=self)
def save_to_file( self): if len( self): fp=open( self.filename, 'w') for url in self: fp.write( url + "\n") fp.close() log( 'Wrote %d history entries.', len( self), sender=self)
def playback_episode( self, channel, episode): self.history_mark_played( episode.url) filename=episode.local_filename() if gpodder.interface == gpodder.MAEMO and not self.config.maemo_allow_custom_player: # Use the built-in Nokia Mediaplayer here filename=filename.encode('utf-8') osso_rpc=osso.Rpc(self.osso_c) service='com.nokia.mediaplayer' path='/com/nokia/mediaplayer' osso_rpc.rpc_run(service, path, service, 'mime_open', ('file://'+filename,)) return (True, service) # Determine the file type and set the player accordingly. file_type=util.file_type_by_extension(util.file_extension_from_url(episode.url)) if file_type == 'video': player=self.config.videoplayer elif file_type == 'audio': player=self.config.player else: log('Non-audio or video file type, using xdg-open for %s', filename, sender=self) player='xdg-open' command_line=shlex.split(util.format_desktop_command(player, filename).encode('utf-8')) log( 'Command line: [ %s ]', ', '.join( [ '"%s"' % p for p in command_line ]), sender=self) try: subprocess.Popen( command_line) except: return ( False, command_line[0] ) return ( True, command_line[0] )
def get_device_name( self): if self.config.device_type == 'ipod': return _('iPod') elif self.config.device_type == 'filesystem': return _('MP3 player') else: log( 'Warning: Called get_device_name() when no device was selected.', sender=self) return '(unknown device)'
def update_metadata_on_file( filename, **metadata): global tag_update_methods ext=filename[-3:] if ext in tag_update_methods: log('Updating tag for %s', filename) return tag_update_methods[ext]( filename, **metadata) log('Do not know how to update file extension %s :/', ext) return False
def set_download_dir( self, new_downloaddir): if self.config.download_dir != new_downloaddir: log( 'Moving downloads from %s to %s', self.config.download_dir, new_downloaddir) try: # Fix error when moving over disk boundaries if os.path.isdir( new_downloaddir) and not os.listdir( new_downloaddir): os.rmdir( new_downloaddir) shutil.move( self.config.download_dir, new_downloaddir) except: log( 'Error while moving %s to %s.', self.config.download_dir, new_downloaddir) return self.config.download_dir=new_downloaddir
def update_tag_ogg( filename, **metadata): data='\n'.join( [ '%s=%s' % ( i.upper(), metadata[i] ) for i in metadata ] + ['']) p=subprocess.Popen3('vorbiscomment -w "%s"' % filename) writer=p.tochild writer.write(data) writer.close() result=p.wait() == 0 if not result: log('Error while running vorbiscomment. Is it installed?! (vorbis-tools)') return result
def del_item( self, data, autosave=True): affected=0 if data and type( data) is types.ListType: # Support passing a list of urls to this function for url in data: affected=affected + self.del_item( url, autosave=False) else: if data in self: log( 'Removing: %s', data, sender=self) self.remove( data) affected=affected + 1 if affected and autosave: self.save_to_file() return affected
def update_tag_mp3( filename, **metadata): if not has_eyed3: log('eyeD3 not found -> please install. no tags have been updated.') return False tag=eyeD3.tag.Tag( fileName=filename) tag.remove( eyeD3.tag.ID3_ANY_VERSION) tag.setVersion( eyeD3.tag.ID3_ANY_VERSION) for key in metadata: if key.lower() == 'artist': tag.setArtist( metadata[key]) elif key.lower() == 'title': tag.setTitle( metadata[key]) elif key.lower() == 'album': tag.setAlbum( metadata[key]) return tag.update( eyeD3.tag.ID3_V2) == 1 and tag.update( eyeD3.tag.ID3_V1) == 1
def image_download_thread( self, url, callback_pixbuf=None, callback_status=None, callback_finished=None, cover_file=None): if callback_status is not None: util.idle_add(callback_status, _('Downloading podcast cover...')) pixbuf=gtk.gdk.PixbufLoader() if cover_file is None: log( 'Downloading %s', url) pixbuf.write( urllib.urlopen(url).read()) if cover_file is not None and not os.path.exists(cover_file): log( 'Downloading cover to %s', cover_file) cachefile=open( cover_file, "w") cachefile.write( urllib.urlopen(url).read()) cachefile.close() if cover_file is not None: log( 'Reading cover from %s', cover_file) try: pixbuf.write( open( cover_file, "r").read()) except: # Probably a data error, delete temp file log('Data error while reading pixbuf. Deleting %s', cover_file, sender=self) util.delete_file(cover_file) try: pixbuf.close() except: # data error, delete temp file util.delete_file( cover_file) MAX_SIZE=400 if callback_pixbuf is not None: pb=pixbuf.get_pixbuf() if pb: if pb.get_width() > MAX_SIZE: factor=MAX_SIZE*1.0/pb.get_width() pb=pb.scale_simple( int(pb.get_width()*factor), int(pb.get_height()*factor), gtk.gdk.INTERP_BILINEAR) if pb.get_height() > MAX_SIZE: factor=MAX_SIZE*1.0/pb.get_height() pb=pb.scale_simple( int(pb.get_width()*factor), int(pb.get_height()*factor), gtk.gdk.INTERP_BILINEAR) util.idle_add(callback_pixbuf, pb) if callback_status is not None: util.idle_add(callback_status, '') if callback_finished is not None: util.idle_add(callback_finished)
def clean_up_downloads( self, delete_partial=False): # Clean up temporary files left behind by old gPodder versions if delete_partial: temporary_files=glob.glob( '%s/*/.tmp-*' % ( self.downloaddir, )) for tempfile in temporary_files: util.delete_file( tempfile) # Clean up empty download folders download_dirs=glob.glob( '%s/*' % ( self.downloaddir, )) for ddir in download_dirs: if os.path.isdir( ddir): globr=glob.glob( '%s/*' % ( ddir, )) if not globr and ddir != self.config.bittorrent_dir: log( 'Stale download directory found: %s', os.path.basename( ddir)) try: os.rmdir( ddir) log( 'Successfully removed %s.', ddir) except: log( 'Could not remove %s.', ddir)
def invoke_torrent( self, url, torrent_filename, target_filename): self.history_mark_played( url) if self.config.use_gnome_bittorrent: if util.find_command('gnome-btdownload') is None: log( 'Cannot find "gnome-btdownload". Please install gnome-bittorrent.', sender=self) return False command='gnome-btdownload "%s" --saveas "%s"' % ( torrent_filename, os.path.join( self.config.bittorrent_dir, target_filename)) log( command, sender=self) os.system( '%s &' % command) return True else: # Simply copy the .torrent with a suitable name try: target_filename=os.path.join( self.config.bittorrent_dir, os.path.splitext( target_filename)[0] + '.torrent') shutil.copyfile( torrent_filename, target_filename) return True except: log( 'Torrent copy failed: %s => %s.', torrent_filename, target_filename) return False
# # # for ogg/vorbis (vorbiscomment utility) import subprocess # for logging from liblogger import log # for mp3 files has_eyed3=True try: import eyeD3 except: log('(tagupdate) eyed3 not found -- tag update disabled') has_eyed3=False # do we provide tagging functions to the user? def tagging_supported(): global has_eyed3 return has_eyed3 tag_update_methods={} def update_metadata_on_file( filename, **metadata): global tag_update_methods ext=filename[-3:] if ext in tag_update_methods:
def __init__( self, filename): self.filename=filename try: self.read_from_file() except: log( 'Creating new history list.', sender=self)