def populate(self, items):
     """
         Populate items
         @param items
     """
     if self.__obj is not None:
         new_items = []
         for item in items:
             if not App().playlists.get_smart(item):
                 new_items.append(item)
         items = new_items
     else:
         items = [i[0] for i in ShownPlaylists.get()] + items
     FlowBoxView.populate(self, items)
Example #2
0
 def add_genre(self, track_id, genre_id):
     """
         Add genre to track
         @param track_id as int
         @param genre_id as int
         @warning: commit needed
     """
     with SqlCursor(App().db, True) as sql:
         genres = self.get_genre_ids(track_id)
         if genre_id not in genres:
             sql.execute("INSERT INTO\
                          track_genres (track_id, genre_id)\
                          VALUES (?, ?)",
                         (track_id, genre_id))
Example #3
0
 def search_track(self, artist, title):
     """
         Get track id for artist and title
         @param artist as string
         @param title as string
         @return track id as int
     """
     artist = noaccents(artist.lower())
     track_ids = self.get_ids_for_name(title)
     for track_id in track_ids:
         album_id = App().tracks.get_album_id(track_id)
         artist_ids = set(App().albums.get_artist_ids(album_id)) &\
             set(App().tracks.get_artist_ids(track_id))
         for artist_id in artist_ids:
             db_artist = noaccents(
                 App().artists.get_name(artist_id).lower())
             if artist.find(db_artist) != -1 or\
                     db_artist.find(artist) != -1:
                 return track_id
         artists = ", ".join(App().tracks.get_artists(track_id)).lower()
         if noaccents(artists) == artist:
             return track_id
     return None
Example #4
0
 def get_mtime(self, track_id):
     """
         Get modification time
         @param track_id as int
         @return modification time as int
     """
     with SqlCursor(App().db) as sql:
         request = "SELECT mtime FROM tracks\
                    WHERE tracks.rowid=?"
         result = sql.execute(request, (track_id,))
         v = result.fetchone()
         if v is not None:
             return v[0]
         return 0
Example #5
0
    def get_loved(self, track_id):
        """
            Get track loved status
            @param track_id as int
            @return loved as int
        """
        with SqlCursor(App().db) as sql:
            result = sql.execute("SELECT loved FROM tracks WHERE\
                                 rowid=?", (track_id,))

            v = result.fetchone()
            if v is not None:
                return v[0]
            return 0
Example #6
0
 def get_randoms(self):
     """
         Return random tracks
         @return array of track ids as int
     """
     if self.__cached_randoms:
         return self.__cached_randoms
     with SqlCursor(App().db) as sql:
         result = sql.execute("SELECT tracks.rowid\
                               FROM tracks WHERE mtime != 0\
                               ORDER BY random() LIMIT 100")
         tracks = list(itertools.chain(*result))
         self.__cached_randoms = list(tracks)
         return tracks
Example #7
0
 def get_album_name(self, track_id):
     """
         Get album name for track id
         @param track_id as int
         @return album name as str
     """
     with SqlCursor(App().db) as sql:
         result = sql.execute("SELECT albums.name from albums,tracks\
                               WHERE tracks.rowid=? AND\
                               tracks.album_id=albums.rowid", (track_id,))
         v = result.fetchone()
         if v is not None:
             return v[0]
         return _("Unknown")
