Example #1
0
    def notify(self, artist=None, album=None, snatched=None):
        title = 'Headphones'
        api = headphones.NMA_APIKEY
        nma_priority = headphones.NMA_PRIORITY

        logger.debug(u"NMA title: " + title)
        logger.debug(u"NMA API: " + api)
        logger.debug(u"NMA Priority: " + str(nma_priority))

        if snatched:
            event = snatched + " snatched!"
            message = "Headphones has snatched: " + snatched
        else:
            event = artist + ' - ' + album + ' complete!'
            message = "Headphones has downloaded and postprocessed: " + artist + ' [' + album + ']'

        logger.debug(u"NMA event: " + event)
        logger.debug(u"NMA message: " + message)

        batch = False

        p = pynma.PyNMA()
        keys = api.split(',')
        p.addkey(keys)

        if len(keys) > 1: batch = True

        response = p.push(title, event, message, priority=nma_priority, batch_mode=batch)

        if not response[api][u'code'] == u'200':
            logger.error(u'Could not send notification to NotifyMyAndroid')
            return False
        else:
            return True
Example #2
0
    def _parseMetaTokenValue(self, value):

        if not value:
            logger.warn('config.meta.parse.token: empty value')
            return None

        r = re.compile('(?P<name>[-\w]+)\s*(?:\((?P<params>[-;\w\d\s]+)\))?')
        m = r.match(value)

        if not m:
            logger.warn('config.meta.parse.token: raw-value has syntax error')
            return None

        t = {}
        t['name'] = m.groupdict()['name']
        params = m.groupdict()['params']
        if params:
            params = params.split(';')
            params = map(lambda x: x.strip(), params)
        else:
            params = []
        t['params'] = params

        logger.debug('config.meta.parse.token parsed token: {0}'.format(t))

        return t
def addTorrent(link):
    method = 'torrent-add'
    arguments = {'filename': link, 'download-dir':headphones.DOWNLOAD_TORRENT_DIR}
    
    response = torrentAction(method,arguments)

    if not response:
        return False
        
    if response['result'] == 'success':
        name = response['arguments']['torrent-added']['name']
        logger.info(u"Torrent sent to Transmission successfully")
        if headphones.PROWL_ENABLED and headphones.PROWL_ONSNATCH:
            logger.info(u"Sending Prowl notification")
            prowl = notifiers.PROWL()
            prowl.notify(name,"Download started")
        if headphones.PUSHOVER_ENABLED and headphones.PUSHOVER_ONSNATCH:
            logger.info(u"Sending Pushover notification")
            prowl = notifiers.PUSHOVER()
            prowl.notify(name,"Download started")
        if headphones.NMA_ENABLED and headphones.NMA_ONSNATCH:
            logger.debug(u"Sending NMA notification")
            nma = notifiers.NMA()
            nma.notify(snatched_nzb=name)

        return response['arguments']['torrent-added']['id']
Example #4
0
def embedLyrics(downloaded_track_list):
    logger.info('Adding lyrics')
    
    # TODO: If adding lyrics for flac & lossy, only fetch the lyrics once
    # and apply it to both files
    for downloaded_track in downloaded_track_list:
        
        try:
            f = MediaFile(downloaded_track)
        except:
            logger.error('Could not read %s. Not checking lyrics' % downloaded_track.decode(headphones.SYS_ENCODING, 'replace'))
            continue
            
        if f.albumartist and f.title:
            metalyrics = lyrics.getLyrics(f.albumartist, f.title)
        elif f.artist and f.title:
            metalyrics = lyrics.getLyrics(f.artist, f.title)
        else:
            logger.info('No artist/track metadata found for track: %s. Not fetching lyrics' % downloaded_track.decode(headphones.SYS_ENCODING, 'replace'))
            metalyrics = None
            
        if lyrics:
            logger.debug('Adding lyrics to: %s' % downloaded_track.decode(headphones.SYS_ENCODING, 'replace'))
            f.lyrics = metalyrics
            f.save()
Example #5
0
def setSeedRatio(result):
    logger.debug('Deluge: Setting seed ratio')
    if not any(delugeweb_auth):
        _get_auth()

    ratio = None
    if result['ratio']:
        ratio = result['ratio']

    try:
        if ratio:
            post_data = json.dumps({"method": "core.set_torrent_stop_at_ratio",
                                    "params": [result['hash'], True],
                                    "id": 5})
            response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth,
                verify=deluge_verify_cert, headers=headers)
            post_data = json.dumps({"method": "core.set_torrent_stop_ratio",
                                    "params": [result['hash'], float(ratio)],
                                    "id": 6})
            response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth,
                verify=deluge_verify_cert, headers=headers)

            return not json.loads(response.text)['error']

        return True
    except Exception as e:
        logger.error('Deluge: Setting seed ratio failed: %s' % str(e))
        formatted_lines = traceback.format_exc().splitlines()
        logger.error('; '.join(formatted_lines))
        return None
Example #6
0
 def markArtists(self, action=None, **args):
     myDB = db.DBConnection()
     artistsToAdd = []
     for ArtistID in args:
         if action == 'delete':
             myDB.action('DELETE from artists WHERE ArtistID=?', [ArtistID])
             myDB.action('DELETE from albums WHERE ArtistID=?', [ArtistID])
             myDB.action('DELETE from tracks WHERE ArtistID=?', [ArtistID])
             myDB.action('DELETE from allalbums WHERE AlbumID=?', [AlbumID])
             myDB.action('DELETE from alltracks WHERE AlbumID=?', [AlbumID])
             myDB.action('INSERT OR REPLACE into blacklist VALUES (?)', [ArtistID])
         elif action == 'pause':
             controlValueDict = {'ArtistID': ArtistID}
             newValueDict = {'Status': 'Paused'}
             myDB.upsert("artists", newValueDict, controlValueDict)
         elif action == 'resume':
             controlValueDict = {'ArtistID': ArtistID}
             newValueDict = {'Status': 'Active'}
             myDB.upsert("artists", newValueDict, controlValueDict)
         else:
             artistsToAdd.append(ArtistID)
     if len(artistsToAdd) > 0:
         logger.debug("Refreshing artists: %s" % artistsToAdd)
         threading.Thread(target=importer.addArtistIDListToDB, args=[artistsToAdd]).start()
     raise cherrypy.HTTPRedirect("home")
Example #7
0
def checkFolder():

    with postprocessor_lock:

        myDB = db.DBConnection()
        snatched = myDB.select('SELECT * from snatched WHERE Status="Snatched"')

        for album in snatched:

            if album["FolderName"]:

                nzb_album_path = os.path.join(headphones.DOWNLOAD_DIR, album["FolderName"]).encode(
                    headphones.SYS_ENCODING
                )
                torrent_album_path = os.path.join(headphones.DOWNLOAD_TORRENT_DIR, album["FolderName"]).encode(
                    headphones.SYS_ENCODING
                )

                if os.path.exists(nzb_album_path):
                    logger.debug("Found %s in NZB download folder. Verifying...." % album["FolderName"])
                    verify(album["AlbumID"], nzb_album_path)

                elif os.path.exists(torrent_album_path):
                    logger.debug("Found %s in torrent download folder. Verifying...." % album["FolderName"])
                    verify(album["AlbumID"], torrent_album_path)
Example #8
0
def startmb(forcemb=False):

	mbuser = None
	mbpass = None
	
	# Can use headphones mirror for queries
	if headphones.MIRROR == "headphones":
		forcemb=False
	
	if forcemb or headphones.MIRROR == "musicbrainz.org":
		mbhost = "musicbrainz.org"
		mbport = 80
		sleepytime = 1
	elif headphones.MIRROR == "custom":
		mbhost = headphones.CUSTOMHOST
		mbport = int(headphones.CUSTOMPORT)
		sleepytime = int(headphones.CUSTOMSLEEP)
	elif headphones.MIRROR == "headphones":
		mbhost = "178.63.142.150"
		mbport = 8181
		mbuser = headphones.HPUSER
		mbpass = headphones.HPPASS
		sleepytime = 0
	else:
		mbhost = "tbueter.com"
		mbport = 5000
		sleepytime = 0
	
	service = ws.WebService(host=mbhost, port=mbport, username=mbuser, password=mbpass, mirror=headphones.MIRROR)
	q = ws.Query(service)
	
	logger.debug('Using the following server values:\nMBHost: %s ; MBPort: %i  ;  Sleep Interval: %i ' % (mbhost, mbport, sleepytime))
	
	return (q, sleepytime)
