Example #1
0
    def checkAlbumsWithDifferentReleases():
        c = MusicDatabase.getCursor()
        sql = text('SELECT album_id, path, '
                   '       COUNT(DISTINCT musicbrainz.release.id) '
                   '  FROM songs_mb, albums, album_songs, musicbrainz.release '
                   ' WHERE albums.id = album_songs.album_id '
                   '   AND releaseid = mbid '
                   '   AND songs_mb.song_id = album_songs.song_id '
                   ' GROUP BY album_songs.album_id, albums.path '
                   ' HAVING COUNT(DISTINCT musicbrainz.release.id) > 1')

        result = c.execute(sql)
        table = [(str(album_id), path, str(count))
                 for album_id, path, count in result.fetchall()]
        if table:
            table.insert(0, ('ALBUMID', 'PATH', 'NUMBER OF RELEASES'))
            aligned = alignColumns(table, (False, True, False))
            print('Albums that contain songs from different releases:')
            for line in aligned:
                print(line)
        return bool(table)
Example #2
0
 def checkMusicBrainzTags():
     c = MusicDatabase.getCursor()
     sql = text('SELECT id, path FROM songs '
                'WHERE root = :root '
                '  AND NOT EXISTS (SELECT song_id '
                '                   FROM songs_mb '
                '                  WHERE recordingid is not NULL '
                '                    AND song_id = id)'
                '     ORDER BY id')
     table = []
     for root in config['musicbrainzTaggedMusicPaths']:
         result = c.execute(sql, {'root': root})
         table.extend((str(song_id), path)
                      for song_id, path in result.fetchall())
     if table:
         table.insert(0, ('SONGID', 'PATH'))
         aligned = alignColumns(table, (False, True))
         print('Songs which should have musicbrainz tags but don\'t:')
         for line in aligned:
             print(line)
     return bool(table)
Example #3
0
    def checkAlbumsWithDifferentFormats():
        c = MusicDatabase.getCursor()
        sql = text('select id, path, format '
                   '  from albums, album_properties '
                   ' where id in (select album_id '
                   '                from (select  album_id, count(*) '
                   '                        from album_properties '
                   '                    group by album_id '
                   '                      having count(*)>1) '
                   '                  as foo) '
                   '   and id = album_id')

        result = c.execute(sql)
        table = [(str(album_id), path, audioFormat)
                 for album_id, path, audioFormat in result.fetchall()]
        if table:
            table.insert(0, ('ALBUMID', 'PATH', 'FORMAT'))
            aligned = alignColumns(table, (False, True, True))
            print('Albums that contain songs with different formats:')
            for line in aligned:
                print(line)
        return bool(table)
