Пример #1
0
    def _get_view_artists(self, genre_ids, artist_ids):
        """
            Get artists view for genres/artists
            @param genre ids as [int]
            @param artist ids as [int]
        """
        def load():
            if genre_ids and genre_ids[0] == Type.ALL:
                items = App().albums.get_ids(artist_ids, [])
            else:
                items = []
                if artist_ids and artist_ids[0] == Type.COMPILATIONS:
                    items += App().albums.get_compilation_ids(genre_ids)
                items += App().albums.get_ids(artist_ids, genre_ids)
            return [
                Album(album_id, genre_ids, artist_ids) for album_id in items
            ]

        if App().window.is_adaptive:
            from lollypop.view_albums_list import AlbumsListView
            view = AlbumsListView(RowListType.DEFAULT, artist_ids, genre_ids)
        else:
            from lollypop.view_artist import ArtistView
            view = ArtistView(artist_ids, genre_ids)
        loader = Loader(target=load, view=view)
        loader.start()
        view.show()
        return view
Пример #2
0
    def __init__(self):
        """
            Init Popover
        """
        BaseView.__init__(self)
        Gtk.Bin.__init__(self)
        self.connect("map", self.__on_map)
        self.connect("unmap", self.__on_unmap)
        self.__timeout_id = None
        self.__current_search = ""
        self.__cancellable = Gio.Cancellable()
        self.__history = []

        builder = Gtk.Builder()
        builder.add_from_resource("/org/gnome/Lollypop/SearchView.ui")
        builder.connect_signals(self)
        self.__widget = builder.get_object("widget")
        self.__new_button = builder.get_object("new_button")
        self.__play_button = builder.get_object("play_button")
        self.__entry = builder.get_object("entry")
        self.__spinner = builder.get_object("spinner")
        self.__header_stack = builder.get_object("header_stack")
        self.__stack = builder.get_object("stack")
        self.__placeholder = builder.get_object("placeholder")
        self.__view = AlbumsListView(RowListType.SEARCH)
        self.__view.show()
        self.__stack.add_named(self.__view, "view")
        self.__set_default_placeholder()
        self.add(self.__widget)
Пример #3
0
 def __init__(self, playlist_id, view_type):
     """
         Init PlaylistView
         @parma playlist_id as int
         @param view_type as ViewType
     """
     AlbumsListView.__init__(self, [], [], view_type |
                             ViewType.SCROLLED |
                             ViewType.OVERLAY)
     SizeAllocationHelper.__init__(self)
     self.__playlist_id = playlist_id
     self.box.set_width(Size.MEDIUM)
     if view_type & ViewType.DND:
         self.dnd_helper.connect("dnd-finished",
                                 self.__on_dnd_finished)
     self.__banner = PlaylistBannerWidget(playlist_id, self)
     self.__banner.show()
     self.add_widget(self.box, self.__banner)
     return [
             (App().playlists, "playlist-track-added",
              "_on_playlist_track_added"),
             (App().playlists, "playlist-track-removed",
              "_on_playlist_track_removed"),
             (App().playlists, "playlists-removed", "_on_playlist_removed"),
             (App().playlists, "playlists-renamed", "_on_playlist_renamed"),
             (App().playlists, "playlists-updated", "_on_playlist_updated")
     ]
Пример #4
0
 def __init__(self, view_type):
     """
         Init view
         @param view_type as ViewType
     """
     AlbumsListView.__init__(self, [], [],
                             view_type |
                             ViewType.SCROLLED |
                             ViewType.OVERLAY |
                             ViewType.PLAYBACK)
     self.box.set_width(Size.MEDIUM)
     if view_type & ViewType.DND:
         self.dnd_helper.connect("dnd-finished", self.__on_dnd_finished)
     self.__banner = CurrentAlbumsBannerWidget(self)
     self.__banner.show()
     self.add_widget(self.box, self.__banner)
     self.allow_duplicate("_on_queue_changed")
     self.allow_duplicate("_on_playback_added")
     self.allow_duplicate("_on_playback_updated")
     self.allow_duplicate("_on_playback_removed")
     return [
         (App().player, "queue-changed", "_on_queue_changed"),
         (App().player, "playback-added", "_on_playback_added"),
         (App().player, "playback-setted", "_on_playback_setted"),
         (App().player, "playback-updated", "_on_playback_updated"),
         (App().player, "playback-removed", "_on_playback_removed")
     ]
Пример #5
0
 def clear(self):
     """
         Clear the view
     """
     if not App().player.radio_cancellable.is_cancelled():
         App().player.radio_cancellable.cancel()
         GLib.timeout_add(500, self.clear)
     else:
         AlbumsListView.clear(self)
         App().player.clear_albums()
 def populate(self, artist_id=None):
     """
         Show information for artists
         @param artist_id as int
     """
     builder = Gtk.Builder()
     builder.add_from_resource("/org/gnome/Lollypop/ArtistInformation.ui")
     builder.connect_signals(self)
     self.__scrolled = builder.get_object("scrolled")
     widget = builder.get_object("widget")
     self.add(widget)
     self.__stack = builder.get_object("stack")
     self.__artist_label = builder.get_object("artist_label")
     title_label = builder.get_object("title_label")
     self.__artist_artwork = builder.get_object("artist_artwork")
     eventbox = builder.get_object("eventbox")
     eventbox.connect("button-release-event",
                      self.__on_label_button_release_event)
     self.__bio_label = builder.get_object("bio_label")
     if artist_id is None and App().player.current_track.id is not None:
         builder.get_object("header").show()
         builder.get_object("lyrics_button").show()
         builder.get_object("lyrics_button").connect(
             "clicked", self.__on_lyrics_button_clicked,
             App().player.current_track)
         artist_id = App().player.current_track.artist_ids[0]
         title_label.set_text(App().player.current_track.title)
     self.__artist_name = App().artists.get_name(artist_id)
     if self.__minimal:
         self.__artist_artwork.hide()
     else:
         builder.get_object("header").show()
         self.__artist_label.set_text(self.__artist_name)
         self.__artist_label.show()
         title_label.show()
         App().art_helper.set_artist_artwork(
             self.__artist_name, ArtSize.ARTIST_SMALL * 3,
             ArtSize.ARTIST_SMALL * 3,
             self.__artist_artwork.get_scale_factor(),
             self.__on_artist_artwork, ArtHelperEffect.ROUNDED)
         albums_view = AlbumsListView(RowListType.READ_ONLY)
         albums_view.set_size_request(300, -1)
         albums_view.show()
         albums_view.set_margin_start(5)
         widget.attach(albums_view, 2, 1, 1, 2)
         albums = []
         for album_id in App().albums.get_ids([artist_id], []):
             albums.append(Album(album_id))
         albums_view.populate(albums)
     App().task_helper.run(InformationStore.get_bio,
                           self.__artist_name,
                           callback=(self.__on_get_bio, ))