Example #9
0
def setTorrentPath(result):
    logger.debug('Deluge: Setting download path')
    if not any(delugeweb_auth):
        _get_auth()

    if headphones.CONFIG.DELUGE_DONE_DIRECTORY or headphones.CONFIG.DOWNLOAD_TORRENT_DIR:
        post_data = json.dumps({"method": "core.set_torrent_move_completed",
                                "params": [result['hash'], True],
                                "id": 7})
        response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth)

        if headphones.CONFIG.DELUGE_DONE_DIRECTORY:
            move_to = headphones.CONFIG.DELUGE_DONE_DIRECTORY
        else:
            move_to = headphones.CONFIG.DOWNLOAD_TORRENT_DIR

        if not os.path.exists(move_to):
            logger.debug('Deluge: %s directory doesn\'t exist, let\'s create it' % move_to)
            os.makedirs(move_to)
        post_data = json.dumps({"method": "core.set_torrent_move_completed_path",
                                "params": [result['hash'], move_to],
                                "id": 8})
        response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth)

        return not json.loads(response.text)['error']

    return True
Example #10
0
def addReleaseById(rid):

    myDB = db.DBConnection()

    rgid = None
    artistid = None
    release_dict = None
    results = myDB.select(
        "SELECT albums.ArtistID, releases.ReleaseGroupID from releases, albums WHERE releases.ReleaseID=? and releases.ReleaseGroupID=albums.AlbumID LIMIT 1",
        [rid],
    )
    for result in results:
        rgid = result["ReleaseGroupID"]
        artistid = result["ArtistID"]
        logger.debug("Found a cached releaseid : releasegroupid relationship: " + rid + " : " + rgid)
    if not rgid:
        # didn't find it in the cache, get the information from MB
        logger.debug("Didn't find releaseID " + rid + " in the cache. Looking up its ReleaseGroupID")
        try:
            release_dict = mb.getRelease(rid)
        except Exception, e:
            logger.info("Unable to get release information for Release: " + str(rid) + " " + str(e))
            return
        if not release_dict:
            logger.info("Unable to get release information for Release: " + str(rid) + " no dict")
            return

        rgid = release_dict["rgid"]
        artistid = release_dict["artist_id"]
Example #11
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)
Example #12
0
def setTorrentPath(result):
    logger.debug('Deluge: Setting download path')
    if not any(delugeweb_auth):
        _get_auth()

    try:
        if headphones.CONFIG.DELUGE_DONE_DIRECTORY or headphones.CONFIG.DOWNLOAD_TORRENT_DIR:
            post_data = json.dumps({"method": "core.set_torrent_move_completed",
                                    "params": [result['hash'], True],
                                    "id": 7})
            response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth,
                verify=deluge_verify_cert, headers=headers)

            if headphones.CONFIG.DELUGE_DONE_DIRECTORY:
                move_to = headphones.CONFIG.DELUGE_DONE_DIRECTORY
            else:
                move_to = headphones.CONFIG.DOWNLOAD_TORRENT_DIR

            if not os.path.exists(move_to):
                logger.debug('Deluge: %s directory doesn\'t exist, let\'s create it' % move_to)
                os.makedirs(move_to)
            post_data = json.dumps({"method": "core.set_torrent_move_completed_path",
                                    "params": [result['hash'], move_to],
                                    "id": 8})
            response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth,
                verify=deluge_verify_cert, headers=headers)

            return not json.loads(response.text)['error']

        return True
    except Exception as e:
        logger.error('Deluge: Setting torrent move-to directory failed: %s' % str(e))
        formatted_lines = traceback.format_exc().splitlines()
        logger.error('; '.join(formatted_lines))
        return None
Example #13
0
def getTagTopArtists(tag, limit=50):
    myDB = db.DBConnection()
    results = myDB.select("SELECT ArtistID from artists")

    logger.info("Fetching top artists from Last.FM for tag: %s", tag)
    data = request_lastfm("tag.gettopartists", limit=limit, tag=tag)

    if data and "topartists" in data:
        artistlist = []
        artists = data["topartists"]["artist"]
        logger.debug("Fetched %d artists from Last.FM", len(artists))

        for artist in artists:
            try:
                artist_mbid = artist["mbid"]
            except KeyError:
                continue

            if not any(artist_mbid in x for x in results):
                artistlist.append(artist_mbid)

        from headphones import importer

        for artistid in artistlist:
            importer.addArtisttoDB(artistid)

        logger.debug("Added %d new artists from Last.FM", len(artistlist))
Example #14
0
def sab_api_call(request_type=None, params={}, **kwargs):
    if not headphones.CONFIG.SAB_HOST.startswith('http'):
        headphones.CONFIG.SAB_HOST = 'http://' + headphones.CONFIG.SAB_HOST

    if headphones.CONFIG.SAB_HOST.endswith('/'):
        headphones.CONFIG.SAB_HOST = headphones.CONFIG.SAB_HOST[
                                     0:len(headphones.CONFIG.SAB_HOST) - 1]

    url = headphones.CONFIG.SAB_HOST + "/" + "api?"

    if headphones.CONFIG.SAB_USERNAME:
        params['ma_username'] = headphones.CONFIG.SAB_USERNAME
    if headphones.CONFIG.SAB_PASSWORD:
        params['ma_password'] = headphones.CONFIG.SAB_PASSWORD
    if headphones.CONFIG.SAB_APIKEY:
        params['apikey'] = headphones.CONFIG.SAB_APIKEY

    if request_type == 'send_nzb' and headphones.CONFIG.SAB_CATEGORY:
        params['cat'] = headphones.CONFIG.SAB_CATEGORY

    params['output'] = 'json'

    response = request.request_json(url, params=params, **kwargs)

    if not response:
        logger.error("Error connecting to SABnzbd on url: %s" % headphones.CONFIG.SAB_HOST)
        return False
    else:
        logger.debug("Successfully connected to SABnzbd on url: %s" % headphones.CONFIG.SAB_HOST)
        return response
Example #15
0
def getFolder(hash):
    logger.debug('getFolder(%s)' % hash)

    qbclient = qbittorrentclient()

    # Get Active Directory from settings
    settings = qbclient._get_settings()
    active_dir = settings['temp_path']
    completed_dir = settings['save_path']

    if not active_dir:
        logger.error('Could not get "Keep incomplete torrents in:" directory from QBitTorrent settings, please ensure it is set')
        return None

    # Get Torrent Folder Name
    torrent_folder = qbclient.get_savepath(hash)

    # If there's no folder yet then it's probably a magnet, try until folder is populated
    if torrent_folder == active_dir or not torrent_folder:
        tries = 1
        while (torrent_folder == active_dir or torrent_folder is None) and tries <= 10:
            tries += 1
            time.sleep(6)
            torrent_folder = qbclient.get_savepath(hash)

    if torrent_folder == active_dir or not torrent_folder:
        torrent_folder = qbclient.get_savepath(hash)
        return torrent_folder
    else:
        if headphones.SYS_PLATFORM != "win32":
            torrent_folder = torrent_folder.replace('\\', '/')
        return os.path.basename(os.path.normpath(torrent_folder))
Example #16
0
def sendNZB(nzb):

    addToTop = False
    nzbgetXMLrpc = "%(username)s:%(password)s@%(host)s/xmlrpc"

    if headphones.CONFIG.NZBGET_HOST is None:
        logger.error(u"No NZBget host found in configuration. Please configure it.")
        return False

    if headphones.CONFIG.NZBGET_HOST.startswith('https://'):
        nzbgetXMLrpc = 'https://' + nzbgetXMLrpc
        headphones.CONFIG.NZBGET_HOST.replace('https://', '', 1)
    else:
        nzbgetXMLrpc = 'http://' + nzbgetXMLrpc
        headphones.CONFIG.NZBGET_HOST.replace('http://', '', 1)

    url = nzbgetXMLrpc % {"host": headphones.CONFIG.NZBGET_HOST, "username": headphones.CONFIG.NZBGET_USERNAME, "password": headphones.CONFIG.NZBGET_PASSWORD}

    nzbGetRPC = xmlrpclib.ServerProxy(url)
    try:
        if nzbGetRPC.writelog("INFO", "headphones connected to drop of %s any moment now." % (nzb.name + ".nzb")):
            logger.debug(u"Successfully connected to NZBget")
        else:
            logger.info(u"Successfully connected to NZBget, but unable to send a message" % (nzb.name + ".nzb"))

    except httplib.socket.error:
        logger.error(u"Please check your NZBget host and port (if it is running). NZBget is not responding to this combination")
        return False

    except xmlrpclib.ProtocolError, e:
        if (e.errmsg == "Unauthorized"):
            logger.error(u"NZBget password is incorrect.")
        else:
            logger.error(u"Protocol Error: " + e.errmsg)
        return False