Example #4
0
    def performBackup(self):
        size_balance = SizeBalance()
        user_input = ''

        for dirpath, dirnames, filenames in os.walk(self.source, topdown=True):
            print(dirpath)
            filenames.sort()
            dirnames.sort()
            for pattern in reversed(self.priorityPatterns):
                for i in range(len(filenames)):
                    if pattern in filenames[i]:
                        filenames.insert(0, filenames.pop(i))

                for i in range(len(dirnames)):
                    if pattern in dirnames[i]:
                        dirnames.insert(0, dirnames.pop(i))

            tgt_dirpath = self.targetPath(dirpath)

            try:
                remoteDirCache = {
                    os.path.join(tgt_dirpath, x.filename): x
                    for x in self.sftp.listdir_attr(tgt_dirpath)
                }
            except FileNotFoundError:
                src_attr = os.stat(self.source)
                mkdir = CreateRemoteDir(self.source, tgt_dirpath, src_attr)
                mkdir.run(self.sftp)
                remoteDirCache = {}

            pending_changes = []
            for filename in filenames:
                path = os.path.join(dirpath, filename)
                target_path = self.targetPath(path)
                shouldCopy, isDifferent, src_attr, tgt_attr = \
                    self.compareSrcAndTgt(path, target_path, remoteDirCache)
                if shouldCopy:
                    upload = Upload(path, target_path, src_attr, tgt_attr)
                    pending_changes.append(upload)

            for dirname in dirnames:
                path = os.path.join(dirpath, dirname)
                target_path = self.targetPath(path)

                try:
                    attr = remoteDirCache[target_path]
                    if not stat.S_ISDIR(attr.st_mode):
                        print('Directory %s is a file in target %s (%s)' %
                              (Color.Filename + path + Color.ENDC,
                               Color.Host + self.target + Color.ENDC,
                               Color.Filename + target_path + Color.ENDC))
                        sys.exit(1)
                except (KeyError, FileNotFoundError):
                    src_attr = os.stat(path)
                    upload = CreateRemoteDir(path, target_path, src_attr)
                    pending_changes.append(upload)

            try:
                attr = self.sftp.stat(tgt_dirpath)
                iterate_tgt_dirpath = stat.S_ISDIR(attr.st_mode)
            except FileNotFoundError:
                iterate_tgt_dirpath = False

            if iterate_tgt_dirpath:
                for f_attr in remoteDirCache.values():
                    is_file = stat.S_ISREG(f_attr.st_mode)
                    is_dir = stat.S_ISDIR(f_attr.st_mode)
                    if ((is_file and f_attr.filename not in filenames)
                            or (is_dir and f_attr.filename not in dirnames)):
                        tgt_filename = os.path.join(tgt_dirpath,
                                                    f_attr.filename)
                        path = os.path.join(dirpath, f_attr.filename)
                        remove = RemoveRemote(path,
                                              tgt_filename,
                                              tgt_attr=f_attr)
                        pending_changes.append(remove)

            if any(c.requires_confirmation() for c in pending_changes):
                pending_changes.sort()
                if any(isinstance(c, RemoveRemote) for c in pending_changes):
                    descs = [c.describe_change() for c in pending_changes]
                else:
                    descs = [
                        c.describe_change() for c in pending_changes
                        if c.requires_confirmation()
                    ]
                descs = [x for x in chain(*descs)]
                aligned_descs = alignColumns(descs, (True, True, False, True))

                txt = ('The following changes in %s require confirmation.\n'
                       '%s\n'
                       f'Submit changes to %s? \n' %
                       (Color.Filename + dirpath + Color.ENDC,
                        '\n'.join(aligned_descs),
                        Color.Host + self.server + Color.ENDC + ':' +
                        Color.Filename + tgt_dirpath + Color.ENDC))
                if user_input not in ['a', 'l']:
                    options = [
                        'S', 'a', 'k', 'l', 'p', 'c', 'll', 'lr', 'llr', 'lrr'
                    ]
                    helpers = {
                        'c':
                        partial(self.compareChangesInTasks,
                                tasks=pending_changes),
                        'll':
                        partial(self.listLocalDir,
                                path=dirpath,
                                pending_changes=pending_changes),
                        'lr':
                        partial(self.listRemoteDir,
                                path=tgt_dirpath,
                                pending_changes=pending_changes),
                        'llr':
                        partial(self.listLocalDir,
                                path=dirpath,
                                pending_changes=pending_changes,
                                recursive=True),
                        'lrr':
                        partial(self.listRemoteDir,
                                path=tgt_dirpath,
                                pending_changes=pending_changes,
                                recursive=True)
                    }
                    options_long = [
                        '(s)ubmit', 'submit (a)lways', '(k)eep remote files',
                        'keep a(l)ways', 'ski(p)', '(c)ompare',
                        '(ll) list local', '(lr) list remote',
                        '(llr) list local recursive',
                        '(lrr) list remote recursive'
                    ]
                    user_input = self.askUser(txt, options, options_long,
                                              helpers)
                else:
                    print(txt + user_input)

                for change in pending_changes:
                    if not change.requires_confirmation():
                        size_balance += change.run(self.sftp)
                        continue

                    if user_input == 'p':
                        continue

                    change.set_overwrite(user_input in ['s', 'a'])

                    size_balance += change.run(self.sftp)
            else:  # changes don't require confirmation
                for change in pending_changes:
                    size_balance += change.run(self.sftp)

        print('---------------------------------------')
