コード例 #1
0
ファイル: info.py プロジェクト: benschumacher/MishMash
    def _run(self):
        logo = figlet_format("``MishMash``", font="graffiti")
        print(Fg.green(logo, Style.BRIGHT))

        self._displayMetaInfo()

        all_libs = {
            l.name: l
            for l in self.db_session.query(Library).filter(
                Library.id > NULL_LIB_ID).all()
        }
        lib_args = set(self.args.libs or all_libs.keys())
        for lib in lib_args:
            if lib not in all_libs.keys():
                print(Fg.red(f"Unknown library: {lib}"))
                continue

            lib = all_libs[lib]

            if self.args.show_artists:
                print(Fg.green(f"\n=== {lib.name} library artists ==="))
                self._displayArtists(lib)
            else:
                print(Fg.green(f"\n=== {lib.name} library ==="))
                self._displayLibraryInfo(lib)
コード例 #2
0
ファイル: utils.py プロジェクト: nicfit/mishmash
def syncImage(img, current, session):
    """Add or updated the Image."""
    def _img_str(i):
        return "%s - %s" % (i.type, i.description)

    for db_img in current.images:
        img_info = (img.type, img.md5, img.size)
        db_img_info = (db_img.type, db_img.md5, db_img.size)

        if db_img_info == img_info:
            img = None
            break
        elif (db_img.type == img.type and
                db_img.description == img.description):

            if img.md5 != db_img.md5:
                # Update image
                current.images.remove(db_img)
                current.images.append(img)
                session.add(current)
                pout(Fg.green("Updating image") + ": " + _img_str(img))
            img = None
            break

    if img:
        # Add image
        current.images.append(img)
        session.add(current)
        pout(Fg.green("Adding image") + ": " + _img_str(img))
コード例 #3
0
def promptArtist(text,
                 name=None,
                 default_name=None,
                 default_city=None,
                 default_state=None,
                 default_country=None,
                 artist=None):
    if text:
        print(text)

    if name is None:
        name = prompt(Fg.green("Artist name"), default=default_name)

    origin = {}
    for o in ("city", "state", "country"):
        origin["origin_%s" % o] = prompt("   %s" % Fg.green(o.title()),
                                         default=locals()["default_%s" % o],
                                         required=False)

    if not artist:
        artist = Artist(name=name, **origin)
    else:
        artist.name = name
        for o in origin:
            setattr(artist, o, origin[o])
    return artist
コード例 #4
0
ファイル: utils.py プロジェクト: benschumacher/MishMash
def syncImage(img, current, session):
    """Add or updated the Image."""
    def _img_str(i):
        return "%s - %s" % (i.type, i.description)

    for db_img in current.images:
        img_info = (img.type, img.md5, img.size)
        db_img_info = (db_img.type, db_img.md5, db_img.size)

        if db_img_info == img_info:
            img = None
            break
        elif (db_img.type == img.type
              and db_img.description == img.description):

            if img.md5 != db_img.md5:
                # Update image
                current.images.remove(db_img)
                current.images.append(img)
                session.add(current)
                pout(Fg.green("Updating image") + ": " + _img_str(img))
            img = None
            break

    if img:
        # Add image
        current.images.append(img)
        session.add(current)
        pout(Fg.green("Adding image") + ": " + _img_str(img))