Example #17
0
def request_lastfm(method, **kwargs):
    """
    Call a Last.FM API method. Automatically sets the method and API key. Method
    will return the result if no error occured.

    By default, this method will request the JSON format, since it is more
    lightweight than XML.
    """

    # Prepare request
    kwargs["method"] = method
    kwargs.setdefault("api_key", API_KEY)
    kwargs.setdefault("format", "json")

    # Send request
    logger.debug("Calling Last.FM method: %s", method)
    logger.debug("Last.FM call parameters: %s", kwargs)

    data = request.request_json(ENTRY_POINT, timeout=TIMEOUT, params=kwargs,
        rate_limit=(lock, REQUEST_LIMIT))

    # Parse response and check for errors.
    if not data:
        logger.error("Error calling Last.FM method: %s", method)
        return

    if "error" in data:
        logger.error("Last.FM returned an error: %s", data["message"])
        return

    return data
Example #18
0
def checkFolder():
    
    with postprocessor_lock:

        myDB = db.DBConnection()
        snatched = myDB.select('SELECT * from snatched WHERE Status="Snatched"')

        for album in snatched:
        
            if album['FolderName']:
                
                # Need to check for variations due to sab renaming. Ideally we'd check the sab config via api to 
                # figure out which options are checked, but oh well

                nzb_album_possibilities = [ album['FolderName'],
                                            sab_replace_dots(album['FolderName']),
                                            sab_replace_spaces(album['FolderName']),
                                            sab_replace_dots(sab_replace_spaces(album['FolderName']))
                                        ]

                torrent_album_path = os.path.join(headphones.DOWNLOAD_TORRENT_DIR, album['FolderName']).encode(headphones.SYS_ENCODING)

                for nzb_folder_name in nzb_album_possibilities:
                    
                    nzb_album_path = os.path.join(headphones.DOWNLOAD_DIR, nzb_folder_name).encode(headphones.SYS_ENCODING)

                    if os.path.exists(nzb_album_path):
                        logger.debug('Found %s in NZB download folder. Verifying....' % album['FolderName'])
                        verify(album['AlbumID'], nzb_album_path)

                if os.path.exists(torrent_album_path):
                    logger.debug('Found %s in torrent download folder. Verifying....' % album['FolderName'])
                    verify(album['AlbumID'], torrent_album_path)
Example #19
0
    def __init__(self, config_file):
        """ Initialize the config with values from a file """
        self._config_file = config_file
        self._config = ConfigObj(self._config_file, encoding='utf-8')

        # meta config preparation
        (_basename, _ext) = os.path.splitext(self._config_file)
        meta_config_name = _basename + '.meta' + _ext
        logger.debug('Read meta config from: [{0}]'.format(meta_config_name))
        self._meta_config = MetaConfig(meta_config_name)

        # used in opt-register , it helps to make the names of sections correct
        self._section_name_spell_check_dic = {}

        self._options = {}
        self._uiparser = ViewParser()

        # -------------------------------------------------------------
        # register options from definition's files:
        # -------------------------------------------------------------
        self._initOptions()

        logger.info('All options registered. Total options: {0}'.format(len(self._options)))

        self._upgrade()
Example #20
0
    def search(self, searchurl, maxsize, minseeders, albumid, bitrate):
        
        if not self.login_done:
            self._doLogin( headphones.CONFIG.TONZE_LOGIN, headphones.CONFIG.TONZE_PASSWORD )

        results = []
        logger.debug(u"Search string: " + searchurl)
        
        r = self.opener.open( searchurl )
        soup = BeautifulSoup( r, "html.parser" )
        resultsTable = soup.find("table", { "class" : "results" })
        if resultsTable:
            rows = resultsTable.find("tbody").findAll("tr")
    
            for row in rows:
                link = row.find("a", title=True)
                title = link['title']
                size = row.find_all('td')[5].text
                                
                seeders = row.find_all('td')[7].text
                size = parseSize(size)
                size = tryInt(size)
                seeders = tryInt(seeders)
                id = row.find_all('td')[2].find_all('a')[0]['href'][1:].replace('torrents/nfo/?id=','')
                downloadURL = ('http://www.t411.me/torrents/download/?id=%s' % id)
                
                
                results.append( T411SearchResult( self.opener, title, downloadURL,size, seeders) )
                
                
        return results
Example #21
0
    def search(self, searchurl, maxsize, minseeders, albumid, bitrate):
        
        if not self.login_done:
            self._doLogin( headphones.CONFIG.FTDB_LOGIN, headphones.CONFIG.FTDB_PASSWORD )

        results = []
        logger.debug(u"Search string: " + searchurl)
        URL = self.url + '/?section=TORRENTS&' + searchurl.replace('!','')
        r = self.opener.open(URL)   
        soup = BeautifulSoup( r, "html5lib" )
        resultsTable = soup.find("div", { "class" : "DataGrid" })
        if resultsTable:
            rows = resultsTable.findAll("ul")
            for row in rows:
                link = row.find("a", title=True)
                title = link['title']
                size= row.findAll('li')[3].text
                size = parseSize(size)
                size = tryInt(size)
                leecher=row.findAll('li')[5].text
                seeder=row.findAll('li')[4].text
                seeders = tryInt(seeder)
                autogetURL = self.url +'/'+ (row.find("li", { "class" : "torrents_name"}).find('a')['href'][1:]).replace('#FTD_MENU','&menu=4')
                r = self.opener.open( autogetURL , 'wb').read()
                soup = BeautifulSoup( r, "html5lib" )
                downloadURL = soup.find("div", { "class" : "autoget"}).find('a')['href']
                
                results.append( FTDBSearchResult( self.opener, title, downloadURL,size, seeders) )
        return results
Example #22
0
def split_baby(split_file, split_cmd):
    '''Let's split baby'''
    logger.info('Splitting %s...', split_file.decode(headphones.SYS_ENCODING, 'replace'))
    logger.debug(subprocess.list2cmdline(split_cmd))

    # Prevent Windows from opening a terminal window
    startupinfo = None

    if headphones.SYS_PLATFORM == "win32":
        startupinfo = subprocess.STARTUPINFO()
        try:
            startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
        except AttributeError:
            startupinfo.dwFlags |= subprocess._subprocess.STARTF_USESHOWWINDOW

    env = os.environ.copy()
    if 'xld' in split_cmd:
        env['PATH'] += os.pathsep + '/Applications'
    elif headphones.CONFIG.CUE_SPLIT_FLAC_PATH:
        env['PATH'] += os.pathsep + headphones.CONFIG.CUE_SPLIT_FLAC_PATH

    process = subprocess.Popen(split_cmd, startupinfo=startupinfo,
                               stdin=open(os.devnull, 'rb'), stdout=subprocess.PIPE,
                               stderr=subprocess.PIPE, env=env)
    stdout, stderr = process.communicate()

    if process.returncode:
        logger.error('Split failed for %s', split_file.decode(headphones.SYS_ENCODING, 'replace'))
        out = stdout if stdout else stderr
        logger.error('Error details: %s', out.decode(headphones.SYS_ENCODING, 'replace'))
        return False
    else:
        logger.info('Split success %s', split_file.decode(headphones.SYS_ENCODING, 'replace'))
        return True
Example #23
0
def getArtists():
    myDB = db.DBConnection()
    results = myDB.select("SELECT ArtistID from artists")

    if not headphones.LASTFM_USERNAME:
        logger.warn("Last.FM username not set, not importing artists.")
        return

    logger.info("Fetching artists from Last.FM for username: %s", headphones.LASTFM_USERNAME)
    data = request_lastfm("library.getartists", limit=10000, user=headphones.LASTFM_USERNAME)

    if data and "artists" in data:
        artistlist = []
        artists = data["artists"]["artist"]
        logger.debug("Fetched %d artists from Last.FM", len(artists))

        for artist in artists:
            artist_mbid = artist["mbid"]

            if not any(artist_mbid in x for x in results):
                artistlist.append(artist_mbid)

        from headphones import importer

        for artistid in artistlist:
            importer.addArtisttoDB(artistid)

        logger.info("Imported %d new artists from Last.FM", len(artistlist))
