Ejemplo n.º 1
0
    def qbittorrent_add_file(self, data):
        host = headphones.CONFIG.QBITTORRENT_HOST
        if not host.startswith('http'):
            host = 'http://' + host
        if host.endswith('/'):
            host = host[:-1]
        if host.endswith('/gui'):
            host = host[:-4]
        base_url = host

        # self.session.auth = HTTPDigestAuth(headphones.CONFIG.QBITTORRENT_USERNAME, headphones.CONFIG.QBITTORRENT_PASSWORD)

        url = base_url + '/login'
        try:
            self.session.post(url, data={'username': headphones.CONFIG.QBITTORRENT_USERNAME,
                                         'password': headphones.CONFIG.QBITTORRENT_PASSWORD})
        except Exception as e:
            logger.exception('Error adding file to qbittorrent %s', e)
            return

        url = base_url + '/command/upload'

        args = {'savepath': headphones.CONFIG.DOWNLOAD_TORRENT_DIR}
        if headphones.CONFIG.QBITTORRENT_LABEL:
            args['category'] = headphones.CONFIG.QBITTORRENT_LABEL

        torrent_files = {'torrents': data}

        try:
            self.session.post(url, data=args, files=torrent_files)
        except Exception as e:
            logger.exception('Error adding file to qbittorrent %s', e)
Ejemplo n.º 2
0
    def utorrent_add_file(self, data):
        host = headphones.CONFIG.UTORRENT_HOST
        if not host.startswith('http'):
            host = 'http://' + host
        if host.endswith('/'):
            host = host[:-1]
        if host.endswith('/gui'):
            host = host[:-4]

        base_url = host
        url = base_url + '/gui/'
        self.session.auth = (headphones.CONFIG.UTORRENT_USERNAME, headphones.CONFIG.UTORRENT_PASSWORD)

        try:
            r = self.session.get(url + 'token.html')
        except Exception as e:
            logger.error('Error getting token: %s', e)
            return

        if r.status_code == 401:
            logger.debug('Error reaching utorrent')
            return

        regex = re.search(r'.+>([^<]+)</div></html>', r.text)
        if regex is None:
            logger.debug('Error reading token')
            return

        self.session.params = {'token': regex.group(1)}
        files = {'torrent_file': ("", data)}

        try:
            self.session.post(url, params={'action': 'add-file'}, files=files)
        except Exception as e:
            logger.exception('Error adding file to utorrent %s', e)
Ejemplo n.º 3
0
def encode(albumPath):

    # Return if xld details not found
    if XLD:
        global xldProfile
        (xldProfile, xldFormat, xldBitrate) = getXldProfile.getXldProfile(headphones.XLDPROFILE)
        if not xldFormat:
            logger.error('Details for xld profile \'%s\' not found, files will not be re-encoded', xldProfile)
            return None

    tempDirEncode=os.path.join(albumPath,"temp")
    musicFiles=[]
    musicFinalFiles=[]
    musicTempFiles=[]
    encoder = ""

    # Create temporary directory, but remove the old one first.
    try:
        if os.path.exists(tempDirEncode):
            shutil.rmtree(tempDirEncode)
            time.sleep(1)

        os.mkdir(tempDirEncode)
    except Exception, e:
        logger.exception("Unable to create temporary directory")
        return None
Ejemplo n.º 4
0
    def utorrent_add_file(self, data):
        host = headphones.CONFIG.UTORRENT_HOST
        if not host.startswith('http'):
            host = 'http://' + host
        if host.endswith('/'):
            host = host[:-1]
        if host.endswith('/gui'):
            host = host[:-4]

        base_url = host
        url = base_url + '/gui/'
        self.session.auth = (headphones.CONFIG.UTORRENT_USERNAME, headphones.CONFIG.UTORRENT_PASSWORD)

        try:
            r = self.session.get(url + 'token.html')
        except Exception as e:
            logger.error('Error getting token: %s', e)
            return

        if r.status_code == 401:
            logger.debug('Error reaching utorrent')
            return

        regex = re.search(r'.+>([^<]+)</div></html>', r.text)
        if regex is None:
            logger.debug('Error reading token')
            return

        self.session.params = {'token': regex.group(1)}
        files = {'torrent_file': ("", data)}

        try:
            self.session.post(url, params={'action': 'add-file'}, files=files)
        except Exception as e:
            logger.exception('Error adding file to utorrent %s', e)
