def __reset_database(self):
        """
            Reset database
        """
        def update_ui():
            App().window.container.go_home()
            App().scanner.update(ScanType.FULL)

        App().player.stop()
        if App().ws_director.collection_ws is not None:
            App().ws_director.collection_ws.stop()
        uris = App().tracks.get_uris()
        i = 0
        SqlCursor.add(App().db)
        SqlCursor.add(self.__history)
        count = len(uris)
        for uri in uris:
            self.del_from_db(uri, True)
            self.__update_progress(i, count, 0.01)
            i += 1
        App().tracks.del_persistent(False)
        App().tracks.clean(False)
        App().albums.clean(False)
        App().artists.clean(False)
        App().genres.clean(False)
        App().cache.clear_table("duration")
        SqlCursor.commit(App().db)
        SqlCursor.remove(App().db)
        SqlCursor.commit(self.__history)
        SqlCursor.remove(self.__history)
        GLib.idle_add(update_ui)
Ejemplo n.º 2
0
 def upgrade(self, db):
     """
         Upgrade db
         @param db as Database
     """
     version = 0
     SqlCursor.add(db)
     with SqlCursor(db, True) as sql:
         result = sql.execute("PRAGMA user_version")
         v = result.fetchone()
         if v is not None:
             version = v[0]
         if version < self.version:
             for i in range(version + 1, self.version + 1):
                 try:
                     if isinstance(self._UPGRADES[i], str):
                         sql.execute(self._UPGRADES[i])
                         SqlCursor.commit(db)
                     else:
                         self._UPGRADES[i](db)
                         SqlCursor.commit(db)
                 except Exception as e:
                     Logger.error("DB upgrade %s failed: %s" % (i, e))
             sql.execute("PRAGMA user_version=%s" % self.version)
     SqlCursor.remove(db)
 def del_from_db(self, uri, backup):
     """
         Delete track from db
         @param uri as str
         @param backup as bool
         @return (popularity, ltime, mtime,
                  loved album, album_popularity)
     """
     try:
         track_id = App().tracks.get_id_by_uri(uri)
         duration = App().tracks.get_duration(track_id)
         album_id = App().tracks.get_album_id(track_id)
         album_artist_ids = App().albums.get_artist_ids(album_id)
         artist_ids = App().tracks.get_artist_ids(track_id)
         track_pop = App().tracks.get_popularity(track_id)
         track_rate = App().tracks.get_rate(track_id)
         track_ltime = App().tracks.get_ltime(track_id)
         album_mtime = App().tracks.get_mtime(track_id)
         track_loved = App().tracks.get_loved(track_id)
         album_pop = App().albums.get_popularity(album_id)
         album_rate = App().albums.get_rate(album_id)
         album_loved = App().albums.get_loved(album_id)
         album_synced = App().albums.get_synced(album_id)
         if backup:
             f = Gio.File.new_for_uri(uri)
             name = f.get_basename()
             self.__history.add(name, duration, track_pop, track_rate,
                                track_ltime, album_mtime, track_loved,
                                album_loved, album_pop, album_rate,
                                album_synced)
         App().tracks.remove(track_id)
         genre_ids = App().tracks.get_genre_ids(track_id)
         App().albums.clean()
         App().genres.clean()
         App().artists.clean()
         App().cache.clear_durations(album_id)
         SqlCursor.commit(App().db)
         item = CollectionItem(album_id=album_id)
         if not App().albums.get_name(album_id):
             item.artist_ids = []
             for artist_id in album_artist_ids + artist_ids:
                 if not App().artists.get_name(artist_id):
                     item.artist_ids.append(artist_id)
             item.genre_ids = []
             for genre_id in genre_ids:
                 if not App().genres.get_name(genre_id):
                     item.genre_ids.append(genre_id)
             emit_signal(self, "updated", item, ScanUpdate.REMOVED)
         else:
             # Force genre for album
             genre_ids = App().tracks.get_album_genre_ids(album_id)
             App().albums.set_genre_ids(album_id, genre_ids)
             emit_signal(self, "updated", item, ScanUpdate.MODIFIED)
         return (track_pop, track_rate, track_ltime, album_mtime,
                 track_loved, album_loved, album_pop, album_rate)
     except Exception as e:
         Logger.error("CollectionScanner::del_from_db: %s" % e)
 def __notify_ui(self, items):
     """
         Notify UI based on current items
         @param items as [CollectionItem]
     """
     SqlCursor.commit(App().db)
     for item in items:
         if item.new_album:
             emit_signal(self, "updated", item, ScanUpdate.ADDED)
         else:
             emit_signal(self, "updated", item, ScanUpdate.MODIFIED)
 def __del_from_db(self, uri):
     """
         Delete track from db
         @param uri as str
     """
     try:
         f = Gio.File.new_for_uri(uri)
         name = f.get_basename()
         track_id = App().tracks.get_id_by_uri(uri)
         album_id = App().tracks.get_album_id(track_id)
         genre_ids = App().tracks.get_genre_ids(track_id)
         album_artist_ids = App().albums.get_artist_ids(album_id)
         artist_ids = App().tracks.get_artist_ids(track_id)
         popularity = App().tracks.get_popularity(track_id)
         rate = App().tracks.get_rate(track_id)
         ltime = App().tracks.get_ltime(track_id)
         mtime = App().tracks.get_mtime(track_id)
         loved_track = App().tracks.get_loved(track_id)
         duration = App().tracks.get_duration(track_id)
         album_popularity = App().albums.get_popularity(album_id)
         album_rate = App().albums.get_rate(album_id)
         loved_album = App().albums.get_loved(album_id)
         uri = App().tracks.get_uri(track_id)
         self.__history.add(name, duration, popularity, rate, ltime, mtime,
                            loved_track, loved_album, album_popularity,
                            album_rate)
         App().tracks.remove(track_id)
         App().tracks.clean(track_id)
         cleaned = App().albums.clean(album_id)
         if cleaned:
             SqlCursor.commit(App().db)
             GLib.idle_add(self.emit, "album-updated", album_id, True)
         for artist_id in album_artist_ids + artist_ids:
             cleaned = App().artists.clean(artist_id)
             # Force update even if not cleaned as artist may
             # have been removed from a selected genre
             GLib.idle_add(self.emit, "artist-updated", artist_id, False)
         for genre_id in genre_ids:
             cleaned = App().genres.clean(genre_id)
             if cleaned:
                 SqlCursor.commit(App().db)
                 GLib.idle_add(self.emit, "genre-updated", genre_id, False)
     except Exception as e:
         Logger.error("CollectionScanner::__del_from_db: %s" % e)
    def __add2db(self, uri, mtime):
        """
            Add new file to db with information
            @param uri as string
            @param mtime as int
            @return track id as int
            @warning, be sure SqlCursor is available for App().db
        """
        f = Gio.File.new_for_uri(uri)
        Logger.debug("CollectionScanner::add2db(): Read tags")
        info = self.get_info(uri)
        tags = info.get_tags()
        name = f.get_basename()
        title = self.get_title(tags, name)
        version = self.get_version(tags)
        artists = self.get_artists(tags)
        composers = self.get_composers(tags)
        performers = self.get_performers(tags)
        a_sortnames = self.get_artist_sortnames(tags)
        aa_sortnames = self.get_album_artist_sortnames(tags)
        album_artists = self.get_album_artists(tags)
        album_name = self.get_album_name(tags)
        mb_album_id = self.get_mb_album_id(tags)
        mb_track_id = self.get_mb_track_id(tags)
        genres = self.get_genres(tags)
        discnumber = self.get_discnumber(tags)
        discname = self.get_discname(tags)
        tracknumber = self.get_tracknumber(tags, name)
        (year, timestamp) = self.get_original_year(tags)
        if year is None:
            (year, timestamp) = self.get_year(tags)
        duration = int(info.get_duration() / 1000000000)

        if version != "":
            title += " (%s)" % version

        # If no artists tag, use album artist
        if artists == "":
            artists = album_artists
        # if artists is always null, no album artists too,
        # use composer/performer
        if artists == "":
            artists = performers
            album_artists = composers
            if artists == "":
                artists = album_artists
            if artists == "":
                artists = _("Unknown")

        Logger.debug("CollectionScanner::add2db(): Restore stats")
        # Restore stats
        (track_pop, track_rate, track_ltime, album_mtime, track_loved,
         album_loved, album_pop,
         album_rate) = self.__history.get(name, duration)
        # If nothing in stats, use track mtime
        if album_mtime == 0:
            album_mtime = mtime

        Logger.debug("CollectionScanner::add2db(): Add artists %s" % artists)
        artist_ids = self.add_artists(artists, a_sortnames)

        Logger.debug("CollectionScanner::add2db(): "
                     "Add album artists %s" % album_artists)
        album_artist_ids = self.add_album_artists(album_artists, aa_sortnames)

        # User does not want compilations
        if self.__disable_compilations and not album_artist_ids:
            album_artist_ids = artist_ids

        missing_artist_ids = list(set(album_artist_ids) - set(artist_ids))
        # https://github.com/gnumdk/lollypop/issues/507#issuecomment-200526942
        # Special case for broken tags
        # Can't do more because don't want to break split album behaviour
        if len(missing_artist_ids) == len(album_artist_ids):
            artist_ids += missing_artist_ids

        Logger.debug("CollectionScanner::add2db(): Add album: "
                     "%s, %s" % (album_name, album_artist_ids))
        album_id = self.add_album(album_name, mb_album_id, album_artist_ids,
                                  uri, album_loved, album_pop, album_rate,
                                  mtime)

        genre_ids = self.add_genres(genres)

        # Add track to db
        Logger.debug("CollectionScanner::add2db(): Add track")
        track_id = App().tracks.add(title, uri, duration, tracknumber,
                                    discnumber, discname, album_id, year,
                                    timestamp, track_pop, track_rate,
                                    track_loved, track_ltime, mtime,
                                    mb_track_id)
        Logger.debug("CollectionScanner::add2db(): Update track")
        self.__update_track(track_id, artist_ids, genre_ids)
        Logger.debug("CollectionScanner::add2db(): Update album")
        SqlCursor.commit(App().db)
        self.__update_album(album_id, album_artist_ids, genre_ids, year,
                            timestamp)
        SqlCursor.commit(App().db)
        for genre_id in genre_ids:
            GLib.idle_add(self.emit, "genre-updated", genre_id, True)
        return track_id
    def __scan(self, uris, saved):
        """
            Scan music collection for music files
            @param uris as [str]
            @param saved as bool
            @thread safe
        """
        modifications = False
        if self.__history is None:
            self.__history = History()
        mtimes = App().tracks.get_mtimes()
        (new_tracks, new_dirs) = self.__get_objects_for_uris(uris)
        orig_tracks = App().tracks.get_uris()
        was_empty = len(orig_tracks) == 0

        count = len(new_tracks) + len(orig_tracks)
        # Add monitors on dirs
        if self.__inotify is not None:
            for d in new_dirs:
                if d.startswith("file://"):
                    self.__inotify.add_monitor(d)

        i = 0
        # Look for new files/modified file
        SqlCursor.add(App().db)
        try:
            to_add = []
            for uri in new_tracks:
                if self.__thread is None:
                    SqlCursor.remove(App().db)
                    return
                try:
                    GLib.idle_add(self.__update_progress, i, count)
                    f = Gio.File.new_for_uri(uri)
                    # We do not use time::modified because many tag editors
                    # just preserve this setting
                    try:
                        info = f.query_info("time::changed",
                                            Gio.FileQueryInfoFlags.NONE, None)
                        mtime = int(
                            info.get_attribute_as_string("time::changed"))
                    except:  # Fallback for remote fs
                        info = f.query_info("time::modified",
                                            Gio.FileQueryInfoFlags.NONE, None)
                        mtime = int(
                            info.get_attribute_as_string("time::modified"))
                    # If songs exists and mtime unchanged, continue,
                    # else rescan
                    if uri in orig_tracks:
                        orig_tracks.remove(uri)
                        i += 1
                        if not saved or mtime <= mtimes.get(uri, mtime + 1):
                            i += 1
                            continue
                        else:
                            SqlCursor.allow_thread_execution(App().db)
                            self.__del_from_db(uri)
                    # If not saved, use 0 as mtime, easy delete on quit
                    if not saved:
                        mtime = 0
                    # On first scan, use modification time
                    # Else, use current time
                    elif not was_empty:
                        mtime = int(time())
                    to_add.append((uri, mtime))
                except Exception as e:
                    Logger.error("CollectionScanner::__scan(mtime): %s" % e)
            if to_add or orig_tracks:
                modifications = True
            # Clean deleted files
            # Now because we need to populate history
            # Only if we are saving
            if saved:
                for uri in orig_tracks:
                    i += 1
                    GLib.idle_add(self.__update_progress, i, count)
                    self.__del_from_db(uri)
                    SqlCursor.allow_thread_execution(App().db)
            # Add files to db
            for (uri, mtime) in to_add:
                try:
                    Logger.debug("Adding file: %s" % uri)
                    i += 1
                    GLib.idle_add(self.__update_progress, i, count)
                    self.__add2db(uri, mtime)
                    SqlCursor.allow_thread_execution(App().db)
                except Exception as e:
                    Logger.error("CollectionScanner::__scan(add): %s, %s" %
                                 (e, uri))
        except Exception as e:
            Logger.error("CollectionScanner::__scan(): %s" % e)
        SqlCursor.commit(App().db)
        SqlCursor.remove(App().db)
        GLib.idle_add(self.__finish, modifications and saved)
        if not saved:
            self.__play_new_tracks(new_tracks)
        del self.__history
        self.__history = None