コード例 #5
0
ファイル: info.py プロジェクト: Python3pkg/MishMash
    def _run(self):
        session = self.db_session

        _output = []

        def _addOutput(k, v):
            _output.append(tuple((k, v)))

        def _printOutput(_format, _olist):
            k_width = max([len(k) for k, v in _olist if k])
            for k, v in _olist:
                if k:
                    print((_format.format(k=k.ljust(k_width), v=v)))
            _olist.clear()

        logo = figlet_format("``MishMash``", font="graffiti")
        print((Fg.green(logo, Style.BRIGHT)))

        def mkkey(k):
            return Style.bright(Fg.blue(str(k)))

        def mkval(v):
            return Style.bright(Fg.blue(str(v)))

        _addOutput(mkkey("Version"), mkval(version))
        _addOutput(mkkey("Database URL"), mkval(self.config.db_url))

        try:
            meta = session.query(Meta).one()
        except (ProgrammingError, OperationalError) as ex:
            printError("\nError querying metadata. Database may not be "
                       "initialized: %s" % str(ex))
            return 1

        _addOutput(mkkey("Database version"), mkval(meta.version))
        _addOutput(mkkey("Last sync"), mkval(meta.last_sync or "Never"))
        _addOutput(mkkey("Configuration file "),
                   mkval(self.args.config.filename or "None"))
        _printOutput("{k} : {v}", _output)

        def mkkey(k):
            return Style.bright(str(k))

        print("")
        for lib in session.query(Library)\
                          .filter(Library.id > NULL_LIB_ID).all():
            print((Fg.yellow("\n=== {} library ===").format(lib.name)))
            _addOutput(None, None)
            for name, orm_type in [
                ("tracks", Track),
                ("artists", Artist),
                ("albums", Album),
                ("tags", Tag),
            ]:
                count = session.query(orm_type).filter_by(lib_id=lib.id)\
                               .count()

                _addOutput(mkkey(count), name)
            _printOutput("{k} music {v}", _output)
コード例 #6
0
ファイル: mgmt.py プロジェクト: nicfit/mishmash
    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))
コード例 #7
0
ファイル: info.py プロジェクト: nicfit/mishmash
    def _run(self):
        logo = figlet_format("``MishMash``", font="graffiti")
        print(Fg.green(logo, Style.BRIGHT))

        self._displayMetaInfo()

        for lib in Library.iterall(self.db_session, names=self.args.libs):
            if self.args.show_artists:
                print(Fg.green(f"\n=== {lib.name} library artists ==="))
                self._displayArtists(lib)
            else:
                print(Fg.green(f"\n=== {lib.name} library ==="))
                self._displayLibraryInfo(lib)
コード例 #8
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))
コード例 #9
0
ファイル: translogger.py プロジェクト: nicfit/unsonic
 def _write_log(self, environ, method, req_uri, start, status, bytes):
     if bytes is None:
         bytes = '-'
     if time.daylight:
         offset = time.altzone / 60 / 60 * -100
     else:
         offset = time.timezone / 60 / 60 * -100
     if offset >= 0:
         offset = "+%0.4d" % (offset)
     elif offset < 0:
         offset = "%0.4d" % (offset)
     remote_addr = '-'
     if environ.get('HTTP_X_FORWARDED_FOR'):
         remote_addr = environ['HTTP_X_FORWARDED_FOR']
     elif environ.get('REMOTE_ADDR'):
         remote_addr = environ['REMOTE_ADDR']
     stat = status.split(None, 1)[0]
     if (environ.get("webob._parsed_query_vars")
             and environ.get("webob._parsed_query_vars")[0].get("u")):
         user = environ.get("webob._parsed_query_vars")[0].get("u")
         user = environ.get('REMOTE_USER') or user or None
     else:
         user = None
     if user:
         remote_addr = "%s@%s" % (user, remote_addr)
     d = {
         'REMOTE_ADDR': remote_addr,
         'REQUEST_METHOD': method,
         'REQUEST_URI': req_uri,
         'HTTP_VERSION': environ.get('SERVER_PROTOCOL'),
         'time': time.strftime('%d/%b/%Y:%H:%M:%S ', start) + offset,
         'status': stat,
         'bytes': bytes,
         'HTTP_REFERER': environ.get('HTTP_REFERER', '-'),
         'HTTP_USER_AGENT': environ.get('HTTP_USER_AGENT', '-'),
     }
     message = self.format % d
     stat = int(stat)
     if stat >= 200 and stat < 300:
         message = Fg.green(message)
     if stat >= 400 and stat < 500:
         message = Fg.yellow(message)
     if stat >= 500:
         message = Fg.red(message)
     self.logger.log(self.logging_level, message)
