Refactoring of the code creating the tracks and the fundations of thumbs up/down

This commit is contained in:
Valentijn 2018-03-10 19:40:50 +01:00
parent e233ce7e41
commit 16f75046d2
5 changed files with 65 additions and 124 deletions

View file

@ -26,6 +26,8 @@ hotkeys:
unappend: mod + u
request_station: mod + p
show_context_menu: meta + p
thumbs_up: meta + u
thumbs_down: meta + d
library_view:
move_to_beginning: home

View file

@ -16,6 +16,8 @@ from gmusicapi.clients import Mobileclient
from clay.eventhook import EventHook
from clay.log import logger
import sys
def asynchronous(func):
"""
Decorates a function to become asynchronous.
@ -86,27 +88,29 @@ class Track(object):
SOURCE_PLAYLIST = 'playlist'
SOURCE_SEARCH = 'search'
def __init__(
self,
title, artist, duration, source,
library_id=None, store_id=None, playlist_item_id=None,
album_name=None, album_url=None, original_data=None
):
self.title = title
self.artist = artist
self.duration = duration
self.source = source
def __init__(self, source, data):
# In playlist items and user uploaded songs the storeIds are missing so
self.store_id = (data['storeId'] if 'storeId' in data else data['id'])
self.playlist_item_id = (UUID(data['id']) if source == self.SOURCE_PLAYLIST else None)
self.library_id = (UUID(data['id']) if source == self.SOURCE_LIBRARY else None)
# To filter out the playlist items we need to reassign the store_id when fetching the track
if 'track' in data:
data = data['track']
self.store_id = data['storeId']
self.title = data['title']
self.artist = data['artist']
self.duration = int(data['durationMillis'])
self.rating = (data['rating'] if 'rating' in data else 0)
self.source = source
self.cached_url = None
self.library_id = library_id
self.store_id = store_id
self.playlist_item_id = playlist_item_id
# User uploaded songs miss a store_id
self.album_name = data['album']
self.album_url = (data['albumArtRef'][0]['url'] if 'albumArtRef' in data else "")
self.album_name = album_name
self.album_url = album_url
self.original_data = original_data
self.original_data = data
@property
def id(self): # pylint: disable=invalid-name
@ -131,100 +135,6 @@ class Track(object):
(self.playlist_item_id and self.playlist_item_id == other.playlist_item_id)
)
@classmethod
def _from_search(cls, data):
"""
Create track from search result data.
"""
# Data contains a nested track representation.
return Track(
title=data['track']['title'],
artist=data['track']['artist'],
duration=int(data['track']['durationMillis']),
source=cls.SOURCE_SEARCH,
store_id=data['track']['storeId'], # or data['trackId']
album_name=data['track']['album'],
album_url=data['track']['albumArtRef'][0]['url'],
original_data=data
)
@classmethod
def _from_station(cls, data):
"""
Create track from station track data.
"""
# Station tracks have all the info in place.
return Track(
title=data['title'],
artist=data['artist'],
duration=int(data['durationMillis']),
source=cls.SOURCE_STATION,
store_id=data['storeId'],
album_name=data['album'],
album_url=data['albumArtRef'][0]['url'],
original_data=data
)
@classmethod
def _from_library(cls, data):
"""
Create track from library track data.
"""
# Data contains all info about track
# including ID in library and ID in store.
UUID(data['id'])
return Track(
title=data['title'],
artist=data['artist'],
duration=int(data['durationMillis']),
source=cls.SOURCE_LIBRARY,
store_id=data['storeId'],
library_id=data['id'],
album_name=data['album'],
album_url=data['albumArtRef'][0]['url'],
original_data=data
)
@classmethod
def _from_playlist(cls, data):
"""
Create track from playlist track data.
"""
if 'track' in data:
# Data contains a nested track representation that can be used
# to construct new track.
return Track(
title=data['track']['title'],
artist=data['track']['artist'],
duration=int(data['track']['durationMillis']),
source=cls.SOURCE_PLAYLIST,
store_id=data['track']['storeId'], # or data['trackId']
playlist_item_id=data['id'],
album_name=data['track']['album'],
album_url=data['track']['albumArtRef'][0]['url'],
original_data=data
)
# We need to find a track in Library by trackId.
UUID(data['trackId'])
track = gp.get_track_by_id(data['trackId'])
return Track(
title=track.title,
artist=track.artist,
duration=track.duration,
source=cls.SOURCE_PLAYLIST,
store_id=track.store_id,
album_name=track.album_name,
album_url=track.album_url,
original_data=data
)
_CREATE_TRACK = {
SOURCE_SEARCH: '_from_search',
SOURCE_STATION: '_from_station',
SOURCE_LIBRARY: '_from_library',
SOURCE_PLAYLIST: '_from_playlist',
}
@classmethod
def from_data(cls, data, source, many=False):
"""
@ -232,15 +142,16 @@ class Track(object):
from Google Play Music API response.
"""
if many:
return [
track
for track
in [cls.from_data(one, source) for one in data]
if track is not None
]
return [track for track in
[cls.from_data(one, source) for one in data]
if track is not None]
try:
return getattr(cls, cls._CREATE_TRACK[source])(data)
if source == cls.SOURCE_PLAYLIST and 'track' not in data:
track = gp.get_track_by_id(UUID(data['trackId']))
else:
track = Track(source, data)
return track
except Exception as error: # pylint: disable=bare-except
logger.error(
'Failed to parse track data: %s, failing data: %s',
@ -642,6 +553,16 @@ class _GP(object):
self.invalidate_caches()
return result
def set_track_rating(self, id_, rating):
"""
Set the rating for song with the specified ID.
0 for no thumb, 1 for down thumb and 5 for up thumb
"""
song = self.mobile_client.get_track_info(id_)
song['rating'] = rating
self.mobileclient.change_song_metadata(song)
@property
def is_authenticated(self):
"""

View file

@ -124,6 +124,7 @@ class _HotkeyManager(object):
hotkey[0] = mod_key
hotkeys[hotkey_name][' '.join(hotkey)] = action
return hotkeys
def keypress(self, name, caller, super_, size, key):

View file

@ -143,6 +143,7 @@ def _dummy_log(data, level, ctx, fmt, args):
pass
#+pylint: disable=unused-argument
class _Player(object):
"""
Interface to libVLC. Uses Queue as a playback plan.
@ -161,11 +162,11 @@ class _Player(object):
def __init__(self):
self.instance = vlc.Instance()
print_func = CFUNCTYPE(c_void_p,
c_void_p, # data
c_int, # level
c_void_p, # context
c_char_p, # fmt
c_void_p) #args
c_void_p, # data
c_int, # level
c_void_p, # context
c_char_p, # fmt
c_void_p) # args
self.instance.log_set(print_func(_dummy_log), None)

View file

@ -107,6 +107,7 @@ class SongListItem(urwid.Pile):
"""
return SongListItem.STATE_ICONS[state]
def update_text(self):
"""
Update text of this item from the attached track.
@ -155,6 +156,21 @@ class SongListItem(urwid.Pile):
return None
return super(SongListItem, self).mouse_event(size, event, button, col, row, focus)
def thumbs_up(self):
"""
Toggle the thumbs up of this song.
"""
if self.track.rating == 5:
gp.set_track_rating(self.track.id, 0)
else:
gp.set_track_rating(self.track.id, 5)
def thumbs_down(self):
if self.track.rating == 1:
gp.set_track_rating(self.track.id, 0)
else:
gp.set_track_rating(self.track.id, 1)
def _send_signal(self, signal):
urwid.emit_signal(self, signal, self)