Example #8
0
 def __search_tracks(self, search, storage_type, cancellable):
     """
         Get tracks for search items
         @param search as str
         @param storage_type as StorageType
         @param cancellable as Gio.Cancellable
         @return [int]
     """
     tracks = []
     track_ids = []
     split = self.__split_string(search)
     for search_str in [search] + split:
         tracks += App().tracks.search_performed(search_str, storage_type)
         tracks += App().tracks.search(search_str, storage_type)
         if cancellable.is_cancelled():
             break
     for (track_id, track_name) in tracks:
         valid = True
         no_accents = noaccents(track_name)
         if not no_accents.startswith(search):
             for word in split:
                 if word not in no_accents:
                     valid = False
                     break
         # Start with same word, adding to result
         else:
             track_ids.append(track_id)
         # All words are valid, adding to result
         if valid:
             track_ids.append(track_id)
         # Detect an artist match, adding to result
         for artist in App().tracks.get_artists(track_id):
             no_accents = noaccents(artist)
             for word in split:
                 if word in no_accents:
                     track_ids.append(track_id)
     return track_ids
    def get_all(self, genre_ids=[]):
        """
            Get all available artists
            @param genre_ids as [int]
            @return [int, str, str]
        """
        if App().settings.get_value("show-artist-sort"):
            select = "artists.rowid, artists.sortname, artists.sortname"
        else:
            select = "artists.rowid, artists.name, artists.sortname"
        with SqlCursor(App().db) as sql:
            result = []
            if not genre_ids or genre_ids[0] == Type.ALL:
                # Only artist that really have an album
                result = sql.execute(
                    "SELECT DISTINCT %s FROM artists, track_artists, tracks\
                                  WHERE artists.rowid=track_artists.artist_id\
                                  AND tracks.rowid=track_artists.track_id\
                                  AND tracks.mtime!=0\
                                  ORDER BY artists.sortname\
                                  COLLATE NOCASE COLLATE LOCALIZED" % select)
            else:
                genres = tuple(genre_ids)
                request = "SELECT DISTINCT %s\
                           FROM artists, tracks, track_genres, track_artists\
                           WHERE artists.rowid=track_artists.artist_id\
                           AND tracks.rowid=track_artists.track_id\
                           AND tracks.mtime!=0\
                           AND track_genres.track_id=tracks.rowid AND ("

                for genre_id in genre_ids:
                    request += "track_genres.genre_id=? OR "
                request += "1=0) ORDER BY artists.sortname\
                            COLLATE NOCASE COLLATE LOCALIZED"

                result = sql.execute(request % select, genres)
            return [(row[0], row[1], row[2]) for row in result]