Ejemplo n.º 8
0
 def __scan_files(self, files, db_uris, scan_type):
     """
         Scan music collection for new audio files
         @param files as [str]
         @param db_uris as [str]
         @param scan_type as ScanType
         @return new track uris as [str]
         @thread safe
     """
     SqlCursor.add(App().db)
     i = 0
     # New tracks present in collection
     new_tracks = []
     # Get mtime of all tracks to detect which has to be updated
     db_mtimes = App().tracks.get_mtimes()
     count = len(files) + 1
     try:
         # Scan new files
         for (mtime, uri) in files:
             # Handle a stop request
             if self.__thread is None and scan_type != ScanType.EPHEMERAL:
                 raise Exception("Scan add cancelled")
             try:
                 if not self.__scan_to_handle(uri):
                     continue
                 if mtime > db_mtimes.get(uri, 0):
                     # If not saved, use 0 as mtime, easy delete on quit
                     if scan_type == ScanType.EPHEMERAL:
                         mtime = 0
                     # Do not use mtime if not intial scan
                     elif db_mtimes:
                         mtime = int(time())
                     Logger.debug("Adding file: %s" % uri)
                     self.__add2db(uri, mtime)
                     SqlCursor.allow_thread_execution(App().db)
                     new_tracks.append(uri)
             except Exception as e:
                 Logger.error("CollectionScanner:: __scan_add_files: % s" %
                              e)
             i += 1
             self.__update_progress(i, count)
         if scan_type != ScanType.EPHEMERAL and self.__thread is not None:
             # We need to check files are always in collections
             if scan_type == ScanType.FULL:
                 collections = App().settings.get_music_uris()
             else:
                 collections = None
             for uri in db_uris:
                 # Handle a stop request
                 if self.__thread is None:
                     raise Exception("Scan del cancelled")
                 in_collection = True
                 if collections is not None:
                     in_collection = False
                     for collection in collections:
                         if collection in uri:
                             in_collection = True
                             break
                 f = Gio.File.new_for_uri(uri)
                 if not in_collection or not f.query_exists():
                     self.del_from_db(uri, True)
                     SqlCursor.allow_thread_execution(App().db)
     except Exception as e:
         Logger.warning("CollectionScanner:: __scan_files: % s" % e)
     SqlCursor.commit(App().db)
     SqlCursor.remove(App().db)
     return new_tracks