コード例 #10
0
def selectArtist(heading, choices=None, multiselect=False, allow_create=True):
    color = Fg.green
    artist = None
    name = None

    if heading:
        print(heading)

    while artist is None:
        if choices:
            name = choices[0].name
            for menu_num, a in enumerate(choices):
                print(("   %d) %s" % (menu_num + 1, a.origin())))
            menu_num += 1

            if not multiselect:
                if allow_create:
                    menu_num += 1
                    print(("   %d) Enter a new artist" % menu_num))

                choice = prompt("Which artist",
                                type_=int,
                                choices=list(range(1, menu_num + 1)))
                choice -= 1
                if choice < len(choices):
                    artist = choices[choice]
                # Otherwise fall through to select artist below
            else:

                def _validate(_resp):
                    try:
                        _ints = [
                            _i for _i in parseIntList(_resp)
                            if _i in range(1, menu_num + 1)
                        ]
                        return bool(_ints)
                    except:
                        return False

                resp = prompt(color("Choose one or more artists"),
                              validate=_validate)
                artists = []
                for choice in [i - 1 for i in parseIntList(resp)]:
                    artists.append(choices[choice])
                # XXX: blech, returning a list here and a single value below
                return artists

        if artist is None:
            artist = promptArtist(None, name=name)
            if choices:
                if not Artist.checkUnique(choices + [artist]):
                    print(
                        (Fg.red("Artist entered is not unique, try again...")))
                    artist = None

    assert (artist)
    return artist
コード例 #11
0
def test_ansi_formats():
    ansi_init(True)

    s = "Heavy Cream - Run Free"
    assert Fg.green(s, Style.BRIGHT,
                       Style.UNDERLINE,
                       Style.ITALICS) == \
           Style.BRIGHT + Style.UNDERLINE + Style.ITALICS + Fg.GREEN + s + \
           Fg.RESET + Style.RESET_ALL

    print("%(BLUE)sNice%(RESET)s" % Fg)
コード例 #12
0
ファイル: console.py プロジェクト: nicfit/mishmash
def promptArtist(text, name=None, default_name=None, default_city=None,
                 default_state=None, default_country=None, artist=None):
    if text:
        print(text)

    if name is None:
        name = prompt(Fg.green("Artist name"), default=default_name)

    origin = {}
    for o in ("city", "state", "country"):
        origin["origin_%s" % o] = prompt("   %s" % Fg.green(o.title()),
                                         default=locals()["default_%s" % o],
                                         required=False)

    if not artist:
        artist = Artist(name=name, **origin)
    else:
        artist.name = name
        for o in origin:
            setattr(artist, o, origin[o])
    return artist
コード例 #13
0
ファイル: console.py プロジェクト: nicfit/mishmash
def selectArtist(heading, choices=None, multiselect=False, allow_create=True):
    color = Fg.green
    artist = None
    name = None
    menu_num = 0

    if heading:
        print(heading)

    while artist is None:
        if choices:
            name = choices[0].name
            for menu_num, a in enumerate(choices, start=1):
                print("   %d) %s" % (menu_num + 1, a.origin()))

            if not multiselect:
                if allow_create:
                    menu_num += 1
                    print("   %d) Enter a new artist" % menu_num)

                choice = prompt("Which artist", type_=int,
                                choices=range(1, menu_num + 1))
                choice -= 1
                if choice < len(choices):
                    artist = choices[choice]
                # Otherwise fall through to select artist below
            else:
                def _validate(_resp):
                    try:
                        _ints = [_i for _i in parseIntList(_resp)
                                    if _i in range(1, menu_num + 1)]
                        return bool(_ints)
                    except Exception:
                        return False

                resp = prompt(color("Choose one or more artists"),
                              validate=_validate)
                artists = []
                for choice in [i - 1 for i in parseIntList(resp)]:
                    artists.append(choices[choice])
                # XXX: blech, returning a list here and a single value below
                return artists

        if artist is None:
            artist = promptArtist(None, name=name)
            if choices:
                if not Artist.checkUnique(choices + [artist]):
                    print(Fg.red("Artist entered is not unique, try again..."))
                    artist = None

    assert(artist)
    return artist