Пример #7
0
    def _get_view_albums(self, genre_ids, artist_ids):
        """
            Get albums view for genres/artists
            @param genre ids as [int]
            @param is compilation as bool
        """
        def load():
            items = []
            is_compilation = artist_ids and artist_ids[0] == Type.COMPILATIONS
            if genre_ids and genre_ids[0] == Type.ALL:
                if is_compilation or\
                        App().settings.get_value(
                            "show-compilations-in-album-view"):
                    items = App().albums.get_compilation_ids([])
                if not is_compilation:
                    items += App().albums.get_ids([], [])
            elif genre_ids and genre_ids[0] == Type.POPULARS:
                items = App().albums.get_rated()
                count = 100 - len(items)
                for album in App().albums.get_populars(count):
                    if album not in items:
                        items.append(album)
            elif genre_ids and genre_ids[0] == Type.LOVED:
                items = App().albums.get_loved_albums()
            elif genre_ids and genre_ids[0] == Type.RECENTS:
                items = App().albums.get_recents()
            elif genre_ids and genre_ids[0] == Type.NEVER:
                items = App().albums.get_never_listened_to()
            elif genre_ids and genre_ids[0] == Type.RANDOMS:
                items = App().albums.get_randoms()
            else:
                if is_compilation or\
                        App().settings.get_value(
                            "show-compilations-in-album-view"):
                    items = App().albums.get_compilation_ids(genre_ids)
                if not is_compilation:
                    items += App().albums.get_ids([], genre_ids)
            return [
                Album(album_id, genre_ids, artist_ids) for album_id in items
            ]

        if App().window.is_adaptive:
            from lollypop.view_albums_list import AlbumsListView
            view = AlbumsListView(RowListType.DEFAULT, genre_ids, artist_ids)
        else:
            from lollypop.view_albums_box import AlbumsBoxView
            view = AlbumsBoxView(genre_ids, artist_ids)
        loader = Loader(target=load, view=view)
        loader.start()
        view.show()
        return view
Пример #8
0
 def __init__(self, view_type):
     """
         Init view
         @param view_type as ViewType
     """
     AlbumsListView.__init__(self, [], [], view_type)
     self.__clear_button = Gtk.Button.new_from_icon_name(
         "edit-clear-all-symbolic", Gtk.IconSize.MENU)
     self.__clear_button.set_relief(Gtk.ReliefStyle.NONE)
     self.__clear_button.set_tooltip_text(_("Clear albums"))
     self.__clear_button.get_style_context().add_class("light-button")
     self.__clear_button.get_style_context().add_class(
         "light-button-no-right-border")
     self.__clear_button.set_sensitive(App().player.albums)
     self.__clear_button.connect("clicked", self.__on_clear_clicked)
     self.__clear_button.set_margin_end(MARGIN_SMALL)
     self.__save_button = Gtk.Button.new_from_icon_name(
         "document-new-symbolic", Gtk.IconSize.MENU)
     self.__save_button.set_relief(Gtk.ReliefStyle.NONE)
     self.__save_button.set_tooltip_text(_("Create a new playlist"))
     self.__save_button.get_style_context().add_class("light-button")
     self.__save_button.set_sensitive(App().player.albums)
     self.__save_button.connect("clicked", self.__on_save_clicked)
     self.__jump_button = Gtk.Button.new_from_icon_name(
         "go-jump-symbolic", Gtk.IconSize.MENU)
     self.__jump_button.set_relief(Gtk.ReliefStyle.NONE)
     self.__jump_button.connect("clicked", self.__on_jump_clicked)
     self.__jump_button.get_style_context().add_class("light-button")
     self.__jump_button.set_tooltip_text(_("Go to current track"))
     self.__jump_button.set_sensitive(App().player.albums)
     label = Gtk.Label.new("<b>" + _("Playing albums") + "</b>")
     label.set_use_markup(True)
     label.set_hexpand(True)
     label.set_margin_start(2)
     label.get_style_context().add_class("dim-label")
     label.set_property("halign", Gtk.Align.START)
     grid = Gtk.Grid()
     grid.set_column_spacing(5)
     grid.add(label)
     grid.set_property("valign", Gtk.Align.CENTER)
     if view_type & ViewType.POPOVER:
         grid.add(self.__jump_button)
         grid.add(self.__save_button)
         grid.add(self.__clear_button)
     grid.show_all()
     grid.set_margin_bottom(MARGIN_SMALL)
     self.set_row_spacing(2)
     self.insert_row(0)
     self.attach(grid, 0, 0, 1, 1)
Пример #9
0
 def populate(self):
     """
         Populate view
     """
     if App().player.queue:
         tracks = [Track(track_id) for track_id in App().player.queue]
         albums = tracks_to_albums(tracks)
     else:
         albums = App().player.albums
     if albums:
         if len(albums) == 1:
             self.add_reveal_albums(albums)
         AlbumsListView.populate(self, albums)
         self.show_placeholder(False)
     else:
         self.show_placeholder(True)