Example #24
0
def checkFolder():
    
    with postprocessor_lock:

        myDB = db.DBConnection()
        snatched = myDB.select('SELECT * from snatched WHERE Status="Snatched"')

        for album in snatched:
        
            if album['FolderName']:
                
                # We're now checking sab config options after sending to determine renaming - but we'll keep the
                # iterations in just in case we can't read the config for some reason

                nzb_album_possibilities = [ album['FolderName'],
                                            sab_replace_dots(album['FolderName']),
                                            sab_replace_spaces(album['FolderName']),
                                            sab_replace_spaces(sab_replace_dots(album['FolderName']))
                                        ]

                torrent_album_path = os.path.join(headphones.DOWNLOAD_TORRENT_DIR, album['FolderName']).encode(headphones.SYS_ENCODING)

                for nzb_folder_name in nzb_album_possibilities:
                    
                    nzb_album_path = os.path.join(headphones.DOWNLOAD_DIR, nzb_folder_name).encode(headphones.SYS_ENCODING)

                    if os.path.exists(nzb_album_path):
                        logger.debug('Found %s in NZB download folder. Verifying....' % album['FolderName'])
                        verify(album['AlbumID'], nzb_album_path, album['Kind'])

                if os.path.exists(torrent_album_path):
                    logger.debug('Found %s in torrent download folder. Verifying....' % album['FolderName'])
                    verify(album['AlbumID'], torrent_album_path, album['Kind'])
Example #25
0
def addFile(data):
    logger.debug('addFile(data)')

    qbclient = qbittorrentclient()
    files = {'torrents': {'filename': '', 'content': data}}

    return qbclient._command('command/upload', filelist=files)
Example #26
0
def findArtist(name, limit=1):

	with mb_lock:
	
		artistlist = []
		attempt = 0
		artistResults = None
		
		chars = set('!?*')
		if any((c in chars) for c in name):
			name = '"'+name+'"'
			
		q, sleepytime = startmb()
		
		while attempt < 5:
		
			try:
				artistResults = q.getArtists(ws.ArtistFilter(query=name, limit=limit))
				break
			except WebServiceError, e:
				logger.warn('Attempt to query MusicBrainz for %s failed: %s [%s:%i]' % (name, e, mbhost, mbport))
				attempt += 1
				time.sleep(5)
		
		time.sleep(sleepytime)
		
		if not artistResults:
			return False		
		
		for result in artistResults:
		
			if result.artist.name != result.artist.getUniqueName() and limit == 1:
				
				logger.debug('Found an artist with a disambiguation: %s - doing an album based search' % name)
				artistdict = findArtistbyAlbum(name)
				
				if not artistdict:
					logger.debug('Cannot determine the best match from an artist/album search. Using top match instead')
					artistlist.append({
						'name': 			result.artist.name,
						'uniquename':		result.artist.getUniqueName(),
						'id':				u.extractUuid(result.artist.id),
						'url': 				result.artist.id,
						'score':			result.score
						})
					
				else:
					artistlist.append(artistdict)
			
			else:
				artistlist.append({
						'name': 			result.artist.name,
						'uniquename':		result.artist.getUniqueName(),
						'id':				u.extractUuid(result.artist.id),
						'url': 				result.artist.id,
						'score':			result.score
						})
			
		return artistlist
Example #27
0
def getSimilar():
    
    myDB = db.DBConnection()
    results = myDB.select('SELECT ArtistID from artists ORDER BY HaveTracks DESC')
    
    artistlist = []
    
    for result in results[:12]:
        
        url = 'http://ws.audioscrobbler.com/2.0/?method=artist.getsimilar&mbid=%s&api_key=%s' % (result['ArtistID'], api_key)
        
        try:
            data = urllib2.urlopen(url, timeout=20).read()
        except:
            time.sleep(1)
            continue
            
        if len(data) < 200:
            continue
            
        try:    
            d = minidom.parseString(data)
        except:
            logger.debug("Could not parse similar artist data from last.fm")
        
        node = d.documentElement
        artists = d.getElementsByTagName("artist")
        
        for artist in artists:
            namenode = artist.getElementsByTagName("name")[0].childNodes
            mbidnode = artist.getElementsByTagName("mbid")[0].childNodes
            
            for node in namenode:
                artist_name = node.data
            for node in mbidnode:
                artist_mbid = node.data
                
            try:
                if not any(artist_mbid in x for x in results):
                    artistlist.append((artist_name, artist_mbid))
            except:
                continue
                
    count = defaultdict(int)
    
    for artist, mbid in artistlist:
        count[artist, mbid] += 1
        
    items = count.items()
    
    top_list = sorted(items, key=lambda x: x[1], reverse=True)[:25]
    
    random.shuffle(top_list)
    
    myDB.action('''DELETE from lastfmcloud''')
    for tuple in top_list:
        artist_name, artist_mbid = tuple[0]
        count = tuple[1]    
        myDB.action('INSERT INTO lastfmcloud VALUES( ?, ?, ?)', [artist_name, artist_mbid, count])
Example #28
0
def addTorrent(link):
    logger.debug('addTorrent(%s)' % link)

    qbclient = qbittorrentclient()
    args = { 'urls':link, 'savepath':headphones.CONFIG.DOWNLOAD_TORRENT_DIR }
    if headphones.CONFIG.QBITTORRENT_LABEL:
	args['label'] = headphones.CONFIG.QBITTORRENT_LABEL
    return qbclient._command('command/download', args, 'application/x-www-form-urlencoded' )
Example #29
0
 def get_savepath(self, hash):
     logger.debug('qb.get_savepath(%s)' % hash)
     status, torrentList = self._get_list()
     for torrent in torrentList:
         if torrent['hash']:
             if torrent['hash'].upper() == hash.upper():
                 return torrent['save_path']
     return None
Example #30
0
def renameFiles(albumpath, downloaded_track_list, release):
	logger.info('Renaming files')
	try:
		year = release['ReleaseDate'][:4]
	except TypeError:
		year = ''
	# Until tagging works better I'm going to rely on the already provided metadata

	for downloaded_track in downloaded_track_list:
		try:
			f = MediaFile(downloaded_track)
		except:
			logger.info("MediaFile couldn't parse: " + downloaded_track)
			continue
			
		if not f.track:
			tracknumber = ''
		else:
			tracknumber = '%02d' % f.track
		
		if not f.title:
			
			basename = unicode(os.path.basename(downloaded_track), headphones.SYS_ENCODING, errors='replace')
			title = os.path.splitext(basename)[0]
			ext = os.path.splitext(basename)[1]
			
			new_file_name = helpers.cleanTitle(title) + ext
			
		else:
			title = f.title
			
			values = {	'Track':		tracknumber,
						'Title':		title,
						'Artist':		release['ArtistName'],
						'Album':		release['AlbumTitle'],
						'Year':			year,
						'track':		tracknumber,
						'title':		title.lower(),
						'artist':		release['ArtistName'].lower(),
						'album':		release['AlbumTitle'].lower(),
						'year':			year
						}
						
			ext = os.path.splitext(downloaded_track)[1]
			
			new_file_name = helpers.replace_all(headphones.FILE_FORMAT, values).replace('/','_') + ext
		
		
		new_file_name = new_file_name.replace('?','_').replace(':', '_').encode(headphones.SYS_ENCODING)

		new_file = os.path.join(albumpath, new_file_name)

		logger.debug('Renaming %s ---> %s' % (downloaded_track, new_file_name))
		try:
			os.rename(downloaded_track, new_file)
		except Exception, e:
			logger.error('Error renaming file: %s. Error: %s' % (downloaded_track, e))
			continue
Example #31
0
def check_setting_int(config, cfg_name, item_name, def_val):
    try:
        my_val = int(config[cfg_name][item_name])
    except:
        my_val = def_val
        try:
            config[cfg_name][item_name] = my_val
        except:
            config[cfg_name] = {}
            config[cfg_name][item_name] = my_val
    logger.debug(item_name + " -> " + str(my_val))
    return my_val