Ejemplo n.º 9
0
    def save_track(self, genres, artists, a_sortnames, mb_artist_id,
                   album_artists, aa_sortnames, mb_album_artist_id, album_name,
                   mb_album_id, uri, album_loved, album_pop, album_rate,
                   album_synced, album_mtime, title, duration, tracknumber,
                   discnumber, discname, year, timestamp, track_mtime,
                   track_pop, track_rate, track_loved, track_ltime,
                   mb_track_id, bpm):
        """
            Add track to DB
            @param genres as str/None
            @param artists as str
            @param a_sortnames as str
            @param mb_artist_id as str
            @param album_artists as str
            @param aa_sortnames as str
            @param mb_album_artist_id as str
            @param album_name as str
            @param mb_album_id as str
            @param uri as str
            @param album_loved as int
            @param album_pop as int
            @param album_rate as int
            @param album_synced as int
            @param album_mtime as int
            @param title as str
            @param duration as int
            @param tracknumber as int
            @param discnumber as int
            @param discname as str
            @param year as int
            @param timestamp as int
            @param track_mtime as int
            @param track_pop as int
            @param track_rate as int
            @param track_loved as int
            @param track_ltime as int
            @param mb_track_id as str
            @param bpm as int
        """
        Logger.debug("CollectionScanner::save_track(): Add artists %s" %
                     artists)
        artist_ids = self.add_artists(artists, a_sortnames, mb_artist_id)

        Logger.debug("CollectionScanner::save_track(): "
                     "Add album artists %s" % album_artists)
        album_artist_ids = self.add_artists(album_artists, aa_sortnames,
                                            mb_album_artist_id)

        # User does not want compilations
        if self.__disable_compilations and not album_artist_ids:
            album_artist_ids = artist_ids

        missing_artist_ids = list(set(album_artist_ids) - set(artist_ids))
        # https://github.com/gnumdk/lollypop/issues/507#issuecomment-200526942
        # Special case for broken tags
        # Can't do more because don't want to break split album behaviour
        if len(missing_artist_ids) == len(album_artist_ids):
            artist_ids += missing_artist_ids

        Logger.debug("CollectionScanner::save_track(): Add album: "
                     "%s, %s" % (album_name, album_artist_ids))
        (album_added,
         album_id) = self.add_album(album_name, mb_album_id, album_artist_ids,
                                    uri, album_loved, album_pop, album_rate,
                                    album_synced, album_mtime)
        if genres is None:
            genre_ids = [Type.WEB]
        else:
            genre_ids = self.add_genres(genres)

        # Add track to db
        Logger.debug("CollectionScanner::save_track(): Add track")
        track_id = App().tracks.add(title, uri, duration, tracknumber,
                                    discnumber, discname, album_id, year,
                                    timestamp, track_pop, track_rate,
                                    track_loved, track_ltime, track_mtime,
                                    mb_track_id, bpm)
        Logger.debug("CollectionScanner::save_track(): Update track")
        self.update_track(track_id, artist_ids, genre_ids)
        Logger.debug("CollectionScanner::save_track(): Update album")
        SqlCursor.commit(App().db)
        self.update_album(album_id, album_artist_ids, genre_ids, year,
                          timestamp)
        SqlCursor.commit(App().db)
        for genre_id in genre_ids:
            # Be sure to not send Type.WEB
            if genre_id >= 0:
                GLib.idle_add(self.emit, "genre-updated", genre_id, True)
        if album_added:
            GLib.idle_add(self.emit, "album-updated", album_id, True)
        return (track_id, album_id)