Example #1
0
    def _displayArtistMusic(self, artist, albums, singles):
        if albums:
            print("%d albums by %s:" % (len(albums),
                                         Style.bright(Fg.blue(artist.name))))
            for alb in albums:
                print("%s %s" % (str(alb.getBestDate()).center(17),
                                  alb.title))

        if singles:
            print("%d single tracks by %s" %
                  (len(singles), Style.bright(Fg.blue(artist.name))))
            for s in singles:
                print("\t%s" % (s.title))
Example #2
0
    def _displayArtistMusic(self, artist, albums, singles):
        if albums:
            print(u"%d albums by %s:" %
                  (len(albums), Style.bright(Fg.blue(artist.name))))
            for alb in albums:
                print(u"%s %s" %
                      (str(alb.getBestDate()).center(17), alb.title))

        if singles:
            print(u"%d single tracks by %s" %
                  (len(singles), Style.bright(Fg.blue(artist.name))))
            for s in singles:
                print(u"\t%s" % (s.title))
Example #3
0
    def _getArtist(self, session, name, resolved_artist):
        artist_rows = session.query(Artist).filter_by(
            name=name, lib_id=self._lib.id).all()
        if artist_rows:
            if len(artist_rows) > 1 and resolved_artist:
                # Use previously resolved artist for this directory.
                artist = resolved_artist
            elif len(artist_rows) > 1:
                # Resolve artist
                try:
                    heading = "Multiple artists names '%s'" % \
                              artist_rows[0].name
                    artist = console.selectArtist(Fg.blue(heading),
                                                  choices=artist_rows,
                                                  allow_create=True)
                except PromptExit:
                    log.warn("Duplicate artist requires user "
                             "intervention to resolve.")
                    artist = None
                else:
                    if artist not in artist_rows:
                        session.add(artist)
                        session.flush()
                        pout(Fg.blue("Updating artist") + ": " + name)
                    resolved_artist = artist
            else:
                # Artist match
                artist = artist_rows[0]
        else:
            # New artist
            artist = Artist(name=name, lib_id=self._lib.id)
            session.add(artist)
            session.flush()
            pout(Fg.green("Adding artist") + ": " + name)

        return artist, resolved_artist
Example #4
0
    def handleDirectory(self, d, _):
        pout(Fg.blue("Syncing directory") + ": " + str(d))
        audio_files = list(self._file_cache)
        self._file_cache = []

        image_files = self._dir_images
        self._dir_images = []

        if not audio_files:
            return

        d_datetime = datetime.fromtimestamp(getctime(d))

        album_type = self._albumTypeHint(audio_files) or LP_TYPE

        album = None
        session = self._db_session
        for audio_file in audio_files:
            try:
                album = self._syncAudioFile(audio_file, album_type, d_datetime,
                                            session)
            except Exception as ex:
                # TODO: log and skip????
                raise

        if album:
            # Directory images.
            for img_file in image_files:
                img_type = art.matchArtFile(img_file)
                if img_type is None:
                    log.warn("Skipping unrecognized image file: %s" % img_file)
                    continue

                new_img = Image.fromFile(img_file, img_type)
                if new_img:
                    new_img.description = os.path.basename(img_file)
                    syncImage(
                        new_img, album if img_type in IMAGE_TYPES["album"] else
                        album.artist, session)
                else:
                    log.warn("Invalid image file: " + img_file)

        session.commit()
        if self.args.monitor:
            self._watchDir(d)
Example #5
0
 def makeResp(self, attrs=None, child=None, status=True, body=None):
     attrs = attrs or {}
     resp = self.req.response
     if isinstance(status, int) and not isinstance(status, bool):
         resp.status = status
         status = Command.E_GENERIC
     if body is None:
         body = self.makeBody(attrs, child, status)
     elif isinstance(body, ET.Element):
         body = "%s%s" % (XML_HEADER, ET.tostring(body).decode("utf-8"))
     pretty = None
     if "f" in self.req.params:
         if self.req.params["f"] == "jsonp" and "callback" in self.req.params:
             dct = self.toDict(body)
             if log.isEnabledFor(logging.DEBUG):
                 pretty = "%s(%s)" % (self.req.params["callback"],
                                      json.dumps(dct, indent=3))
             else:
                 pretty = body
             txt = "%s(%s)" % (self.req.params["callback"], json.dumps(dct))
             resp.text = txt
             resp.content_type = "application/javascript"
         elif self.req.params["f"] == "json":
             dct = self.toDict(body)
             if log.isEnabledFor(logging.DEBUG):
                 pretty = json.dumps(dct, indent=3)
             else:
                 pretty = body
             resp.text = json.dumps(dct)
             resp.content_type = "application/json"
     if not pretty:
         pretty = body
         resp.text = body
         resp.content_type = "text/xml"
     resp.charset = "UTF-8"
     log.info("Response(%s): %s" % (self.name, Fg.blue(pretty)))
     return resp
