diff --git a/clay/app.py b/clay/app.py index d93f44a..9dbb15e 100755 --- a/clay/app.py +++ b/clay/app.py @@ -13,13 +13,13 @@ import urwid from clay.player import Player from clay.playbar import PlayBar -from clay.startup import StartUpPage from clay.mylibrary import MyLibraryPage from clay.myplaylists import MyPlaylistsPage from clay.playerqueue import QueuePage from clay.search import SearchPage from clay.settings import SettingsPage from clay.notifications import NotificationArea +from clay.gp import GP PALETTE = [ ('logo', '', '', '', '#F54', ''), @@ -94,7 +94,6 @@ class AppWidget(urwid.Frame): def __init__(self): self.pages = [ - StartUpPage(self), MyLibraryPage(self), MyPlaylistsPage(self), QueuePage(self), @@ -105,9 +104,12 @@ class AppWidget(urwid.Frame): AppWidget.Tab(page) for page in self.pages - if page.key is not None ] + self.current_page = None + NotificationArea.set_app(self) + self._login_notification = None + self.header = urwid.Pile([ # urwid.Divider('\u2500'), urwid.AttrWrap(urwid.Columns([ @@ -135,13 +137,13 @@ class AppWidget(urwid.Frame): ]), self.playbar ]) - self.current_page = self.pages[0] + # self.current_page = self.pages[0] super(AppWidget, self).__init__( header=self.header, footer=self.panel, - body=self.current_page + body=urwid.Filler(urwid.Text('Loading...', align='center')) ) - self.current_page.activate() + # self.current_page.activate() self.loop = None @@ -151,6 +153,46 @@ class AppWidget(urwid.Frame): player.track_changed += self.track_changed player.playback_flags_changed += self.playback_flags_changed + self.set_page('MyLibraryPage') + self.log_in() + + def on_login(self, success, error): + """ + Called once user authorization finishes. + If *error* is ``None``, switch app to "My library" page.' + """ + if error: + self._login_notification.update('Failed to log in: {}'.format(str(error))) + return + + if not success: + self._login_notification.update( + 'Google Play Music login failed (API returned false)' + ) + return + + self._login_notification.close() + + def log_in(self): + """ + Called when this page is shown. + + Request user authorization. + """ + if SettingsPage.is_config_valid(): + config = SettingsPage.get_config() + self._login_notification = NotificationArea.notify('Logging in...') + GP.get().login_async( + config['username'], + config['password'], + config['device_id'], + callback=self.on_login + ) + else: + NotificationArea.notify( + 'Please set your credentials on the settings page.' + ) + def set_loop(self, loop): """ Assign a MainLoop to this app. @@ -226,7 +268,7 @@ class AppWidget(urwid.Frame): def keypress(self, size, key): """ Handle keypress. - Can switch tabs, control playbackm, flags, notifications and app state. + Can switch tabs, control playback, flags, notifications and app state. """ for tab in self.tabs: if 'meta {}'.format(tab.page.key) == key: diff --git a/clay/mylibrary.py b/clay/mylibrary.py index 7f64839..3cc349b 100644 --- a/clay/mylibrary.py +++ b/clay/mylibrary.py @@ -55,6 +55,7 @@ class MyLibraryPage(urwid.Columns, Page): self.songlist.set_placeholder(u'\n \uf01e Loading song list...') GP.get().get_all_tracks_async(callback=self.on_get_all_songs) + self.app.redraw() # self.notification = NotificationArea.notify('Loading library...') def activate(self): diff --git a/clay/notifications.py b/clay/notifications.py index f356bbf..0f013a4 100644 --- a/clay/notifications.py +++ b/clay/notifications.py @@ -16,7 +16,7 @@ class Notification(urwid.Columns): self.area = area self._id = notification_id self.text = urwid.Text('') - self.update(message) + self._set_text(message) super(Notification, self).__init__([ urwid.AttrWrap( urwid.Columns([ @@ -34,17 +34,35 @@ class Notification(urwid.Columns): """ return self._id - def update(self, message): + def _set_text(self, message): """ - Update notification message. + Set contents for this notification. """ message = message.split('\n') message = '\n'.join([ message[0] ] + [' {}'.format(line) for line in message[1:]]) self.text.set_text(Notification.TEMPLATE.format(message)) + + def update(self, message): + """ + Update notification message. + """ + self._set_text(message) + if not self.is_alive: + self.area.append_notification(self) self.area.__class__.app.redraw() + @property + def is_alive(self): + """ + Return True if notification is currently visible. + """ + for notification, _ in self.area.contents: + if notification is self: + return True + return False + def close(self): """ Close notification. @@ -53,6 +71,9 @@ class Notification(urwid.Columns): if notification is self: self.area.contents.remove((notification, props)) + if self.area.__class__.app is not None: + self.area.__class__.app.redraw() + class NotificationArea(urwid.Pile): """ @@ -117,6 +138,13 @@ class NotificationArea(urwid.Pile): """ self.last_id += 1 notification = Notification(self, self.last_id, message) + self.append_notification(notification) + return notification + + def append_notification(self, notification): + """ + Append an existing notification (that was probably closed). + """ self.contents.append( ( notification, @@ -125,7 +153,6 @@ class NotificationArea(urwid.Pile): ) if self.__class__.app is not None: self.__class__.app.redraw() - return notification def do_close_all(self): """ diff --git a/clay/search.py b/clay/search.py index d79c4e3..729d0e5 100644 --- a/clay/search.py +++ b/clay/search.py @@ -75,6 +75,9 @@ class SearchPage(urwid.Columns, Page): """ Search tracks by query. """ + self.songlist.set_placeholder(u' \U0001F50D Searching for "{}"...'.format( + query + )) GP.get().search_async(query, callback=self.search_finished) def search_finished(self, results, error): @@ -86,6 +89,7 @@ class SearchPage(urwid.Columns, Page): return self.songlist.populate(results.get_tracks()) + self.app.redraw() def activate(self): pass diff --git a/clay/songlist.py b/clay/songlist.py index 9e055de..702969b 100644 --- a/clay/songlist.py +++ b/clay/songlist.py @@ -272,6 +272,8 @@ class SongListBox(urwid.ListBox): self.update_indexes() if current_index is not None: self.walker.set_focus(current_index) + elif len(self.walker) >= 1: + self.walker.set_focus(0) def append_track(self, track): """ diff --git a/clay/startup.py b/clay/startup.py deleted file mode 100644 index eaf3e98..0000000 --- a/clay/startup.py +++ /dev/null @@ -1,81 +0,0 @@ -""" -Initial startup page. -""" -# pylint: disable=too-many-ancestors -import urwid - -from clay.page import Page -from clay.settings import SettingsPage -from clay.gp import GP -from clay.meta import VERSION -from clay.notifications import NotificationArea - - -class StartUpPage(urwid.Filler, Page): - """ - Initial page. - - Shown when app is started or login credentials are changed. - """ - def __init__(self, app): - self.app = app - - super(StartUpPage, self).__init__( - urwid.Pile([ - urwid.Padding( - urwid.AttrWrap(urwid.BigText( - 'Clay', - urwid.font.HalfBlock5x4Font() - ), 'logo'), - 'center', - None - ), - urwid.AttrWrap(urwid.Text('Version {}'.format(VERSION), align='center'), 'line1'), - urwid.AttrWrap(urwid.Text('Authorizing...', align='center'), 'line2') - ]) - ) - - @property - def name(self): - pass - - @property - def key(self): - pass - - def on_login(self, success, error): - """ - Called once user authorization finishes. - If *error* is ``None``, switch app to "My library" page.' - """ - if error: - NotificationArea.notify('Failed to log in: {}'.format(str(error))) - return - - if not success: - NotificationArea.notify( - 'Google Play Music login failed ' - '(API returned false)' - ) - return - - self.app.set_page('MyLibraryPage') - - def activate(self): - """ - Called when this page is shown. - - Request user authorization. - """ - if SettingsPage.is_config_valid(): - config = SettingsPage.get_config() - GP.get().login_async( - config['username'], - config['password'], - config['device_id'], - callback=self.on_login - ) - else: - NotificationArea.notify( - 'Please set your credentials on the settings page.' - ) diff --git a/docs/source/ref/startup.rst b/docs/source/ref/startup.rst deleted file mode 100644 index 2aadb0c..0000000 --- a/docs/source/ref/startup.rst +++ /dev/null @@ -1,8 +0,0 @@ -startup.py -########## - -.. automodule:: clay.startup - :members: - :private-members: - :special-members: -