Ejemplo n.º 5
0
    def qbittorrent_add_file(self, data):
        host = headphones.CONFIG.QBITTORRENT_HOST
        if not host.startswith('http'):
            host = 'http://' + host
        if host.endswith('/'):
            host = host[:-1]
        if host.endswith('/gui'):
            host = host[:-4]
        base_url = host

        # self.session.auth = HTTPDigestAuth(headphones.CONFIG.QBITTORRENT_USERNAME, headphones.CONFIG.QBITTORRENT_PASSWORD)

        url = base_url + '/login'
        try:
            self.session.post(url, data={'username': headphones.CONFIG.QBITTORRENT_USERNAME,
                                         'password': headphones.CONFIG.QBITTORRENT_PASSWORD})
        except Exception as e:
            logger.exception('Error adding file to qbittorrent %s', e)
            return

        url = base_url + '/command/upload'

        args = {'savepath': headphones.CONFIG.DOWNLOAD_TORRENT_DIR}
        if headphones.CONFIG.QBITTORRENT_LABEL:
            args['category'] = headphones.CONFIG.QBITTORRENT_LABEL

        torrent_files = {'torrents': data}

        try:
            self.session.post(url, data=args, files=torrent_files)
        except Exception as e:
            logger.exception('Error adding file to qbittorrent %s', e)
Ejemplo n.º 6
0
def encode(albumPath):

    # Return if xld details not found
    if XLD:
        global xldProfile
        (xldProfile, xldFormat,
         xldBitrate) = getXldProfile.getXldProfile(headphones.XLDPROFILE)
        if not xldFormat:
            logger.error(
                'Details for xld profile \'%s\' not found, files will not be re-encoded',
                xldProfile)
            return None

    tempDirEncode = os.path.join(albumPath, "temp")
    musicFiles = []
    musicFinalFiles = []
    musicTempFiles = []
    encoder = ""

    # Create temporary directory, but remove the old one first.
    try:
        if os.path.exists(tempDirEncode):
            shutil.rmtree(tempDirEncode)
            time.sleep(1)

        os.mkdir(tempDirEncode)
    except Exception, e:
        logger.exception("Unable to create temporary directory")
        return None
Ejemplo n.º 7
0
    def realdebrid_add_file(self, data):
        host = "https://api.real-debrid.com/rest/1.0"
        apikey = headphones.CONFIG.REALDEBRID_APIKEY

        url = host + "/torrents/addTorrent?auth_token=" + apikey

        try:
            self.session.post(url, data)
        except Exception as e:
            logger.exception('Error adding file to utorrent %s', e)
Ejemplo n.º 8
0
def command_map(args):
    """
    This method is used for the multiprocessing.map() method as a wrapper.
    """

    try:
        return command(*args)
    except Exception, e:
        logger.exception("Encoder exception, will return failed")
        return False
Ejemplo n.º 9
0
def command_map(args):
    """
    This method is used for the multiprocessing.map() method as a wrapper.
    """

    try:
        return command(*args)
    except Exception, e:
        logger.exception("Encoder exception, will return failed")
        return False
Ejemplo n.º 10
0
def command_map(args):
    """
    Wrapper for the '[multiprocessing.]map()' method, to unpack the arguments
    and wrap exceptions.
    """

    # Initialize multiprocessing logger
    if multiprocessing.current_process().name != "MainProcess":
        logger.initMultiprocessing()

    # Start encoding
    try:
        return command(*args)
    except Exception:
        logger.exception("Encoder raised an exception.")
        return False
Ejemplo n.º 11
0
def command_map(args):
    """
    Wrapper for the '[multiprocessing.]map()' method, to unpack the arguments
    and wrap exceptions.
    """

    # Initialize multiprocessing logger
    if multiprocessing.current_process().name != "MainProcess":
        logger.initMultiprocessing()

    # Start encoding
    try:
        return command(*args)
    except Exception:
        logger.exception("Encoder raised an exception.")
        return False