Example #6
0
    def _run(self):
        session = self.db_session

        artists = session.query(Artist)\
                         .filter(Artist.name == self.args.artist).all()
        if not artists:
            print(u"Artist not found: %s" % self.args.artist)
            return 1
        elif len(artists) > 1:
            artist = selectArtist(Fg.blue("Select which '%s' to split...") %
                                  artists[0].name,
                                  choices=artists,
                                  allow_create=False)
        else:
            artist = artists[0]

        # Albums by artist
        albums = list(artist.albums) + artist.getAlbumsByType(VARIOUS_TYPE)
        # Singles by artist and compilations the artist appears on
        singles = artist.getTrackSingles()

        if len(albums) < 2 and len(singles) < 2:
            print("%d albums and %d singles found for '%s', nothing to do." %
                  (len(albums), len(singles), artist.name))
            return 0

        self._displayArtistMusic(artist, albums, singles)

        def _validN(_n):
            return _n > 1 and _n <= len(albums)

        n = prompt("\nEnter the number of distinct artists",
                   type_=int,
                   validate=_validN)
        new_artists = []
        for i in range(1, n + 1):
            print(Style.bright(u"\n%s #%d") % (Fg.blue(artist.name), i))

            # Reuse original artist for first
            a = artist if i == 1 else Artist(name=artist.name,
                                             date_added=artist.date_added)
            a.origin_city = prompt("   City", required=False)
            a.origin_state = prompt("   State", required=False)
            a.origin_country = prompt("   Country",
                                      required=False,
                                      type_=normalizeCountry)

            new_artists.append(a)

        if not Artist.checkUnique(new_artists):
            print(Fg.red("Artists must be unique."))
            return 1

        for a in new_artists:
            session.add(a)

        # New Artist objects need IDs
        session.flush()

        print(Style.bright("\nAssign albums to the correct artist."))
        for i, a in enumerate(new_artists):
            print(
                "Enter %s%d%s for %s from %s%s%s" %
                (Style.BRIGHT, i + 1, Style.RESET_BRIGHT, a.name, Style.BRIGHT,
                 a.origin(country_code="iso3c",
                          title_case=False), Style.RESET_BRIGHT))

        # prompt for correct artists
        def _promptForArtist(_text):
            a = prompt(_text,
                       type_=int,
                       choices=range(1,
                                     len(new_artists) + 1))
            return new_artists[a - 1]

        print("")
        for alb in albums:
            # Get some of the path to help the decision
            path = commonDirectoryPrefix(*[t.path for t in alb.tracks])
            path = os.path.join(*path.split(os.sep)[-2:])

            a = _promptForArtist("%s (%s)" % (alb.title, path))
            if alb.type != "various":
                alb.artist_id = a.id
            for track in alb.tracks:
                if track.artist_id == artist.id:
                    track.artist_id = a.id

        print("")
        for track in singles:
            a = _promptForArtist(track.title)
            track.artist_id = a.id

        session.flush()
