_TIMESTAMP_FORMAT = "%Y%m%d-%H%M%S"
_PRETTY_FORMAT = "%c"
PROCESS_START_TIME = time.time()  # time of importing this file
_MIN_REASONABLE_TIMESTAMP = parse_human_readable("20081130-000000")
_MAX_REASONABLE_TIMESTAMP = parse_human_readable("20380119-000000")
_SECONDS_PER_MINUTE = 60
_SECONDS_PER_HOUR = 60 * _SECONDS_PER_MINUTE
_SECONDS_PER_DAY = 24 * _SECONDS_PER_HOUR
def now():
def now_ms():
def process_start_time_ms():
def process_age_ms():
def get_human_readable(ts=None):
def get_human_readable_ms(ms_ts):
def get_pretty(ts=None):
def get_pretty_ms(ms_ts):
def parse_human_readable(human_readable_str):
def is_valid(ts):
def get_human_readable_duration(duration_s):
    def _pluralize(n, unit):
def get_human_readable_duration_ms(duration_ms): class CustomPrint(object):
    def __init__(self):
    def __call__(self, message=None, **kwargs):
    def default_write(self, message=None, **kwargs):
    def use_write_function(self, func): class AlbumError(Exception):
class Album(object):
def _compute_album_id(all_au_files):
def _standardize_tags(all_au_files, new_album_name=None):
    def __init__(self, all_au_files):
    def _sort(self):
    def standardize(self, new_album_name=None):
    def set_volume_and_import_timestamp(self, vol, imp_ts):
    def drop_payloads(self):
    def ensure_payloads(self):
    def title(self):
    def tags(self):
    def _get_tpe1_breakdown(self):
    def is_compilation(self):
    def artist_name(self):
    def all_artist_names():
    def import_timestamp(self):
    def __str__(self):
def from_directory(dirpath, fast=False): _MINIMUM_FRAMES = 100
_MINIMUM_REASONABLE_FILE_SIZE = 100<<10  # Files should be larger than 100k...
_MAXIMUM_REASONABLE_FILE_SIZE = 20<<20   # ...and smaller than 20MB.
class InvalidFileError(Exception):
def analyze(file_obj, au_file, compute_fingerprint=True, get_payload=True):
def sample_and_analyze(au_file, mp3_path_list): _LIBRARY_DATA_PREFIX = os.path.join(ROOT_DIR, "library", "data")
_WHITELIST_FILE = os.path.join(_LIBRARY_DATA_PREFIX, "artist-whitelist")
_MAPPINGS_FILE = os.path.join(_LIBRARY_DATA_PREFIX, "artist-mappings")
_MAPPINGS_SEP = u"\xbb\xbb\xbb"
_BASE_SPLIT_PATTERN = (
    r"\s*((feat\.?)|(ft\.)|(featuring)|(with)|(w/)|(and)|(&))"
    r"\s+(?P<feat>.*)")
_SPLIT_RES = [
    re.compile(r"%s%s%s" % (a, _BASE_SPLIT_PATTERN, b), re.IGNORECASE)
    for a, b in ((r"\(", r"\)"), (r"\[", r"\]"), ("", ""))]
    MAX_DIST = 10
    MAX_NORM_DIST = 0.25
def all():
def sort_key(artist_name):
def _standardize_simple(artist_name, whitelist, mappings):
def _standardize(artist_name, whitelist, mappings):
def standardize(artist_name):
def is_standardized(artist_name):
def split(artist_name):
def split_and_standardize(artist_name):
def suggest(name):
def _seq_to_whitelist(seq_of_names):
def reset_artist_whitelist(seq_of_names):
def _read_artist_whitelist_from_file(file_obj):
def _read_artist_mappings_from_file(file_obj):
def _init():
def merge_whitelist_and_mappings(whitelist, raw_mappings): class AudioFile(object):
    def __eq__(self, other):
        def _mutagen_id3_set(obj):
    def has_ufid(self):
    def ufid(self):
    def ufid_tag(self):
    def tpe1(self):
    def talb(self):
    def tit2(self):
    def canonical_directory(self, prefix=""):
    def canonical_filename(self):
    def canonical_path(self, prefix=""):
def _get_mp3(path):
def _tag_to_int(au_file, tag_key):
def scan_fast(path, _read_id3_hook=None):
def scan(path, _read_id3_hook=None): def _insert(target, table_name, insert_tuple):
def _insert_tags(conn, fingerprint, timestamp, mutagen_id3):
def _get_tags(conn, au_file, cutoff_timestamp):
def _audio_file_generator(conn, sql):
    def __init__(self, name):
    def _get_connection(self):
    def create_tables(self):
    def get_all(self):
    def get_all_imports(self):
    def get_by_import(self, vol, import_timestamp):
    def get_by_fingerprint(self, fingerprint):
    def begin_add(self, volume, import_timestamp):
    def update(self, au_file, timestamp):
    def __init__(self, volume, import_timestamp, conn):
    def add(self, au_file):
    def commit(self):
    def revert(self):
