Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/env python3
- from concurrent import futures
- from gi.repository import GObject# Possibly remove this, moving GObject calls to GObjectUI
- from gi.repository import Gdk, GdkPixbuf, Gtk, Pango
- from gi.repository.Gtk import main_quit
- from gi._glib import GError
- # import requests
- from genericui import GenericUI
- from lyrics import Lyrics
- '''find out under what conditions ending the gui process ends the shell-fm process.
- it may depend on when shell-fm launches. User should have easy option to close '''
- # class GTKUI(GObjectUI):
- class GTKUI(GenericUI):
- def __init__(self):
- # super().__init__(bus_name='org.mpris.MediaPlayer2.chipmunk.gtkui')
- super().__init__()
- self.builder = Gtk.Builder()
- self.builder.add_from_file('chipsperimental.glade')
- self.builder.connect_signals(self)
- # Why won't this work when set in glade instead?:
- self.builder.get_object('window_main').connect('delete-event', main_quit)
- self.builder.get_object('progressbar_track').override_font(Pango.FontDescription('mono'))
- self.load_credentials()
- self.load_last_station()
- # sweep some of these into more organized methods to declutter as groupings emerge
- try:
- self.activate('switch_quick_actions', self.config['quick actions'] == 'on')
- except KeyError:
- self.activate('switch_quick_actions')
- try:
- self.builder.get_object('image_album_core').set_pixel_size(int(self.config['small art size']))
- except KeyError:
- pass
- try:
- min_context_height = int(self.config['minimum context height'])
- except KeyError:
- min_context_height = 300
- self.builder.get_object('spinbutton_min_context_height').set_value(min_context_height)
- try:
- max_art_size = int(self.config['maximum art size'])
- except KeyError:
- max_art_size = 1080
- self.builder.get_object('spinbutton_max_art_size').set_value(max_art_size)
- self.info_monitor = GObject.timeout_add(900, self.update_track_info)
- self.progress_monitor = GObject.timeout_add(1100, self.update_track_progress)
- self.status_monitor = GObject.timeout_add(1000, self.update_status)
- self.mode_monitor = GObject.timeout_add(2000, self.update_modes)
- self.update_track_info()
- self.update_status()
- self.update_station_suggestions()
- # for widget in ['scrobble', 'settings', 'album_art']:
- for widget in ['scrobble', 'settings']:
- self.get_label('togglebutton_' + widget).set_visible(False)
- def on_spinbutton_min_context_height_value_changed(self, widget):
- value = widget.get_value_as_int()
- for context in ['lyrics', 'bio', 'playlist']:
- sw = self.builder.get_object('scrolledwindow_' + context)
- sw.set_min_content_height(value)
- self.config['minimum context height'] = value
- def on_spinbutton_max_art_size_value_changed(self, widget):
- value = widget.get_value_as_int()
- self.config['maximum art size'] = value
- def on_comboboxtextentry_station_changed(self, entry):
- if not entry.get_text() or self.service.player.status == 'disconnected':
- switch_possible = False
- else:
- switch_possible = self.user_text_to_lastfm_uri(entry.get_text()) != self.current_station_uri
- for widget in ['button_switch_station', 'togglebutton_switch_station_after_track']:
- self.builder.get_object(widget).set_visible(switch_possible)
- def on_comboboxtextentry_station_icon_press(self, entry, position, event):
- if position == Gtk.EntryIconPosition.PRIMARY:
- entry.set_text('')
- else:
- self.toggle_context('infobar_station_syntax')
- def on_comboboxtextentry_station_activate(self, widget):
- if self.builder.get_object('button_switch_station').get_visible():
- self.switch()
- def on_entry_lastfm_name_activate(self, widget):
- # self.builder.get_object('entry_lastfm_pass').grab_focus()
- self.activate('button_apply_lastfm_credentials')
- def on_entry_lastfm_pass_activate(self, widget):
- self.activate('button_apply_lastfm_credentials')
- def on_button_close_station_syntax_infobar_clicked(self, widget):
- self.toggle_context('infobar_station_syntax')
- # def on_button_hide_track_info_clicked(self, widget):
- # self.toggle_context('infobar_track')
- def on_button_copy_title_clicked(self, widget):
- self.on_button_copy_clicked('togglebutton_title')
- def on_button_copy_artist_clicked(self, widget):
- self.on_button_copy_clicked('togglebutton_artist')
- def on_button_copy_album_clicked(self, widget):
- self.on_button_copy_clicked('togglebutton_album')
- def on_button_copy_clicked(self, widget_name):
- widget = self.builder.get_object(widget_name)
- text = widget.get_label()
- widget.get_clipboard(Gdk.SELECTION_CLIPBOARD).set_text(text, -1)
- def on_button_switch_station_clicked(self, widget):
- # self.deactivate('togglebutton_switch_station_after_track')
- self.switch()
- def deactivate(self, widgets):
- self.activate(widgets, False)
- def activate(self, widgets, activate=True):
- if not isinstance(widgets, list):
- widgets = [widgets]
- for widget in widgets:
- try:
- self.builder.get_object(widget).set_active(activate)
- except AttributeError:
- self.builder.get_object(widget).activate()
- def on_togglebutton_bio_toggled(self, widget):
- self.toggle_context('scrolledwindow_bio', widget, ['togglebutton_lyrics', 'togglebutton_playlist'])
- def on_togglebutton_playlist_toggled(self, widget):
- self.toggle_context('scrolledwindow_playlist', widget, ['togglebutton_bio', 'togglebutton_lyrics'])
- def on_togglebutton_lyrics_toggled(self, widget):
- self.toggle_context('scrolledwindow_lyrics', widget, ['togglebutton_bio', 'togglebutton_playlist'])
- def toggle_context(self, display, control=None, conflicts=[]):
- '''Toggle widgets' visibility.
- display: a widget name or list of names.
- control: a widget object that can be active;
- if provided, it's state determines whether to show or hide display.
- conflicts: a list of control widgets (names) to deactivate when showing display.'''
- if isinstance(display, list):
- for disp in display:
- self.toggle_context(disp, control, conflicts)
- else:
- try:
- show = control.get_active()
- except AttributeError:
- show = not self.builder.get_object(display).get_visible()
- if show:
- self.deactivate(conflicts)
- self.builder.get_object(display).set_visible(show)
- if not show:
- self.shrink_window_height()
- def on_button_similar_clicked(self, widget):
- entry = self.builder.get_object('comboboxtext-entry_station')
- entry.set_text('similar: ' + self.current_artist_name)
- def on_switch_quick_actions_notify(self, widget, nothing_apparently):
- # print(type(nothing_apparently), ':', nothing_apparently)
- self.toggle_context('grid_quick_actions', widget)
- self.config['quick actions'] = {True: 'on', False: 'off'}[widget.get_active()]
- def get_lyrics(self):
- # Maybe make the timeout a configuration option. For now, 10.
- return Lyrics(10).fetch(self.current_artist_name, self.current_track_title)
- def task(self, func, on_done=None):
- # func should not have gtk code or shell-fm (self.service.player) communications
- executor = futures.ProcessPoolExecutor()
- future = executor.submit(func)
- if on_done:
- future.add_done_callback(on_done)
- def update_lyrics(self):
- self.task(self.get_lyrics, self.set_lyrics)
- def set_lyrics(self, data):
- data = self.defutured(data)
- lyrics, url = data['lyrics'], data['url']
- artist, title = data['artist'], data['title']
- textview = self.builder.get_object('textview_lyrics')
- source = self.builder.get_object('linkbutton_lyrics_source')
- search = self.builder.get_object('linkbutton_search_lyrics')
- try:
- textview.get_buffer().set_text(lyrics)
- source.set_uri(url)
- source.set_label(url)
- source.set_visible(True)
- search.set_visible(False)
- self.builder.get_object('togglebutton_lyrics').set_label('L_yrics*')
- except TypeError:
- textview.get_buffer().set_text('Sorry, lyrics not found.')
- source.set_visible(False)
- search.set_visible(True)
- search_uri = 'https://duckduckgo.com/?q=lyrics "{}" "{}"'
- search_uri = search_uri.format(artist, title).replace('&', '%26')
- search_uri = search_uri.replace('#', '%23')
- search.set_uri(search_uri)
- self.builder.get_object('togglebutton_lyrics').set_label('L_yrics')
- self.readjust('scrolledwindow_lyrics')
- def readjust(self, scrolledwindow):
- self.builder.get_object(scrolledwindow).get_vadjustment().set_value(0)
- self.builder.get_object(scrolledwindow).get_hadjustment().set_value(0)
- def get_artist_bio(self):
- return self.lastfm.get_artist_bio(self.current_artist_name)
- def set_artist_bio(self, text):
- text = self.defutured(text)
- textview = self.builder.get_object('textview_bio')
- if not text:
- text = 'Sorry, bio not found.'
- self.builder.get_object('togglebutton_bio').set_label('B_io')
- else:
- self.builder.get_object('togglebutton_bio').set_label('B_io*')
- textview.get_buffer().set_text(text)
- self.readjust('scrolledwindow_bio')
- def update_bio(self):
- self.task(self.get_artist_bio, self.set_artist_bio)
- def on_togglebutton_stop_after_track_toggled(self, widget):
- self.stop_after_track = widget.get_active()
- def on_togglebutton_switch_station_after_track_toggled(self, widget):
- self.switch_station_after_track = widget.get_active()
- @property
- def stop_after_track(self):
- return self.builder.get_object('togglebutton_stop_after_track').get_active()
- @stop_after_track.setter
- def stop_after_track(self, enable):
- if enable:
- self.stop_artist = self.current_artist_name
- self.stop_title = self.current_track_title
- self.stop_monitor = GObject.timeout_add(100, self.check_for_stop)
- else:
- GObject.source_remove(self.stop_monitor)
- @property
- def switch_station_after_track(self):
- return self.builder.get_object('togglebutton_switch_station_after_track').get_active()
- @switch_station_after_track.setter
- def switch_station_after_track(self, enable):
- if enable:
- self.switch_artist = self.current_artist_name
- self.switch_title = self.current_track_title
- self.switch_monitor = GObject.timeout_add(100, self.check_for_switch)
- else:
- GObject.source_remove(self.switch_monitor)
- def check_for_stop(self):
- if self.current_artist_name != self.stop_artist or self.current_track_title != self.stop_title:
- self.service.player.stop()
- self.deactivate('togglebutton_stop_after_track')
- return True
- def check_for_switch(self):
- if self.current_artist_name != self.switch_artist or self.current_track_title != self.switch_title:
- self.switch()
- self.deactivate('togglebutton_switch_station_after_track')
- return True
- def switch(self, attempt=1):
- self.deactivate('togglebutton_switch_station_after_track')
- text = self.builder.get_object('comboboxtext-entry_station').get_text()
- uri = self.user_text_to_lastfm_uri(text)
- status = self.service.player.status
- self.builder.get_object('infobar_check_credentials').set_visible(status == 'disconnected')
- if status == 'disconnected':
- if attempt == 3:
- self.activate('togglebutton_settings')
- self.builder.get_object('entry_lastfm_name').grab_focus()
- else:
- self.switch(attempt + 1)
- elif uri:
- self.service.player.play(uri)
- self.config['last station'] = text
- self.update_station_suggestions()
- else:
- self.builder.get_object('comboboxtext-entry_station').grab_focus()
- @property
- def current_station_uri(self):
- return self.service.player.current_station['uri']
- @current_station_uri.setter
- def current_station_uri(self, uri):
- entry = self.builder.get_object('comboboxtext-entry_station')
- entry.set_placeholder_text(self.lastfm_uri_to_user_text(uri))
- self.on_comboboxtextentry_station_changed(entry)
- def on_volumebutton_value_changed(self, widget, vol):
- self.service.player.volume = int(vol * 100)
- # @property
- # def recent_stations(self):
- # stations = self.get_recent_stations()
- # return stations
- def update_station_suggestions(self):
- self.update_combobox_station_suggestions()
- # self.update_quick_station_suggestions()
- # @property
- # def friends(self):
- # return self.get_friends()
- def get_friends(self):
- return self.service.get_friends(self.service.player.config['username'])
- def get_hidden_station_texts(self):
- texts = []
- for category in ['recommended: ', 'personal: ', 'mix: ', 'neighbors: ']:
- texts += [category + self.service.player.config['username']]
- for friend in self.get_friends():
- for category in ['personal: ', 'mix: ', '']:
- texts += [category + friend]
- return texts
- def update_combobox_station_suggestions(self):
- combobox = self.builder.get_object('comboboxtext_station')
- combobox.remove_all()
- self.task(self.get_recent_stations, self.append_combobox_station_suggestions)
- def append_combobox_station_suggestions(self, station_uris):
- station_uris = self.defutured(station_uris)
- if station_uris != None:
- if isinstance(station_uris, str):
- station_uris = [station_uris]
- combobox = self.builder.get_object('comboboxtext_station')
- for station in station_uris:
- text = self.lastfm_uri_to_user_text(station)
- combobox.append_text(text)
- self.task(self.get_hidden_station_texts, lambda x: self.add_completion(combobox, x))
- else:
- print('station_uris == None')
- # # ensure that user's personal and mix stations are there -- just by bookmarking? unsure...
- # # add similar to current artist station
- # # add some (3?) tag stations of current artist
- # # add fans of current artist station
- # # add bookmarked stations
- def add_completion(self, combobox, hidden=[]):
- hidden = self.defutured(hidden)
- model = Gtk.ListStore(str)
- for row in combobox.get_model():
- model.append([row[0]])
- if row[0].startswith('similar:'):
- for synonym in ['like:', 'artist:']:
- hidden += [row[0].replace('similar:', synonym, 1)]
- for extra in hidden:
- if extra.lower() not in [row[0].lower() for row in model]:
- model.append([extra])
- completion = Gtk.EntryCompletion()
- completion.set_model(model)
- completion.set_text_column(0)
- combobox.get_child().set_completion(completion)
- def get_recent_stations(self):
- # try:
- r = self.service.get_recent_stations(self.service.player.config['username'])
- if r == 'invalid session key':
- # except LookupError:
- self.request_lastfm_authorization()
- return None
- return r
- def on_togglebutton_play_toggled(self, widget):
- if not getattr(self, 'ignore_play_button', False):
- self.on_play_button_clicked()
- self.set_play_button(widget.get_active())
- def on_togglebutton_love_toggled(self, widget):
- if not getattr(self, 'ignore_love_button', False):
- self.service.player.love(widget.get_active())
- self.set_love_button(widget.get_active())
- def on_togglebutton_scrobble_toggled(self, widget):
- if not getattr(self, 'ignore_scrobble_button', False):
- self.service.player.toggle_scrobbling()
- self.set_scrobble_button(widget.get_active())
- def on_button_skip_clicked(self, widget):
- self.service.player.skip()
- def on_togglebutton_settings_toggled(self, widget):
- self.toggle_context('grid_settings', widget)
- def request_lastfm_authorization(self):
- self.on_button_authorize_lastfm_clicked(self.builder.get_object('button_authorize_lastfm'))
- self.activate('togglebutton_settings')
- def on_button_apply_lastfm_credentials_clicked(self, widget):
- name = self.builder.get_object('entry_lastfm_name').get_text()
- password = self.builder.get_object('entry_lastfm_pass').get_text()
- self.set_credentials(name, password)
- self.builder.get_object('comboboxtext-entry_station').set_text(self.config['default station'])
- self.request_lastfm_authorization()
- self.builder.get_object('infobar_check_credentials').set_visible(False)
- def on_button_authorize_lastfm_clicked(self, widget):
- self.service.request_authorization()
- widget.set_visible(False)
- self.builder.get_object('button_authorize_lastfm_completed').show()
- def on_button_authorize_lastfm_completed_clicked(self, widget):
- self.service.authenticate()
- widget.set_visible(False)
- self.deactivate('togglebutton_settings')
- # self.recent_stations = self.recent_stations
- self.update_station_suggestions()
- # any other values that require authorization should be set here as well
- def load_credentials(self):
- try:
- name = self.service.player.config['username']
- self.builder.get_object('entry_lastfm_name').set_text(name)
- except KeyError:
- pass
- def load_last_station(self):
- try:
- self.builder.get_object('comboboxtext-entry_station').set_text(self.config['last station'])
- except KeyError:
- try:
- self.builder.get_object('comboboxtext-entry_station').set_text(self.config['default station'])
- except KeyError:
- pass
- def on_button_stop_clicked(self, widget):
- self.service.player.stop()
- # def on_button_title_clicked(self, widget):
- # pass
- # def on_button_artist_clicked(self, widget):
- # pass
- # def on_button_album_clicked(self, widget):
- # pass
- def on_togglebutton_album_art_toggled(self, widget):
- show_art = widget.get_active()
- art = 'image_album_core'
- self.builder.get_object(art).set_visible(show_art)
- if not show_art:
- self.shrink_window()
- # def on_togglebutton_upcoming_toggled(self, widget):
- # show_upcoming = widget.get_active()
- # self.builder.get_object('as-yet-unmade-upcoming-widget').set_visible(show_upcoming)
- # if self.builder.get_object('togglebutton_album_art').get_active():
- # self.builder.get_object('image_album_core').set_visible(not show_upcoming)
- # self.builder.get_object('image_album_upcoming').set_visible(show_upcoming)
- # if not show_upcoming:
- # self.shrink_window()
- def shrink_window_height(self):
- widget = self.builder.get_object('window_main')
- width = widget.get_size()[0]
- widget.resize(width, 1)
- def shrink_window(self):
- self.builder.get_object('window_main').resize(1, 1)
- def update_large_art(self):
- self.task(self.download_large_art, self.set_large_art)
- def update_small_art(self):
- self.task(self.download_small_art, self.set_small_art)
- # image = self.builder.get_object('image_album_core')
- # try:
- # if image.get_pixel_size() != int(self.config['small art size']):
- # self.config['small art size'] = image.get_pixel_size()
- # except KeyError:
- # self.config['small art size'] = image.get_pixel_size()
- def set_small_art(self, filename):
- filename = self.defutured(filename)
- widget = self.builder.get_object('image_album_core')
- size = widget.get_pixel_size()
- try:
- if size != int(self.config['small art size']):
- self.config['small art size'] = size
- except KeyError:
- self.config['small art size'] = size
- if filename:
- try:
- pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(filename, size, size)
- except GError as e:
- print(e, '[small art]')
- return
- try:
- widget.set_from_pixbuf(pixbuf)
- except TypeError as e:
- print(e, '[small art]')
- widget.set_from_icon_name('media-optical', size)
- else:
- widget.set_from_icon_name('media-optical', size)
- def set_large_art(self, filename):
- filename = self.defutured(filename)
- widget = self.builder.get_object('image_large_art')
- if filename:
- try:
- pixbuf = GdkPixbuf.Pixbuf.new_from_file(filename)
- except GError as e:
- print(e, '[large art]')
- return
- max_size = int(self.config['maximum art size'])
- min_size = 350# maybe configurable, maybe not.
- if pixbuf.get_height() > max_size:
- pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(filename, max_size, max_size)
- elif pixbuf.get_height() < min_size:
- pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(filename, min_size, min_size)
- try:
- widget.set_from_pixbuf(pixbuf)
- except TypeError as e:
- print(e, '[large art]')
- widget.set_from_icon_name('media-optical', 350)
- else:
- widget.set_from_icon_name('media-optical', 350)
- def on_image_album_core_scroll_event(self, widget, event):
- image = self.builder.get_object('image_album_core')
- step = 1
- if event.direction == Gdk.ScrollDirection.DOWN:
- step = -step
- size = image.get_pixel_size() + step
- image.set_pixel_size(size)
- pixbuf = image.get_pixbuf()
- if pixbuf:
- self.set_small_art(self.config.sibling('small_art'))
- def download_small_art(self):
- url = self.service.get_small_album_art_url(self.current_album_name, self.current_artist_name)
- return self.download(url, 'small_art')
- def download_large_art(self):
- url = self.service.get_large_album_art_url(self.current_album_name, self.current_artist_name)
- return self.download(url, 'large_art')
- def update_track_progress(self):
- self.current_track_remaining_seconds = self.current_track_remaining_seconds
- return True
- def on_enter_show_label(self, widget, event):
- self.get_label(widget).set_visible(True)
- # I can probably merge these by reading event attributes to see if it is an entrance or an exit
- def on_leave_hide_label(self, widget, event):
- self.get_label(widget).set_visible(False)
- def on_progressbar_track_enter_notify_event(self, *args):
- widget = self.builder.get_object('progressbar_track')
- widget.set_show_text(True)
- def on_progressbar_track_leave_notify_event(self, *args):
- if not getattr(self, 'progressbar_text_on', False):
- widget = self.builder.get_object('progressbar_track')
- widget.set_show_text(False)
- def set_window_title(self, title='', artist=''):
- prefix = title + ' by ' * {True: 1, False: 0}[len(artist) > 0] + artist
- prefix += ' - ' * {True: 1, False: 0}[len(prefix) > 0]
- self.builder.get_object('window_main').set_title(prefix + 'Chipmunk')
- def update_modes(self):
- self.set_love_button(self.service.player.current_track_is_loved)
- self.set_scrobble_button(self.service.player.scrobbling)
- return True
- @property
- def status(self):
- return self.service.player.status
- @status.setter
- def status(self, text):
- self.set_play_button(text == 'playing')
- # self.set_scrobble_button(self.service.player.scrobbling)
- # self.set_love_button(self.service.player.current_track_is_loved)
- for widget in ['togglebutton_title', 'togglebutton_artist', 'togglebutton_album',
- 'button_stop', 'button_skip', 'togglebutton_stop_after_track',
- 'grid_title_actions', 'grid_artist_actions', 'grid_album_actions',
- 'button_copy_title', 'button_copy_artist', 'button_copy_album',
- 'togglebutton_love', 'togglebutton_quick_love',
- 'button_share_track', 'button_quick_share_track',
- 'button_ban', 'button_quick_ban',
- 'button_similar', 'button_quick_similar']:
- self.builder.get_object(widget).set_sensitive(text in ['playing', 'paused'])
- def set_play_button(self, is_playing=False):
- self.set_togglebutton('togglebutton_play', is_playing, 'ignore_play_button',
- {False: '_Play', True: '_Playing'},
- {False: 'icon_play', True: 'icon_pause'})
- def set_togglebutton(self, widget_names, set_active, ignore_str=None, texts={}, images={}):
- try:
- image = images[set_active]
- except KeyError:
- image = None
- try:
- text = texts[set_active]
- except KeyError:
- text = None
- if not isinstance(widget_names, list):
- widget_names = [widget_names]
- for widget_name in widget_names:
- widget = self.builder.get_object(widget_name)
- if text:
- if widget_name == 'togglebutton_play':
- # method 1:
- widget.set_label(text)
- # method 2:
- else:
- try:
- self.get_label(widget).set_markup_with_mnemonic(text)
- except AttributeError as e:
- print('get_label returned None', e, widget)
- # investigate: for some reason, the play button needs method 1 while
- # the scrobble button needs method 2
- if image:
- widget.set_image(self.builder.get_object(image))
- if ignore_str:
- setattr(self, ignore_str, True)
- widget.set_active(set_active)
- setattr(self, ignore_str, False)
- def get_label(self, container):
- if isinstance(container, str):
- widget = self.builder.get_object(container)
- else:
- widget = container
- if isinstance(widget, Gtk.Container):
- for child in widget.get_children():
- if isinstance(child, Gtk.Label):
- return child
- for child in widget.get_children():
- label = self.get_label(child)
- if label:
- return label
- return None
- def set_love_button(self, loves=False):
- self.set_togglebutton(['togglebutton_love', 'togglebutton_quick_love'], loves,
- 'ignore_love_button', {False: '_Love', True: '_Loved'})
- def set_scrobble_button(self, is_scrobbling=True):
- self.set_togglebutton('togglebutton_scrobble', is_scrobbling,
- # 'ignore_scrobble_button', {False: 'Scrobble', True: 'Scrobbling'})
- 'ignore_scrobble_button', {False: 'S_crobble', True: 'S_crobbling'})
- def on_button_ban_clicked(self, widget):
- self.on_ban_button_clicked()
- def on_grid_context_toggles_scroll_event(self, widget, event):
- toggles = ['lyrics', 'bio', 'playlist']
- if event.direction == Gdk.ScrollDirection.UP:
- toggles.reverse()
- toggle_next = False
- for toggle in toggles:
- if toggle_next:
- self.activate('togglebutton_' + toggle)
- break
- if self.builder.get_object('togglebutton_' + toggle).get_active():
- toggle_next = True
- def update_status(self):
- self.status = self.status
- return True
- def update_track_info(self):
- title, artist, album = self.current_track_title, self.current_artist_name, self.current_album_name
- self.current_track_title = title
- self.current_artist_name = artist
- self.current_album_name = album
- self.set_window_title(title, artist)
- if artist != getattr(self, 'last_artist', '') or title != getattr(self, 'last_title', ''):
- self.update_lyrics()
- self.current_station_uri = self.current_station_uri
- self.update_bio()
- self.update_small_art()#these are not backgrounded enough!
- self.update_large_art()#these are not backgrounded enough!
- self.update_wiki()
- self.update_tour_info()
- self.last_artist, self.last_title = artist, title
- return True
- def get_track_wiki(self):
- return self.service.get_track_wiki(self.current_track_title, self.current_artist_name)
- def update_wiki(self):
- self.task(self.get_track_wiki, self.set_infobar_track)
- # pass
- def get_tour_info(self):
- return self.service.get_upcoming_events(self.current_artist_name)
- def update_tour_info(self):
- self.task(self.get_tour_info, self.set_infobar_tour)
- def defutured(self, potential_future):
- if isinstance(potential_future, futures.Future):
- # try:
- return potential_future.result()
- # except TypeError:
- # print('defutured TypeError:', type(potential_future), potential_future)
- # return None
- return potential_future
- def set_infobar_track(self, text):
- text = self.defutured(text)
- if text:
- self.builder.get_object('label_track_info').set_text(text)
- self.builder.get_object('togglebutton_track_info').set_visible(True)
- self.readjust('scrolledwindow_track_info')
- # self.builder.get_object('scrolledwindow_track_info').get_vadjustment().set_value(0)
- # self.builder.get_object('scrolledwindow_track_info').get_hadjustment().set_value(0)
- else:
- self.deactivate('togglebutton_track_info')
- self.builder.get_object('togglebutton_track_info').set_visible(False)
- def set_infobar_tour(self, events):
- events = self.defutured(events)
- if events:
- event_strings = []
- for event in events:
- event_strings.append(str(TourEvent(event)))
- self.builder.get_object('label_tour_info').set_text('\n\n'.join(event_strings))
- # self.builder.get_object('label_tour_info').set_text('\n\n'.join(event_strings).strip())
- self.builder.get_object('togglebutton_on_tour').set_visible(True)
- self.readjust('scrolledwindow_tour_info')
- # self.builder.get_object('scrolledwindow_tour_info').get_vadjustment().set_value(0)
- # self.builder.get_object('scrolledwindow_tour_info').get_hadjustment().set_value(0)
- else:
- self.deactivate('togglebutton_on_tour')
- self.builder.get_object('togglebutton_on_tour').set_visible(False)
- # self.builder.get_object('scrolledwindow_tour_info').get_vadjustment().set_value(0)
- def on_togglebutton_track_info_toggled(self, widget):
- self.toggle_context('scrolledwindow_track_info', widget)
- def on_togglebutton_on_tour_toggled(self, widget):
- self.toggle_context('scrolledwindow_tour_info', widget)
- def on_progressbar_track_button_press_event(self, widget, event):
- self.progressbar_text_on = not getattr(self, 'progressbar_text_on', False)
- def on_image_album_core_button_press_event(self, widget, eventbutton=None):
- window = self.builder.get_object('window_large_art')
- if window.get_visible():
- window.hide()
- else:
- window.show()
- def on_image_large_art_button_press_event(self, widget, eventbutton=None):
- self.builder.get_object('window_large_art').hide()
- @property
- def current_track_remaining_seconds(self):
- return self.service.player.current_track_remaining_seconds
- @current_track_remaining_seconds.setter
- def current_track_remaining_seconds(self, seconds):
- bar = self.builder.get_object('progressbar_track')
- if seconds < 0:
- bar.pulse()
- else:
- try:
- bar.set_fraction(seconds/self.current_track_duration)
- except ZeroDivisionError:
- bar.set_fraction(0)
- bar.set_text(self.time_string(seconds))
- def time_string(self, seconds):
- hours, mins = divmod(seconds, 3600)
- hours = str(hours)
- mins, secs = [str(num) for num in divmod(mins, 60)]
- if len(secs) == 1:
- secs = '0' + secs
- if hours != '0':
- if len(mins) == 1:
- mins = '0' + mins
- text = '{}:{}:{}'.format(hours, mins, secs)
- elif mins != '0':
- text = '{}:{}'.format(mins, secs)
- else:
- text = ':' + secs
- return text
- @property
- def current_track_duration(self):
- return self.service.player.current_track_duration
- def on_togglebutton_title_toggled(self, widget):
- self.toggle_context(['grid_title_actions', 'button_copy_title'], widget, ['togglebutton_artist', 'togglebutton_album'])
- self.builder.get_object('grid_quick_title_actions').set_visible(not widget.get_active())
- def on_togglebutton_artist_toggled(self, widget):
- self.toggle_context(['grid_artist_actions', 'button_copy_artist'], widget, ['togglebutton_title', 'togglebutton_album'])
- self.builder.get_object('button_quick_similar').set_visible(not widget.get_active())
- def on_togglebutton_album_toggled(self, widget):
- self.toggle_context(['grid_album_actions', 'button_copy_album'], widget, ['togglebutton_artist', 'togglebutton_title'])
- def on_button_share_track_clicked(self, widget):
- pass
- @property
- def current_track_title(self):
- return self.service.player.current_track_title
- @current_track_title.setter
- def current_track_title(self, title):
- self.builder.get_object('togglebutton_title').set_label(title)
- @property
- def current_album_name(self):
- return self.service.player.current_album_name
- @current_album_name.setter
- def current_album_name(self, album):
- self.builder.get_object('togglebutton_album').set_label(album)
- @property
- def current_artist_name(self):
- return self.service.player.current_artist_name
- @current_artist_name.setter
- def current_artist_name(self, artist):
- self.builder.get_object('togglebutton_artist').set_label(artist)
- def on_window_main_key_press_event(self, widget, event):
- # this method of shortcut recognition may negatively impact responsiveness
- if event.keyval in [65507, 65508]:# ctrl
- self.ctrl_down = True
- elif event.keyval in [108, 76]:# 'l' or 'L'
- if getattr(self, 'ctrl_down', False):
- self.builder.get_object('comboboxtext-entry_station').grab_focus()
- def on_window_main_key_release_event(self, widget, event):
- if event.keyval == 65507:
- self.ctrl_down = False
- class TourEvent():
- def __init__(self, event_data):
- self.data = event_data
- def __str__(self):
- output = []
- output += [self.data['startDate']]
- output += [self.data['title']]
- artists = self.data['artists']['artist']
- if isinstance(artists, str):
- output += ['with ' + artists]
- else:
- output += ['with ' + ' and '.join([', '.join(artists[:-1]), artists[-1]])]
- output += [', '.join(['at ' + self.data['venue']['name'], 'in ' + self.data['venue']['location']['city'], self.data['venue']['location']['country']])]
- output += [self.data['venue']['website']]
- ouput = [line.strip() for line in output]
- # for prop in ['tickets', 'url', 'attendance', 'description', 'image']:
- # print(prop, ':', self.data[prop])
- return '\n'.join(output).strip()
- if __name__ == '__main__':
- GTKUI()
- Gtk.main()
Add Comment
Please, Sign In to add comment