Example #10
0
 def __init__(self, widget, rowid, mask):
     """
         Init menu
         @param widget as Gtk.Widget
         @param rowid as int
         @param mask as SelectionListMask
     """
     Popover.__init__(self)
     self.__widget = widget
     self.__rowid = rowid
     self.__mask = mask
     menu = Gio.Menu()
     self.bind_model(menu, None)
     # Startup menu
     if rowid in [Type.POPULARS, Type.RADIOS, Type.LOVED,
                  Type.ALL, Type.RECENTS, Type.YEARS,
                  Type.RANDOMS, Type.NEVER,
                  Type.PLAYLISTS, Type.ARTISTS] and\
             not App().settings.get_value("save-state"):
         startup_menu = Gio.Menu()
         if self.__mask & SelectionListMask.LIST_TWO:
             exists = rowid in App().settings.get_value("startup-two-ids")
         else:
             exists = rowid in App().settings.get_value("startup-one-ids")
         action = Gio.SimpleAction.new_stateful(
             "default_selection_id", None, GLib.Variant.new_boolean(exists))
         App().add_action(action)
         action.connect("change-state", self.__on_default_change_state,
                        rowid)
         item = Gio.MenuItem.new(_("Default on startup"),
                                 "app.default_selection_id")
         startup_menu.append_item(item)
         menu.insert_section(0, _("Startup"), startup_menu)
     # Shown menu
     shown_menu = Gio.Menu()
     if mask & SelectionListMask.PLAYLISTS:
         lists = ShownPlaylists.get(True)
         wanted = App().settings.get_value("shown-playlists")
     else:
         lists = ShownLists.get(mask, True)
         wanted = App().settings.get_value("shown-album-lists")
     for item in lists:
         exists = item[0] in wanted
         encoded = sha256(item[1].encode("utf-8")).hexdigest()
         action = Gio.SimpleAction.new_stateful(
             encoded, None, GLib.Variant.new_boolean(exists))
         action.connect("change-state", self.__on_shown_change_state,
                        item[0])
         App().add_action(action)
         shown_menu.append(item[1], "app.%s" % encoded)
     # Translators: shown => items
     menu.insert_section(1, _("Shown"), shown_menu)
 def __init__(self):
     """
         Init box
     """
     Gtk.Box.__init__(self)
     # Prevent updating progress while seeking
     self.__seeking = False
     # Update pogress position
     self.__timeout_id = None
     self.__time_label = Gtk.Label.new()
     self.__time_label.show()
     self.__progress = Gtk.Scale.new(Gtk.Orientation.HORIZONTAL, None)
     self.__progress.show()
     self.__progress.set_hexpand(True)
     self.__progress.set_draw_value(False)
     self.__progress.connect("change-value", self.__on_change_value)
     self.__multi_press = Gtk.GestureMultiPress.new(self.__progress)
     self.__multi_press.set_propagation_phase(Gtk.PropagationPhase.TARGET)
     self.__multi_press.connect("pressed", self.__on_multi_pressed)
     self.__multi_press.connect("released", self.__on_multi_released)
     self.__multi_press.set_button(1)
     self.__event_controller = Gtk.EventControllerScroll.new(
         self.__progress, Gtk.EventControllerScrollFlags.BOTH_AXES)
     self.__event_controller.set_propagation_phase(
         Gtk.PropagationPhase.TARGET)
     self.__event_controller.connect("scroll", self.__on_scroll)
     self.__total_time_label = Gtk.Label.new()
     self.__total_time_label.show()
     self.set_spacing(MARGIN_SMALL)
     self.pack_start(self.__time_label, False, False, 0)
     self.pack_start(self.__progress, False, True, 0)
     self.pack_start(self.__total_time_label, False, False, 0)
     self.connect("destroy", self.__on_destroy)
     return [(App().player, "current-changed", "_on_current_changed"),
             (App().player, "status-changed", "_on_status_changed"),
             (App().player, "duration-changed", "_on_duration_changed"),
             (App().player, "seeked", "_on_seeked")]
 def __on_shown_change_state(self, action, variant, rowid):
     """
         Set action value
         @param action as Gio.SimpleAction
         @param variant as GLib.Variant
         @param rowid as int
     """
     action.set_state(variant)
     if self.__mask & SelectionListMask.PLAYLISTS:
         option = "shown-playlists"
     else:
         option = "shown-album-lists"
     wanted = list(App().settings.get_value(option))
     if variant:
         wanted.append(rowid)
     else:
         wanted.remove(rowid)
     App().settings.set_value(option, GLib.Variant("ai", wanted))
     if self.__mask & SelectionListMask.PLAYLISTS:
         items = ShownPlaylists.get(True)
     else:
         items = ShownLists.get(self.__mask, True)
     if variant:
         for item in items:
             if item[0] == rowid:
                 self.__widget.add_value(item)
                 break
     else:
         self.__widget.remove_value(rowid)
         if self.__mask & SelectionListMask.LIST_ONE:
             ids = list(App().settings.get_value("startup-one-ids"))
             if rowid in ids:
                 ids.remove(rowid)
             App().settings.set_value("startup-one-ids",
                                      GLib.Variant("ai", ids))
             App().settings.set_value("startup-two-ids",
                                      GLib.Variant("ai", []))
 def __set_popularity(self, pop):
     """
         Set popularity as kid3 is installed
         @param pop as int
     """
     try:
         if App().art.kid3_available:
             if pop == 0:
                 value = 0
             elif pop == 1:
                 value = 1
             elif pop == 2:
                 value = 64
             elif pop == 3:
                 value = 128
             elif pop == 4:
                 value = 196
             else:
                 value = 255
             path = GLib.filename_from_uri(self.__object.uri)[0]
             if GLib.find_program_in_path("flatpak-spawn") is not None:
                 argv = [
                     "flatpak-spawn", "--host", "kid3-cli", "-c",
                     "set POPM %s" % value, path
                 ]
             else:
                 argv = ["kid3-cli", "-c", "set POPM %s" % value, path]
             if App().scanner.inotify is not None:
                 App().scanner.inotify.disable()
             (pid, stdin, stdout, stderr) = GLib.spawn_async(
                 argv,
                 flags=GLib.SpawnFlags.SEARCH_PATH
                 | GLib.SpawnFlags.STDOUT_TO_DEV_NULL)
             # Force mtime update to not run a collection update
             App().tracks.set_mtime(self.__object.id, int(time()) + 10)
     except Exception as e:
         Logger.error("RatingWidget::__on_can_set_popularity(): %s" % e)