Ejemplo n.º 12
0
    def utorrent_add_file(self, filename):

        host = headphones.UTORRENT_HOST
        if not host.startswith('http'):
            host = 'http://' + host
        if host.endswith('/'):
            host = host[:-1]
        if host.endswith('/gui'):
            host = host[:-4]

        base_url = host
        username = headphones.UTORRENT_USERNAME
        password = headphones.UTORRENT_PASSWORD

        session = requests.Session()
        url = base_url + '/gui/'
        session.auth = (username, password)

        try:
            r = session.get(url + 'token.html')
        except Exception:
            logger.exception('Error getting token')
            return

        if r.status_code == '401':
            logger.debug('Error reaching utorrent')
            return

        regex = re.search(r'.+>([^<]+)</div></html>', r.text)
        if regex is None:
            logger.debug('Error reading token')
            return

        session.params = {'token': regex.group(1)}

        with open(filename, 'rb') as f:
            try:
                session.post(url, params={'action': 'add-file'},
                    files={'torrent_file': f})
            except Exception:
                logger.exception('Error adding file to utorrent')
                return
Ejemplo n.º 13
0
def initialize(config_file):
    with INIT_LOCK:

        global CONFIG
        global SOFT_CHROOT
        global _INITIALIZED
        global CURRENT_VERSION
        global LATEST_VERSION
        global UMASK

        CONFIG = headphones.config.Config(config_file)

        assert CONFIG is not None

        if _INITIALIZED:
            return False

        if CONFIG.HTTP_PORT < 21 or CONFIG.HTTP_PORT > 65535:
            headphones.logger.warn(
                'HTTP_PORT out of bounds: 21 < %s < 65535', CONFIG.HTTP_PORT)
            CONFIG.HTTP_PORT = 8181

        if CONFIG.HTTPS_CERT == '':
            CONFIG.HTTPS_CERT = os.path.join(DATA_DIR, 'server.crt')
        if CONFIG.HTTPS_KEY == '':
            CONFIG.HTTPS_KEY = os.path.join(DATA_DIR, 'server.key')

        if not CONFIG.LOG_DIR:
            CONFIG.LOG_DIR = os.path.join(DATA_DIR, 'logs')

        if not os.path.exists(CONFIG.LOG_DIR):
            try:
                os.makedirs(CONFIG.LOG_DIR)
            except OSError:
                CONFIG.LOG_DIR = None

                if not QUIET:
                    sys.stderr.write("Unable to create the log directory. "
                                     "Logging to screen only.\n")

        # Start the logger, disable console if needed
        logger.initLogger(console=not QUIET, log_dir=CONFIG.LOG_DIR,
                          verbose=VERBOSE)

        try:
            SOFT_CHROOT = SoftChroot(str(CONFIG.SOFT_CHROOT))
            if SOFT_CHROOT.isEnabled():
                logger.info("Soft-chroot enabled for dir: %s", str(CONFIG.SOFT_CHROOT))
        except headphones.exceptions.SoftChrootError as e:
            logger.error("SoftChroot error: %s", e)
            raise e

        if not CONFIG.CACHE_DIR:
            # Put the cache dir in the data dir for now
            CONFIG.CACHE_DIR = os.path.join(DATA_DIR, 'cache')
        if not os.path.exists(CONFIG.CACHE_DIR):
            try:
                os.makedirs(CONFIG.CACHE_DIR)
            except OSError as e:
                logger.error("Could not create cache dir '%s': %s", DATA_DIR, e)

        # Sanity check for search interval. Set it to at least 6 hours
        if CONFIG.SEARCH_INTERVAL and CONFIG.SEARCH_INTERVAL < 360:
            logger.info("Search interval too low. Resetting to 6 hour minimum.")
            CONFIG.SEARCH_INTERVAL = 360

        # Initialize the database
        logger.info('Checking to see if the database has all tables....')
        try:
            dbcheck()
        except Exception as e:
            logger.error("Can't connect to the database: %s", e)

        # Get the currently installed version. Returns None, 'win32' or the git
        # hash.
        CURRENT_VERSION, CONFIG.GIT_BRANCH = versioncheck.getVersion()

        # Write current version to a file, so we know which version did work.
        # This allowes one to restore to that version. The idea is that if we
        # arrive here, most parts of Headphones seem to work.
        if CURRENT_VERSION:
            version_lock_file = os.path.join(DATA_DIR, "version.lock")

            try:
                with open(version_lock_file, "w") as fp:
                    fp.write(CURRENT_VERSION)
            except IOError as e:
                logger.error("Unable to write current version to file '%s': %s",
                             version_lock_file, e)

        # Check for new versions
        if CONFIG.CHECK_GITHUB and CONFIG.CHECK_GITHUB_ON_STARTUP:
            try:
                LATEST_VERSION = versioncheck.checkGithub()
            except:
                logger.exception("Unhandled exception")
                LATEST_VERSION = CURRENT_VERSION
        else:
            LATEST_VERSION = CURRENT_VERSION

        # Store the original umask
        UMASK = os.umask(0)
        os.umask(UMASK)

        _INITIALIZED = True
        return True
