def do_import(self): self.running = True self.cont = threading.Event() self.transcoder.set_format(self.format) if self.quality != -1: self.transcoder.set_quality(self.quality) self.transcoder.end_cb = self._end_cb for tr in self.tracks: self.cont.clear() self.current = tr self.current_len = tr.get_tag_raw('__length') loc = tr.get_loc_for_io() trackno, device = loc[7:].split("/#") src = "cdparanoiasrc track=%s device=\"%s\"" % (trackno, device) self.transcoder.set_raw_input(src) outloc = self.get_output_location(tr) self.transcoder.set_output(outloc) self.transcoder.start_transcode() self.cont.wait() if not self.running: break tr2 = trax.Track("file://" + outloc) for t in tr.list_tags(): if not t.startswith("__"): tr2.set_tag_raw(t, tr.get_tag_raw(t)) tr2.write_tags() try: incr = tr.get_tag_raw('__length') / self.duration self.progress += incr except Exception: raise self.progress = 100.0
def _parse_podcast(self, url, add_to_db=False): try: url = url.replace('itpc://', 'http://') self._set_status(_('Loading %s...') % url) d = fp.parse(url) entries = d['entries'] title = d['feed']['title'] if add_to_db: self._add_to_db(url, title) pl = playlist.Playlist(md5(url).hexdigest()) tracks = [] for e in entries: for link in e.get('enclosures', []): tr = trax.Track(link.href) date = e['updated_parsed'] tr.set_tag_raw('artist', title) tr.set_tag_raw('title', '%s: %s' % (e['title'], link.href.split('/')[-1])) tr.set_tag_raw('date', "%d-%02d-%02d" % (date.tm_year, date.tm_mon, date.tm_mday)) tracks.append(tr) pl.extend(tracks) self._set_status('') self._open_podcast(pl, title) self.podcast_playlists.save_playlist(pl, overwrite=True) except: traceback.print_exc() self._set_status(_('Error loading podcast.'), 2)
def open_disc(self): toc = CDTocParser(self.device) lengths = toc.get_track_lengths() songs = {} for count, length in enumerate(lengths): count += 1 song = trax.Track("cdda://%d/#%s" % (count, self.device)) song.set_tag_raw('title', "Track %d" % count) song.set_tag_raw('tracknumber', str(count)) song.set_tag_raw('__length', length) songs[song.get_loc_for_io()] = song # FIXME: this can probably be cleaner sort_tups = [ (int(s.get_tag_raw('tracknumber')[0]),s) \ for s in songs.values() ] sort_tups.sort() sorted = [s[1] for s in sort_tups] self.extend(sorted) if CDDB_AVAIL: self.get_cddb_info()
def update_track(self, gloc, force_update=False): """ Rescan the track at a given location :param gloc: the location :type gloc: :class:`Gio.File` :param force_update: Force update of file (default only updates file when mtime has changed) returns: the Track object, None if it could not be updated """ uri = gloc.get_uri() if not uri: # we get segfaults if this check is removed return None mtime = gloc.query_info("time::modified", Gio.FileQueryInfoFlags.NONE, None).get_modification_time() mtime = mtime.tv_sec + (mtime.tv_usec / 100000.0) tr = self.collection.get_track_by_loc(uri) if tr: if force_update or tr.get_tag_raw('__modified') < mtime: tr.read_tags() tr.set_tag_raw('__modified', mtime) else: tr = trax.Track(uri) if tr._scan_valid == True: tr.set_tag_raw('__date_added', time.time()) self.collection.add(tr) tr.set_tag_raw('__modified', mtime) # Track already existed. This fixes trax.get_tracks_from_uri # on windows, unknown why fix isnt needed on linux. elif not tr._init: self.collection.add(tr) return tr
def update_track(self, gloc: Gio.File, force_update: bool = False) -> Optional[trax.Track]: """ Rescan the track at a given location :param gloc: the location :type gloc: :class:`Gio.File` :param force_update: Force update of file (default only updates file when mtime has changed) returns: the Track object, None if it could not be updated """ uri = gloc.get_uri() if not uri: # we get segfaults if this check is removed return None tr = self.collection.get_track_by_loc(uri) if tr: tr.read_tags(force=force_update) else: tr = trax.Track(uri) if tr._scan_valid: self.collection.add(tr) # Track already existed. This fixes trax.get_tracks_from_uri # on windows, unknown why fix isnt needed on linux. elif not tr._init: self.collection.add(tr) return tr
def add_to_playlist(self, widget, start_editing=None, wget=False): playlist_handle = self.play.get_selected_playlist().playlist model, mysel = self.tw.get_selection().get_selected_rows() myTrack = [] for i in mysel: tr = trax.Track(self.comp[i[0]]["mp3"]) tr.set_tag_raw("artist", self.comp[i[0]]["artist"]) tr.set_tag_raw("title", self.comp[i[0]]["track"]) tr.set_tag_raw("album", "Vkontakte.ru") myTrack.append(tr) if not wget: playlist_handle.add_tracks(myTrack, None) else: for i in mysel: path = settings.get_option("vk_exaile/path", os.getenv("HOME")) if path.strip() == "": settings.set_option("vk_exaile/path", os.getenv("HOME")) path = os.getenv("HOME") elif not os.path.exists(path.strip()): os.system("mkdir '%s'" % path.strip()) res = os.system( 'wget -b -O "%s/%s - %s.mp3" %s -o /dev/null' % (path, self.comp[i[0]]["artist"], self.comp[i[0]]["track"], self.comp[i[0]]["mp3"]))
def on_location_changed(self, monitor, gfile, other_gfile, event): """ Updates the library on changes of the location """ if event == Gio.FileMonitorEvent.CHANGES_DONE_HINT: self.__process_change_queue(gfile) elif event == Gio.FileMonitorEvent.CREATED or \ event == Gio.FileMonitorEvent.CHANGED: # Enqueue tracks retrieval if gfile not in self.__queue: self.__queue[gfile] = True # File monitor only emits the DONE_HINT when using inotify, # and only on single files. Give it some time, but don't # lose the change notification GLib.timeout_add(500, self.__process_change_queue, gfile) # Set up new monitor if directory fileinfo = gfile.query_info('standard::type', Gio.FileQueryInfoFlags.NONE, None) if fileinfo.get_file_type() == Gio.FileType.DIRECTORY and \ gfile not in self.__monitors: for directory in common.walk_directories(gfile): monitor = directory.monitor_directory(Gio.FileMonitorFlags.NONE, None) monitor.connect('changed', self.on_location_changed) self.__monitors[directory] = monitor self.emit('location-added', directory) elif event == Gio.FileMonitorEvent.DELETED: removed_tracks = [] track = trax.Track(gfile.get_uri()) if track in self.__library.collection: # Deleted file was a regular track removed_tracks += [track] else: # Deleted file was most likely a directory for track in self.__library.collection: track_gfile = Gio.File.new_for_uri(track.get_loc_for_io()) if track_gfile.has_prefix(gfile): removed_tracks += [track] self.__library.collection.remove_tracks(removed_tracks) # Remove obsolete monitors removed_directories = [d for d in self.__monitors if d == gfile or d.has_prefix(gfile)] for directory in removed_directories: self.__monitors[directory].cancel() del self.__monitors[directory] self.emit('location-removed', directory)
def _migrate_old_tracks(oldsettings, db, ntdb): libraries = eval(oldsettings.get('DEFAULT', 'search_paths')) oldtracks = oldexailelib.load_tracks(db) rating_steps = 5 # old dbs are hardcoded to 5 steps for library in libraries: ntdb.add_library(collection.Library(library)) newtracks = [] for oldtrack in oldtracks: # we shouldn't be checking os.path.isfile() here, since if it is a radio link, it will not be migrated newtrack = trax.Track(uri=oldtrack.loc, scan=False) if oldtrack._rating: # filter '' and 0 oldtrack._rating = max(0, oldtrack._rating) oldtrack._rating = min(oldtrack._rating, rating_steps) newtrack.set_tag_raw( '__rating', float((100.0 * oldtrack._rating) / rating_steps)) db_map = { 'artist': 'artist', 'album': 'album', 'track': 'tracknumber', 'genre': 'genre', 'date': 'date', 'title': 'title', 'playcount': '__playcount' } newtrack.set_tag_raw('__length', int(getattr(oldtrack, 'duration'))) # Apparently, there is a bug in exaile 0.2.xx that dumps the time as hh:mm:YYYY, rather than hh:mm:ss. This is a workaround, that takes the seconds == 0, since this information is lost b/c of the bug temp_time = oldtrack.time_added try: newtrack.set_tag_raw( '__date_added', time.mktime( time.strptime(temp_time[0:len(temp_time) - 5], '%Y-%m-%d %H:%M'))) except ValueError: try: newtrack.set_tag_raw( '__date_added', time.mktime( time.strptime(temp_time[0:len(temp_time) - 3], '%Y-%m-%d %H:%M'))) except ValueError: pass for item in db_map.keys(): newtrack.set_tag_raw(db_map[item], getattr(oldtrack, item)) newtrack._dirty = True newtracks.append(newtrack) ntdb.add_tracks(newtracks) ntdb.save_to_location()
def generate_tracks(self, chapters): tracks = [] for chapter in chapters: chapter_track = trax.Track(chapter[1]) chapter_track.set_tag_raw('artist', 'Librivox.org') chapter_track.set_tag_raw('title', chapter[0]) chapter_track.set_tag_raw('album', 'Audiobook') tracks.append(chapter_track) return tracks
def get_track(self, f): """ Returns a single track from a Gio.File """ uri = f.get_uri() if not trax.is_valid_track(uri): return None tr = trax.Track(uri) return tr
def generate_tracks(self, chapters): tracks = [] for chapter in chapters: chapter_track = trax.Track(chapter[1]) chapter_track.set_tags(artist='Librivox.org', title=chapter[0], album='Audiobook') tracks.append(chapter_track) return tracks
def get_cover_data(self, db_string): tag, index, uri = db_string.split(':', 2) track = trax.Track(uri, scan=False) covers = track.get_tag_disk(tag) if not covers: return None return covers[int(index)].data
def add_tracks_to_playlist(self, track_list): # convert list to list of xl.Track objects as opposed to jamtree.Track objects xltrack_list = [] for track in track_list: tr = xltrack.Track(track.url, scan=False) tr.set_tags(title=track.name, artist=track.artist_name, album=track.album_name) xltrack_list.append(tr) self.exaile.gui.main.get_selected_page().playlist.extend(xltrack_list)
def convert_list(self): """ Converts the DAAP track database into Exaile Tracks. """ # Convert DAAPTrack's attributes to Tracks. eqiv = { 'title': 'minm', 'artist': 'asar', 'album': 'asal', 'tracknumber': 'astn', 'date': 'asyr', 'discnumber': 'asdn', 'albumartist': 'asaa', } # 'genre':'asgn','enc':'asfm','bitrate':'asbr'} for tr in self.tracks: if tr is not None: # http://<server>:<port>/databases/<dbid>/items/<id>.<type>?session-id=<sessionid> uri = "http://%s:%s/databases/%s/items/%s.%s?session-id=%s" % ( self.server, self.port, self.database.id, tr.id, tr.type, self.session.sessionid, ) # Don't scan tracks because gio is slow! temp = trax.Track(uri, scan=False) for field in eqiv.keys(): try: tag = '%s' % tr.atom.getAtom(eqiv[field]) if tag != 'None': temp.set_tag_raw(field, [tag], notify_changed=False) except Exception: if field == 'tracknumber': temp.set_tag_raw('tracknumber', [0], notify_changed=False) # TODO: convert year (asyr) here as well, what's the formula? try: temp.set_tag_raw( "__length", tr.atom.getAtom('astm') // 1000, notify_changed=False, ) except Exception: temp.set_tag_raw("__length", 0, notify_changed=False) self.all.append(temp)
def display_bookmark(self, key, pos): """ Create menu entrees for this bookmark. """ pix = None # add menu item try: item = trax.Track(key) title = item.get_tag_display('title') if self.use_covers: image = covers.MANAGER.get_cover(item, set_only=True) if image: try: pix = icons.MANAGER.pixbuf_from_data(image, size=(16, 16)) except GLib.GError: logger.warn('Could not load cover') pix = None # no cover else: pix = None except Exception: logger.exception("Cannot open %s", key) # delete offending key? return time = '%d:%02d' % (pos / 60, pos % 60) label = '%s @ %s' % (title, time) counter = self.counter # closure magic (workaround for factories not having access to item) # factory for new bookmarks def factory(menu_, parent, context): menu_item = Gtk.ImageMenuItem.new_with_mnemonic(label) if pix: menu_item.set_image(Gtk.image_new_from_pixbuf(pix)) if menu_ is self.menu: menu_item.connect('activate', self.do_bookmark, (key, pos)) else: menu_item.connect('activate', self.delete_bookmark, (counter, key, pos)) return menu_item item = menu.MenuItem('bookmark{0}'.format(self.counter), factory, ['sep']) self.menu.add_item(item) self.delete_menu.add_item(item) self.counter += 1 # save addition self.save_db()
def __on_bookmark_activated(self, _widget): """ This is called to resume a bookmark. """ # check if it's already playing track = player.PLAYER.current if track and track.get_loc_for_io() == self.__path: player.PLAYER.unpause() player.PLAYER.seek(self.__time) else: # play it using the QUEUE track = trax.Track(self.__path) if track: # make sure we got one player.QUEUE.play(track) player.PLAYER.seek(self.__time)
def add(self, loc: str, move: bool = False) -> None: """ Copies (or moves) a file into the library and adds it to the collection """ oldgloc = Gio.File.new_for_uri(loc) newgloc = Gio.File.new_for_uri(self.location).resolve_relative_path( oldgloc.get_basename()) if move: oldgloc.move(newgloc) else: oldgloc.copy(newgloc) tr = trax.Track(newgloc.get_uri()) if tr._scan_valid: self.collection.add(tr)
def open_disc(self): toc = CDTocParser(self.__device) lengths = toc._get_track_lengths() songs = [] for count, length in enumerate(lengths): count += 1 song = trax.Track("cdda://%d/#%s" % (count, self.__device)) song.set_tags( title="Track %d" % count, tracknumber=str(count), __length=length ) songs.append(song) self.extend(songs) if CDDB_AVAIL: self.get_cddb_info()
def _handle_unknown_drag_data(self, loc): """ Handles unknown drag data that has been recieved by drag_data_received. Unknown drag data is classified as any loc (location) that is not in the collection of tracks (i.e. a new song, or a new playlist) @param loc: the location of the unknown drag data @returns: a 2 tuple in which the first part is a list of tracks and the second is a list of playlist """ filetype = None info = urlparse.urlparse(loc) # don't use gio to test the filetype if it's a non-local file # (otherwise gio will try to connect to every remote url passed in and # cause the gui to hang) if info.scheme in ('file', ''): try: filetype = ( Gio.File.new_for_uri(loc) .query_info('standard::type', Gio.FileQueryInfoFlags.NONE, None) .get_file_type() ) except GLib.Error: filetype = None if trax.is_valid_track(loc) or info.scheme not in ('file', ''): new_track = trax.Track(loc) return ([new_track], []) elif xl_playlist.is_valid_playlist(loc): # User is dragging a playlist into the playlist list # so we add all of the songs in the playlist # to the list new_playlist = xl_playlist.import_playlist(loc) return ([], [new_playlist]) elif filetype == Gio.FileType.DIRECTORY: return (trax.get_tracks_from_uri(loc), []) else: # We don't know what they dropped return ([], [])
def do_bookmark(self, widget, data): """ This is called to resume a bookmark. """ key, pos = data exaile = self.exaile if not (key and pos): return # check if it's already playing track = player.PLAYER.current if track and track.get_loc_for_io() == key: player.PLAYER.unpause() player.PLAYER.seek(pos) return else: # play it using the QUEUE track = trax.Track(key) if track: # make sure we got one player.QUEUE.play(track) player.PLAYER.seek(pos)
def __fetch_metadata(self, *_args): # "gtk-menu-images" is deprecated and is being ignored on most # platforms. On GNOME/Wayland it can be enabled by editing # ~/.config/gtk-3.0/settings.ini and adding `gtk-menu-images=1`. use_covers = Gtk.Settings.get_default().props.gtk_menu_images try: item = trax.Track(self.__path) if not self.__title: self.__title = item.get_tag_display('title') if use_covers: image = covers.MANAGER.get_cover(item, set_only=True) if image: try: self.__cover_pixbuf = pixbuf_from_data(image, size=(16, 16)) except GLib.GError: LOGGER.warning('Could not load cover') else: self.__cover_pixbuf = None except Exception: LOGGER.exception("Cannot open %s", self.__path) return
def get_playlist(self): tr = trax.Track() tr['title'] = 'Test Track' pl = playlist.Playlist('Test Playlist') pl.add_tracks([tr]) return pl