Example #14
0
    def _on_button_release_event(self, widget, event):
        """
            Set album popularity
            @param widget as Gtk.EventBox
            @param event as Gdk.Event
        """
        user_rating = True
        rate = self.__object.rate
        if rate < 1:
            rate = self.__object.get_popularity()
            user_rating = False
        max_star = self.__star_from_rate(rate)
        event_star = widget.get_children()[0]
        if event_star in self._stars:
            position = self._stars.index(event_star)
        else:
            position = -1
        pop = position + 1
        if event.button != 1:
            self.__object.set_popularity(pop)
            self.__object.set_rate(0)
            self._on_leave_notify_event(None, None)
        elif pop == 0 or pop == max_star:
            if user_rating:
                self.__object.set_rate(0)
            else:
                self.__object.set_popularity(0)
            self._on_leave_notify_event(None, None)
        else:
            self.__object.set_rate(pop)

        # Save to tags if needed
        if App().settings.get_value("save-to-tags") and\
                isinstance(self.__object, Track) and\
                self.__object.id >= 0:
            App().task_helper.run(self.__set_popularity, pop)
        return True
Example #15
0
 def __setup_content(self):
     """
         Setup window content
     """
     self.__container = Container()
     self.set_stack(self.container.stack)
     self.__container.show()
     self.__vgrid = Gtk.Grid()
     self.__vgrid.set_orientation(Gtk.Orientation.VERTICAL)
     self.__vgrid.show()
     self.__toolbar = Toolbar(self)
     self.__toolbar.show()
     if App().settings.get_value("disable-csd") or is_unity():
         self.__vgrid.add(self.__toolbar)
     else:
         self.set_titlebar(self.__toolbar)
         self.__toolbar.set_show_close_button(
             not App().settings.get_value("disable-csd"))
     self.__vgrid.add(self.__container)
     self.add(self.__vgrid)
     self.drag_dest_set(Gtk.DestDefaults.DROP | Gtk.DestDefaults.MOTION, [],
                        Gdk.DragAction.MOVE)
     self.drag_dest_add_uri_targets()
     self.connect("drag-data-received", self.__on_drag_data_received)
Example #16
0
 def __init__(self, view_type):
     """
         Init widget
         @param view_type as ViewType
     """
     if App().settings.get_value("force-single-column"):
         view_type &= ~ViewType.TWO_COLUMNS
     self._view_type = view_type
     self._width = None
     self.__discs = []
     self._responsive_widget = None
     self._orientation = None
     self.__populated = False
     self.__allocation_timeout_id = None
     self.__cancellable = Gio.Cancellable()
Example #17
0
 def __init__(self):
     """
         Init player
     """
     BinPlayer.__init__(self)
     QueuePlayer.__init__(self)
     LinearPlayer.__init__(self)
     ShufflePlayer.__init__(self)
     PlaylistPlayer.__init__(self)
     RadioPlayer.__init__(self)
     SimilarsPlayer.__init__(self)
     self.__stop_after_track_id = None
     self.update_crossfading()
     App().settings.connect("changed::repeat", self.__on_repeat_changed)
     self._albums_backup = []
Example #18
0
 def __init__(self, album, genre_ids, artist_ids):
     """
         Init Album widget
         @param album as Album
         @param genre_ids as [int]
         @param artist_ids as [int]
     """
     self._artwork = None
     self._album = album
     self._genre_ids = genre_ids
     self._artist_ids = artist_ids
     self.__filtered = False
     self.connect("destroy", self.__on_destroy)
     self._scan_signal = App().scanner.connect("album-updated",
                                               self._on_album_updated)
