Source

VPlayer / bufferer.py

Full commit
# -*- coding: utf-8 -*-
import os
import sys
import httplib
from time import sleep

from PyQt4 import QtCore
from PyQt4 import QtSql

from vplayer import common
from vplayer import queries
from vplayer.logger import log
from vplayer.track import Track
from vplayer.tagreader import decoder

from urllib import getproxies

class DownloadError(Exception):
    pass


class Buffer(QtCore.QThread):
    "Download a track to temporary file"
    length = None
    pos = None
    started = False
    finished = False
    track = None
    failed = False
    data = str()
    die = False

    def __init__(self, track):
        QtCore.QThread.__init__(self)
        self.track = track
        self.url = track.url
        log.info('Buffer initialized')

    def stop(self):
        self.die = True

    def run(self):
        if self.track:
            if self.track.local:
                try:
                    self.data = bytes(open(self.track.url).read())
                    self.length = len(self.data)
                    self.started = True
                    self.finished = True
                    self.pos = self.length
                    return
                except:
                    pass
            query = QtSql.QSqlQuery()
            url = queries.get_track_url(self.track)
            if url:
                log.info('Track found in local DB, playing it.')
                try:
                    f = open(unicode(url), 'rb')
                    self.data = f.read()
                    f.close()
                    self.length = len(self.data)
                    self.pos = self.length
                    self.started = True
                    self.finished = True
                    return
                except:
                    pass
            else:
                log.info('Track not found in local DB')
        try:
            log.info('Downloading track from url: %s' % self.url)
            self.download(self.url, self.begin, self.update, self.end)
        except DownloadError as e:
            log.error('Track could not be downloaded: %s' % unicode(self.track))
            self.track.set_bad(True)
            log.error(str(e))
            self.failed = True

    def download(self, url, start = None, status = None, end = None):

        addr = httplib.urlsplit(url)
        try:
            proxy = getproxies()['http']
            host, port = proxy.split(':')
            conn = httplib.HTTPConnection(host, port)
        except:
            conn = httplib.HTTPConnection(addr.netloc)
        try:
            conn.request("GET", url)
        except:
            raise DownloadError()
        r1 = conn.getresponse()
        if r1.status != 200:
            log.error('Could not get track')
            raise DownloadError()
        length = r1.length
        start(length)
        self.data = str()
        try:
            while len(self.data) < length:
                if self.die:
                    self.failed = True
                    log.debug('Buffer stopped')
                    return
                newdata = r1.read(8196)
                if not newdata:
                    raise DownloadError()
                self.data += newdata
                status(len(self.data))
        except DownloadError as e:
            log.error('DownloadError on %d of %d bytes' % (len(self.data), self.length) )
            log.error(str(e))
            raise DownloadError()
        end(len(self.data))
        conn.close()

    def begin(self, length):
        "For internal use only"
        self.started = True
        self.length = length

    def update(self, read):
        "For internal use only"
        self.pos = read

    def stop(self):
        self.die = True

    def end(self, size):
        "For internal use only"
        "Saving file and writing db when downloaded"

        log.info('Track download completed, writing to disk')
        self.finished = True
        settings = QtCore.QSettings()
        if settings.value('caching/use', QtCore.QVariant(False)).toBool():
            log.debug('Caching is ON: saving file.')
            if not self.track:
                log.error('No self.track variable. Strange error.')
                return
            std = os.path.join(common.get_config_dir(), 'cached')
            coldir = unicode(settings.value('caching/directory',
                                    QtCore.QVariant(std)).toString())
            if not os.path.exists(coldir):
                try:
                    log.debug('Cache directory does not exist: creating it')
                    os.mkdir(coldir)
                except:
                    log.error('Caching directory does not exist and can not be created')
                    return
            if not os.path.isdir(coldir):
                log.error('Caching directory does not exist and can not be created')
                return
            art = common.clean_string(self.track.artist)
            artdir = os.path.join(coldir, art)
            if not os.path.exists(artdir):
                try:
                    os.mkdir(artdir)
                except:
                    log.error('Could not create artist directory.')
                    return

            log.debug('Getting album name from ID3')
            if self.track.got_album:
                album = self.track.album
            else:
                album = 'Unknown'
            year = 0
            trackno = 0
            if self.data[0:3]=='ID3':
                log.debug('Found ID3v2 tags')
                if not self.track.got_album:
                    talb = self.data.find('TALB')
                    if talb>-1:
                        begin = talb + 11
                        end = talb + 10 + ord(self.data[talb+7:talb+8])
                        album = self.data[begin:end].replace('\x00','')
                    else:
                        log.debug('Album name tag not present in ID3v2')
                tdrc = self.data.find('TDRC')
                tyer = self.data.find('TYER')
                if tdrc>-1:
                    begin = tdrc + 11
                    end = tdrc + 10 + ord(self.data[tdrc+7:tdrc+8])
                    try:
                        year = int(self.data[begin : end].replace('\x00',''))
                    except:
                        year = 0
                elif tyer>-1:
                    begin = tyer + 11
                    end = tyer + 10 + ord(self.data[tyer+7:tyer+8])
                    try:
                        year = int(self.data[begin : end].replace('\x00',''))
                    except:
                        year = 0
                else:
                    log.debug('Year tag not present in ID3v2')
                trck = self.data.find('TRCK')
                if trck>-1:
                    begin = trck + 11
                    end = trck + 10 + ord(self.data[trck+7:trck+8])
                    try:
                        trackno = int(self.data[begin : end].replace('\x00',''))
                    except:
                        trackno = 0

            if (len(self.data) > 128 and self.data[-128:-125] == 'TAG') \
                and (album == 'Unknown' or year == '0'):
                log.debug('Found ID3v1 tags')
                talbum = self.data[-65:-35].replace('\x00','')
                if not talbum:
                    log.debug('Album name tag not present in ID3v1')
                else:
                    album = talbum
                try:
                    tyear = int(self.data[-35:-31].replace('\x00',''))
                except:
                    tyear = 0
                if not tyear:
                    log.debug('Year tag not present in ID3v1')
                else:
                    year = tyear

            log.debug('Decoding album name...')
            tags = {'album': album }
            tags = decoder(tags)
            album = unicode(tags['album'])
            album = common.clean_spaces(album)

            log.debug('Checking year...')
            log.debug('Using album name: %s' % album)
            log.debug('Using year: %s' % year)

            albdirname = unicode(year) + ' - ' + common.clean_string(album)

            albdir = os.path.join(artdir, albdirname)

            if not os.path.exists(albdir):
                try:
                    os.mkdir(albdir)
                except:
                    log.error('Could not create album directory.')
                    return

            log.debug('Choosing file name...')
            tit = common.clean_string(self.track.title)
            if trackno:
                filep = os.path.join(albdir, str(trackno)+' - '+tit)
            else:
                filep = os.path.join(albdir, tit)
            while os.path.exists(filep+'.mp3'):
                filep+='_'
            filep += '.mp3'
            log.info('Wrinting file %s.' % filep)
            workfile = open(filep,'w')
            workfile.write(self.data)
            workfile.close()
            log.info('Writing track info to DB')
            self.track.url = filep
            self.track.album = album
            self.track.trackno = trackno
            self.track.year = year
            if not queries.insert_track(self.track, cache = True):
                log.error("Could not add track to database")
                raise DownloadError()
            query = QtSql.QSqlQuery()
            log.info('Track saved successfully')
            self.emit(QtCore.SIGNAL('database_changed'))
        else:
            log.debug('Caching if OFF: not saving file')