Example #32
0
def startmb():

    mbuser = None
    mbpass = None

    if headphones.CONFIG.MIRROR == "musicbrainz.org":
        mbhost = "musicbrainz.org"
        mbport = 80
        sleepytime = 1
    elif headphones.CONFIG.MIRROR == "custom":
        mbhost = headphones.CONFIG.CUSTOMHOST
        mbport = int(headphones.CONFIG.CUSTOMPORT)
        mbuser = headphones.CONFIG.CUSTOMUSER
        mbpass = headphones.CONFIG.CUSTOMPASS
        sleepytime = int(headphones.CONFIG.CUSTOMSLEEP)
    elif headphones.CONFIG.MIRROR == "headphones":
        mbhost = "144.76.94.239"
        mbport = 8181
        mbuser = headphones.CONFIG.HPUSER
        mbpass = headphones.CONFIG.HPPASS
        sleepytime = 0
    else:
        return False

    musicbrainzngs.set_useragent("headphones", "0.0",
                                 "https://github.com/rembo10/headphones")
    musicbrainzngs.set_hostname(mbhost + ":" + str(mbport))

    # Their rate limiting should be redundant to our lock
    if sleepytime == 0:
        musicbrainzngs.set_rate_limit(False)
    else:
        #calling it with an it ends up blocking all requests after the first
        musicbrainzngs.set_rate_limit(limit_or_interval=float(sleepytime))
        mb_lock.minimum_delta = sleepytime

    # Add headphones credentials
    if headphones.CONFIG.MIRROR == "headphones" or headphones.CONFIG.CUSTOMAUTH:
        if not mbuser or not mbpass:
            logger.warn("No username or password set for MusicBrainz server")
        else:
            musicbrainzngs.hpauth(mbuser, mbpass)

    # Let us know if we disable custom authentication
    if not headphones.CONFIG.CUSTOMAUTH and headphones.CONFIG.MIRROR == "custom":
        musicbrainzngs.disable_hpauth()

    logger.debug(
        'Using the following server values: MBHost: %s, MBPort: %i, Sleep Interval: %i',
        mbhost, mbport, sleepytime)

    return True
Example #33
0
def cleanupFiles(albumpath):
    logger.info('Cleaning up files')
    for r, d, f in os.walk(albumpath):
        for files in f:
            if not any(files.lower().endswith('.' + x.lower())
                       for x in headphones.MEDIA_FORMATS):
                logger.debug('Removing: %s' % files)
                try:
                    os.remove(os.path.join(r, files))
                except Exception, e:
                    logger.error(
                        u'Could not remove file: %s. Error: %s' %
                        (files.decode(headphones.SYS_ENCODING, 'replace'), e))
Example #34
0
def embedAlbumArt(artwork, downloaded_track_list):
	logger.info('Embedding album art')
	
	for downloaded_track in downloaded_track_list:
		try:
			f = MediaFile(downloaded_track)
		except:
			logger.error('Could not read %s. Not adding album art' % downloaded_track)
			continue
			
		logger.debug('Adding album art to: %s' % downloaded_track)
		f.art = artwork
		f.save()
def embedAlbumArt(artwork, downloaded_track_list):
    logger.info('Embedding album art')
    
    for downloaded_track in downloaded_track_list:
        try:
            f = MediaFile(downloaded_track)
        except:
            logger.error(u'Could not read %s. Not adding album art' % downloaded_track.decode(headphones.SYS_ENCODING, 'replace'))
            continue
            
        logger.debug('Adding album art to: %s' % downloaded_track)
        f.art = artwork
        f.save()
Example #36
0
def startmb(forcemb=False):

    mbuser = None
    mbpass = None

    # Can use headphones mirror for queries
    if headphones.MIRROR == "headphones" or "custom":
        forcemb = False

    if forcemb or headphones.MIRROR == "musicbrainz.org":
        mbhost = "musicbrainz.org"
        mbport = 80
        sleepytime = 1
    elif headphones.MIRROR == "custom":
        mbhost = headphones.CUSTOMHOST
        mbport = int(headphones.CUSTOMPORT)
        sleepytime = int(headphones.CUSTOMSLEEP)
    elif headphones.MIRROR == "headphones":
        mbhost = "178.63.142.150"
        mbport = 8181
        mbuser = headphones.HPUSER
        mbpass = headphones.HPPASS
        sleepytime = 0
    else:
        mbhost = "tbueter.com"
        mbport = 5000
        sleepytime = 0

    musicbrainzngs.set_useragent("headphones", "0.0",
                                 "https://github.com/rembo10/headphones")
    musicbrainzngs.set_hostname(mbhost + ":" + str(mbport))
    if sleepytime == 0:
        musicbrainzngs.set_rate_limit(False)
    else:
        musicbrainzngs.set_rate_limit(True)

    # Add headphones credentials
    if headphones.MIRROR == "headphones":
        if not mbuser and mbpass:
            logger.warn("No username or password set for VIP server")
        else:
            musicbrainzngs.hpauth(mbuser, mbpass)

    # Don't really need to return q anymore since ngs, but maybe we can return an 'initialized=True' instead?
    q = musicbrainzngs

    logger.debug(
        'Using the following server values:\nMBHost: %s ; MBPort: %i  ;  Sleep Interval: %i '
        % (mbhost, mbport, sleepytime))

    return (q, sleepytime)
Example #37
0
def initialize_scheduler():
    """
    Start the scheduled background tasks. Because this method can be called
    multiple times, the old tasks will be first removed.
    """

    from headphones import updater, searcher, librarysync, postprocessor, \
        torrentfinished

    with SCHED_LOCK:
        # Remove all jobs
        count = len(SCHED.get_jobs())

        if count > 0:
            logger.debug("Current number of background tasks: %d", count)
            SCHED.shutdown()
            SCHED.remove_all_jobs()

        # Regular jobs
        if CONFIG.UPDATE_DB_INTERVAL > 0:
            SCHED.add_job(
                updater.dbUpdate,
                trigger=IntervalTrigger(hours=CONFIG.UPDATE_DB_INTERVAL))
        if CONFIG.SEARCH_INTERVAL > 0:
            SCHED.add_job(
                searcher.searchforalbum,
                trigger=IntervalTrigger(minutes=CONFIG.SEARCH_INTERVAL))
        if CONFIG.LIBRARYSCAN_INTERVAL > 0:
            SCHED.add_job(
                librarysync.libraryScan,
                trigger=IntervalTrigger(hours=CONFIG.LIBRARYSCAN_INTERVAL))
        if CONFIG.DOWNLOAD_SCAN_INTERVAL > 0:
            SCHED.add_job(
                postprocessor.checkFolder,
                trigger=IntervalTrigger(minutes=CONFIG.DOWNLOAD_SCAN_INTERVAL))

        # Update check
        if CONFIG.CHECK_GITHUB:
            SCHED.add_job(
                versioncheck.checkGithub,
                trigger=IntervalTrigger(minutes=CONFIG.CHECK_GITHUB_INTERVAL))

        # Remove Torrent + data if Post Processed and finished Seeding
        if CONFIG.TORRENT_REMOVAL_INTERVAL > 0:
            SCHED.add_job(torrentfinished.checkTorrentFinished,
                          trigger=IntervalTrigger(
                              minutes=CONFIG.TORRENT_REMOVAL_INTERVAL))

        # Start scheduler
        logger.info("(Re-)Scheduling background tasks")
        SCHED.start()
Example #38
0
def getTorrentFolder(result):
    logger.debug('Deluge: Get torrent folder name')
    if not any(delugeweb_auth):
        _get_auth()

    try:
        post_data = json.dumps({
            "method": "web.get_torrent_status",
            "params": [result['hash'], ["total_done"]],
            "id": 22
        })
        response = requests.post(delugeweb_url,
                                 data=post_data.encode('utf-8'),
                                 cookies=delugeweb_auth)
        result['total_done'] = json.loads(
            response.text)['result']['total_done']

        tries = 0
        while result['total_done'] == 0 and tries < 10:
            tries += 1
            time.sleep(5)
            response = requests.post(delugeweb_url,
                                     data=post_data.encode('utf-8'),
                                     cookies=delugeweb_auth)
            result['total_done'] = json.loads(
                response.text)['result']['total_done']

        post_data = json.dumps({
            "method":
            "web.get_torrent_status",
            "params": [
                result['hash'],
                [
                    "name", "save_path", "total_size", "num_files", "message",
                    "tracker", "comment"
                ]
            ],
            "id":
            23
        })

        response = requests.post(delugeweb_url,
                                 data=post_data.encode('utf-8'),
                                 cookies=delugeweb_auth)

        result['save_path'] = json.loads(response.text)['result']['save_path']
        result['name'] = json.loads(response.text)['result']['name']

        return json.loads(response.text)['result']['name']
    except Exception as e:
        logger.debug('Deluge: Could not get torrent folder name: %s' % str(e))