Example #19
0
 def get(self, current_search, cancellable, callback):
     """
         Get track for name
         @param current_search as str
         @param cancellable as Gio.Cancellable
         @param callback as callback
     """
     search_items = []
     for item in current_search.lower().split():
         if item not in search_items:
             search_items.append(item)
     App().task_helper.run(self.__get,
                           search_items,
                           cancellable,
                           callback=callback)
Example #20
0
    def __on_remove_action_activate(self, action, variant):
        """
            Remove playlist
            @param Gio.SimpleAction
            @param GLib.Variant
        """
        def remove_playlist():
            App().playlists.remove(self.__playlist_id)

        from lollypop.app_notification import AppNotification
        notification = AppNotification(_("Remove this playlist?"),
                                       [_("Confirm")], [remove_playlist])
        notification.show()
        App().window.container.add_overlay(notification)
        notification.set_reveal_child(True)
Example #21
0
 def set_smart_sql(self, playlist_id, request):
     """
         Set playlist SQL smart request
         @param playlist_id as int
         @param request as str
     """
     name = self.get_name(playlist_id)
     # Clear cache
     App().art.remove_artwork_from_cache("playlist_" + name, "ROUNDED")
     with SqlCursor(self, True) as sql:
         sql.execute("UPDATE playlists\
                     SET smart_sql=?\
                     WHERE rowid=?",
                     (request, playlist_id))
         emit_signal(self, "playlists-updated", playlist_id)
 def __get_playlists_random(self):
     """
         Return a track from current playlist
         @return Track
     """
     for track in sorted(self._playlist_tracks,
                         key=lambda *args: random.random()):
         # Ignore current track, not an issue if playing one track
         # in shuffle because LinearPlayer will handle next()
         if track != App().player.current_track and (
                 track.album not in self.__already_played_tracks.keys()
                 or track not in self.__already_played_tracks[track.album]):
             return track
     self._next_context = NextContext.STOP
     return Track()
Example #23
0
 def _on_activate(self, flowbox, child):
     """
         Save artwork
         @param flowbox as Gtk.FlowBox
         @param child as ArtworkSearchChild
     """
     try:
         if isinstance(child, ArtworkSearchChild):
             self._close_popover()
             App().art.save_album_artwork(child.bytes, self.__album)
             self._streams = {}
         else:
             ArtworkSearchWidget._on_activate(self, flowbox, child)
     except Exception as e:
         Logger.error("AlbumArtworkSearchWidget::_on_activate(): %s", e)
Example #24
0
 def _on_primary_press_gesture(self, x, y, event):
     """
         Show covers popover
         @param x as int
         @param y as int
         @param event as Gdk.Event
     """
     if self.__view_type & ViewType.ALBUM:
         from lollypop.widgets_menu import MenuBuilder
         from lollypop.menu_artwork import AlbumArtworkMenu
         menu = Gio.Menu()
         if App().window.folded:
             from lollypop.menu_header import AlbumMenuHeader
             menu.append_item(AlbumMenuHeader(self.__album))
         menu_widget = MenuBuilder(menu, False)
         menu_widget.show()
         menu_ext = AlbumArtworkMenu(self.__album, self.__view_type, False)
         menu_ext.connect("hidden", self.__close_artwork_menu)
         menu_ext.show()
         menu_widget.add_widget(menu_ext, False)
         self.__artwork_popup = popup_widget(menu_widget, self, None, None,
                                             None)
     else:
         App().window.container.show_view([Type.ALBUM], self.__album)