Example #5
0
def print_change_description(descs, prefix='', end='\n'):
    if isinstance(descs, tuple):
        print(prefix + ' '.join(descs), end=end)
        return
    print(prefix + '\n'.join(alignColumns(descs, (True, True, False, True))),
          end=end)
Example #6
0
def print_song_info(song, userID=None, print_analysis=True):  # noqa: C901
    song.loadMetadataInfo()
    print("----------")
    try:
        filesize = "%d bytes" % os.path.getsize(song.path())
    except FileNotFoundError:
        filesize = "File not found"
    print("%s (%s)" % (song.path(), filesize))
    print("song id:", song.id)

    rating = song.userRating(userID)
    if rating:
        print("my rating:", '*' * rating, '(%d/10)' % rating)
    avgrating = song.avgRating(userID)
    if avgrating:
        print("avg rating:", '*' * avgrating, '(%d/10)' % avgrating)

    if not rating and not avgrating:
        rating = song.rating(userID)
        print("rating:", '*' * rating, '(%d/10)' % rating)

    for k in sorted(song.metadata):
        v = song.metadata[k]
        print(TerminalColors.Header + str(k) + TerminalColors.ENDC +
              ' : ' + str(v)[:100])
        if k in ('TMCL', 'TIPL'):
            if len(v) > 1:
                print('*** More than one %s tag in file: ' +
                      'The following list might be incomplete ***')
            txt_repr = v[0]
            m = re.search(r'people=', txt_repr)
            if m:
                pos_bracket = simple_find_matching_square_bracket(
                    txt_repr, m.end())
                txt = txt_repr[m.end():pos_bracket + 1]
                list_artists = eval(txt)
                for instrument, artist in list_artists:
                    print('    %s : %s' % (instrument, artist))

    print("file sha256sum: ", song.fileSha256sum())
    print("audio track sha256sum: ", song.audioSha256sum())

    print('duration: %s s' % formatLength(song.duration()))
    print(('duration without silences: %s s' %
           formatLength(song.durationWithoutSilences())),
          ' (silences: %s + %s)' %
          (formatLength(song.silenceAtStart()),
           formatLength(song.silenceAtEnd())))
    printProperties(song)
    if song.coverWidth():
        print('cover:  %dx%d' %
              (song.coverWidth(), song.coverHeight()))

    if print_analysis:
        print_song_info_analysis(song)

    similar_pairs = MusicDatabase.getSimilarSongsToSongID(song.id)
    if similar_pairs:
        print('Similar songs:')

    similarSongs = []
    for otherID, offset, similarity in similar_pairs:
        otherSong = getSongs(songID=otherID)[0]
        try:
            audioComparison = song.audioCmp(otherSong,
                                            interactive=False)
        except (DifferentLengthException):
            audioComparison = 2
            colors = {'length': TerminalColors.Magenta}
        except (CantCompareSongsException):
            audioComparison = 3
            colors = {}
        else:
            colors = {}
        colors['bitrate'] = TerminalColors.Blue
        propertiesString = getPropertiesAsString(otherSong, colors)
        color = {-1: TerminalColors.Worse,
                 0: TerminalColors.ENDC,
                 1: TerminalColors.Better,
                 2: TerminalColors.DifferentLength,
                 3: TerminalColors.CantCompareSongs}[audioComparison]
        songpath = (color + ' %d ' % otherID + otherSong.path() +
                    TerminalColors.ENDC)
        songprop = '(%d %f %s)' % (offset, similarity,
                                   propertiesString)
        similarSongs.append([songpath] + songprop.split())
    aligned = alignColumns(similarSongs)
    for line in aligned:
        print(line)