Пример #10
0
    def __init__(self):
        """
            Init Popover
        """
        BaseView.__init__(self)
        Gtk.Bin.__init__(self)
        self.connect("map", self.__on_map)
        self.connect("unmap", self.__on_unmap)
        self.__timeout_id = None
        self.__new_album_signal_id = None
        self.__search_finished_signal_id = None
        self.__current_search = ""
        self.__cancellable = Gio.Cancellable()
        self.__history = []

        self.__search_type_action = Gio.SimpleAction.new_stateful(
            "search_type", GLib.VariantType.new("s"),
            GLib.Variant("s", "local"))
        self.__search_type_action.connect("change-state",
                                          self.__on_search_action_change_state)
        App().add_action(self.__search_type_action)
        builder = Gtk.Builder()
        builder.add_from_resource("/org/gnome/Lollypop/SearchView.ui")
        self.__widget = builder.get_object("widget")
        self.__new_button = builder.get_object("new_button")
        self.__play_button = builder.get_object("play_button")
        self.__bottom_buttons = builder.get_object("bottom_buttons")
        self.__entry = builder.get_object("entry")
        self.__spinner = builder.get_object("spinner")
        self.__header_stack = builder.get_object("header_stack")
        self.__combo_locale = builder.get_object("combo_locale")
        self.__combo_locale.set_active_id(
            App().settings.get_value("spotify-charts-locale").get_string())
        self.__button_stack = builder.get_object("button_stack")
        self.__stack = builder.get_object("stack")
        self.__placeholder = builder.get_object("placeholder")
        self.__view = AlbumsListView([], [], ViewType.SEARCH)
        self.__view.set_margin_start(MARGIN_SMALL)
        self.__view.show()
        self.__stack.add_named(self.__view, "view")
        self.__set_default_placeholder()
        self.add(self.__widget)
        builder.connect_signals(self)
Пример #11
0
    def _get_view_albums_years(self, years):
        """
            Get album view for years
            @param years as [int]
        """
        def load():
            items = []
            for year in years:
                items += App().albums.get_compilations_for_year(year)
                items += App().albums.get_albums_for_year(year)
            return [Album(album_id, [Type.YEARS], []) for album_id in items]

        if App().window.is_adaptive:
            from lollypop.view_albums_list import AlbumsListView
            view = AlbumsListView(RowListType.DEFAULT, years, [Type.YEARS])
        else:
            from lollypop.view_albums_box import AlbumsBoxView
            view = AlbumsBoxView([Type.YEARS], years)
        loader = Loader(target=load, view=view)
        loader.start()
        view.show()
        return view
Пример #12
0
 def _on_queue_changed(self, *ignore):
     """
         Clean view and reload if empty
     """
     queue = App().player.queue
     if queue:
         for row in self.children:
             if row.revealed:
                 for subrow in row.children:
                     if subrow.track.id not in queue:
                         subrow.destroy()
                         break
             count = len(row.album.tracks)
             for track in row.album.tracks:
                 if track.id not in queue:
                     row.album.remove_track(track)
                     if count == 1:
                         row.destroy()
                     break
     else:
         self.stop()
         AlbumsListView.clear(self)
         self.populate()
Пример #13
0
 def _on_playback_setted(self, player, albums):
     """
         Add album
         @param player as Player
         @param albums as [Album]
     """
     if albums:
         self.stop()
         AlbumsListView.clear(self)
         AlbumsListView.populate(self, albums)
         self.show_placeholder(False)
     else:
         self.stop()
         AlbumsListView.clear(self)
         self.show_placeholder(True)
Пример #14
0
class SearchView(BaseView, Gtk.Bin):
    """
        View for searching albums/tracks
    """
    def __init__(self):
        """
            Init Popover
        """
        BaseView.__init__(self)
        Gtk.Bin.__init__(self)
        self.connect("map", self.__on_map)
        self.connect("unmap", self.__on_unmap)
        self.__timeout_id = None
        self.__current_search = ""
        self.__cancellable = Gio.Cancellable()
        self.__history = []

        builder = Gtk.Builder()
        builder.add_from_resource("/org/gnome/Lollypop/SearchView.ui")
        builder.connect_signals(self)
        self.__widget = builder.get_object("widget")
        self.__new_button = builder.get_object("new_button")
        self.__play_button = builder.get_object("play_button")
        self.__entry = builder.get_object("entry")
        self.__spinner = builder.get_object("spinner")
        self.__header_stack = builder.get_object("header_stack")
        self.__stack = builder.get_object("stack")
        self.__placeholder = builder.get_object("placeholder")
        self.__view = AlbumsListView(RowListType.SEARCH)
        self.__view.show()
        self.__stack.add_named(self.__view, "view")
        self.__set_default_placeholder()
        self.add(self.__widget)

    def set_text(self, text):
        """
            Set search text
        """
        self.__entry.set_text(text)

#######################
# PROTECTED           #
#######################

    def _on_play_button_clicked(self, button):
        """
            Play search
            @param button as Gtk.Button
        """
        try:
            App().player.clear_albums()
            shuffle_setting = App().settings.get_enum("shuffle")
            children = self.__view.children
            if shuffle_setting == Shuffle.ALBUMS:
                shuffle(children)
            for child in children:
                App().player.add_album(child.album)
            App().player.load(App().player.albums[0].tracks[0])
        except Exception as e:
            Logger.error("SearchPopover::_on_play_button_clicked(): %s", e)

    def _on_new_button_clicked(self, button):
        """
            Create a new playlist based on search
            @param button as Gtk.Button
        """
        button.set_sensitive(False)
        App().task_helper.run(self.__search_to_playlist)

    def _on_search_changed(self, widget):
        """
            Timeout filtering
            @param widget as Gtk.TextEntry
        """
        if self.__timeout_id:
            GLib.source_remove(self.__timeout_id)
            self.__timeout_id = None
        self.__cancellable.cancel()
        self.__view.stop()
        self.__current_search = widget.get_text().strip()
        self.__timeout_id = GLib.timeout_add(500,
                                             self.__on_search_changed_timeout)