Ejemplo n.º 14
0
def encode(albumPath):
    use_xld = headphones.CONFIG.ENCODER == 'xld'

    # Return if xld details not found
    if use_xld:
        (xldProfile, xldFormat, xldBitrate) = getXldProfile.getXldProfile(
            headphones.CONFIG.XLDPROFILE)
        if not xldFormat:
            logger.error('Details for xld profile \'%s\' not found, files will not be re-encoded',
                         xldProfile)
            return None
    else:
        xldProfile = None

    tempDirEncode = os.path.join(albumPath, "temp")
    musicFiles = []
    musicFinalFiles = []
    musicTempFiles = []
    encoder = ""

    # Create temporary directory, but remove the old one first.
    try:
        if os.path.exists(tempDirEncode):
            shutil.rmtree(tempDirEncode)
            time.sleep(1)

        os.mkdir(tempDirEncode)
    except Exception as e:
        logger.exception("Unable to create temporary directory")
        return None

    for r, d, f in os.walk(albumPath):
        for music in f:
            if any(music.lower().endswith('.' + x.lower()) for x in headphones.MEDIA_FORMATS):
                if not use_xld:
                    encoderFormat = headphones.CONFIG.ENCODEROUTPUTFORMAT.encode(
                        headphones.SYS_ENCODING)
                else:
                    xldMusicFile = os.path.join(r, music)
                    xldInfoMusic = MediaFile(xldMusicFile)
                    encoderFormat = xldFormat

                if headphones.CONFIG.ENCODERLOSSLESS:
                    ext = os.path.normpath(os.path.splitext(music)[1].lstrip(".")).lower()
                    if not use_xld and ext == 'flac' or use_xld and (
                            ext != xldFormat and (xldInfoMusic.bitrate / 1000 > 400)):
                        musicFiles.append(os.path.join(r, music))
                        musicTemp = os.path.normpath(
                            os.path.splitext(music)[0] + '.' + encoderFormat)
                        musicTempFiles.append(os.path.join(tempDirEncode, musicTemp))
                    else:
                        logger.debug('%s is already encoded', music)
                else:
                    musicFiles.append(os.path.join(r, music))
                    musicTemp = os.path.normpath(os.path.splitext(music)[0] + '.' + encoderFormat)
                    musicTempFiles.append(os.path.join(tempDirEncode, musicTemp))

    if headphones.CONFIG.ENCODER_PATH:
        encoder = headphones.CONFIG.ENCODER_PATH.encode(headphones.SYS_ENCODING)
    else:
        if use_xld:
            encoder = os.path.join('/Applications', 'xld')
        elif headphones.CONFIG.ENCODER == 'lame':
            if headphones.SYS_PLATFORM == "win32":
                # NEED THE DEFAULT LAME INSTALL ON WIN!
                encoder = "C:/Program Files/lame/lame.exe"
            else:
                encoder = "lame"
        elif headphones.CONFIG.ENCODER == 'ffmpeg':
            if headphones.SYS_PLATFORM == "win32":
                encoder = "C:/Program Files/ffmpeg/bin/ffmpeg.exe"
            else:
                encoder = "ffmpeg"
        elif headphones.CONFIG.ENCODER == 'libav':
            if headphones.SYS_PLATFORM == "win32":
                encoder = "C:/Program Files/libav/bin/avconv.exe"
            else:
                encoder = "avconv"

    i = 0
    encoder_failed = False
    jobs = []

    for music in musicFiles:
        infoMusic = MediaFile(music)
        encode = False

        if use_xld:
            if xldBitrate and (infoMusic.bitrate / 1000 <= xldBitrate):
                logger.info('%s has bitrate <= %skb, will not be re-encoded',
                            music.decode(headphones.SYS_ENCODING, 'replace'), xldBitrate)
            else:
                encode = True
        elif headphones.CONFIG.ENCODER == 'lame':
            if not any(
                    music.decode(headphones.SYS_ENCODING, 'replace').lower().endswith('.' + x) for x
                    in ["mp3", "wav"]):
                logger.warn('Lame cannot encode %s format for %s, use ffmpeg',
                            os.path.splitext(music)[1], music)
            else:
                if music.decode(headphones.SYS_ENCODING, 'replace').lower().endswith('.mp3') and (
                    int(infoMusic.bitrate / 1000) <= headphones.CONFIG.BITRATE):
                    logger.info('%s has bitrate <= %skb, will not be re-encoded', music,
                                headphones.CONFIG.BITRATE)
                else:
                    encode = True
        else:
            if headphones.CONFIG.ENCODEROUTPUTFORMAT == 'ogg':
                if music.decode(headphones.SYS_ENCODING, 'replace').lower().endswith('.ogg'):
                    logger.warn('Cannot re-encode .ogg %s',
                                music.decode(headphones.SYS_ENCODING, 'replace'))
                else:
                    encode = True
            else:
                if music.decode(headphones.SYS_ENCODING, 'replace').lower().endswith('.' + headphones.CONFIG.ENCODEROUTPUTFORMAT) and (int(infoMusic.bitrate / 1000) <= headphones.CONFIG.BITRATE):
                    logger.info('%s has bitrate <= %skb, will not be re-encoded', music, headphones.CONFIG.BITRATE)
                else:
                    encode = True
        # encode
        if encode:
            job = (encoder, music, musicTempFiles[i], albumPath, xldProfile)
            jobs.append(job)
        else:
            musicFiles[i] = None
            musicTempFiles[i] = None

        i = i + 1

    # Encode music files
    if len(jobs) > 0:
        processes = 1

        # Use multicore if enabled
        if headphones.CONFIG.ENCODER_MULTICORE:
            if headphones.CONFIG.ENCODER_MULTICORE_COUNT == 0:
                processes = multiprocessing.cpu_count()
            else:
                processes = headphones.CONFIG.ENCODER_MULTICORE_COUNT

            logger.debug("Multi-core encoding enabled, spawning %d processes",
                         processes)

        # Use multiprocessing only if it's worth the overhead. and if it is
        # enabled. If not, then use the old fashioned way.
        if processes > 1:
            with logger.listener():
                pool = multiprocessing.Pool(processes=processes)
                results = pool.map_async(command_map, jobs)

                # No new processes will be created, so close it and wait for all
                # processes to finish
                pool.close()
                pool.join()

                # Retrieve the results
                results = results.get()
        else:
            results = map(command_map, jobs)

        # The results are either True or False, so determine if one is False
        encoder_failed = not all(results)

    musicFiles = filter(None, musicFiles)
    musicTempFiles = filter(None, musicTempFiles)

    # check all files to be encoded now exist in temp directory
    if not encoder_failed and musicTempFiles:
        for dest in musicTempFiles:
            if not os.path.exists(dest):
                encoder_failed = True
                logger.error("Encoded file '%s' does not exist in the destination temp directory",
                             dest)

    # No errors, move from temp to parent
    if not encoder_failed and musicTempFiles:
        i = 0
        for dest in musicTempFiles:
            if os.path.exists(dest):
                source = musicFiles[i]
                if headphones.CONFIG.DELETE_LOSSLESS_FILES:
                    os.remove(source)
                check_dest = os.path.join(albumPath, os.path.split(dest)[1])
                if os.path.exists(check_dest):
                    os.remove(check_dest)
                try:
                    shutil.move(dest, albumPath)
                except Exception as e:
                    logger.error('Could not move %s to %s: %s', dest, albumPath, e)
                    encoder_failed = True
                    break
            i += 1

    # remove temp directory
    shutil.rmtree(tempDirEncode)

    # Return with error if any encoding errors
    if encoder_failed:
        logger.error(
            "One or more files failed to encode. Ensure you have the latest version of %s installed.",
            headphones.CONFIG.ENCODER)
        return None

    time.sleep(1)
    for r, d, f in os.walk(albumPath):
        for music in f:
            if any(music.lower().endswith('.' + x.lower()) for x in headphones.MEDIA_FORMATS):
                musicFinalFiles.append(os.path.join(r, music))

    if not musicTempFiles:
        logger.info('Encoding for folder \'%s\' is not required', albumPath)

    return musicFinalFiles
