From 9842a52e9af2dbcc9ddfdf45547a5bbde7b7fd16 Mon Sep 17 00:00:00 2001 From: unhandyandy Date: Tue, 31 Dec 2013 13:02:44 -0500 Subject: [PATCH 1/5] search composer field --- sonata/library.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sonata/library.py b/sonata/library.py index b2923573..bd6810f6 100644 --- a/sonata/library.py +++ b/sonata/library.py @@ -45,8 +45,8 @@ def __init__(self, config, client, artwork, TAB_LIBRARY, album_filename, setting self.NOTAG = _("Untagged") self.VAstr = _("Various Artists") - self.search_terms = [_('Artist'), _('Title'), _('Album'), _('Genre'), _('Filename'), _('Everything')] - self.search_terms_mpd = ['artist', 'title', 'album', 'genre', 'file', 'any'] + self.search_terms = [_('Artist'), _('Title'), _('Album'), _('Genre'), _('Filename'), _('Composer'), _('Everything!')] + self.search_terms_mpd = ['artist', 'title', 'album', 'genre', 'file', 'composer', 'any'] self.libfilterbox_cmd_buf = None self.libfilterbox_cond = None From 1d8c626d12a7fc85ab7a00a028c702aa3551cad7 Mon Sep 17 00:00:00 2001 From: unhandyandy Date: Thu, 2 Jan 2014 13:22:31 -0500 Subject: [PATCH 2/5] first pass at composer view --- sonata/consts.py | 12 +-- sonata/current.py | 10 ++- sonata/library.py | 133 +++++++++++++++++++++-------- sonata/main.py | 29 +++++-- sonata/pixmaps/sonata-composer.png | Bin 0 -> 3417 bytes sonata/pixmaps/sonata-composer.xcf | Bin 0 -> 5441 bytes 6 files changed, 134 insertions(+), 50 deletions(-) create mode 100644 sonata/pixmaps/sonata-composer.png create mode 100644 sonata/pixmaps/sonata-composer.xcf diff --git a/sonata/consts.py b/sonata/consts.py index 8e54a49e..74658f47 100644 --- a/sonata/consts.py +++ b/sonata/consts.py @@ -22,8 +22,9 @@ def __init__(self): self.ART_LOCAL_REMOTE = 1 self.VIEW_FILESYSTEM = 0 self.VIEW_ARTIST = 1 - self.VIEW_GENRE = 2 - self.VIEW_ALBUM = 3 + self.VIEW_COMPOSER = 2 + self.VIEW_GENRE = 3 + self.VIEW_ALBUM = 4 self.LYRIC_TIMEOUT = 10 self.NOTIFICATION_WIDTH_MAX = 500 self.NOTIFICATION_WIDTH_MIN = 350 @@ -44,9 +45,10 @@ def __init__(self): self.COVERS_TYPE_STANDARD = 0 self.COVERS_TYPE_STYLIZED = 1 self.LIB_LEVEL_GENRE = 0 - self.LIB_LEVEL_ARTIST = 1 - self.LIB_LEVEL_ALBUM = 2 - self.LIB_LEVEL_SONG = 3 + self.LIB_LEVEL_COMPOSER = 1 + self.LIB_LEVEL_ARTIST = 2 + self.LIB_LEVEL_ALBUM = 3 + self.LIB_LEVEL_SONG = 4 self.NUM_ARTISTS_FOR_VA = 2 # the names of the plug-ins that will be enabled by default diff --git a/sonata/current.py b/sonata/current.py index 6775e2c6..268b30e2 100644 --- a/sonata/current.py +++ b/sonata/current.py @@ -350,6 +350,9 @@ def on_current_column_click(self, column): self.sort('col' + str(col_num), column) return + def on_sort_by_composer(self, _action): + self.sort('composer') + def on_sort_by_artist(self, _action): self.sort('artist') @@ -398,7 +401,12 @@ def sort(self, mode, column=None): # Those items that don't have the specified tag will be put at # the end of the list (hence the 'zzzzzzz'): zzz = 'zzzzzzzz' - if mode == 'artist': + if mode == 'composer': + record["sortby"] = (misc.lower_no_the(mpdh.get(track, 'composer', zzz)), + mpdh.get(track, 'artist', zzz).lower(), + mpdh.get(track, 'disc', '0', True, 0), + mpdh.get(track, 'track', '0', True, 0)) + elif mode == 'artist': record["sortby"] = (misc.lower_no_the(mpdh.get(track, 'artist', zzz)), mpdh.get(track, 'album', zzz).lower(), mpdh.get(track, 'disc', '0', True, 0), diff --git a/sonata/library.py b/sonata/library.py index bd6810f6..fbc216e9 100644 --- a/sonata/library.py +++ b/sonata/library.py @@ -10,16 +10,17 @@ from consts import consts import breadcrumbs -def library_set_data(album=None, artist=None, genre=None, year=None, path=None): +def library_set_data(album=None, artist=None, composer=None, genre=None, year=None, path=None): if album is not None: album = unicode(album) if artist is not None: artist = unicode(artist) + if composer is not None: composer = unicode(composer) if genre is not None: genre = unicode(genre) if year is not None: year = unicode(year) if path is not None: path = unicode(path) - return (album, artist, genre, year, path) + return (album, artist, composer, genre, year, path) def library_get_data(data, *args): - name_to_index = {'album':0, 'artist':1, 'genre':2, 'year':3, 'path':4} + name_to_index = {'album':0, 'artist':1, 'composer':2, 'genre':3, 'year':4, 'path':5} # Data retrieved from the gtktreeview model is not in # unicode anymore, so convert it. retlist = [unicode(data[name_to_index[arg]]) if data[name_to_index[arg]] else None for arg in args] @@ -61,8 +62,10 @@ def __init__(self, config, client, artwork, TAB_LIBRARY, album_filename, setting self.lib_view_filesystem_cache = None self.lib_view_artist_cache = None + self.lib_view_composer_cache = None self.lib_view_genre_cache = None self.lib_view_album_cache = None + self.lib_list_composers = None self.lib_list_genres = None self.lib_list_artists = None self.lib_list_albums = None @@ -105,6 +108,7 @@ def __init__(self, config, client, artwork, TAB_LIBRARY, album_filename, setting self.openpb = self.library.render_icon(gtk.STOCK_OPEN, gtk.ICON_SIZE_MENU) self.harddiskpb = self.library.render_icon(gtk.STOCK_HARDDISK, gtk.ICON_SIZE_MENU) self.albumpb = gtk.gdk.pixbuf_new_from_file_at_size(album_filename, consts.LIB_COVER_SIZE, consts.LIB_COVER_SIZE) + self.composerpb = self.library.render_icon('composer', gtk.ICON_SIZE_LARGE_TOOLBAR) self.genrepb = self.library.render_icon('gtk-orientation-portrait', gtk.ICON_SIZE_LARGE_TOOLBAR) self.artistpb = self.library.render_icon('artist', gtk.ICON_SIZE_LARGE_TOOLBAR) self.sonatapb = self.library.render_icon('sonata', gtk.ICON_SIZE_MENU) @@ -119,6 +123,8 @@ def __init__(self, config, client, artwork, TAB_LIBRARY, album_filename, setting 'artist', _("Artists")), (consts.VIEW_GENRE, 'genre', gtk.STOCK_ORIENTATION_PORTRAIT, _("Genres")), + (consts.VIEW_COMPOSER, 'composer', + 'composer', _("Composers")), ] self.library_view_assign_image() @@ -192,6 +198,8 @@ def on_libraryview_chosen(self, action): self.config.lib_view = consts.VIEW_FILESYSTEM elif action.get_name() == 'artistview': self.config.lib_view = consts.VIEW_ARTIST + elif action.get_name() == 'composerview': + self.config.lib_view = consts.VIEW_COMPOSER elif action.get_name() == 'genreview': self.config.lib_view = consts.VIEW_GENRE elif action.get_name() == 'albumview': @@ -220,8 +228,10 @@ def view_caches_reset(self): self.lib_view_filesystem_cache = None self.lib_view_artist_cache = None self.lib_view_genre_cache = None + self.lib_view_composer_cache = None self.lib_view_album_cache = None self.lib_list_genres = None + self.lib_list_composers = None self.lib_list_artists = None self.lib_list_albums = None self.lib_list_years = None @@ -335,6 +345,18 @@ def library_browse(self, _widget=None, root=None): bd = self.library_populate_data(artist=artist) else: bd = self.library_populate_toplevel_data(artistview=True) + elif self.config.lib_view == consts.VIEW_COMPOSER: + composer, album, artist, year = self.library_get_data(self.config.wd, 'composer', 'album', 'artist', 'year') + if composer is not None and artist is not None and album is not None and year is not None: + bd = self.library_populate_data(composer=composer, artist=artist, album=album, year=year) + elif composer is not None and artist is not None and album is not None: + bd = self.library_populate_data(composer=composer, artist=artist, album=album) + elif composer is not None and artist is not None: + bd = self.library_populate_data(composer=composer, artist=artist) + elif composer is not None: + bd = self.library_populate_data(composer=composer) + else: + bd = self.library_populate_toplevel_data(composerview=True) elif self.config.lib_view == consts.VIEW_GENRE: genre, artist, album, year = self.library_get_data(self.config.wd, 'genre', 'artist', 'album', 'year') if genre is not None and artist is not None and album is not None: @@ -410,11 +432,11 @@ def update_breadcrumbs(self): else: if view == consts.VIEW_ALBUM: # We don't want to show an artist button in album view - keys = 'genre', 'album' - nkeys = 2 - else: - keys = 'genre', 'artist', 'album' + keys = 'genre', 'composer', 'album' nkeys = 3 + else: + keys = 'genre', 'composer', 'artist', 'album' + nkeys = 4 parts = self.library_get_data(self.config.wd, *keys) # append a crumb for each part for i, key, part in zip(range(nkeys), keys, parts): @@ -494,8 +516,10 @@ def library_populate_filesystem_data(self, path): self.lib_view_filesystem_cache = bd return bd - def library_get_toplevel_cache(self, genreview=False, artistview=False, albumview=False): - if genreview and self.lib_view_genre_cache is not None: + def library_get_toplevel_cache(self, composerview=False, genreview=False, artistview=False, albumview=False): + if composerview and self.lib_view_composer_cache is not None: + bd = self.lib_view_composer_cache + elif genreview and self.lib_view_genre_cache is not None: bd = self.lib_view_genre_cache elif artistview and self.lib_view_artist_cache is not None: bd = self.lib_view_artist_cache @@ -514,27 +538,33 @@ def library_get_toplevel_cache(self, genreview=False, artistview=False, albumvie info[0] = pb2 return bd - def library_populate_toplevel_data(self, genreview=False, artistview=False, albumview=False): - bd = self.library_get_toplevel_cache(genreview, artistview, albumview) + def library_populate_toplevel_data(self, composerview=False, genreview=False, artistview=False, albumview=False): + bd = self.library_get_toplevel_cache(composerview, genreview, artistview, albumview) if bd is not None: # We have our cached data, woot. return bd bd = [] - if genreview or artistview: + if genreview or artistview or composerview: # Only for artist/genre views, album view is handled differently # since multiple artists can have the same album name if genreview: items = self.library_return_list_items('genre') pb = self.genrepb - else: + elif artistview: items = self.library_return_list_items('artist') pb = self.artistpb + else: + items = self.library_return_list_items('composer') + pb = self.composerpb if not (self.NOTAG in items): items.append(self.NOTAG) for item in items: if genreview: playtime, num_songs = self.library_return_count(genre=item) data = self.library_set_data(genre=item) + elif composerview: + playtime, num_songs = self.library_return_count(composer=item) + data = self.library_set_data(composer=item) else: playtime, num_songs = self.library_return_count(artist=item) data = self.library_set_data(artist=item) @@ -579,6 +609,8 @@ def library_populate_toplevel_data(self, genreview=False, artistview=False, albu self.lib_view_artist_cache = bd elif albumview: self.lib_view_album_cache = bd + elif composerview: + self.lib_view_composer_cache = bd return bd def list_identify_VA_albums(self, albums): @@ -614,10 +646,10 @@ def list_identify_VA_albums(self, albums): def get_VAstr(self): return self.VAstr - def library_populate_data(self, genre=None, artist=None, album=None, year=None): + def library_populate_data(self, composer=None, genre=None, artist=None, album=None, year=None): # Create treeview model info bd = [] - if genre is not None and artist is None and album is None: + if genre is not None and artist is None and album is None and composer is None: # Artists within a genre artists = self.library_return_list_items('artist', genre=genre) if len(artists) > 0: @@ -630,11 +662,24 @@ def library_populate_data(self, genre=None, artist=None, album=None, year=None): display += self.add_display_info(num_songs, int(playtime)/60) data = self.library_set_data(genre=genre, artist=artist) bd += [(misc.lower_no_the(artist), [self.artistpb, data, display])] + elif composer is not None and artist is None and album is None: + # Artists within a genre + artists = self.library_return_list_items('artist', composer=composer) + if len(artists) > 0: + if not self.NOTAG in artists: + artists.append(self.NOTAG) + for artist in artists: + playtime, num_songs = self.library_return_count(composer=composer, artist=artist) + if num_songs > 0: + display = misc.escape_html(artist) + display += self.add_display_info(num_songs, int(playtime)/60) + data = self.library_set_data(composer=composer, artist=artist) + bd += [(misc.lower_no_the(artist), [self.artistpb, data, display])] elif artist is not None and album is None: # Albums/songs within an artist and possibly genre # Albums first: - if genre is not None: - albums = self.library_return_list_items('album', genre=genre, artist=artist) + if composer is not None: + albums = self.library_return_list_items('album', composer=composer, artist=artist) else: albums = self.library_return_list_items('album', artist=artist) for album in albums: @@ -669,18 +714,20 @@ def library_populate_data(self, genre=None, artist=None, album=None, year=None): pb = self.artwork.get_library_artwork_cached_pb(cache_data, self.albumpb) bd += [(ordered_year + misc.lower_no_the(album), [pb, data, display])] # Now, songs not in albums: - bd += self.library_populate_data_songs(genre, artist, self.NOTAG, None) + bd += self.library_populate_data_songs(composer, genre, artist, self.NOTAG, None) else: # Songs within an album, artist, year, and possibly genre - bd += self.library_populate_data_songs(genre, artist, album, year) + bd += self.library_populate_data_songs(composer, genre, artist, album, year) if len(bd) > 0: bd = self.library_populate_add_parent_rows() + bd bd.sort(locale.strcoll, key=operator.itemgetter(0)) return bd - def library_populate_data_songs(self, genre, artist, album, year): + def library_populate_data_songs(self, composer, genre, artist, album, year): bd = [] - if genre is not None: + if composer is not None: + songs, _playtime, _num_songs = self.library_return_search_items(composer=composer, artist=artist, album=album, year=year) + elif genre is not None: songs, _playtime, _num_songs = self.library_return_search_items(genre=genre, artist=artist, album=album, year=year) else: songs, _playtime, _num_songs = self.library_return_search_items(artist=artist, album=album, year=year) @@ -694,21 +741,21 @@ def library_populate_data_songs(self, genre, artist, album, year): bd += [('f' + disc + track + unicode(mpdh.get(song, 'file')).lower(), [self.sonatapb, data, formatting.parse(self.config.libraryformat, song, True)])] return bd - def library_return_list_items(self, itemtype, genre=None, artist=None, album=None, year=None, ignore_case=True): + def library_return_list_items(self, itemtype, composer=None, genre=None, artist=None, album=None, year=None, ignore_case=True): # Returns all items of tag 'itemtype', in alphabetical order, # using mpd's 'list'. If searchtype is passed, use # a case insensitive search, via additional 'list' # queries, since using a single 'list' call will be # case sensitive. results = [] - searches = self.library_compose_list_count_searchlist(genre, artist, album, year) + searches = self.library_compose_list_count_searchlist(composer, genre, artist, album, year) if len(searches) > 0: for s in searches: # If we have untagged tags (''), use search instead # of list because list will not return anything. if '' in s: items = [] - songs, playtime, num_songs = self.library_return_search_items(genre, artist, album, year) + songs, playtime, num_songs = self.library_return_search_items(composer, genre, artist, album, year) for song in songs: items.append(mpdh.get(song, itemtype)) else: @@ -717,7 +764,7 @@ def library_return_list_items(self, itemtype, genre=None, artist=None, album=Non if len(item) > 0: results.append(item) else: - if genre is None and artist is None and album is None and year is None: + if genre is None and artist is None and album is None and year is None and composer is None: for item in mpdh.call(self.client, 'list', itemtype): if len(item) > 0: results.append(item) @@ -726,13 +773,13 @@ def library_return_list_items(self, itemtype, genre=None, artist=None, album=Non results.sort(locale.strcoll) return results - def library_return_count(self, genre=None, artist=None, album=None, year=None): + def library_return_count(self, composer=None, genre=None, artist=None, album=None, year=None): # Because mpd's 'count' is case sensitive, we have to # determine all equivalent items (case insensitive) and # call 'count' for each of them. Using 'list' + 'count' # involves much less data to be transferred back and # forth than to use 'search' and count manually. - searches = self.library_compose_list_count_searchlist(genre, artist, album, year) + searches = self.library_compose_list_count_searchlist(composer, genre, artist, album, year) playtime = 0 num_songs = 0 for s in searches: @@ -741,7 +788,7 @@ def library_return_count(self, genre=None, artist=None, album=None, year=None): # Can't return count for empty tags, use search instead: - _results, playtime, num_songs = self.library_return_search_items(genre=genre, artist=artist, album=album, year=year) + _results, playtime, num_songs = self.library_return_search_items(composer=composer, genre=genre, artist=artist, album=album, year=year) else: @@ -778,8 +825,11 @@ def library_compose_list_count_searchlist_single(self, search, typename, cached_ s = searchlist return s, cached_list - def library_compose_list_count_searchlist(self, genre=None, artist=None, album=None, year=None): + def library_compose_list_count_searchlist(self, composer=None, genre=None, artist=None, album=None, year=None): s = [] + s, self.lib_list_composers = self.library_compose_list_count_searchlist_single(composer, 'composer', self.lib_list_composers, s) + if s is None: + return [] s, self.lib_list_genres = self.library_compose_list_count_searchlist_single(genre, 'genre', self.lib_list_genres, s) if s is None: return [] @@ -812,18 +862,19 @@ def library_compose_search_searchlist_single(self, search, typename, searchlist) s = searchlist return s - def library_compose_search_searchlist(self, genre=None, artist=None, album=None, year=None): + def library_compose_search_searchlist(self, composer=None, genre=None, artist=None, album=None, year=None): s = [] + s = self.library_compose_search_searchlist_single(composer, 'composer', s) s = self.library_compose_search_searchlist_single(genre, 'genre', s) s = self.library_compose_search_searchlist_single(album, 'album', s) s = self.library_compose_search_searchlist_single(artist, 'artist', s) s = self.library_compose_search_searchlist_single(year, 'date', s) return s - def library_return_search_items(self, genre=None, artist=None, album=None, year=None): + def library_return_search_items(self, composer=None, genre=None, artist=None, album=None, year=None): # Returns all mpd items, using mpd's 'search', along with # playtime and num_songs. - searches = self.library_compose_search_searchlist(genre, artist, album, year) + searches = self.library_compose_search_searchlist(composer, genre, artist, album, year) for s in searches: args_tuple = tuple(map(str, s)) playtime = 0 @@ -925,8 +976,8 @@ def library_get_data_level(self, data): # Returns the number of items stored in data, excluding # the path: level = 0 - album, artist, genre, year = library_get_data(data, 'album', 'artist', 'genre', 'year') - for item in [album, artist, genre, year]: + album, artist, composer, genre, year = library_get_data(data, 'album', 'artist', 'composer', 'genre', 'year') + for item in [album, artist, composer, genre, year]: if item is not None: level += 1 return level @@ -1009,6 +1060,14 @@ def library_get_parent(self): value = self.library_set_data(genre=genre) else: value = self.library_set_data(path="/") + elif self.config.lib_view == consts.VIEW_COMPOSER: + album, artist, composer = self.library_get_data(self.config.wd, 'album', 'artist', 'composer') + if album is not None: + value = self.library_set_data(composer=composer, artist=artist) + elif artist is not None: + value = self.library_set_data(composer=composer) + else: + value = self.library_set_data(path="/") else: newvalue = '/'.join(self.library_get_data(self.config.wd, 'path').split('/')[:-1]) or '/' value = self.library_set_data(path=newvalue) @@ -1049,8 +1108,8 @@ def get_path_child_filenames(self, return_root, selected_only=True): data = model.get_value(i, 1) value = model.get_value(i, 2) if value != ".." and value != "/": - album, artist, year, genre, path = self.library_get_data(data, 'album', 'artist', 'year', 'genre', 'path') - if path is not None and album is None and artist is None and year is None and genre is None: + album, artist, year, composer, genre, path = self.library_get_data(data, 'album', 'artist', 'year', 'composer', 'genre', 'path') + if path is not None and album is None and artist is None and year is None and genre is None and composer is None: if pb == self.sonatapb: # File items.append(path) @@ -1061,7 +1120,7 @@ def get_path_child_filenames(self, return_root, selected_only=True): else: items.append(path) else: - results, _playtime, _num_songs = self.library_return_search_items(genre=genre, artist=artist, album=album, year=year) + results, _playtime, _num_songs = self.library_return_search_items(composer=composer, genre=genre, artist=artist, album=album, year=year) for item in results: items.append(mpdh.get(item, 'file')) # Make sure we don't have any EXACT duplicates: diff --git a/sonata/main.py b/sonata/main.py index 3af99653..5122f661 100644 --- a/sonata/main.py +++ b/sonata/main.py @@ -87,6 +87,7 @@ def __init__(self, args, window=None, _sugar=False): self.remote_albumentry = None self.remote_artistentry = None + self.remote_composerentry = None self.remote_dest_filename = None self.remotefilelist = None self.seekidle = None @@ -197,6 +198,7 @@ def __init__(self, args, window=None, _sugar=False): self.iconfactory = gtk.IconFactory() ui.icon(self.iconfactory, 'sonata', self.find_path('sonata.png')) ui.icon(self.iconfactory, 'artist', self.find_path('sonata-artist.png')) + ui.icon(self.iconfactory, 'composer', self.find_path('sonata-composer.png')) ui.icon(self.iconfactory, 'album', self.find_path('sonata-album.png')) icon_theme = gtk.icon_theme_get_default() if HAVE_SUGAR: @@ -340,6 +342,7 @@ def __init__(self, args, window=None, _sugar=False): + @@ -368,6 +371,7 @@ def __init__(self, args, window=None, _sugar=False): + @@ -423,6 +427,7 @@ def __init__(self, args, window=None, _sugar=False): currentactions = [ ('centerplaylistkey', None, 'Center Playlist Key', 'i', None, self.current.center_song_in_list), + ('sortbycomposer', None, _('By Composer'), None, None, self.current.on_sort_by_composer), ('sortbyartist', None, _('By Artist'), None, None, self.current.on_sort_by_artist), ('sortbyalbum', None, _('By Album'), None, None, self.current.on_sort_by_album), ('sortbytitle', None, _('By Song Title'), None, None, self.current.on_sort_by_title), @@ -1787,9 +1792,12 @@ def update_infofile(self): info_file.write('Title: ' + mpdh.get(self.songinfo, 'artist') + ' - ' + mpdh.get(self.songinfo, 'title') + '\n') except: try: - info_file.write('Title: ' + mpdh.get(self.songinfo, 'title') + '\n') # No Arist in streams + info_file.write('Title: ' + mpdh.get(self.songinfo, 'composer') + ' - ' + mpdh.get(self.songinfo, 'title') + '\n') except: - info_file.write('Title: No - ID Tag\n') + try: + info_file.write('Title: ' + mpdh.get(self.songinfo, 'title') + '\n') # No Arist in streams + except: + info_file.write('Title: No - ID Tag\n') info_file.write('Album: ' + mpdh.get(self.songinfo, 'album', 'No Data') + '\n') info_file.write('Track: ' + mpdh.get(self.songinfo, 'track', '0') + '\n') info_file.write('File: ' + mpdh.get(self.songinfo, 'file', 'No Data') + '\n') @@ -2035,16 +2043,18 @@ def on_image_activate(self, widget, event): else: self.switch_to_tab_name(self.last_tab) elif event.button == 3: + composer = None artist = None album = None stream = None if self.status_is_play_or_pause(): self.UIManager.get_widget('/imagemenu/chooseimage_menu/').show() self.UIManager.get_widget('/imagemenu/localimage_menu/').show() + composer = mpdh.get(self.songinfo, 'composer', None) artist = mpdh.get(self.songinfo, 'artist', None) album = mpdh.get(self.songinfo, 'album', None) stream = mpdh.get(self.songinfo, 'name', None) - if not (artist or album or stream): + if not (composer or artist or album or stream): self.UIManager.get_widget('/imagemenu/localimage_menu/').hide() self.UIManager.get_widget('/imagemenu/resetimage_menu/').hide() self.UIManager.get_widget('/imagemenu/chooseimage_menu/').hide() @@ -2282,11 +2292,12 @@ def image_remote(self, _widget): hbox = gtk.HBox() vbox = gtk.VBox() vbox.pack_start(ui.label(markup=' '), False, False, 0) + self.remote_composerentry = ui.entry() self.remote_artistentry = ui.entry() self.remote_albumentry = ui.entry() - text = [("Artist"), _("Album")] + text = [("Composer"), ("Artist"), _("Album")] labels = [ui.label(text=labelname + ": ") for labelname in text] - entries = [self.remote_artistentry, self.remote_albumentry] + entries = [self.remote_composerentry, self.remote_artistentry, self.remote_albumentry] for entry, label in zip(entries, labels): tmphbox = gtk.HBox() tmphbox.pack_start(label, False, False, 5) @@ -2319,6 +2330,7 @@ def image_remote(self, _widget): artist = self.album_current_artist[1] imagewidget.connect('item-activated', self.image_remote_replace_cover, artist.replace("/", ""), album.replace("/", ""), stream) self.choose_dialog.connect('response', self.image_remote_response, imagewidget, artist, album, stream) + self.remote_composerentry.set_text(composer) self.remote_artistentry.set_text(artist) self.remote_albumentry.set_text(album) self.allow_art_search = True @@ -2888,7 +2900,9 @@ def seek(self, song, seektime): def on_link_click(self, linktype): browser_not_loaded = False - if linktype == 'artist': + if linktype == 'composer': + browser_not_loaded = not misc.browser_load("http://www.wikipedia.org/wiki/Special:Search/" + urllib.quote(mpdh.get(self.songinfo, 'composer')), self.config.url_browser, self.window) + elif linktype == 'artist': browser_not_loaded = not misc.browser_load("http://www.wikipedia.org/wiki/Special:Search/" + urllib.quote(mpdh.get(self.songinfo, 'artist')), self.config.url_browser, self.window) elif linktype == 'album': browser_not_loaded = not misc.browser_load("http://www.wikipedia.org/wiki/Special:Search/" + urllib.quote(mpdh.get(self.songinfo, 'album')), self.config.url_browser, self.window) @@ -3130,7 +3144,8 @@ def on_about(self, _action): if self.conn: # Extract some MPD stats: mpdstats = mpdh.call(self.client, 'stats') - stats = {'artists': mpdstats['artists'], + stats = {'composers': mpdstats['composers'], + 'artists': mpdstats['artists'], 'albums': mpdstats['albums'], 'songs': mpdstats['songs'], 'db_playtime': mpdstats['db_playtime'], diff --git a/sonata/pixmaps/sonata-composer.png b/sonata/pixmaps/sonata-composer.png new file mode 100644 index 0000000000000000000000000000000000000000..6bc3a1eee63e636fcc6973290ed518be0254d078 GIT binary patch literal 3417 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 literal 0 HcmV?d00001 diff --git a/sonata/pixmaps/sonata-composer.xcf b/sonata/pixmaps/sonata-composer.xcf new file mode 100644 index 0000000000000000000000000000000000000000..02fa6e8b26217c3978a05d0f74a1abebc20dccba GIT binary patch literal 5441 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 Date: Sat, 4 Jan 2014 13:26:17 -0500 Subject: [PATCH 3/5] composer tag --- sonata/info.py | 6 +++++ sonata/library.py | 29 ++++++++++++++++++---- sonata/pixmaps/sonata-composer.png | Bin 3417 -> 3181 bytes sonata/pixmaps/sonata-composer.xcf | Bin 5441 -> 0 bytes sonata/tagedit.py | 37 ++++++++++++++++++++++------- 5 files changed, 59 insertions(+), 13 deletions(-) delete mode 100644 sonata/pixmaps/sonata-composer.xcf diff --git a/sonata/info.py b/sonata/info.py index b6a36752..2496df54 100644 --- a/sonata/info.py +++ b/sonata/info.py @@ -77,6 +77,8 @@ def _widgets_song(self): self.info_labels = {} self.info_boxes_in_more = [] labels = [(_("Title"), 'title', False, "", False), + (_("Composer"), 'composer', True, + _("Launch composer in Wikipedia"), False), (_("Artist"), 'artist', True, _("Launch artist in Wikipedia"), False), (_("Album"), 'album', True, @@ -232,6 +234,7 @@ def update(self, playing_or_paused, newbitrate, songinfo, update_all): getattr(self, "_update_%s" % func)(songinfo) def _update_song(self, songinfo): + composerlabel = self.info_labels['composer'] artistlabel = self.info_labels['artist'] tracklabel = self.info_labels['track'] albumlabel = self.info_labels['album'] @@ -242,6 +245,9 @@ def _update_song(self, songinfo): label.set_text(mpdh.get(songinfo, name)) tracklabel.set_text(mpdh.get(songinfo, 'track', '', False)) + composerlabel.set_markup(misc.link_markup(misc.escape_html( + mpdh.get(songinfo, 'composer')), False, False, + self.linkcolor)) artistlabel.set_markup(misc.link_markup(misc.escape_html( mpdh.get(songinfo, 'artist')), False, False, self.linkcolor)) diff --git a/sonata/library.py b/sonata/library.py index fbc216e9..424c6528 100644 --- a/sonata/library.py +++ b/sonata/library.py @@ -265,6 +265,7 @@ def _on_library_scrolled(self): def library_browse(self, _widget=None, root=None): # Populates the library list with entries + #print "root = ", root if not self.connected(): return @@ -347,12 +348,17 @@ def library_browse(self, _widget=None, root=None): bd = self.library_populate_toplevel_data(artistview=True) elif self.config.lib_view == consts.VIEW_COMPOSER: composer, album, artist, year = self.library_get_data(self.config.wd, 'composer', 'album', 'artist', 'year') - if composer is not None and artist is not None and album is not None and year is not None: - bd = self.library_populate_data(composer=composer, artist=artist, album=album, year=year) - elif composer is not None and artist is not None and album is not None: - bd = self.library_populate_data(composer=composer, artist=artist, album=album) + # print "config.wd = ", self.config.wd + # print "composer = ", composer + # print "artist = ", artist + if composer is not None and artist is not None and year is not None: + bd = self.library_populate_data(composer=composer, artist=artist, year=year) elif composer is not None and artist is not None: bd = self.library_populate_data(composer=composer, artist=artist) + elif album is not None: + bd = self.library_populate_data(album=album) + elif artist is not None: + bd = self.library_populate_data(artist=artist) elif composer is not None: bd = self.library_populate_data(composer=composer) else: @@ -371,6 +377,7 @@ def library_browse(self, _widget=None, root=None): if len(bd) == 0: # Nothing found; go up a level until we reach the top level # or results are found + #print "Nothing found!" last_wd = self.config.wd self.config.wd = self.library_get_parent() if self.config.wd == last_wd: @@ -443,6 +450,7 @@ def update_breadcrumbs(self): if part is None: continue partdata = dict(zip(keys, parts)[:i+1]) + #print "partdata = ", partdata target = self.library_set_data(**partdata) pb, icon = None, None if key == 'album': @@ -454,6 +462,8 @@ def update_breadcrumbs(self): icon = 'album' elif key == 'artist': icon = 'artist' + elif key == 'composer': + icon = 'composer' else: icon = gtk.STOCK_ORIENTATION_PORTRAIT crumbs.append((part, icon, pb, target)) @@ -461,6 +471,7 @@ def update_breadcrumbs(self): # add a button for each crumb for crumb in crumbs: text, icon, pb, target = crumb + #print "target = ", target text = misc.escape_html(text) if crumb is crumbs[-1]: text = "%s" % text @@ -726,7 +737,7 @@ def library_populate_data(self, composer=None, genre=None, artist=None, album=No def library_populate_data_songs(self, composer, genre, artist, album, year): bd = [] if composer is not None: - songs, _playtime, _num_songs = self.library_return_search_items(composer=composer, artist=artist, album=album, year=year) + songs, _playtime, _num_songs = self.library_return_search_items(composer=composer, genre=genre, artist=artist, album=album, year=year) elif genre is not None: songs, _playtime, _num_songs = self.library_return_search_items(genre=genre, artist=artist, album=album, year=year) else: @@ -984,6 +995,7 @@ def library_get_data_level(self, data): def on_library_key_press(self, widget, event): if event.keyval == gtk.gdk.keyval_from_name('Return'): + #print "key_press: ", widget.get_cursor()[0] self.on_library_row_activated(widget, widget.get_cursor()[0]) return True @@ -1034,6 +1046,12 @@ def on_library_row_activated(self, _widget, path, _column=0): else: return value = self.librarydata.get_value(self.librarydata.get_iter(path), 1) + + #print "value = ", value + # print "path = ", path + # print "self.librarydata.get_iter(path) = ", self.librarydata.get_iter(path) + # print "self.librarydata = ", self.librarydata + icon = self.librarydata.get_value(self.librarydata.get_iter(path), 0) if icon == self.sonatapb: # Song found, add item @@ -1077,6 +1095,7 @@ def library_browse_parent(self, _action): if not self.search_visible(): if self.library.is_focus(): value = self.library_get_parent() + #print "value = ", value self.library_browse(None, value) return True diff --git a/sonata/pixmaps/sonata-composer.png b/sonata/pixmaps/sonata-composer.png index 6bc3a1eee63e636fcc6973290ed518be0254d078..d914afd973f10834799f750dfab42e6b4884021c 100644 GIT binary patch delta 3128 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 diff --git a/sonata/pixmaps/sonata-composer.xcf b/sonata/pixmaps/sonata-composer.xcf deleted file mode 100644 index 02fa6e8b26217c3978a05d0f74a1abebc20dccba..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5441 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 Date: Fri, 10 Jan 2014 21:27:35 -0500 Subject: [PATCH 5/5] ogg tags read/write --- sonata/tagedit.py | 139 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 108 insertions(+), 31 deletions(-) diff --git a/sonata/tagedit.py b/sonata/tagedit.py index d8528425..da973ec7 100644 --- a/sonata/tagedit.py +++ b/sonata/tagedit.py @@ -16,6 +16,8 @@ tagpy = None # module loaded when needed import ui, misc +#from ctypes import * +#from ptr import * class TagEditor(): @@ -236,12 +238,52 @@ def tags_win_apply_all(self, _button, item, tags, entry): # Update the entry for the current song: entry.set_text(str(tags[self.tagnum]['track'])) + def getFileFormat(self, fpath): + _, fileext = os.path.splitext( fpath ) + ftype = "" + if fileext in [ ".ogg", ".oga", ".OGG", ".OGA" ]: + ftype = "oga" + elif fileext in [ ".mp3", ".MP3" ]: + ftype = "mp3" + else: + ftype = "" + return ftype + + def getTagCode(self, ext, nm ): + print "ext = ", ext + print "nm = ", nm + if ext == "mp3": + codes = { 'composer': "TCOM" } + elif ext == "oga": + codes = { 'composer': "COMPOSER" } + else: + return "" + return codes[ nm ] + + def getFileOb( self, fpath, ext): + if ext == "mp3": + return tagpy.mpeg.File( fpath ) + elif ext == "oga": + return tagpy.ogg.vorbis.File( fpath ) + else: + return tagpy.FileRef( fpath ) + + def getTagOb( self, fob, ext ): + if ext == "mp3": + return fob.ID3v2Tag() + else: + return fob.tag() + + def tags_win_update(self, window, tags, entries, entries_names): #print "entries_names = ", entries_names current_tag = tags[self.tagnum] #print "current_tag = ", current_tag - tag = tagpy.FileRef(current_tag['fullpath']).tag() - mpegtag = tagpy.mpeg.File(current_tag['fullpath']).ID3v2Tag() + fpath = current_tag['fullpath'] + ext = self.getFileFormat( fpath ) + #print "fpath = ", fpath + fob = self.getFileOb( fpath, ext ) + tag = self.getTagOb( fob, ext ) #print "tag = ", dir(tag) # Update interface: for entry, entry_name in zip(entries, entries_names): @@ -250,7 +292,7 @@ def tags_win_update(self, window, tags, entries, entries_names): if not entry_name in ['composer']: current_tag[entry_name] = getattr(tag, entry_name, '') else: - current_tag[entry_name] = self.getOtherFromTag(mpegtag, entry_name) + current_tag[entry_name] = self.getOtherFromTag( tag, ext, entry_name ) tag_value = current_tag[entry_name] #print "entry_name = ", entry_name #print "tag_value: ", tag_value @@ -272,30 +314,58 @@ def tags_win_update(self, window, tags, entries, entries_names): (self.tagnum+1, len(tags))) self.tags_win_set_sensitive(window.action_area) - def getOtherFromTag( self, mtag, nm ): + def getOtherFromTag( self, tag, ext, nm ): # workaround to get other fields e.g. composer (ajd) - codes = { 'composer': "TCOM" } - cd = codes[ nm ] - #print "cd = ", cd - frmlst = mtag.frameListMap() - #print "frmlst = ", frmlst.keys() - try: - res = frmlst[cd][0].toString() - except: - res = "" - #print "attr: = ", res - return res - - def setOtherToTag( self, mtag, nm, val ): + cd = self.getTagCode( ext, nm ) + + print "cd = ", cd + + if ext == "mp3": + frmlstmp = tag.frameListMap() + #print "frmlst = ", frmlst.keys() + try: + res = frmlstmp[cd][0].toString() + except: + res = "" + #print "attr: = ", res + return res + elif ext == "oga": + try: + xiphcomp = tag.fieldListMap()[ cd ] + res = xiphcomp[ 0 ] + except: + res = "" + return res + else: + return "" + + + def setOtherToTag( self, tag, ext, nm, val ): # workaround to set other fields e.g. composer (ajd) - codes = { 'composer': "TCOM" } - cd = codes[ nm ] - #print "cd = ", cd - frmlst = mtag.frameListMap() - try: - frmlst[cd][0].setText( val ) - except: - print "Failed to set {}.".format( nm ) + ### Must check file type!!!! + cd = self.getTagCode( ext, nm ) + if ext == "mp3": + frmlstmp = tag.frameListMap() + try: + frmlstmp[cd][0].setText( val ) + except: + newframe = tagpy.id3v2.TextIdentificationFrame( cd ) + newframe.setText( val ) + tag.addFrame( newframe ) + elif ext == "oga": + #try: + tag.addField( cd, val, True ) + #pckt = xiphprops.render().encode('utf-8') + #print "pckt = \n", pckt + #fob.setPacket( 1, pckt ) + #print "new fob: \n", fob.packet( 1 ) + #save_success = fob.save() + #print "saved fob: \n", fob.packet( 1 ) + # except: + # newframe = tagpy.id3v2.TextIdentificationFrame( cd ) + # newframe.setText( val ) + # mtag.addFrame( newframe ) + def tags_win_set_sensitive(self, action_area): @@ -312,16 +382,23 @@ def tags_win_save_all(self, _button, window, tags, entries, entries_names): self.tags_win_response(window, gtk.RESPONSE_ACCEPT, tags, entries, entries_names) def tags_win_response(self, window, response, tags, entries, entries_names): + #print "entries_names = ", entries_names + current_tag = tags[self.tagnum] + #print "current_tag = ", current_tag + if response == gtk.RESPONSE_REJECT: self.tags_win_hide(window, None, tags) elif response == gtk.RESPONSE_ACCEPT: window.action_area.set_sensitive(False) while window.action_area.get_property("sensitive") or gtk.events_pending(): gtk.main_iteration() - filetag = tagpy.FileRef(tags[self.tagnum]['fullpath']) - tag = filetag.tag() - mpegfiletag = tagpy.mpeg.File(tags[self.tagnum]['fullpath']) - mpegtag = mpegfiletag.ID3v2Tag() + fpath = current_tag['fullpath'] + ext = self.getFileFormat( fpath ) + #print "fpath = ", fpath + fob = self.getFileOb( fpath, ext ) + tag = self.getTagOb( fob, ext ) + #print "tag = ", dir(tag) + # Set tag fields according to entry text for entry, field in zip(entries, entries_names): tag_value = entry.get_text().strip() @@ -335,9 +412,9 @@ def tags_win_response(self, window, response, tags, entries, entries_names): if not field in ( 'composer' ): setattr(tag, field, tag_value) else: - self.setOtherToTag( mpegtag, field, tag_value ) + self.setOtherToTag( tag, ext, field, tag_value ) - save_success = filetag.save() and mpegfiletag.save() + save_success = fob.save() if not (save_success): # FIXME: was (save_success and self.conn and self.status): ui.show_msg(self.window, _("Unable to save tag to music file."), _("Edit Tags"), 'editTagsError', gtk.BUTTONS_CLOSE, response_cb=ui.dialog_destroy) if self.tags_next_tag(tags):