Example #7
0
    def _run(self):
        session = self.db_session

        merge_list = []
        for artist_arg in self.args.artists:
            artists = session.query(Artist)\
                             .filter(Artist.name == artist_arg).all()
            if len(artists) == 1:
                merge_list.append(artists[0])
            elif len(artists) > 1:
                merge_list += selectArtist(
                    Fg.blue("Select the artists to merge..."),
                    multiselect=True,
                    choices=artists)

        if len(merge_list) > 1:
            # Reuse lowest id
            artist_ids = {a.id: a for a in merge_list}
            min_id = min(*artist_ids.keys())
            artist = artist_ids[min_id]

            mc = mostCommonItem
            new_artist = promptArtist(
                "Merging %d artists into new artist..." % len(merge_list),
                default_name=mc([a.name for a in merge_list]),
                default_city=mc([a.origin_city for a in merge_list]),
                default_state=mc([a.origin_state for a in merge_list]),
                default_country=mc([a.origin_country for a in merge_list]),
                artist=artist)
        else:
            print("Nothing to do, %s" %
                  ("artist not found"
                   if not len(merge_list) else "only one artist found"))
            return 1

        assert (new_artist in merge_list)

        for artist in merge_list:
            if artist is new_artist:
                continue

            with session.no_autoflush:
                for alb in list(artist.albums):
                    # FIXME: use constant
                    if alb.type != "various":
                        alb.artist_id = new_artist.id
                        artist.albums.remove(alb)
                        with session.no_autoflush:
                            new_artist.albums.append(alb)

                    for track in alb.tracks:
                        if track.artist_id == artist.id:
                            # gotta check in case alb is type various
                            track.artist_id = new_artist.id

                for track in artist.getTrackSingles():
                    track.artist_id = new_artist.id

            # flush to get new artist ids in sync before delete, otherwise
            # cascade happens.
            session.flush()
            session.delete(artist)

            session.flush()
Example #8
0
def test_ansi():
    CSI = "\033["

    class KnownFg:
        (GREY, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN,
         WHITE) = [*range(30, 38)]
        RESET = 39

    class KnownBg:
        (GREY, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN,
         WHITE) = [*range(40, 48)]
        RESET = 49

    class KnownStyle:
        (RESET_ALL, BRIGHT, DIM, ITALICS, UNDERLINE, BLINK_SLOW, BLINK_FAST,
         INVERSE) = [*range(0, 8)]
        STRIKE_THRU = 9
        (RESET_BRIGHT, RESET_ITALICS, RESET_UNDERLINE, RESET_BLINK_SLOW,
         RESET_BLINK_FAST, RESET_INVERSE) = [*range(22, 28)]
        RESET_STRIKE_THRU = 29
        RESET_DIM = RESET_BRIGHT

    def mkcode(c):
        return "{}{}m".format(CSI, c)

    ansi_init(True)

    for known_palette, palette in (
        (KnownFg, Fg),
        (KnownBg, Bg),
        (KnownStyle, Style),
    ):
        code_list = [c for c in dir(known_palette) if c == c.upper()]

        # Test values and members
        if known_palette in (KnownFg, KnownBg):
            assert len(code_list) == 9
        else:
            assert len(code_list) == 17

        for c in code_list:
            assert type(getattr(palette, c)) is str
            assert mkcode(getattr(known_palette, c)) == \
                getattr(palette, c)
            if palette is Style and c.lower().startswith("reset_"):
                # Style.reset_*() functions don't exist
                continue

            assert isinstance(getattr(palette, c.lower()), types.FunctionType)

            # Test palette functions vs codes
            assert Fg.BLUE + "SNFU" + Fg.RESET == Fg.blue("SNFU")

            code = getattr(palette, c)
            if palette is Style:
                reset = getattr(palette, "RESET_{}".format(c))
            else:
                reset = getattr(palette, "RESET")
            func = getattr(palette, c.lower())

            assert code + "SNFU" + reset == func("SNFU")