Ejemplo n.º 15
0
def initialize(config_file):
    with INIT_LOCK:

        global CONFIG
        global SOFT_CHROOT
        global _INITIALIZED
        global CURRENT_VERSION
        global LATEST_VERSION
        global UMASK

        CONFIG = headphones.config.Config(config_file)

        assert CONFIG is not None

        if _INITIALIZED:
            return False

        if CONFIG.HTTP_PORT < 21 or CONFIG.HTTP_PORT > 65535:
            headphones.logger.warn('HTTP_PORT out of bounds: 21 < %s < 65535',
                                   CONFIG.HTTP_PORT)
            CONFIG.HTTP_PORT = 8181

        if CONFIG.HTTPS_CERT == '':
            CONFIG.HTTPS_CERT = os.path.join(DATA_DIR, 'server.crt')
        if CONFIG.HTTPS_KEY == '':
            CONFIG.HTTPS_KEY = os.path.join(DATA_DIR, 'server.key')

        if not CONFIG.LOG_DIR:
            CONFIG.LOG_DIR = os.path.join(DATA_DIR, 'logs')

        if not os.path.exists(CONFIG.LOG_DIR):
            try:
                os.makedirs(CONFIG.LOG_DIR)
            except OSError:
                CONFIG.LOG_DIR = None

                if not QUIET:
                    sys.stderr.write("Unable to create the log directory. "
                                     "Logging to screen only.\n")

        # Start the logger, disable console if needed
        logger.initLogger(console=not QUIET,
                          log_dir=CONFIG.LOG_DIR,
                          verbose=VERBOSE)

        try:
            SOFT_CHROOT = SoftChroot(str(CONFIG.SOFT_CHROOT))
            if SOFT_CHROOT.isEnabled():
                logger.info("Soft-chroot enabled for dir: %s",
                            str(CONFIG.SOFT_CHROOT))
        except headphones.exceptions.SoftChrootError as e:
            logger.error("SoftChroot error: %s", e)
            raise e

        if not CONFIG.CACHE_DIR:
            # Put the cache dir in the data dir for now
            CONFIG.CACHE_DIR = os.path.join(DATA_DIR, 'cache')
        if not os.path.exists(CONFIG.CACHE_DIR):
            try:
                os.makedirs(CONFIG.CACHE_DIR)
            except OSError as e:
                logger.error("Could not create cache dir '%s': %s", DATA_DIR,
                             e)

        # Sanity check for search interval. Set it to at least 6 hours
        if CONFIG.SEARCH_INTERVAL and CONFIG.SEARCH_INTERVAL < 360:
            logger.info(
                "Search interval too low. Resetting to 6 hour minimum.")
            CONFIG.SEARCH_INTERVAL = 360

        # Initialize the database
        logger.info('Checking to see if the database has all tables....')
        try:
            dbcheck()
        except Exception as e:
            logger.error("Can't connect to the database: %s", e)

        # Get the currently installed version. Returns None, 'win32' or the git
        # hash.
        CURRENT_VERSION, CONFIG.GIT_BRANCH = versioncheck.getVersion()

        # Write current version to a file, so we know which version did work.
        # This allowes one to restore to that version. The idea is that if we
        # arrive here, most parts of Headphones seem to work.
        if CURRENT_VERSION:
            version_lock_file = os.path.join(DATA_DIR, "version.lock")

            try:
                with open(version_lock_file, "w") as fp:
                    fp.write(CURRENT_VERSION)
            except IOError as e:
                logger.error(
                    "Unable to write current version to file '%s': %s",
                    version_lock_file, e)

        # Check for new versions
        if CONFIG.CHECK_GITHUB and CONFIG.CHECK_GITHUB_ON_STARTUP:
            try:
                LATEST_VERSION = versioncheck.checkGithub()
            except:
                logger.exception("Unhandled exception")
                LATEST_VERSION = CURRENT_VERSION
        else:
            LATEST_VERSION = CURRENT_VERSION

        # Store the original umask
        UMASK = os.umask(0)
        os.umask(UMASK)

        _INITIALIZED = True
        return True