Example #39
0
def checkGithub():
    headphones.COMMITS_BEHIND = 0

    # Get the latest version available from github
    logger.info('Retrieving latest version information from GitHub')
    url = 'https://api.github.com/repos/%s/headphones/commits/%s' % (
    headphones.CONFIG.GIT_USER, headphones.CONFIG.GIT_BRANCH)
    version = request.request_json(url, timeout=20, validator=lambda x: type(x) == dict)

    if version is None:
        logger.warn(
            'Could not get the latest version from GitHub. Are you running a local development version?')
        return headphones.CURRENT_VERSION

    headphones.LATEST_VERSION = version['sha']
    logger.debug("Latest version is %s", headphones.LATEST_VERSION)

    # See how many commits behind we are
    if not headphones.CURRENT_VERSION:
        logger.info(
            'You are running an unknown version of Headphones. Run the updater to identify your version')
        return headphones.LATEST_VERSION

    if headphones.LATEST_VERSION == headphones.CURRENT_VERSION:
        logger.info('Headphones is up to date')
        return headphones.LATEST_VERSION

    logger.info('Comparing currently installed version with latest GitHub version')
    url = 'https://api.github.com/repos/%s/headphones/compare/%s...%s' % (
    headphones.CONFIG.GIT_USER, headphones.LATEST_VERSION, headphones.CURRENT_VERSION)
    commits = request.request_json(url, timeout=20, whitelist_status_code=404,
                                   validator=lambda x: type(x) == dict)

    if commits is None:
        logger.warn('Could not get commits behind from GitHub.')
        return headphones.LATEST_VERSION

    try:
        headphones.COMMITS_BEHIND = int(commits['behind_by'])
        logger.debug("In total, %d commits behind", headphones.COMMITS_BEHIND)
    except KeyError:
        logger.info('Cannot compare versions. Are you running a local development version?')
        headphones.COMMITS_BEHIND = 0

    if headphones.COMMITS_BEHIND > 0:
        logger.info(
            'New version is available. You are %s commits behind' % headphones.COMMITS_BEHIND)
    elif headphones.COMMITS_BEHIND == 0:
        logger.info('Headphones is up to date')

    return headphones.LATEST_VERSION
Example #40
0
def encode_multipart(args, files, boundary=None):
    logger.debug('encode_multipart()')

    def escape_quote(s):
        return s.replace('"', '\\"')

    if boundary is None:
        boundary = ''.join(random.choice(_BOUNDARY_CHARS) for i in range(30))
    lines = []

    if args:
        for name, value in args.items():
            lines.extend((
                '--{0}'.format(boundary),
                'Content-Disposition: form-data; name="{0}"'.format(
                    escape_quote(name)),
                '',
                str(value),
            ))
    logger.debug(''.join(lines))

    if files:
        for name, value in files.items():
            filename = value['filename']
            if 'mimetype' in value:
                mimetype = value['mimetype']
            else:
                mimetype = mimetypes.guess_type(
                    filename)[0] or 'application/octet-stream'
            lines.extend((
                '--{0}'.format(boundary),
                'Content-Disposition: form-data; name="{0}"; filename="{1}"'.
                format(escape_quote(name), escape_quote(filename)),
                'Content-Type: {0}'.format(mimetype),
                '',
                value['content'],
            ))

    lines.extend((
        '--{0}--'.format(boundary),
        '',
    ))
    body = '\r\n'.join(lines)

    headers = {
        'Content-Type': 'multipart/form-data; boundary={0}'.format(boundary),
        'Content-Length': str(len(body)),
    }

    return (body, headers)
Example #41
0
def setTorrentPath(result):
    logger.debug('Deluge: Setting download path')
    if not any(delugeweb_auth):
        _get_auth()

    try:
        if headphones.CONFIG.DELUGE_DONE_DIRECTORY or headphones.CONFIG.DOWNLOAD_TORRENT_DIR:
            post_data = json.dumps({
                "method": "core.set_torrent_move_completed",
                "params": [result['hash'], True],
                "id": 7
            })
            response = requests.post(delugeweb_url,
                                     data=post_data.encode('utf-8'),
                                     cookies=delugeweb_auth,
                                     verify=deluge_verify_cert,
                                     headers=headers)

            if headphones.CONFIG.DELUGE_DONE_DIRECTORY:
                move_to = headphones.CONFIG.DELUGE_DONE_DIRECTORY
            else:
                move_to = headphones.CONFIG.DOWNLOAD_TORRENT_DIR

            # If Deluge host is remote, disable path checks for now
            if headphones.CONFIG.DELUGE_FOREIGN != 1:
                if not os.path.exists(move_to):
                    logger.debug(
                        'Deluge: %s directory doesn\'t exist, let\'s create it'
                        % move_to)
                    os.makedirs(move_to)
            post_data = json.dumps({
                "method": "core.set_torrent_move_completed_path",
                "params": [result['hash'], move_to],
                "id": 8
            })
            response = requests.post(delugeweb_url,
                                     data=post_data.encode('utf-8'),
                                     cookies=delugeweb_auth,
                                     verify=deluge_verify_cert,
                                     headers=headers)

            return not json.loads(response.text)['error']

        return True
    except Exception as e:
        logger.error('Deluge: Setting torrent move-to directory failed: %s' %
                     str(e))
        formatted_lines = traceback.format_exc().splitlines()
        logger.error('; '.join(formatted_lines))
        return None
Example #42
0
def removeTorrent(torrentid, remove_data=False):
    logger.debug('Deluge: Remove torrent %s' % torrentid)
    if not any(delugeweb_auth):
        _get_auth()

    try:
        logger.debug('Deluge: Checking if torrent %s finished seeding' % str(torrentid))
        post_data = json.dumps({"method": "web.get_torrent_status",
                                "params": [
                                    torrentid,
                                    [
                                        "name",
                                        "ratio",
                                        "state"
                                    ]
                                ],
                                "id": 26})

        response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth,
            verify=deluge_verify_cert, headers=headers)

        try:
            state = json.loads(response.text)['result']['state']
        except KeyError as e:
            logger.debug('Deluge: "state" KeyError when trying to remove torrent %s' % str(torrentid))
            return False

        not_finished = ["queued", "seeding", "downloading", "checking", "error"]
        result = False
        if state.lower() in not_finished:
            logger.debug('Deluge: Torrent %s is either queued or seeding, not removing yet' % str(torrentid))
            return False
        else:
            logger.debug('Deluge: Removing torrent %s' % str(torrentid))
            post_data = json.dumps({"method": "core.remove_torrent",
                                    "params": [
                                        torrentid,
                                        remove_data
                                        ],
                                    "id": 25})
            response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth,
                verify=deluge_verify_cert, headers=headers)
            result = json.loads(response.text)['result']

            return result
    except Exception as e:
        logger.error('Deluge: Removing torrent failed: %s' % str(e))
        formatted_lines = traceback.format_exc().splitlines()
        logger.error('; '.join(formatted_lines))
        return None
Example #43
0
def _add_torrent_file(result):
    logger.debug('Deluge: Adding file')
    if not any(delugeweb_auth):
        _get_auth()
    try:
        # content is torrent file contents that needs to be encoded to base64
        post_data = json.dumps({
            "method":
            "core.add_torrent_file",
            "params": [
                result['name'] + '.torrent',
                b64encode(result['content'].encode('utf8')), {}
            ],
            "id":
            2
        })
        response = requests.post(delugeweb_url,
                                 data=post_data.encode('utf-8'),
                                 cookies=delugeweb_auth,
                                 verify=deluge_verify_cert)
        result['hash'] = json.loads(response.text)['result']
        logger.debug('Deluge: Response was %s' %
                     str(json.loads(response.text)))
        return json.loads(response.text)['result']
    except UnicodeDecodeError:
        try:
            # content is torrent file contents that needs to be encoded to base64
            # this time let's try leaving the encoding as is
            logger.debug(
                'Deluge: There was a decoding issue, let\'s try again')
            post_data = json.dumps({
                "method":
                "core.add_torrent_file",
                "params": [
                    result['name'] + '.torrent',
                    b64encode(result['content']), {}
                ],
                "id":
                22
            })
            response = requests.post(delugeweb_url,
                                     data=post_data.encode('utf-8'),
                                     cookies=delugeweb_auth,
                                     verify=deluge_verify_cert)
            result['hash'] = json.loads(response.text)['result']
            logger.debug('Deluge: Response was %s' %
                         str(json.loads(response.text)))
            return json.loads(response.text)['result']
        except Exception as e:
            logger.error(
                'Deluge: Adding torrent file failed after decode: %s' % str(e))
            formatted_lines = traceback.format_exc().splitlines()
            logger.error('; '.join(formatted_lines))
            return False
    except Exception as e:
        logger.error('Deluge: Adding torrent file failed: %s' % str(e))
        formatted_lines = traceback.format_exc().splitlines()
        logger.error('; '.join(formatted_lines))
        return False