Example #25
0
 def __on_dir_changed(self, monitor, changed_file, other_file, event):
     """
         Stop collection scanner if running
         Delayed update by default
         @param monitor as Gio.FileMonitor
         @param changed_file as Gio.File/None
         @param other_file as Gio.File/None
         @param event as Gio.FileMonitorEvent
     """
     update = False
     # Stop collection scanner and wait
     if App().scanner.is_locked():
         App().scanner.stop()
         GLib.timeout_add(self.__TIMEOUT, self.__on_dir_changed, monitor,
                          changed_file, other_file, event)
     # Run update delayed
     else:
         uri = changed_file.get_uri()
         d = Gio.File.new_for_uri(uri)
         if d.query_exists():
             # If a directory, monitor it
             if changed_file.query_file_type(
                     Gio.FileQueryInfoFlags.NONE,
                     None) == Gio.FileType.DIRECTORY:
                 self.add_monitor(uri)
             # If not an audio file, exit
             elif is_audio(changed_file):
                 update = True
         else:
             update = True
         if update:
             if self.__timeout is not None:
                 GLib.source_remove(self.__timeout)
                 self.__timeout = None
             self.__timeout = GLib.timeout_add(self.__TIMEOUT,
                                               self.__run_collection_update)
Example #26
0
def format_artist_name(name):
    """
        Return formated artist name
        @param name as str
    """
    if not App().settings.get_value("smart-artist-sort"):
        return name
    # Handle language ordering
    # Translators: Add here words that shoud be ignored for artist sort order
    # Translators: Add The the too
    for special in _("The the").split():
        if name.startswith(special + " "):
            strlen = len(special) + 1
            name = name[strlen:] + ", " + special
    return name
 def __on_list_two_selected(self, selection_list):
     """
         Update view based on selected object
         @param selection_list as SelectionList
     """
     Logger.debug("Container::__on_list_two_selected()")
     self._stack.destroy_non_visible_children()
     if not App().window.is_adaptive:
         App().window.emit("show-can-go-back", False)
         App().window.emit("can-go-back-changed", False)
     genre_ids = self._list_one.selected_ids
     selected_ids = self._list_two.selected_ids
     if not selected_ids or not genre_ids:
         return
     if genre_ids[0] == Type.PLAYLISTS:
         view = self._get_view_playlists(selected_ids)
     elif genre_ids[0] == Type.YEARS:
         view = self._get_view_albums_years(selected_ids)
     elif selected_ids[0] == Type.COMPILATIONS:
         view = self._get_view_albums(genre_ids, selected_ids)
     else:
         view = self._get_view_artists(genre_ids, selected_ids)
     self._stack.add(view)
     self._stack.set_visible_child(view)
Example #28
0
 def __on_remove_track(self, row):
     """
         Remove row's track
         @param row as PlaylistRow
     """
     if row.track.id != self.__last_drag_id:
         App().player.remove_from_queue(row.track.id)
     if row.previous_row is None:
         row.next_row.set_previous_row(None)
     elif row.next_row is None:
         row.previous_row.set_next_row(None)
     else:
         row.next_row.set_previous_row(row.previous_row)
         row.previous_row.set_next_row(row.next_row)
     self.__last_drag_id = None
Example #29
0
 def skip_album(self):
     """
         Skip current album
     """
     # In party or shuffle, just update next track
     if self.is_party or\
             App().settings.get_enum("shuffle") == Shuffle.TRACKS:
         self.set_next()
         # We send this signal to update next popover
         self.emit("queue-changed")
     elif self._current_track.id is not None:
         self.pause()
         self.load(self._current_track.album.tracks[-1])
         self.set_next()
         self.next()
Example #30
0
 def remove(self, playlist_id):
     """
         Remove playlist
         @param playlist_id as int
     """
     name = self.get_name(playlist_id)
     with SqlCursor(self, True) as sql:
         sql.execute("DELETE FROM playlists\
                     WHERE rowid=?",
                     (playlist_id,))
         sql.execute("DELETE FROM tracks\
                     WHERE playlist_id=?",
                     (playlist_id,))
     emit_signal(self, "playlists-removed", playlist_id)
     App().art.remove_artwork_from_cache("playlist_" + name, "ROUNDED")