Example #9
0
    def _run(self):
        session = self.db_session

        lib = session.query(Library).filter(Library.name == self.args.lib).one()
        artists = session.query(Artist).filter(Artist.lib_id == lib.id)\
                                       .filter(Artist.name == self.args.artist)\
                                       .all()
        if not artists:
            print("Artist not found: %s" % self.args.artist)
            return 1
        elif len(artists) > 1:
            artist = selectArtist(Fg.blue("Select which '%s' to split...") %
                                  artists[0].name,
                                  choices=artists, allow_create=False)
        else:
            artist = artists[0]

        # Albums by artist
        albums = list(artist.albums) + artist.getAlbumsByType(VARIOUS_TYPE)
        # Singles by artist and compilations the artist appears on
        singles = artist.getTrackSingles()

        if len(albums) < 2 and len(singles) < 2:
            print("%d albums and %d singles found for '%s', nothing to do." %
                    (len(albums), len(singles), artist.name))
            return 0

        self._displayArtistMusic(artist, albums, singles)

        def _validN(_n):
            try:
                return _n > 1 and _n <= len(albums)
            except Exception:
                return False

        n = prompt("\nEnter the number of distinct artists", type_=int,
                   validate=_validN)
        new_artists = []
        for i in range(1, n + 1):
            print(Style.bright("\n%s #%d") % (Fg.blue(artist.name), i))

            # Reuse original artist for first
            a = artist if i == 1 else Artist(name=artist.name,
                                             date_added=artist.date_added,
                                             lib_id=artist.lib_id)
            a.origin_city = prompt("   City", required=False)
            a.origin_state = prompt("   State", required=False)
            a.origin_country = prompt("   Country", required=False,
                                      type_=normalizeCountry)

            new_artists.append(a)

        if not Artist.checkUnique(new_artists):
            print(Fg.red("Artists must be unique."))
            return 1

        for a in new_artists:
            session.add(a)

        # New Artist objects need IDs
        session.flush()

        print(Style.bright("\nAssign albums to the correct artist."))
        for i, a in enumerate(new_artists):
            print("Enter %s%d%s for %s from %s%s%s" %
                  (Style.BRIGHT, i + 1, Style.RESET_BRIGHT,
                  a.name,
                  Style.BRIGHT, a.origin(country_code="iso3c",
                                         title_case=False),
                  Style.RESET_BRIGHT))

        # prompt for correct artists
        def _promptForArtist(_text):
            a = prompt(_text, type_=int,
                       choices=range(1, len(new_artists) + 1))
            return new_artists[a - 1]

        print("")
        for alb in albums:
            # Get some of the path to help the decision
            path = commonDirectoryPrefix(*[t.path for t in alb.tracks])
            path = os.path.join(*path.split(os.sep)[-2:])

            a = _promptForArtist("%s (%s)" % (alb.title, path))
            if alb.type != VARIOUS_TYPE:
                alb.artist_id = a.id
            for track in alb.tracks:
                if track.artist_id == artist.id:
                    track.artist_id = a.id

        print("")
        for track in singles:
            a = _promptForArtist(track.title)
            track.artist_id = a.id

        session.flush()
Example #10
0
    def _run(self):
        session = self.db_session

        lib = session.query(Library).filter(Library.name == self.args.lib).one()

        merge_list = []
        for artist_arg in self.args.artists:
            artists = session.query(Artist)\
                             .filter(Artist.name == artist_arg)\
                             .filter(Artist.lib_id == lib.id).all()
            if len(artists) == 1:
                merge_list.append(artists[0])
            elif len(artists) > 1:
                merge_list += selectArtist(
                        Fg.blue("Select the artists to merge..."),
                        multiselect=True, choices=artists)

        if len(merge_list) > 1:
            # Reuse lowest id
            artist_ids = {a.id: a for a in merge_list}
            min_id = min(*artist_ids.keys())
            artist = artist_ids[min_id]

            mc = mostCommonItem
            new_artist = promptArtist(
                    "Merging %d artists into new artist..." % len(merge_list),
                    default_name=mc([a.name for a in merge_list]),
                    default_city=mc([a.origin_city for a in merge_list]),
                    default_state=mc([a.origin_state for a in merge_list]),
                    default_country=mc([a.origin_country for a in merge_list]),
                    artist=artist)
            new_artist.lib_id = lib.id
        else:
            print("Nothing to do, %s" %
                    ("artist not found" if not len(merge_list)
                                        else "only one artist found"))
            return 1

        assert(new_artist in merge_list)

        for artist in merge_list:
            if artist is new_artist:
                continue

            with session.no_autoflush:
                for alb in list(artist.albums):
                    if alb.type != VARIOUS_TYPE:
                        alb.artist_id = new_artist.id
                        artist.albums.remove(alb)
                        with session.no_autoflush:
                            new_artist.albums.append(alb)

                    for track in alb.tracks:
                        if track.artist_id == artist.id:
                            # gotta check in case alb is type various
                            track.artist_id = new_artist.id

                for track in artist.getTrackSingles():
                    track.artist_id = new_artist.id

            # flush to get new artist ids in sync before delete, otherwise
            # cascade happens.
            session.flush()
            session.delete(artist)

            session.flush()
Example #11
0
 def mkkey(k):
     return Style.bright(Fg.blue(str(k)))
Example #12
0
 def mkkey(k):
     return Style.bright(Fg.blue(str(k)))
Example #13
0
 def mkval(v):
     return Style.bright(Fg.blue(str(v)))