Example #44
0
def checkFolder():

    with postprocessor_lock:

        myDB = db.DBConnection()
        snatched = myDB.select(
            'SELECT * from snatched WHERE Status="Snatched"')

        for album in snatched:

            if album['FolderName']:

                if album['Kind'] == 'nzb':
                    # We're now checking sab config options after sending to determine renaming - but we'll keep the
                    # iterations in just in case we can't read the config for some reason

                    nzb_album_possibilities = [
                        album['FolderName'],
                        sab_replace_dots(album['FolderName']),
                        sab_replace_spaces(album['FolderName']),
                        sab_replace_spaces(
                            sab_replace_dots(album['FolderName']))
                    ]

                    for nzb_folder_name in nzb_album_possibilities:

                        nzb_album_path = os.path.join(
                            headphones.DOWNLOAD_DIR,
                            nzb_folder_name).encode(headphones.SYS_ENCODING,
                                                    'replace')

                        if os.path.exists(nzb_album_path):
                            logger.debug(
                                'Found %s in NZB download folder. Verifying....'
                                % album['FolderName'])
                            verify(album['AlbumID'], nzb_album_path, 'nzb')

                if album['Kind'] == 'torrent':

                    torrent_album_path = os.path.join(
                        headphones.DOWNLOAD_TORRENT_DIR,
                        album['FolderName']).encode(headphones.SYS_ENCODING,
                                                    'replace')

                    if os.path.exists(torrent_album_path):
                        logger.debug(
                            'Found %s in torrent download folder. Verifying....'
                            % album['FolderName'])
                        verify(album['AlbumID'], torrent_album_path, 'torrent')
Example #45
0
def getName(hash):
    logger.debug('getName(%s)' % hash)

    qbclient = qbittorrentclient()

    tries = 1
    while tries <= 6:
        time.sleep(10)
        status, torrentlist = qbclient._get_list()
        for torrent in torrentlist:
            if torrent['hash'].lower() == hash.lower():
                return torrent['name']
        tries += 1

    return None
Example #46
0
def check_setting_str(config, cfg_name, item_name, def_val, log=True):
    try:
        my_val = config[cfg_name][item_name]
    except:
        my_val = def_val
        try:
            config[cfg_name][item_name] = my_val
        except:
            config[cfg_name] = {}
            config[cfg_name][item_name] = my_val

    if log:
        logger.debug(item_name + " -> " + my_val)
    else:
        logger.debug(item_name + " -> ******")
    return my_val
Example #47
0
def removeTorrent(hash, remove_data=False):

    logger.debug('removeTorrent(%s,%s)' % (hash, remove_data))

    qbclient = qbittorrentclient()
    status, torrentList = qbclient._get_list()
    for torrent in torrentList:
        if torrent['hash'].upper() == hash.upper():
            if torrent['state'] == 'uploading' or torrent['state'] == 'stalledUP':
                logger.info('%s has finished seeding, removing torrent and data' % torrent['name'])
                qbclient.remove(hash, remove_data)
                return True
            else:
                logger.info('%s has not finished seeding yet, torrent will not be removed, will try again on next run' % torrent['name'])
                return False
    return False
Example #48
0
def checkFolder():

    myDB = db.DBConnection()
    snatched = myDB.select('SELECT * from snatched WHERE Status="Snatched"')

    for album in snatched:

        if album['FolderName']:

            album_path = os.path.join(headphones.DOWNLOAD_DIR,
                                      album['FolderName']).encode(
                                          headphones.SYS_ENCODING)

            if os.path.exists(album_path):
                logger.debug('Found %s. Verifying....' % album['FolderName'])
                verify(album['AlbumID'], album_path)
Example #49
0
def sendNZB(nzb):

    addToTop = False
    nzbgetXMLrpc = "%(protocol)s://%(username)s:%(password)s@%(host)s/xmlrpc"

    if not headphones.CONFIG.NZBGET_HOST:
        logger.error(
            u"No NZBget host found in configuration. Please configure it.")
        return False

    if headphones.CONFIG.NZBGET_HOST.startswith('https://'):
        protocol = 'https'
        host = headphones.CONFIG.NZBGET_HOST.replace('https://', '', 1)
    else:
        protocol = 'http'
        host = headphones.CONFIG.NZBGET_HOST.replace('http://', '', 1)

    url = nzbgetXMLrpc % {
        "protocol": protocol,
        "host": host,
        "username": headphones.CONFIG.NZBGET_USERNAME,
        "password": headphones.CONFIG.NZBGET_PASSWORD
    }

    nzbGetRPC = xmlrpclib.ServerProxy(url)
    try:
        if nzbGetRPC.writelog(
                "INFO", "headphones connected to drop of %s any moment now." %
            (nzb.name + ".nzb")):
            logger.debug(u"Successfully connected to NZBget")
        else:
            logger.info(
                u"Successfully connected to NZBget, but unable to send a message"
                % (nzb.name + ".nzb"))

    except httplib.socket.error:
        logger.error(
            u"Please check your NZBget host and port (if it is running). NZBget is not responding to this combination"
        )
        return False

    except xmlrpclib.ProtocolError, e:
        if e.errmsg == "Unauthorized":
            logger.error(u"NZBget password is incorrect.")
        else:
            logger.error(u"Protocol Error: " + e.errmsg)
        return False
Example #50
0
def _add_torrent_file(result):
    logger.debug('Deluge: Adding file')

    options = {}

    if headphones.CONFIG.DELUGE_DOWNLOAD_DIRECTORY:
        options['download_location'] = headphones.CONFIG.DELUGE_DOWNLOAD_DIRECTORY

    if headphones.CONFIG.DELUGE_DONE_DIRECTORY or headphones.CONFIG.DOWNLOAD_TORRENT_DIR:
        options['move_completed'] = 1
        if headphones.CONFIG.DELUGE_DONE_DIRECTORY:
            options['move_completed_path'] = headphones.CONFIG.DELUGE_DONE_DIRECTORY
        else:
            options['move_completed_path'] = headphones.CONFIG.DOWNLOAD_TORRENT_DIR

    if headphones.CONFIG.DELUGE_PAUSED:
        options['add_paused'] = headphones.CONFIG.DELUGE_PAUSED

    if not any(delugeweb_auth):
        _get_auth()
    try:
        # content is torrent file contents that needs to be encoded to base64
        post_data = json.dumps({"method": "core.add_torrent_file",
                                "params": [result['name'] + '.torrent',
                                b64encode(result['content'].encode('utf8')),
                                options],
                                "id": 2})
        response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth,
            verify=deluge_verify_cert, headers=headers)
        result['hash'] = json.loads(response.text)['result']
        logger.debug('Deluge: Response was %s' % str(json.loads(response.text)))
        return json.loads(response.text)['result']
    except UnicodeDecodeError:
        try:
            # content is torrent file contents that needs to be encoded to base64
            # this time let's try leaving the encoding as is
            logger.debug('Deluge: There was a decoding issue, let\'s try again')
            post_data = json.dumps({"method": "core.add_torrent_file",
                                    "params": [result['name'].decode('utf8') + '.torrent',
                                    b64encode(result['content']),
                                    options],
                                    "id": 22})
            response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth,
                verify=deluge_verify_cert, headers=headers)
            result['hash'] = json.loads(response.text)['result']
            logger.debug('Deluge: Response was %s' % str(json.loads(response.text)))
            return json.loads(response.text)['result']
        except Exception as e:
            logger.error('Deluge: Adding torrent file failed after decode: %s' % str(e))
            formatted_lines = traceback.format_exc().splitlines()
            logger.error('; '.join(formatted_lines))
            return False
    except Exception as e:
        logger.error('Deluge: Adding torrent file failed: %s' % str(e))
        formatted_lines = traceback.format_exc().splitlines()
        logger.error('; '.join(formatted_lines))
        return False
Example #51
0
def setTorrentLabel(result):
    logger.debug('Deluge: Setting label')
    label = headphones.CONFIG.DELUGE_LABEL

    if not any(delugeweb_auth):
        _get_auth()

    if ' ' in label:
        logger.error('Deluge: Invalid label. Label can\'t contain spaces - replacing with underscores')
        label = label.replace(' ', '_')
    if label:
        # check if label already exists and create it if not
        post_data = json.dumps({"method": 'label.get_labels',
                                "params": [],
                                "id": 3})
        response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth,
            verify=deluge_verify_cert, headers=headers)
        labels = json.loads(response.text)['result']

        if labels is not None:
            if label not in labels:
                try:
                    logger.debug('Deluge: %s label doesn\'t exist in Deluge, let\'s add it' % label)
                    post_data = json.dumps({"method": 'label.add',
                                            "params": [label],
                                            "id": 4})
                    response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth,
                        verify=deluge_verify_cert, headers=headers)
                    logger.debug('Deluge: %s label added to Deluge' % label)
                except Exception as e:
                    logger.error('Deluge: Setting label failed: %s' % str(e))
                    formatted_lines = traceback.format_exc().splitlines()
                    logger.error('; '.join(formatted_lines))

            # add label to torrent
            post_data = json.dumps({"method": 'label.set_torrent',
                                    "params": [result['hash'], label],
                                    "id": 5})
            response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth,
                verify=deluge_verify_cert, headers=headers)
            logger.debug('Deluge: %s label added to torrent' % label)
        else:
            logger.debug('Deluge: Label plugin not detected')
            return False

    return not json.loads(response.text)['error']