#######################
# PRIVATE             #
#######################

    def __set_no_result_placeholder(self):
        """
            Set placeholder for no result
        """
        self.__placeholder.set_markup(
            _("<big>No results for this search</big>"))

    def __set_default_placeholder(self):
        """
            Set placeholder for no result
        """
        self.__placeholder.set_markup(
            _("<big>Search for artists, albums and tracks</big>"))

    def __populate(self):
        """
            Populate searching items
            in db based on text entry current text
        """
        self.__cancellable.reset()
        self.__header_stack.set_visible_child(self.__spinner)
        self.__spinner.start()
        self.__history = []
        if self.__current_search:
            search = Search()
            search.get(self.__current_search,
                       self.__cancellable,
                       callback=(self.__on_search_get, ))
        else:
            self.__stack.set_visible_child_name("placeholder")
            self.__set_default_placeholder()
            self.__header_stack.set_visible_child(self.__new_button)
            GLib.idle_add(self.__spinner.stop)

    def __search_to_playlist(self):
        """
            Create a new playlist based on search
        """
        tracks = []
        for child in self.__view.children:
            tracks += child.album.tracks
        if tracks:
            playlist_id = App().playlists.get_id(self.__current_search)
            if playlist_id == Type.NONE:
                App().playlists.add(self.__current_search)
                playlist_id = App().playlists.get_id(self.__current_search)
            App().playlists.add_tracks(playlist_id, tracks)

    def __on_search_get(self, result):
        """
            Add rows for internal results
            @param result as [(int, Album, bool)]
        """
        if result:
            albums = []
            reveal_albums = []
            for (score, album, in_tracks) in result:
                albums.append(album)
                if in_tracks:
                    reveal_albums.append(album.id)
            self.__view.set_reveal(reveal_albums)
            self.__view.populate(albums)
            self.__stack.set_visible_child_name("view")
        else:
            self.__stack.set_visible_child_name("placeholder")
            self.__set_no_result_placeholder()
        self.__header_stack.set_visible_child(self.__new_button)
        GLib.idle_add(self.__spinner.stop)

    def __on_map(self, widget):
        """
            Grab focus
            @param widget as Gtk.Widget
        """
        GLib.idle_add(self.__entry.grab_focus)

    def __on_unmap(self, widget):
        """
            Stop loading
            @param widget as Gtk.Widget
        """
        self.__cancellable.cancel()
        self.__view.stop()
        self.__header_stack.set_visible_child(self.__new_button)
        self.__spinner.stop()

    def __on_search_changed_timeout(self):
        """
            Populate widget
        """
        if self.__view.children:
            self.__view.stop()
            self.__view.clear()
            return True
        self.__timeout_id = None
        self.__populate()
        if self.__current_search != "":
            self.__play_button.set_sensitive(True)
            self.__new_button.set_sensitive(True)
        else:
            self.__play_button.set_sensitive(False)
            self.__new_button.set_sensitive(False)
Пример #15
0
 def on_load(albums):
     self.banner.spinner.stop()
     self.add_reveal_albums(albums)
     AlbumsListView.populate(self, albums)
Пример #16
0
class FullScreen(Gtk.Window, InformationController, PlaybackController,
                 ProgressController):
    """
        Show a fullscreen window showing current track context
    """
    def __init__(self, app):
        """
            Init window for app
            @param app as Gio.Application
        """
        Gtk.Window.__init__(self)
        self.set_title("Lollypop")
        rotate_album = App().settings.get_value("rotate-fullscreen-album")
        PlaybackController.__init__(self)
        ProgressController.__init__(self)
        if rotate_album:
            InformationController.__init__(self, False,
                                           ArtHelperEffect.ROUNDED)
        else:
            InformationController.__init__(self, False, ArtHelperEffect.NONE)
        self.set_application(app)
        self.__timeout_id = None
        self.__signal1_id = self.__signal2_id = self.__signal3_id = None
        self.set_decorated(False)

        builder = Gtk.Builder()
        builder.add_from_resource("/org/gnome/Lollypop/FullScreen.ui")
        builder.connect_signals(self)

        # Calculate cover size
        screen = Gdk.Screen.get_default()
        monitor = screen.get_monitor_at_window(App().window.get_window())
        geometry = screen.get_monitor_geometry(monitor)
        # We want 500 and 200 in full hd
        if geometry.width > geometry.height:
            self.__artsize = int(ArtSize.FULLSCREEN * geometry.height / 1080)
        else:
            self.__artsize = int(ArtSize.FULLSCREEN * geometry.width / 1920)
        self.__font_size = int(14 * geometry.height / 1080)
        widget = builder.get_object("widget")
        grid = builder.get_object("grid")
        self._play_button = builder.get_object("play_btn")
        self._next_button = builder.get_object("next_btn")
        self._prev_button = builder.get_object("prev_btn")
        self._play_image = builder.get_object("play_image")
        self._pause_image = builder.get_object("pause_image")
        close_btn = builder.get_object("close_btn")
        preferences = Gio.Settings.new("org.gnome.desktop.wm.preferences")
        layout = preferences.get_value("button-layout").get_string()
        if layout.split(":")[0] == "close":
            grid.attach(close_btn, 0, 0, 1, 1)
            close_btn.set_property("halign", Gtk.Align.START)
        else:
            grid.attach(close_btn, 2, 0, 1, 1)
            close_btn.set_property("halign", Gtk.Align.END)
        self._artwork = builder.get_object("cover")
        if rotate_album:
            self._artwork.get_style_context().add_class("image-rotate")
        else:
            self._artwork.get_style_context().add_class("cover-frame")
        self._title_label = builder.get_object("title")
        self._artist_label = builder.get_object("artist")
        self._album_label = builder.get_object("album")
        self.__revealer = builder.get_object("reveal")
        self._datetime = builder.get_object("datetime")
        self._progress = builder.get_object("progress_scale")
        self._timelabel = builder.get_object("playback")
        self._total_time_label = builder.get_object("duration")
        self.connect("key-release-event", self.__on_key_release_event)

        # Add an AlbumListView on the right
        self.__view = AlbumsListView(RowListType.DND)
        self.__view.get_style_context().add_class("background-opacity")
        self.__view.show()
        self.__revealer.add(self.__view)
        self.add(widget)

    def do_show(self):
        """
            Init signals, set color and go party mode if nothing is playing
        """
        App().window.hide()
        self.__signal1_id = App().player.connect("current-changed",
                                                 self.on_current_changed)
        self.__signal2_id = App().player.connect("status-changed",
                                                 self.on_status_changed)
        self.__signal3_id = App().player.connect("party-changed",
                                                 self.__on_party_changed)
        self.on_status_changed(App().player)
        self.on_current_changed(App().player)
        self.__view.populate(App().player.albums)
        Gtk.Window.do_show(self)
        if self.__timeout_id is None:
            try:
                interface = Gio.Settings.new("org.gnome.desktop.interface")
                show_seconds = interface.get_value("clock-show-seconds")
            except:
                show_seconds = False
            self.__update_datetime(show_seconds)
            self.__timeout_id = GLib.timeout_add(1000, self.__update_datetime,
                                                 show_seconds)
        self.update_position(App().player.position / Gst.SECOND)
        screen = Gdk.Screen.get_default()
        monitor = screen.get_monitor_at_window(App().window.get_window())
        self.fullscreen_on_monitor(screen, monitor)

        # Disable screensaver (idle)
        App().inhibitor.manual_inhibit(Gtk.ApplicationInhibitFlags.IDLE
                                       | Gtk.ApplicationInhibitFlags.SUSPEND)

    def do_hide(self):
        """
            Remove signals and unset color
        """
        App().window.setup()
        App().window.show()
        self.__view.stop()
        Gtk.Window.do_hide(self)
        if self.__signal1_id is not None:
            App().player.disconnect(self.__signal1_id)
            self.__signal1_id = None
        if self.__signal2_id is not None:
            App().player.disconnect(self.__signal2_id)
            self.__signal2_id = None
        if self.__signal3_id is not None:
            App().player.disconnect(self.__signal3_id)
            self.__signal3_id = None
        if self.__timeout_id is not None:
            GLib.source_remove(self.__timeout_id)
            self.__timeout_id = None
        App().inhibitor.manual_uninhibit()
        ProgressController.do_destroy(self)

    def show_hide_volume_control(self):
        """
            Show/Hide volume control
        """
        self._show_volume_control = not self._show_volume_control
        self._update_state()

    def on_status_changed(self, player):
        """
            Update controller
            @param player as Player
        """
        ProgressController.on_status_changed(self, player)
        PlaybackController.on_status_changed(self, player)
        context = self._artwork.get_style_context()
        if not App().settings.get_value("rotate-fullscreen-album"):
            return
        if player.is_playing:
            context.add_class("image-rotate")
        else:
            context.remove_class("image-rotate")

    def on_current_changed(self, player):
        """
            Update controllers
            @param player as Player
        """
        InformationController.on_current_changed(self, self.__artsize,
                                                 self.__font_size)
        ProgressController.on_current_changed(self, player)
        if player.current_track.id is not None:
            album_name = player.current_track.album.name
            if player.current_track.year:
                album_name += " (%s)" % player.current_track.year
            self._album_label.set_markup(
                "<span font='%s'>%s</span>" %
                (self.__font_size - 1, GLib.markup_escape_text(album_name)))