Ejemplo n.º 16
0
def encode(albumPath):
    use_xld = headphones.CONFIG.ENCODER == 'xld'

    # Return if xld details not found
    if use_xld:
        (xldProfile, xldFormat, xldBitrate) = getXldProfile.getXldProfile(headphones.CONFIG.XLDPROFILE)
        if not xldFormat:
            logger.error('Details for xld profile \'%s\' not found, files will not be re-encoded', xldProfile)
            return None
    else:
        xldProfile = None

    tempDirEncode = os.path.join(albumPath, "temp")
    musicFiles = []
    musicFinalFiles = []
    musicTempFiles = []
    encoder = ""

    # Create temporary directory, but remove the old one first.
    try:
        if os.path.exists(tempDirEncode):
            shutil.rmtree(tempDirEncode)
            time.sleep(1)

        os.mkdir(tempDirEncode)
    except Exception as e:
        logger.exception("Unable to create temporary directory")
        return None

    for r, d, f in os.walk(albumPath):
        for music in f:
            if any(music.lower().endswith('.' + x.lower()) for x in headphones.MEDIA_FORMATS):
                if not use_xld:
                    encoderFormat = headphones.CONFIG.ENCODEROUTPUTFORMAT.encode(headphones.SYS_ENCODING)
                else:
                    xldMusicFile = os.path.join(r, music)
                    xldInfoMusic = MediaFile(xldMusicFile)
                    encoderFormat = xldFormat

                if headphones.CONFIG.ENCODERLOSSLESS:
                    ext = os.path.normpath(os.path.splitext(music)[1].lstrip(".")).lower()
                    if not use_xld and ext == 'flac' or use_xld and (ext != xldFormat and (xldInfoMusic.bitrate / 1000 > 400)):
                        musicFiles.append(os.path.join(r, music))
                        musicTemp = os.path.normpath(os.path.splitext(music)[0] + '.' + encoderFormat)
                        musicTempFiles.append(os.path.join(tempDirEncode, musicTemp))
                    else:
                        logger.debug('%s is already encoded', music)
                else:
                    musicFiles.append(os.path.join(r, music))
                    musicTemp = os.path.normpath(os.path.splitext(music)[0] + '.' + encoderFormat)
                    musicTempFiles.append(os.path.join(tempDirEncode, musicTemp))

    if headphones.CONFIG.ENCODER_PATH:
        encoder = headphones.CONFIG.ENCODER_PATH.encode(headphones.SYS_ENCODING)
    else:
        if use_xld:
            encoder = os.path.join('/Applications', 'xld')
        elif headphones.CONFIG.ENCODER == 'lame':
            if headphones.SYS_PLATFORM == "win32":
                ## NEED THE DEFAULT LAME INSTALL ON WIN!
                encoder = "C:/Program Files/lame/lame.exe"
            else:
                encoder = "lame"
        elif headphones.CONFIG.ENCODER == 'ffmpeg':
            if headphones.SYS_PLATFORM == "win32":
                encoder = "C:/Program Files/ffmpeg/bin/ffmpeg.exe"
            else:
                encoder = "ffmpeg"
        elif headphones.CONFIG.ENCODER == 'libav':
            if headphones.SYS_PLATFORM == "win32":
                encoder = "C:/Program Files/libav/bin/avconv.exe"
            else:
                encoder = "avconv"

    i = 0
    encoder_failed = False
    jobs = []

    for music in musicFiles:
        infoMusic = MediaFile(music)
        encode = False

        if use_xld:
            if xldBitrate and (infoMusic.bitrate / 1000 <= xldBitrate):
                logger.info('%s has bitrate <= %skb, will not be re-encoded', music.decode(headphones.SYS_ENCODING, 'replace'), xldBitrate)
            else:
                encode = True
        elif headphones.CONFIG.ENCODER == 'lame':
            if not any(music.decode(headphones.SYS_ENCODING, 'replace').lower().endswith('.' + x) for x in ["mp3", "wav"]):
                logger.warn('Lame cannot encode %s format for %s, use ffmpeg', os.path.splitext(music)[1], music)
            else:
                if music.decode(headphones.SYS_ENCODING, 'replace').lower().endswith('.mp3') and (int(infoMusic.bitrate / 1000) <= headphones.CONFIG.BITRATE):
                    logger.info('%s has bitrate <= %skb, will not be re-encoded', music, headphones.CONFIG.BITRATE)
                else:
                    encode = True
        else:
            if headphones.CONFIG.ENCODEROUTPUTFORMAT == 'ogg':
                if music.decode(headphones.SYS_ENCODING, 'replace').lower().endswith('.ogg'):
                    logger.warn('Cannot re-encode .ogg %s', music.decode(headphones.SYS_ENCODING, 'replace'))
                else:
                    encode = True
            elif headphones.CONFIG.ENCODEROUTPUTFORMAT == 'mp3' or headphones.CONFIG.ENCODEROUTPUTFORMAT == 'm4a':
                if music.decode(headphones.SYS_ENCODING, 'replace').lower().endswith('.' + headphones.CONFIG.ENCODEROUTPUTFORMAT) and (int(infoMusic.bitrate / 1000) <= headphones.CONFIG.BITRATE):
                    logger.info('%s has bitrate <= %skb, will not be re-encoded', music, headphones.CONFIG.BITRATE)
                else:
                    encode = True
        # encode
        if encode:
            job = (encoder, music, musicTempFiles[i], albumPath, xldProfile)
            jobs.append(job)
        else:
            musicFiles[i] = None
            musicTempFiles[i] = None

        i = i + 1

    # Encode music files
    if len(jobs) > 0:
        processes = 1

        # Use multicore if enabled
        if headphones.CONFIG.ENCODER_MULTICORE:
            if headphones.CONFIG.ENCODER_MULTICORE_COUNT == 0:
                processes = multiprocessing.cpu_count()
            else:
                processes = headphones.CONFIG.ENCODER_MULTICORE_COUNT

            logger.debug("Multi-core encoding enabled, spawning %d processes",
                processes)

        # Use multiprocessing only if it's worth the overhead. and if it is
        # enabled. If not, then use the old fashioned way.
        if processes > 1:
            with logger.listener():
                pool = multiprocessing.Pool(processes=processes)
                results = pool.map_async(command_map, jobs)

                # No new processes will be created, so close it and wait for all
                # processes to finish
                pool.close()
                pool.join()

                # Retrieve the results
                results = results.get()
        else:
            results = map(command_map, jobs)

        # The results are either True or False, so determine if one is False
        encoder_failed = not all(results)

    musicFiles = filter(None, musicFiles)
    musicTempFiles = filter(None, musicTempFiles)

    # check all files to be encoded now exist in temp directory
    if not encoder_failed and musicTempFiles:
        for dest in musicTempFiles:
            if not os.path.exists(dest):
                encoder_failed = True
                logger.error("Encoded file '%s' does not exist in the destination temp directory", dest)

    # No errors, move from temp to parent
    if not encoder_failed and musicTempFiles:
        i = 0
        for dest in musicTempFiles:
            if os.path.exists(dest):
                source = musicFiles[i]
                if headphones.CONFIG.DELETE_LOSSLESS_FILES:
                    os.remove(source)
                check_dest = os.path.join(albumPath, os.path.split(dest)[1])
                if os.path.exists(check_dest):
                    os.remove(check_dest)
                try:
                    shutil.move(dest, albumPath)
                except Exception as e:
                    logger.error('Could not move %s to %s: %s', dest, albumPath, e)
                    encoder_failed = True
                    break
            i += 1

    # remove temp directory
    shutil.rmtree(tempDirEncode)

    # Return with error if any encoding errors
    if encoder_failed:
        logger.error("One or more files failed to encode. Ensure you have the latest version of %s installed.", headphones.CONFIG.ENCODER)
        return None

    time.sleep(1)
    for r, d, f in os.walk(albumPath):
        for music in f:
            if any(music.lower().endswith('.' + x.lower()) for x in headphones.MEDIA_FORMATS):
                musicFinalFiles.append(os.path.join(r, music))

    if not musicTempFiles:
        logger.info('Encoding for folder \'%s\' is not required', albumPath)

    return musicFinalFiles