Example #52
0
def getSimilar():
    myDB = db.DBConnection()
    results = myDB.select(
        "SELECT ArtistID from artists ORDER BY HaveTracks DESC")

    logger.info("Fetching similar artists from Last.FM for tag cloud")
    artistlist = []

    for result in results[:12]:
        data = request_lastfm("artist.getsimilar", mbid=result["ArtistId"])
        time.sleep(10)

        if data and "similarartists" in data:
            artists = data["similarartists"]["artist"]

            for artist in artists:
                try:
                    artist_mbid = artist["mbid"]
                    artist_name = artist["name"]
                except TypeError:
                    continue

                if not any(artist_mbid in x for x in results):
                    artistlist.append((artist_name, artist_mbid))

    # Add new artists to tag cloud
    logger.debug("Fetched %d artists from Last.FM", len(artistlist))
    count = defaultdict(int)

    for artist, mbid in artistlist:
        count[artist, mbid] += 1

    items = count.items()
    top_list = sorted(items, key=lambda x: x[1], reverse=True)[:25]

    random.shuffle(top_list)

    myDB.action("DELETE from lastfmcloud")
    for item in top_list:
        artist_name, artist_mbid = item[0]
        count = item[1]

        myDB.action("INSERT INTO lastfmcloud VALUES( ?, ?, ?)",
                    [artist_name, artist_mbid, count])

    logger.debug("Inserted %d artists into Last.FM tag cloud", len(top_list))
Example #53
0
 def _get_sid(self, base_url, username, password):
     # login so we can capture SID cookie
     login_data = urllib.urlencode({
         'username': username,
         'password': password
     })
     try:
         self.opener.open(base_url + '/login', login_data)
     except urllib2.URLError as err:
         logger.debug(
             'Error getting SID. qBittorrent responded with error: ' +
             str(err.reason))
         return
     for cookie in self.cookiejar:
         logger.debug('login cookie: ' + cookie.name + ', value: ' +
                      cookie.value)
     return
Example #54
0
def _add_torrent_url(result):
    logger.debug('Deluge: Adding URL')
    if not any(delugeweb_auth):
        _get_auth()
    try:
        post_data = json.dumps({"method": "web.download_torrent_from_url",
                                "params": [result['url'], {}],
                                "id": 32})
        response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth,
            verify=deluge_verify_cert, headers=headers)
        result['location'] = json.loads(response.text)['result']
        logger.debug('Deluge: Response was %s' % str(json.loads(response.text)))
        return json.loads(response.text)['result']
    except Exception as e:
        logger.error('Deluge: Adding torrent URL failed: %s' % str(e))
        formatted_lines = traceback.format_exc().splitlines()
        logger.error('; '.join(formatted_lines))
        return False
Example #55
0
    def _action(self, params, body=None, content_type=None):
        url = self.base_url + '/gui/' + '?token=' + self.token + '&' + urllib.urlencode(
            params)
        request = urllib2.Request(url)

        if body:
            request.add_data(body)
            request.add_header('Content-length', len(body))
        if content_type:
            request.add_header('Content-type', content_type)

        try:
            response = self.opener.open(request)
            return response.code, json.loads(response.read())
        except urllib2.HTTPError as err:
            logger.debug('URL: ' + str(url))
            logger.debug('uTorrent webUI raised the following error: ' +
                         str(err))
Example #56
0
def setTorrentPause(result):
    logger.debug('Deluge: Pausing torrent')
    if not any(delugeweb_auth):
        _get_auth()

    if headphones.CONFIG.DELUGE_PAUSED:
        post_data = json.dumps({
            "method": "core.pause_torrent",
            "params": [[result['hash']]],
            "id": 9
        })
        response = requests.post(delugeweb_url,
                                 data=post_data.encode('utf-8'),
                                 cookies=delugeweb_auth)

        return not json.loads(response.text)['error']

    return True
Example #57
0
def startmb():

    mbuser = None
    mbpass = None

    if headphones.MIRROR == "musicbrainz.org":
        mbhost = "musicbrainz.org"
        mbport = 80
        sleepytime = 1
    elif headphones.MIRROR == "custom":
        mbhost = headphones.CUSTOMHOST
        mbport = int(headphones.CUSTOMPORT)
        sleepytime = int(headphones.CUSTOMSLEEP)
    elif headphones.MIRROR == "headphones":
        mbhost = "178.63.142.150"
        mbport = 8181
        mbuser = headphones.HPUSER
        mbpass = headphones.HPPASS
        sleepytime = 0
    else:
        return False

    musicbrainzngs.set_useragent("headphones", "0.0",
                                 "https://github.com/rembo10/headphones")
    musicbrainzngs.set_hostname(mbhost + ":" + str(mbport))
    if sleepytime == 0:
        musicbrainzngs.set_rate_limit(False)
    else:
        #calling it with an it ends up blocking all requests after the first
        musicbrainzngs.set_rate_limit(limit_or_interval=float(sleepytime))

    # Add headphones credentials
    if headphones.MIRROR == "headphones":
        if not mbuser and mbpass:
            logger.warn("No username or password set for VIP server")
        else:
            musicbrainzngs.hpauth(mbuser, mbpass)

    logger.debug(
        'Using the following server values:\nMBHost: %s ; MBPort: %i  ;  Sleep Interval: %i '
        % (mbhost, mbport, sleepytime))

    return True
Example #58
0
def addTorrent(link):
    logger.debug('addTorrent(%s)' % link)

    qbclient = qbittorrentclient()
    if qbclient.version == 2:
        return qbclient.qb.download_from_link(
            link,
            savepath=headphones.CONFIG.DOWNLOAD_TORRENT_DIR,
            category=headphones.CONFIG.QBITTORRENT_LABEL)
    else:
        args = {
            'urls': link,
            'savepath': headphones.CONFIG.DOWNLOAD_TORRENT_DIR
        }
        if headphones.CONFIG.QBITTORRENT_LABEL:
            args['category'] = headphones.CONFIG.QBITTORRENT_LABEL

        return qbclient._command('command/download', args,
                                 'multipart/form-data')
Example #59
0
    def _inner(root, directories, files):
        for directory in directories:
            path = os.path.join(root, directory)

            if followlinks and os.path.islink(path):
                real_path = os.path.abspath(os.readlink(path))

                if real_path in traversed:
                    logger.debug("Skipping '%s' since it is a symlink to " \
                                 "'%s', which is already visited.", path, real_path)
                else:
                    traversed.append(real_path)

                    for args in os.walk(real_path):
                        for result in _inner(*args):
                            yield result

        # Pass on actual result
        yield root, directories, files
Example #60
0
def checkFolder():

    with postprocessor_lock:

        myDB = db.DBConnection()
        snatched = myDB.select(
            'SELECT * from snatched WHERE Status="Snatched"')

        for album in snatched:

            if album['FolderName']:

                # Need to check for variations due to sab renaming. Ideally we'd check the sab config via api to
                # figure out which options are checked, but oh well

                nzb_album_possibilities = [
                    album['FolderName'],
                    sab_replace_dots(album['FolderName']),
                    sab_replace_spaces(album['FolderName']),
                    sab_replace_dots(sab_replace_spaces(album['FolderName']))
                ]

                torrent_album_path = os.path.join(
                    headphones.DOWNLOAD_TORRENT_DIR,
                    album['FolderName']).encode(headphones.SYS_ENCODING)

                for nzb_folder_name in nzb_album_possibilities:

                    nzb_album_path = os.path.join(headphones.DOWNLOAD_DIR,
                                                  nzb_folder_name).encode(
                                                      headphones.SYS_ENCODING)

                    if os.path.exists(nzb_album_path):
                        logger.debug(
                            'Found %s in NZB download folder. Verifying....' %
                            album['FolderName'])
                        verify(album['AlbumID'], nzb_album_path)

                if os.path.exists(torrent_album_path):
                    logger.debug(
                        'Found %s in torrent download folder. Verifying....' %
                        album['FolderName'])
                    verify(album['AlbumID'], torrent_album_path)