class Database(object):
class _AddTransaction(object): class Dropbox(object):
    def __init__(self, dropbox_path=None):
    def files(self):
    def scan_fast(self):
    def albums(self):
    def tracks(self): class ImportFileError(Exception):
def _fix_file_tags(au_file):
def standardize_file(au_file):
def write_file(au_file, prefix): class ImportTransaction(object):
    def __init__(self, db, volume, import_timestamp, tmp_prefix,
                 dry_run=True):
    def num_tracks(self):
    def add_album(self, alb, new_album_name=None):
    def commit(self, target_prefix):
import logging
import os
import sys
from chirp.common import timestamp
from chirp.common.conf import (LIBRARY_PREFIX, LIBRARY_DB,
                                   LIBRARY_TMP_PREFIX)
from chirp.common.printing import cprint
from chirp.library import album
from chirp.library import analyzer
from chirp.library import artists
from chirp.library import audio_file
from chirp.library import database
from chirp.library import dropbox
from chirp.library import import_file
from chirp.library import import_transaction
#!/usr/bin/env python



VOLUME_NUMBER = 1
IMPORT_SIZE_LIMIT = 0.95 * (3 << 30)  # 95% of 3GB.


def import_albums(dry_run):
    inbox = dropbox.Dropbox()
    prescan_timestamp = timestamp.now()
    error_count = 0
    album_count = 0
    seen_fp = {}

    db = database.Database(LIBRARY_DB)

    try:
        for alb in inbox.albums():
            alb.drop_payloads()
            album_count += 1
            cprint(u'#{num} "{title}"'.format(num=album_count, title=alb.title()))
            if alb.tags():
                cprint(u"(%s)" % ", ".join(alb.tags()))
            else:
                print
            duration_ms = sum(au.duration_ms for au in alb.all_au_files)
            if alb.is_compilation():
                cprint("Compilation")
                for i, au in enumerate(alb.all_au_files):
                    artist = au.mutagen_id3["TPE1"]
                    cprint(u"  {:02d}: {}".format(i+1, artist))
            else:
                cprint(alb.artist_name())
            cprint(u"{} tracks / {} minutes".format(
                len(alb.all_au_files), int(duration_ms / 60000)))
            cprint(u"ID=%015x" % alb.album_id)
            sys.stdout.flush()

            # Check that the album isn't already in library.
            collision = False
            for au in alb.all_au_files:
                if au.fingerprint in seen_fp:
                    cprint(u"***** ERROR: DUPLICATE TRACK WITHIN IMPORT", type='error')
                    cprint(u"This one is at %s" % au.path)
                    cprint(u"Other one is at %s" % seen_fp[au.fingerprint].path)
                    collision = True
                    break
                fp_au_file = db.get_by_fingerprint(au.fingerprint)
                if fp_au_file is not None:
                    cprint(u"***** ERROR: TRACK ALREADY IN LIBRARY", type='error')
                    cprint(fp_au_file.mutagen_id3)
                    collision = True
                    break
                seen_fp[au.fingerprint] = au

            if collision:
                sys.stdout.flush()
                error_count += 1

            # Attach a dummy volume # and timestamp
            alb.set_volume_and_import_timestamp(0xff, prescan_timestamp)
            try:
                alb.standardize()
                cprint("OK!\n")
            except (import_file.ImportFileError, album.AlbumError), ex:
                cprint("***** IMPORT ERROR")
                cprint("*****   %s\n" % str(ex))
                error_count += 1

            sys.stdout.flush()
            yield # scanned an album
    except analyzer.InvalidFileError as ex:
        cprint("***** INVALID FILE ERROR", type='error')
        cprint("*****   %s\n" % str(ex), type='error')
        error_count += 1

    cprint("-" * 40)
    cprint("Found %d albums" % album_count)
    if error_count > 0:
        cprint("Saw %d errors" % error_count, type='failure')
        return
    cprint("No errors found")

    if dry_run:
        cprint("Dry run --- terminating", type='success')
        return

    txn = None
    for alb in inbox.albums():
        if txn is None:
            txn = import_transaction.ImportTransaction(db, VOLUME_NUMBER,
                                                       timestamp.now(),
                                                       LIBRARY_TMP_PREFIX,
                                                       dry_run=dry_run)
        txn.add_album(alb)
        # If the transaction has grown too large, commit it.
        if txn.total_size_in_bytes > IMPORT_SIZE_LIMIT:
            txn.commit(LIBRARY_PREFIX)
            txn = None
        yield # import an album
    # Flush out any remaining tracks.
    if txn:
        txn.commit(LIBRARY_PREFIX)
    return


def main():
    dry_run = "--actually-do-import" not in sys.argv
    cprint()
    if dry_run:
        cprint("+++ This is only a dry run.  No actual import will occur.")
        cprint("+++ We will only scan the dropbox looking for errors.")
    else:
        cprint("*" * 70)
        cprint("***")
        cprint("*** WARNING!  This is a real import!")
        cprint("*** If no errors are found, the music library will be updated!")

        cprint("***")