#######################
# PROTECTED           #
#######################

    def _on_close_button_clicked(self, widget):
        """
            Destroy self
            @param widget as Gtk.Button
        """
        self.__view.stop()
        self.destroy()

    def _on_motion_notify_event(self, widget, event):
        """
            Show/Hide track list if mouse on the right
            @param widget as Gtk.Widget
            @param event as Gdk.EventMotion
        """
        if event.window == widget.get_window():
            allocated_width = widget.get_allocated_width()
            reveal = event.x > allocated_width -\
                self.__view.get_allocated_width() - 100 and\
                event.x < allocated_width + 100
            self.__revealer.set_reveal_child(reveal)

    def _on_image_realize(self, eventbox):
        """
            Set cursor
            @param eventbox as Gtk.EventBox
        """
        try:
            eventbox.get_window().set_cursor(Gdk.Cursor(Gdk.CursorType.HAND2))
        except:
            Logger.warning(_("You are using a broken cursor theme!"))

    def _on_image_button_release_event(self, widget, event):
        """
            Change artwork type
            @param widget as Gtk.Widget
            @param event as Gdk.Event
        """
        rotate_album = not App().settings.get_value("rotate-fullscreen-album")
        App().settings.set_value("rotate-fullscreen-album",
                                 GLib.Variant("b", rotate_album))
        context = self._artwork.get_style_context()
        if rotate_album:
            context.add_class("image-rotate")
            context.remove_class("cover-frame")
            InformationController.__init__(self, False,
                                           ArtHelperEffect.ROUNDED)
        else:
            context.remove_class("image-rotate")
            context.add_class("cover-frame")
            InformationController.__init__(self, False, ArtHelperEffect.NONE)
        InformationController.on_current_changed(self, self.__artsize,
                                                 self.__font_size)

#######################
# PRIVATE             #
#######################

    def __update_datetime(self, show_seconds=False):
        """
            Update datetime in headerbar
            @param show_seconds as bool
        """
        now = datetime.now()
        if show_seconds:
            self._datetime.set_label(now.strftime("%a %d %b, %X"))
        else:
            self._datetime.set_label(now.strftime("%a %d %b, %X")[:-3])
        if self.__timeout_id is None:
            self.__timeout_id = GLib.timeout_add(60000, self.__update_datetime)
            return False
        return True

    def __on_party_changed(self, player, party):
        """
            Populate view again
            @param player as Player
            @param party as bool
        """
        self.__view.populate(player.albums)

    def __on_key_release_event(self, widget, event):
        """
            Destroy window if Esc
            @param widget as Gtk.Widget
            @param event as Gdk.event
        """
        if event.keyval == Gdk.KEY_Escape:
            self.destroy()
