def _start_backend(self): # spawn the backend process args = [ 'python', os.path.dirname(__file__) + '/backend/main.py', self.name, self.logfile ] self._candy_dirty = True self.server = subprocess.Popen(args, stdout=sys.stdout, stderr=sys.stderr) retry = 50 if os.path.exists(kaa.tempfile(self.name)): os.unlink(kaa.tempfile(self.name)) while True: try: self.ipc = kaa.rpc.connect(self.name) yield kaa.inprogress(self.ipc) break except Exception, e: retry -= 1 if retry == 0: raise e time.sleep(0.1)
def _update_series(self, id): info = self._db.query_one(type='series', tvdb=id) if info: signals['sync'].emit(info.get('name')) else: signals['sync'].emit() # download thetvdb information f = open(kaa.tempfile('thetvdb-%s.zip' % id), 'w') f.write((yield download(self.api + 'series/%s/all/en.zip' % id))) f.close() # load zip data z = zipfile.ZipFile(f.name) parent = None for name, data in (yield parse(z.open('en.xml')))[1]: if name == 'Series': objid = self._update_db('series', int(data.get('id')), name=data.get('SeriesName'), data=data) parent = ('series', objid) elif name == 'Episode': if not parent: raise ValueError('Unexpected parse error: got Episode element before Series') self._update_db('episode', int(data.get('id')), name=data.get('EpisodeName'), parent=parent, season=int(data.get('SeasonNumber')), episode=int(data.get('EpisodeNumber')), data=data) else: log.error('unknown element: %s', name) self._db.commit() # load image information for name, data in (yield parse(z.open('banners.xml')))[1]: if name == 'Banner': self._update_db('banner', int(data.get('id')), btype=data.get('BannerType'), data=data, parent=parent) else: log.error('unknown element: %s', name) self._db.commit() os.unlink(f.name) # download metadata images info = self._db.query_one(type='series', tvdb=id) if not info: yield None serie = Series(self, info) if serie.image and not os.path.isfile(serie.image): data = yield serie.get_all_images()[0].fetch() open(serie.image, 'w').write(data) if serie.poster and not os.path.isfile(serie.poster): data = yield serie.get_all_posters()[0].fetch() open(serie.poster, 'w').write(data) if serie.banner and not os.path.isfile(serie.banner): data = yield serie.get_all_banners()[0].fetch() open(serie.banner, 'w').write(data) for season in serie.seasons: if season.poster and season.get_all_posters() and not os.path.isfile(season.poster): data = yield season.get_all_posters()[0].fetch() open(season.poster, 'w').write(data) if season.banner and season.get_all_banners() and not os.path.isfile(season.banner): data = yield season.get_all_banners()[0].fetch() open(season.banner, 'w').write(data)
def update(epg, xmltv_file=None): """ Interface to source_xmltv. """ if not xmltv_file and config.xmltv.grabber: log.info('grabbing listings using %s', config.xmltv.grabber) xmltv_file = kaa.tempfile('TV.xml') if config.xmltv.data_file: xmltv_file = config.xmltv.data_file log_file = kaa.tempfile('TV.xml.log') # using os.system is ugly because it blocks ... but we are inside a thread so it # seems to be ok. ec = os.system('%s --output %s --days %s >%s 2>%s' % \ (config.xmltv.grabber, xmltv_file, config.days, log_file, log_file)) if not os.path.exists(xmltv_file) or ec: log.error('grabber failed, see %s', log_file) return if config.xmltv.sort: log.info('sorting listings') shutil.move(xmltv_file, xmltv_file + '.tmp') os.system('%s --output %s %s.tmp >>%s 2>>%s' % \ (config.xmltv.sort, xmltv_file, xmltv_file, log_file, log_file)) os.unlink(xmltv_file + '.tmp') if not os.path.exists(xmltv_file): log.error('sorting failed, see %s', log_file) return else: log.info('not configured to use tv_sort, skipping') elif not xmltv_file: xmltv_file = config.xmltv.data_file # Now we have a xmltv file and need to parse it parser = XmltvParser() parser.add_channel = epg.add_channel parser.add_program = epg.add_program parser.parse(xmltv_file) return True
def create(config_dir, scheduler=None): """ Create thumbnail Unix socket and object """ # create tmp dir and change directory to it tmpdir = kaa.tempfile('thumb') if not os.path.isdir(tmpdir): os.mkdir(tmpdir) os.chdir(tmpdir) try: return Thumbnailer(tmpdir, config_dir, scheduler) except IOError, e: log.error('thumbnail: %s' % e) time.sleep(0.1) sys.exit(0)
def _create(self, sid, blocksize, stream): """ Create the FIFO and return the kaa.Socket and the IBBSocket object. """ # Make a kaa.Socket to kaa.Socket connection. One socket will be # given to the outside, the other one will be used to communicate # with the IBBSocket. filename = kaa.tempfile('ibb', unique=True) socket1 = kaa.net.tls.TLSSocket() wait = kaa.inprogress(socket1.signals['new-client']) socket2 = kaa.Socket() socket1.listen(filename) yield socket2.connect(filename) socket1 = yield wait socket1.sid = sid socket2.signals['closed'].connect(self._app_close, sid) yield socket1, IBBSocket(socket2, self.remote.iqset, sid, blocksize, stream)
def _get_api_zipfile(self, url): STAY_LOCAL = os.getenv('STAY_LOCAL', 0) tmpname = kaa.tempfile(hashlib.md5(kaa.py3_b(url, fs=True)).hexdigest() + '.zip') url = self._apiurl + url if STAY_LOCAL and os.path.exists(tmpname): status = 200 else: # Try 3 times before giving up, unless it's a permanent error log.debug('fetching zip file %s', url) status, curl = yield download(url, tmpname, retry=3, resume=False) if status != 200: if os.path.exists(tmpname): os.unlink(tmpname) raise ProviderError('thetvdb gave status %d for %s' % (status, url)) try: z = zipfile.ZipFile(tmpname) except zipfile.BadZipfile: os.unlink(tmpname) raise ProviderError('invalid zip file from thetvdb at %s' % url) yield z
def _update_series(self, id): tmp = kaa.tempfile('thetvdb/%s' % id) if not os.path.isdir(tmp): os.mkdir(tmp) return f = open('%s/en.zip' % tmp, 'w') f.write((yield download(self.api + 'series/%s/all/en.zip' % id))) f.close() z = zipfile.ZipFile('%s/en.zip' % tmp) z.extract('en.xml', tmp) z.extract('banners.xml', tmp) os.unlink(tmp + '/en.zip') parent = None for name, data in (yield parse(open(tmp + '/en.xml')))[1]: if name == 'Series': s = self._update_db('series', int(data.get('id')), name=data.get('SeriesName'), data=data) parent = ('series', s) elif name == 'Episode': if not parent: raise RuntimeError() self._update_db('episode', int(data.get('id')), name=data.get('EpisodeName'), parent=parent, season=int(data.get('SeasonNumber')), episode=int(data.get('EpisodeNumber')), data=data) else: log.error('unknown element: %s', name) self._db.commit() os.unlink(tmp + '/en.xml') for name, data in (yield parse(open(tmp + '/banners.xml')))[1]: if name == 'Banner': self._update_db('banner', int(data.get('id')), btype=data.get('BannerType'), data=data, parent=parent) else: log.error('unknown element: %s', name) self._db.commit() os.unlink(tmp + '/banners.xml') os.rmdir(tmp)
def play(self): """ Start playback. """ log.debug('mplayer start playback') # we know that self._mp_info has to be there or the object would # not be selected by the generic one. FIXME: verify that! assert(self._mp_info) # create mplayer object self._mplayer = ChildProcess(self._mp_cmd, gdb = log.getEffectiveLevel() == logging.DEBUG) # get argument and filter list args, filters = self._mplayer.args, self._mplayer.filters if 'x11' in self._mp_info['video_drivers']: args.append('-nomouseinput') #if 'outbuf' in self._mp_info['video_filters']: # filters.append("outbuf=%s:yv12" % self._frame_shmkey) if self._properties['deinterlace'] == True or \ (self._properties['deinterlace'] == 'auto' and \ self._media.get('interlaced')): # add deinterlacer filters.append(config.mplayer.deinterlacer) if self._media.get('corrupt'): # File marked as corrupt. This happens for avi and mkv files # with no index. To make seeking work, add -idx args.append('-idx') # FIXME: self._filters_pre / add doesn't get included if no window. if self._window: self.configure_video() else: args.add(vo='null') self.configure_audio() # There is no way to make MPlayer ignore keys from the X11 window. So # this hack makes a temp input file that maps all keys to a dummy (and # non-existent) command which causes MPlayer not to react to any key # presses, allowing us to implement our own handlers. The temp file is # deleted once MPlayer has read it. tempfile = kaa.tempfile('popcorn/mplayer-input.conf') if not os.path.isfile(tempfile): keys = filter(lambda x: x not in string.whitespace, string.printable) keys = list(keys) + self._mp_info["keylist"] fd = open(tempfile, 'w') for key in keys: fd.write("%s noop\n" % key) fd.close() # only prevent input if the player is embedded if config.mplayer.embedded: args.add(input='conf=%s' % tempfile) # set properties subtitle filename and subtitle track if self._properties.get('subtitle-filename'): sub = self._properties.get('subtitle-filename') if os.path.splitext(sub)[1].lower() in ('.ifo', '.idx', '.sub'): args.add(vobsub=os.path.splitext(sub)[0], vobsubid=self._properties.get('subtitle-track')) else: args.add(subfile=sub) elif self._properties.get('subtitle-track') != None: args.add(sid=self._properties.get('subtitle-track')) if self._properties.get('cache') == 'auto': if self._media.scheme == "dvd": args.add(cache=8192) if self._media.scheme == "vcd": args.add(cache=4096) if self._media.scheme == "dvb": args.add(cache=1024) if self._media.scheme == "http": args.add(cache=8192, cache_min=5) else: args.add(cache=5000) else: args.add(cache=self._properties.get('cache')) # connect to signals self._mplayer.signals['readline'].connect_weak(self._child_handle_line) # start playback self._mplayer.start(self._media).connect_weak(self._child_exited)
def get_cachefile(self, url): """ Return the cache filename for the given url """ base = hashlib.md5(url).hexdigest() + os.path.splitext(url)[1] return kaa.tempfile('candy-images/' + base)
def update(epg): """ Interface to source_epgdata. """ if not config.epgdata.pin: log.error('PIN for epgdata.com is missing in tvserver.conf') return False # create a tempdir as working area tempdir = kaa.tempfile('epgdata') if not os.path.isdir(tempdir): os.mkdir(tempdir) # and clear it if needed for i in glob.glob(os.path.join(tempdir, '*')): os.remove(i) # temp file tmpfile = os.path.join(tempdir, 'temp.zip') # logfile logfile = kaa.tempfile('epgdata.log') # empty list for the xml docs docs = [] # count of the nodes that have to be parsed nodes = 0 # create download adresse for meta data address = 'http://www.epgdata.com/index.php' address += '?action=sendInclude&iLang=de&iOEM=xml&iCountry=de' address += '&pin=%s' % config.epgdata.pin address += '&dataType=xml' # remove old file if needed try: os.remove(tmpfile) except OSError: pass # download the meta data file log.info('Downloading meta data') # FIXME: don't rely on wget exit = os.system('wget -N -O %s "%s" >>%s 2>>%s' % (tmpfile, address, logfile, logfile)) if not os.path.exists(tmpfile) or exit: log.error('Cannot get file from epgdata.com, see %s' % logfile) return False # and unzip the zip file log.info('Unzipping data for meta data') # FIXME: don't rely on unzip (can probably use zipfile module) exit = os.system('unzip -uo -d %s %s >>%s 2>>%s' % (tempdir, tmpfile, logfile, logfile)) if exit: log.error('Cannot unzip the downloaded file, see %s' % logfile) return False # list of channel info xml files chfiles = glob.glob(os.path.join(tempdir, 'channel*.xml')) if len(chfiles) == 0: log.error('no channel xml files for parsing') return False # parse this files channels = ChannelParser() channels.add_channel = epg.add_channel for xmlfile in chfiles: # return the list of channels from the config file channels.parse(xmlfile) metadata = MetaParser() metadata.parse(os.path.join(tempdir, 'genre.xml')) metadata.parse(os.path.join(tempdir, 'category.xml')) # create download adresse for programm files address = 'http://www.epgdata.com/index.php' address += '?action=sendPackage&iLang=de&iOEM=xml&iCountry=de' address += '&pin=%s' % config.epgdata.pin address += '&dayOffset=%s&dataType=xml' # get the file for each day for i in range(0, int(config.days)): # remove old file if needed try: os.remove(tmpfile) except OSError: pass # download the zip file log.info('Getting data for day %s' % (i + 1)) exit = os.system('wget -N -O %s "%s" >>%s 2>>%s' % (tmpfile, address % i, logfile, logfile)) if not os.path.exists(tmpfile) or exit: log.error('Cannot get file from epgdata.com, see %s' % logfile) return False # and unzip the zip file log.info('Unzipping data for day %s' % (i + 1)) exit = os.system('unzip -uo -d %s %s >>%s 2>>%s' % (tempdir, tmpfile, logfile, logfile)) if exit: log.error('Cannot unzip the downloaded file, see %s' % logfile) return False # list of program xml files that must be parsed progfiles = glob.glob(os.path.join(tempdir, '*de_q[a-z].xml')) if len(progfiles) == 0: log.warning('no progam xml files for parsing') # parse the progam xml files prgparser = ProgramParser() prgparser.channels = channels prgparser.metadata = metadata prgparser.add_program = epg.add_program log.info('found %s files' % len(progfiles)) for xmlfile in progfiles: log.info('process %s' % xmlfile) prgparser.parse(xmlfile) return True
def update(epg): """ Interface to source_epgdata. """ if not config.epgdata.pin: log.error('PIN for epgdata.com is missing in tvserver.conf') return False # create a tempdir as working area tempdir = kaa.tempfile('epgdata') if not os.path.isdir(tempdir): os.mkdir(tempdir) # and clear it if needed for i in glob.glob(os.path.join(tempdir,'*')): os.remove(i) # temp file tmpfile = os.path.join(tempdir,'temp.zip') # logfile logfile = kaa.tempfile('epgdata.log') # empty list for the xml docs docs = [] # count of the nodes that have to be parsed nodes = 0 # create download adresse for meta data address = 'http://www.epgdata.com/index.php' address+= '?action=sendInclude&iLang=de&iOEM=xml&iCountry=de' address+= '&pin=%s' % config.epgdata.pin address+= '&dataType=xml' # remove old file if needed try: os.remove(tmpfile) except OSError: pass # download the meta data file log.info ('Downloading meta data') # FIXME: don't rely on wget exit = os.system('wget -N -O %s "%s" >>%s 2>>%s' %(tmpfile, address, logfile, logfile)) if not os.path.exists(tmpfile) or exit: log.error('Cannot get file from epgdata.com, see %s' %logfile) return False # and unzip the zip file log.info('Unzipping data for meta data') # FIXME: don't rely on unzip (can probably use zipfile module) exit = os.system('unzip -uo -d %s %s >>%s 2>>%s' %(tempdir, tmpfile, logfile, logfile)) if exit: log.error('Cannot unzip the downloaded file, see %s' %logfile) return False # list of channel info xml files chfiles = glob.glob(os.path.join(tempdir,'channel*.xml')) if len(chfiles)==0: log.error('no channel xml files for parsing') return False # parse this files channels = ChannelParser() channels.add_channel = epg.add_channel for xmlfile in chfiles: # return the list of channels from the config file channels.parse(xmlfile) metadata = MetaParser() metadata.parse(os.path.join(tempdir, 'genre.xml')) metadata.parse(os.path.join(tempdir, 'category.xml')) # create download adresse for programm files address = 'http://www.epgdata.com/index.php' address+= '?action=sendPackage&iLang=de&iOEM=xml&iCountry=de' address+= '&pin=%s' % config.epgdata.pin address+= '&dayOffset=%s&dataType=xml' # get the file for each day for i in range(0, int(config.days)): # remove old file if needed try: os.remove(tmpfile) except OSError: pass # download the zip file log.info('Getting data for day %s' %(i+1)) exit = os.system('wget -N -O %s "%s" >>%s 2>>%s' %(tmpfile, address %i, logfile, logfile)) if not os.path.exists(tmpfile) or exit: log.error('Cannot get file from epgdata.com, see %s' %logfile) return False # and unzip the zip file log.info('Unzipping data for day %s' %(i+1)) exit = os.system('unzip -uo -d %s %s >>%s 2>>%s' %(tempdir, tmpfile, logfile, logfile)) if exit: log.error('Cannot unzip the downloaded file, see %s' %logfile) return False # list of program xml files that must be parsed progfiles = glob.glob(os.path.join(tempdir,'*de_q[a-z].xml')) if len(progfiles)==0: log.warning('no progam xml files for parsing') # parse the progam xml files prgparser = ProgramParser() prgparser.channels = channels prgparser.metadata = metadata prgparser.add_program = epg.add_program log.info('found %s files' % len(progfiles)) for xmlfile in progfiles: log.info('process %s' % xmlfile) prgparser.parse(xmlfile) return True
def play(self): config = self._proxy._config vf = [] args = self._media._mplayer_args[:] args.extend('-slave -v -osdlevel 0 -fixed-vo -demuxer lavf') if self.audio_delay: args.add(delay=self.audio_delay) if self.cache == 0: args.add(nocache=True) elif isinstance(self.cache, (long, float, int)) or self.cache.isdigit(): args.add(cache=self.cache) if self._ss_seek: args.add(ss=self._ss_seek) self._ss_seek = None if self._media.get('corrupt'): # Index for the given file is corrupt. Must add -idx to allow # seeking. FIXME: for large filesthis can take a while. We # should: 1. provide progress feedback, 2. use -saveidx to keep # the index for next time. args.append('-idx') window = self._proxy.window if window is None: args.add(vo='null') elif config.video.vdpau.enabled and 'vdpau' in self._mp_info['video_drivers']: deint = {'cheap': 1, 'good': 2, 'better': 3, 'best': 4}.get(config.video.deinterlacing.method, 3) args.add(vo='vdpau:deint=%d,xv,x11' % deint) # Decide which codecs to enable based on what the user specified. We do # a simple substring match. regexp = '|'.join('.*%s.*vdpau' % c for c in config.video.vdpau.formats.replace(' ', '').split(',')) args.add(vc=','.join(codec for codec in self._mp_info['video_codecs'] if re.search(regexp, codec)) + ',') # If the display rate is less than the frame rate, -framedrop is # needed or else the audio will continually drift. # TODO: we could decide to add this only if the above condition is args.append('-framedrop') else: args.add(vo='xv,x11') vf.append(getattr(config.mplayer.deinterlacer, config.video.deinterlacing.method)) if isinstance(window, kaa.display.X11Window): # Create a new inner window. We must do this each time we start # MPlayer because MPlayer destroys the window (even the ones it # doesn't manage [!!]) at exit. inner = self._proxy._window_inner = kaa.display.X11Window(size=(1,1), parent=window) inner.show() # Set owner to False so we don't try to destroy the window. inner.owner = False args.add(wid=hex(inner.id).rstrip('L')) window.resize(self.width, self.height) if vf: args.add(vf=','.join(vf)) # MPlayer has issues with interlaced h264 inside transport streams that # can be fixed with -nocorrect-pts (and -demuxer lavf, which we're # already forcing). Unfortunately, kaa.metadata doesn't support these # files yet, nor can we tell if the content is interlaced. So we guess # based on file extension. Luckily -nocorrect-pts doesn't seem to hurt # progressive content. ext = os.path.splitext(self.uri)[1].lower() if self.vfourcc == 'H264' and ext in ('.ts', '.m2ts'): args.append('-nocorrect-pts') # Audio settings # XXX Currently only passthrough is handled here. All the # XXX other options and what device to use are ignored. if config.audio.passthrough: args.add(ac='hwac3,hwdts,') # There is no way to make MPlayer ignore keys from the X11 window. So # this hack makes a temp input file that maps all keys to a dummy (and # non-existent) command which causes MPlayer not to react to any key # presses, allowing us to implement our own handlers. tempfile = kaa.tempfile('popcorn/mplayer-input.conf') if not os.path.isfile(tempfile): keys = filter(lambda x: x not in string.whitespace, string.printable) keys = list(keys) + self._mp_info['keylist'] fd = open(tempfile, 'w') for key in keys: fd.write("%s noop\n" % key) fd.close() args.add(input='conf=%s' % tempfile) log.debug('Starting MPlayer with args: %s', ' '.join(args)) self.state = STATE_STARTING self._spawn(args, interactive=True) yield self._wait_for_signals('play', task='Play') # Play has begun successfully. _handle_child_line() will already # have set state to STATE_PLAYING. if isinstance(window, kaa.display.X11Window): # XXX: is it reasonable to automatically show the window now? # Maybe we should have an autoshow property? window.show()
def _update_series(self, id): info = self._db.query_one(type='series', tvdb=id) if info: signals['sync'].emit(info.get('name')) else: signals['sync'].emit() # download thetvdb information f = open(kaa.tempfile('thetvdb-%s.zip' % id), 'w') f.write((yield download(self.api + 'series/%s/all/en.zip' % id))) f.close() # load zip data z = zipfile.ZipFile(f.name) parent = None for name, data in (yield parse(z.open('en.xml')))[1]: if name == 'Series': objid = self._update_db('series', int(data.get('id')), name=data.get('SeriesName'), data=data) parent = ('series', objid) # delete old entries for e in self._db.query(type='episode', parent=parent): self._db.delete(e) elif name == 'Episode': if not parent: raise ValueError( 'Unexpected parse error: got Episode element before Series' ) self._update_db('episode', int(data.get('id')), name=data.get('EpisodeName'), parent=parent, season=int(data.get('SeasonNumber')), episode=int(data.get('EpisodeNumber')), date=data.get('FirstAired', None), data=data) else: log.error('unknown element: %s', name) self._db.commit() # load image information for name, data in (yield parse(z.open('banners.xml')))[1]: if name == 'Banner': self._update_db('banner', int(data.get('id')), btype=data.get('BannerType'), data=data, parent=parent) else: log.error('unknown element: %s', name) self._db.commit() os.unlink(f.name) # download metadata images info = self._db.query_one(type='series', tvdb=id) if not info: yield None serie = Series(self, info) if serie.image and not os.path.isfile(serie.image): data = yield serie.get_all_images()[0].fetch() open(serie.image, 'w').write(data) if serie.poster and not os.path.isfile(serie.poster): data = yield serie.get_all_posters()[0].fetch() open(serie.poster, 'w').write(data) if serie.banner and not os.path.isfile(serie.banner): data = yield serie.get_all_banners()[0].fetch() open(serie.banner, 'w').write(data) for season in serie.seasons: if season.poster and season.get_all_posters( ) and not os.path.isfile(season.poster): data = yield season.get_all_posters()[0].fetch() open(season.poster, 'w').write(data) if season.banner and season.get_all_banners( ) and not os.path.isfile(season.banner): data = yield season.get_all_banners()[0].fetch() open(season.banner, 'w').write(data)