コード例 #14
0
ファイル: utils.py プロジェクト: benschumacher/MishMash
def deleteOrphans(session):
    num_orphaned_artists = 0
    num_orphaned_albums = 0
    num_orphaned_tracks = 0
    found_ids = set()

    # Tracks
    for track in session.query(Track).all():
        if not os.path.exists(track.path):
            pout(Fg.red("Removing track") + ": " + track.path)
            session.delete(track)
            num_orphaned_tracks += 1
            log.warn("Deleting track: %s" % str(track))

    session.flush()

    # Artists
    found_ids.clear()
    for artist in session.query(Artist).all():
        if (artist.id == VARIOUS_ARTISTS_ID or artist.id in found_ids):
            continue

        any_track = session.query(Track).filter(Track.artist_id == artist.id) \
                                        .first()
        any_album = session.query(Album).filter(Album.artist_id == artist.id) \
                                        .first()
        if not any_track and not any_album:
            log.warn("Deleting artist: %s" % str(artist))
            session.delete(artist)
            num_orphaned_artists += 1
        else:
            found_ids.add(artist.id)

    session.flush()

    # Albums
    found_ids.clear()
    for album in session.query(Album).all():
        if album.id in found_ids:
            continue

        any_track = session.query(Track).filter(Track.album_id == album.id) \
                                        .first()
        if not any_track:
            log.warn("Deleting album: %s" % str(album))
            session.delete(album)
            num_orphaned_albums += 1
        else:
            found_ids.add(album.id)

    return (num_orphaned_tracks, num_orphaned_artists, num_orphaned_albums)
コード例 #15
0
ファイル: utils.py プロジェクト: nicfit/mishmash
def deleteOrphans(session):
    num_orphaned_artists = 0
    num_orphaned_albums = 0
    num_orphaned_tracks = 0
    found_ids = set()

    # Tracks
    for track in session.query(Track).all():
        if not os.path.exists(track.path):
            pout(Fg.red("Removing track") + ": " + track.path)
            session.delete(track)
            num_orphaned_tracks += 1
            log.warn("Deleting track: %s" % str(track))
    session.flush()

    # Albums
    found_ids.clear()
    for album in session.query(Album).all():
        if album.id in found_ids:
            continue

        any_track = session.query(Track).filter(Track.album_id == album.id).first()
        if not any_track:
            log.warn("Deleting album: %s" % str(album))
            session.delete(album)
            num_orphaned_albums += 1
        else:
            found_ids.add(album.id)
    session.flush()

    # Artists
    found_ids.clear()
    for artist in session.query(Artist).all():
        if (artist.id == VARIOUS_ARTISTS_ID or
                artist.id in found_ids):
            continue

        any_track = session.query(Track).filter(Track.artist_id == artist.id) \
                                        .first()
        any_album = session.query(Album).filter(Album.artist_id == artist.id) \
                                        .first()
        if not any_track and (not any_album or not any_album.tracks):
            log.warn("Deleting artist: %s" % str(artist))
            session.delete(artist)
            num_orphaned_artists += 1
        else:
            found_ids.add(artist.id)
    session.flush()

    return (num_orphaned_tracks, num_orphaned_artists, num_orphaned_albums)
コード例 #16
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
コード例 #17
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)
コード例 #18
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
コード例 #19
0
ファイル: mgmt.py プロジェクト: nicfit/mishmash
    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()
コード例 #20
0
ファイル: info.py プロジェクト: nicfit/mishmash
 def mkkey(k):
     return Style.bright(Fg.blue(str(k)))