Пример #17
0
class SearchView(BaseView, Gtk.Bin):
    """
        View for searching albums/tracks
    """
    def __init__(self):
        """
            Init Popover
        """
        BaseView.__init__(self)
        Gtk.Bin.__init__(self)
        self.connect("map", self.__on_map)
        self.connect("unmap", self.__on_unmap)
        self.__timeout_id = None
        self.__new_album_signal_id = None
        self.__search_finished_signal_id = None
        self.__current_search = ""
        self.__cancellable = Gio.Cancellable()
        self.__history = []

        self.__search_type_action = Gio.SimpleAction.new_stateful(
            "search_type", GLib.VariantType.new("s"),
            GLib.Variant("s", "local"))
        self.__search_type_action.connect("change-state",
                                          self.__on_search_action_change_state)
        App().add_action(self.__search_type_action)
        builder = Gtk.Builder()
        builder.add_from_resource("/org/gnome/Lollypop/SearchView.ui")
        self.__widget = builder.get_object("widget")
        self.__new_button = builder.get_object("new_button")
        self.__play_button = builder.get_object("play_button")
        self.__bottom_buttons = builder.get_object("bottom_buttons")
        self.__entry = builder.get_object("entry")
        self.__spinner = builder.get_object("spinner")
        self.__header_stack = builder.get_object("header_stack")
        self.__combo_locale = builder.get_object("combo_locale")
        self.__combo_locale.set_active_id(
            App().settings.get_value("spotify-charts-locale").get_string())
        self.__button_stack = builder.get_object("button_stack")
        self.__stack = builder.get_object("stack")
        self.__placeholder = builder.get_object("placeholder")
        self.__view = AlbumsListView([], [], ViewType.SEARCH)
        self.__view.set_margin_start(MARGIN_SMALL)
        self.__view.show()
        self.__stack.add_named(self.__view, "view")
        self.__set_default_placeholder()
        self.add(self.__widget)
        builder.connect_signals(self)

    def set_search(self, search):
        """
            Set search text
            @param search as str
        """
        parsed = urlparse(search)
        search = search.replace("%s://" % parsed.scheme, "")
        if parsed.scheme == "local":
            self.__entry.set_text(search)
            GLib.idle_add(self.__search_type_action.set_state,
                          GLib.Variant("s", "local"))
        elif parsed.scheme == "web":
            self.__entry.set_text(search)
            GLib.idle_add(self.__search_type_action.set_state,
                          GLib.Variant("s", "web"))

    def cancel(self):
        """
            Cancel current search and replace cancellable
        """
        self.__cancellable.cancel()
        self.__cancellable = Gio.Cancellable()

    @property
    def should_destroy(self):
        """
            User want to get search between mode
        """
        return False

#######################
# PROTECTED           #
#######################

    def _on_play_button_clicked(self, button):
        """
            Play search
            @param button as Gtk.Button
        """
        try:
            App().player.clear_albums()
            shuffle_setting = App().settings.get_enum("shuffle")
            children = self.__view.children
            if shuffle_setting == Shuffle.ALBUMS:
                shuffle(children)
            for child in children:
                App().player.add_album(child.album)
            App().player.load(App().player.albums[0].tracks[0])
        except Exception as e:
            Logger.error("SearchPopover::_on_play_button_clicked(): %s", e)

    def _on_new_button_clicked(self, button):
        """
            Create a new playlist based on search
            @param button as Gtk.Button
        """
        App().task_helper.run(self.__search_to_playlist)

    def _on_search_changed(self, widget):
        """
            Timeout filtering
            @param widget as Gtk.TextEntry
        """
        state = self.__search_type_action.get_state().get_string()
        if state == "local":
            timeout = 500
        else:
            timeout = 1000
        if self.__timeout_id:
            GLib.source_remove(self.__timeout_id)
            self.__timeout_id = None
        self.cancel()
        self.__view.stop()
        self.__current_search = widget.get_text().strip()
        self.__timeout_id = GLib.timeout_add(timeout,
                                             self.__on_search_changed_timeout)

    def _on_combo_locale_changed(self, combobox):
        """
            Save setting
            @param combobox as Gtk.ComboBoxText
        """
        App().settings.set_value("spotify-charts-locale",
                                 GLib.Variant("s", combobox.get_active_id()))
        self.__on_search_action_change_state(self.__search_type_action,
                                             GLib.Variant("s", "charts"))

#######################
# PRIVATE             #
#######################

    def __set_no_result_placeholder(self):
        """
            Set placeholder for no result
        """
        self.__placeholder.set_markup(
            _("<big>No results for this search</big>"))

    def __set_default_placeholder(self):
        """
            Set placeholder for no result
        """
        self.__placeholder.set_markup(
            _("<big>Search for artists, albums and tracks</big>"))

    def __populate(self):
        """
            Populate searching items
            in db based on text entry current text
        """
        self.__cancellable = Gio.Cancellable()
        self.__button_stack.set_visible_child(self.__spinner)
        self.__history = []
        if len(self.__current_search) > 2:
            self.__spinner.start()
            state = self.__search_type_action.get_state().get_string()
            current_search = self.__current_search.lower()
            if state == "local":
                search = Search()
                search.get(current_search,
                           self.__cancellable,
                           callback=(self.__on_search_get, current_search))
            elif state == "web":
                App().task_helper.run(App().spotify.search, current_search,
                                      self.__cancellable)
        else:
            self.__stack.set_visible_child_name("placeholder")
            self.__set_default_placeholder()
            self.__button_stack.set_visible_child(self.__new_button)
            GLib.idle_add(self.__spinner.stop)

    def __search_to_playlist(self):
        """
            Create a new playlist based on search
        """
        tracks = []
        for child in self.__view.children:
            tracks += child.album.tracks
        if tracks:
            playlist_id = App().playlists.get_id(self.__current_search)
            if playlist_id == Type.NONE:
                App().playlists.add(self.__current_search)
                playlist_id = App().playlists.get_id(self.__current_search)
            App().playlists.add_tracks(playlist_id, tracks)
        GLib.idle_add(self.__show_playlist, playlist_id)

    def __show_playlist(self, playlist_id):
        """
            Hide if in a popover and show playlist
        """
        if App().settings.get_value("show-sidebar"):
            App().window.container.show_lists([Type.PLAYLISTS], [playlist_id])
        else:
            App().window.container.show_view(Type.PLAYLISTS, [playlist_id])
        popover = self.get_ancestor(Gtk.Popover)
        if popover is not None:
            popover.hide()
        else:
            self.destroy()

    def __on_search_get(self, result, search):
        """
            Add rows for internal results
            @param result as [(int, Album, bool)]
        """
        self.__on_search_finished(None)
        if result:
            albums = []
            reveal_albums = []
            for (album, in_tracks) in result:
                albums.append(album)
                if in_tracks:
                    reveal_albums.append(album)
            self.__view.set_reveal(reveal_albums)
            self.__view.populate(albums)
            self.__stack.set_visible_child_name("view")

    def __on_map(self, widget):
        """
            Init signals and grab focus
            @param widget as Gtk.Widget
        """
        App().enable_special_shortcuts(False)
        if GLib.find_program_in_path("youtube-dl") is None or\
                not get_network_available("SPOTIFY") or\
                not get_network_available("YOUTUBE"):
            self.__bottom_buttons.hide()
        else:
            self.__bottom_buttons.show()
        if self.__new_album_signal_id is None:
            self.__new_album_signal_id = App().spotify.connect(
                "new-album", self.__on_new_spotify_album)
        if self.__search_finished_signal_id is None:
            self.__search_finished_signal_id = App().spotify.connect(
                "search-finished", self.__on_search_finished)
        GLib.idle_add(self.__entry.grab_focus)

    def __on_unmap(self, widget):
        """
            Clean up
            @param widget as Gtk.Widget
        """
        App().enable_special_shortcuts(True)
        if self.__new_album_signal_id is not None:
            App().spotify.disconnect(self.__new_album_signal_id)
            self.__new_album_signal_id = None
        if self.__search_finished_signal_id is not None:
            App().spotify.disconnect(self.__search_finished_signal_id)
            self.__search_finished_signal_id = None
        self.cancel()
        self.__view.stop()
        self.__button_stack.set_visible_child(self.__new_button)
        self.__spinner.stop()

    def __on_new_spotify_album(self, spotify, album, cover_uri):
        """
            Add album
            @param spotify as SpotifyHelper
            @param album as Album
            @param cover_uri as str
        """
        self.__view.insert_album(album, len(album.tracks) == 1, -1, cover_uri)
        self.__stack.set_visible_child_name("view")

    def __on_search_finished(self, api):
        """
            Stop spinner
            @param api ignored
        """
        self.__spinner.stop()
        self.__button_stack.set_visible_child(self.__new_button)
        if not self.__view.children:
            self.__stack.set_visible_child_name("placeholder")
            self.__set_no_result_placeholder()

    def __on_search_changed_timeout(self):
        """
            Populate widget
        """
        if self.__view.children:
            self.__view.stop()
            self.__view.clear()
            return True
        self.__timeout_id = None
        self.__populate()
        if self.__current_search != "":
            self.__new_button.set_sensitive(True)
        else:
            self.__new_button.set_sensitive(False)

    def __on_search_action_change_state(self, action, value):
        """
            Update action value
            @param action as Gio.SimpleAction
            @param value as GLib.Variant
        """
        self.cancel()
        self.__view.stop()
        action.set_state(value)
        state = value.get_string()
        # A new album signal may be in queue, so clear after
        GLib.idle_add(self.__view.clear)
        if state == "local":
            self.__new_button.show()
            self.__button_stack.set_visible_child(self.__new_button)
        else:
            self.__new_button.hide()
        if state == "charts":
            self.__header_stack.set_visible_child_name("locale")
            self.__play_button.set_sensitive(True)
            self.__button_stack.set_visible_child(self.__spinner)
            self.__history = []
            self.__spinner.start()
            self.__stack.set_visible_child_name("view")
            App().task_helper.run(App().spotify.charts, self.__cancellable,
                                  self.__combo_locale.get_active_id())
        else:
            self.__header_stack.set_visible_child_name("entry")
            self.__populate()
            GLib.idle_add(self.__entry.grab_focus)