コード例 #21
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()
コード例 #22
0
    def _syncAudioFile(self, audio_file, album_type, d_datetime, session):
        path = audio_file.path
        info = audio_file.info
        tag = audio_file.tag

        album = None
        is_various = (album_type == VARIOUS_TYPE)

        if not info or not tag:
            log.warn("File missing %s, skipping: %s" %
                     ("audio" if not info else "tag/metadata", path))
            return
        elif None in (tag.title, tag.artist):
            log.warn("File missing required artist and/or title "
                     "metadata, skipping: %s" % path)
            return

        # Used when a duplicate artist is resolved for the entire directory.
        resolved_artist = None
        resolved_album_artist = None

        try:
            track = session.query(Track)\
                           .filter_by(path=path, lib_id=self._lib.id).one()
        except NoResultFound:
            track = None
        else:
            if datetime.fromtimestamp(getctime(path)) == track.ctime:
                # Track is in DB and the file is not modified.
                # stash the album though, we'll look for artwork
                # updates later
                album = track.album
                return

        # Either adding the track (track == None)
        # or modifying (track != None)

        artist, resolved_artist = self._getArtist(session, tag.artist,
                                                  resolved_artist)
        if tag.album_type != SINGLE_TYPE:
            if tag.album_artist and tag.artist != tag.album_artist:
                album_artist, resolved_album_artist = \
                        self._getArtist(session, tag.album_artist,
                                        resolved_album_artist)
            else:
                album_artist = artist

            if artist is None:
                # see PromptExit
                return

            album_artist_id = album_artist.id if not is_various \
                                              else VARIOUS_ARTISTS_ID
            album_rows = session.query(Album)\
                                .filter_by(title=tag.album,
                                           lib_id=self._lib.id,
                                           artist_id=album_artist_id).all()
            rel_date = tag.release_date
            rec_date = tag.recording_date
            or_date = tag.original_release_date

            if album_rows:
                if len(album_rows) > 1:
                    # This artist has more than one album with the same
                    # title.
                    raise NotImplementedError("FIXME")
                album = album_rows[0]

                album.type = album_type
                album.release_date = rel_date
                album.original_release_date = or_date
                album.recording_date = rec_date
                pout(Fg.yellow("Updating album") + ": " + album.title)
            elif tag.album:
                album = Album(title=tag.album,
                              lib_id=self._lib.id,
                              artist_id=album_artist_id,
                              type=album_type,
                              release_date=rel_date,
                              original_release_date=or_date,
                              recording_date=rec_date,
                              date_added=d_datetime)
                session.add(album)
                pout(Fg.green("Adding album") + ": " + album.title)

            session.flush()

        if not track:
            track = Track(audio_file=audio_file, lib_id=self._lib.id)
            self._num_added += 1
            pout(Fg.green("Adding track") + ": " + path)
        else:
            track.update(audio_file)
            self._num_modified += 1
            pout(Fg.yellow("Updating track") + ": " + path)

        genre = tag.genre
        genre_tag = None
        if genre:
            try:
                genre_tag = session.query(Tag)\
                                   .filter_by(name=genre.name,
                                              lib_id=self._lib.id).one()
            except NoResultFound:
                genre_tag = Tag(name=genre.name, lib_id=self._lib.id)
                session.add(genre_tag)
                session.flush()

        track.artist_id = artist.id
        track.album_id = album.id if album else None
        if genre_tag:
            track.tags.append(genre_tag)
        session.add(track)

        if album:
            # Tag images
            for img in tag.images:
                for img_type in art.TO_ID3_ART_TYPES:
                    if img.picture_type in \
                            art.TO_ID3_ART_TYPES[img_type]:
                        break
                    img_type = None

                if img_type is None:
                    log.warn("Skipping unsupported image type: %s" %
                             img.picture_type)
                    continue

                new_img = Image.fromTagFrame(img, img_type)
                if new_img:
                    syncImage(
                        new_img, album if img_type in IMAGE_TYPES["album"] else
                        album.artist, session)
                else:
                    log.warn("Invalid image in tag")

        return album
コード例 #23
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()
コード例 #24
0
ファイル: info.py プロジェクト: benschumacher/MishMash
 def mkkey(k):
     return Style.bright(Fg.blue(str(k)))
コード例 #25
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")
コード例 #26
0
ファイル: test_main.py プロジェクト: redshodan/nicfit.py
def test_NicfitApp_default(capfd):
    with pytest.raises(SystemExit):
        app.run([])
    out, _ = capfd.readouterr()
    assert out == Fg.red("\m/ {} \m/".format(Style.inverse("Welcome"))) + "\n"
コード例 #27
0
ファイル: info.py プロジェクト: Python3pkg/MishMash
 def mkval(v):
     return Style.bright(Fg.blue(str(v)))
コード例 #28
0
ファイル: mgmt.py プロジェクト: nicfit/mishmash
    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()