Пример #18
0
 def populate(self, artist_id=None):
     """
         Show information for artists
         @param artist_id as int
     """
     builder = Gtk.Builder()
     builder.add_from_resource("/org/gnome/Lollypop/ArtistInformation.ui")
     builder.connect_signals(self)
     self.__scrolled = builder.get_object("scrolled")
     widget = builder.get_object("widget")
     self.add(widget)
     self.__stack = builder.get_object("stack")
     self.__artist_label = builder.get_object("artist_label")
     self.__artist_label.connect("realize", on_realize)
     title_label = builder.get_object("title_label")
     self.__artist_artwork = builder.get_object("artist_artwork")
     eventbox = builder.get_object("eventbox")
     eventbox.connect("button-release-event",
                      self.__on_label_button_release_event)
     self.__information_label = builder.get_object("bio_label")
     if artist_id is None and App().player.current_track.id is not None:
         builder.get_object("header").show()
         builder.get_object("lyrics_button").show()
         builder.get_object("lyrics_button").connect(
             "clicked", self.__on_lyrics_button_clicked,
             App().player.current_track)
         artist_id = App().player.current_track.album.artist_ids[0]
         title_label.set_text(App().player.current_track.title)
     self.__artist_name = App().artists.get_name(artist_id)
     if self.__minimal:
         self.__information_label.set_margin_start(MARGIN)
         self.__information_label.set_margin_end(MARGIN)
         self.__information_label.set_margin_top(MARGIN)
         self.__information_label.set_margin_bottom(MARGIN)
         self.__artist_artwork.hide()
     else:
         self.__artist_artwork.set_margin_start(MARGIN_SMALL)
         builder.get_object("header").show()
         self.__artist_label.set_text(self.__artist_name)
         self.__artist_label.show()
         title_label.show()
         App().art_helper.set_artist_artwork(
             self.__artist_name, ArtSize.ARTIST_SMALL * 3,
             ArtSize.ARTIST_SMALL * 3,
             self.__artist_artwork.get_scale_factor(), ArtBehaviour.ROUNDED
             | ArtBehaviour.CROP_SQUARE | ArtBehaviour.CACHE,
             self.__on_artist_artwork)
         albums_view = AlbumsListView([], [], ViewType.POPOVER)
         albums_view.set_size_request(300, -1)
         albums_view.show()
         albums_view.set_margin_start(5)
         widget.attach(albums_view, 2, 1, 1, 2)
         albums = []
         for album_id in App().albums.get_ids([artist_id], []):
             albums.append(Album(album_id))
         if not albums:
             albums = [App().player.current_track.album]
         # Allows view to be shown without lag
         GLib.idle_add(albums_view.populate, albums)
     App().task_helper.run(self.__information_store.get_information,
                           self.__artist_name,
                           callback=(self.__set_information_content, True))
Пример #19
0
 def populate(self, artist_id=None):
     """
         Show information for artists
         @param artist_id as int
     """
     builder = Gtk.Builder()
     builder.add_from_resource(
         "/org/gnome/Lollypop/ArtistInformation.ui")
     builder.connect_signals(self)
     self.__scrolled = builder.get_object("scrolled")
     widget = builder.get_object("widget")
     self.add(widget)
     self.__stack = builder.get_object("stack")
     self.__listbox = builder.get_object("listbox")
     self.__artist_label = builder.get_object("artist_label")
     title_label = builder.get_object("title_label")
     self.__artist_artwork = builder.get_object("artist_artwork")
     bio_eventbox = builder.get_object("bio_eventbox")
     artist_label_eventbox = builder.get_object("artist_label_eventbox")
     bio_eventbox.connect("realize", set_cursor_type)
     artist_label_eventbox.connect("realize", set_cursor_type)
     self.__gesture1 = GesturesHelper(
         bio_eventbox,
         primary_press_callback=self._on_info_label_press)
     self.__gesture2 = GesturesHelper(
         artist_label_eventbox,
         primary_press_callback=self._on_artist_label_press)
     self.__bio_label = builder.get_object("bio_label")
     if artist_id is None and App().player.current_track.id is not None:
         builder.get_object("header").show()
         if App().player.current_track.album.artist_ids[0] ==\
                 Type.COMPILATIONS:
             artist_id = App().player.current_track.artist_ids[0]
         else:
             artist_id = App().player.current_track.album.artist_ids[0]
         title_label.set_text(App().player.current_track.title)
     self.__artist_name = App().artists.get_name(artist_id)
     if self.__minimal:
         self.__bio_label.set_margin_start(MARGIN)
         self.__bio_label.set_margin_end(MARGIN)
         self.__bio_label.set_margin_top(MARGIN)
         self.__bio_label.set_margin_bottom(MARGIN)
         self.__artist_artwork.hide()
     else:
         self.__artist_artwork.set_margin_start(MARGIN_SMALL)
         builder.get_object("header").show()
         self.__artist_label.set_text(self.__artist_name)
         self.__artist_label.show()
         title_label.show()
         App().art_helper.set_artist_artwork(
                                 self.__artist_name,
                                 ArtSize.SMALL * 3,
                                 ArtSize.SMALL * 3,
                                 self.__artist_artwork.get_scale_factor(),
                                 ArtBehaviour.ROUNDED |
                                 ArtBehaviour.CROP_SQUARE |
                                 ArtBehaviour.CACHE,
                                 self.__on_artist_artwork)
         albums_view = AlbumsListView([], [],
                                      ViewType.SCROLLED)
         albums_view.set_size_request(300, -1)
         albums_view.show()
         albums_view.set_margin_start(5)
         albums_view.add_widget(albums_view.box)
         widget.attach(albums_view, 2, 1, 1, 2)
         albums = []
         storage_type = get_default_storage_type()
         for album_id in App().albums.get_ids([], [artist_id],
                                              storage_type, True):
             albums.append(Album(album_id))
         if not albums:
             albums = [App().player.current_track.album]
         albums_view.populate(albums)
     content = self.__information_store.get_information(self.__artist_name,
                                                        ARTISTS_PATH)
     if content is None:
         self.__bio_label.set_text(_("Loading information"))
         from lollypop.information_downloader import InformationDownloader
         downloader = InformationDownloader()
         downloader.get_information(self.__artist_name,
                                    self.__on_artist_information,
                                    self.__artist_name)
     else:
         App().task_helper.run(self.__to_markup, content,
                               callback=(self.__bio_label.set_markup,))
Пример #20
0
    def __init__(self, app):
        """
            Init window for app
            @param app as Gio.Application
        """
        Gtk.Window.__init__(self)
        self.set_title("Lollypop")
        rotate_album = App().settings.get_value("rotate-fullscreen-album")
        PlaybackController.__init__(self)
        ProgressController.__init__(self)
        if rotate_album:
            InformationController.__init__(self, False,
                                           ArtHelperEffect.ROUNDED)
        else:
            InformationController.__init__(self, False, ArtHelperEffect.NONE)
        self.set_application(app)
        self.__timeout_id = None
        self.__signal1_id = self.__signal2_id = self.__signal3_id = None
        self.set_decorated(False)

        builder = Gtk.Builder()
        builder.add_from_resource("/org/gnome/Lollypop/FullScreen.ui")
        builder.connect_signals(self)

        # Calculate cover size
        screen = Gdk.Screen.get_default()
        monitor = screen.get_monitor_at_window(App().window.get_window())
        geometry = screen.get_monitor_geometry(monitor)
        # We want 500 and 200 in full hd
        if geometry.width > geometry.height:
            self.__artsize = int(ArtSize.FULLSCREEN * geometry.height / 1080)
        else:
            self.__artsize = int(ArtSize.FULLSCREEN * geometry.width / 1920)
        self.__font_size = int(14 * geometry.height / 1080)
        widget = builder.get_object("widget")
        grid = builder.get_object("grid")
        self._play_button = builder.get_object("play_btn")
        self._next_button = builder.get_object("next_btn")
        self._prev_button = builder.get_object("prev_btn")
        self._play_image = builder.get_object("play_image")
        self._pause_image = builder.get_object("pause_image")
        close_btn = builder.get_object("close_btn")
        preferences = Gio.Settings.new("org.gnome.desktop.wm.preferences")
        layout = preferences.get_value("button-layout").get_string()
        if layout.split(":")[0] == "close":
            grid.attach(close_btn, 0, 0, 1, 1)
            close_btn.set_property("halign", Gtk.Align.START)
        else:
            grid.attach(close_btn, 2, 0, 1, 1)
            close_btn.set_property("halign", Gtk.Align.END)
        self._artwork = builder.get_object("cover")
        if rotate_album:
            self._artwork.get_style_context().add_class("image-rotate")
        else:
            self._artwork.get_style_context().add_class("cover-frame")
        self._title_label = builder.get_object("title")
        self._artist_label = builder.get_object("artist")
        self._album_label = builder.get_object("album")
        self.__revealer = builder.get_object("reveal")
        self._datetime = builder.get_object("datetime")
        self._progress = builder.get_object("progress_scale")
        self._timelabel = builder.get_object("playback")
        self._total_time_label = builder.get_object("duration")
        self.connect("key-release-event", self.__on_key_release_event)

        # Add an AlbumListView on the right
        self.__view = AlbumsListView(RowListType.DND)
        self.__view.get_style_context().add_class("background-opacity")
        self.__view.show()
        self.__revealer.add(self.__view)
        self.add(widget)
Пример #21
0
 def on_load(albums):
     self.add_reveal_albums(albums)
     AlbumsListView.populate(self, albums)