def register(self, overlay_bridge, metadataDBHandler, session): self.overlay_bridge = overlay_bridge self.subtitlesDb = metadataDBHandler self.config_dir = os.path.abspath(session.get_state_dir()) subs_path = os.path.join(self.config_dir, session.get_subtitles_collecting_dir()) self.subs_dir = os.path.abspath(session.get_subtitles_collecting_dir()) self._upload_rate = session.get_subtitles_upload_rate() self.max_subs_message_size = MAX_SUBS_MESSAGE_SIZE self._session = session tokenBucket = SimpleTokenBucket(self._upload_rate, self.max_subs_message_size) self._subsMsgHndlr = SubsMessageHandler(self.overlay_bridge, tokenBucket, MAX_SUBTITLE_SIZE) self._subsMsgHndlr.registerListener(self) if os.path.isdir(self.config_dir): if not os.path.isdir(self.subs_dir): try: os.mkdir(self.subs_dir) except: msg = u'Cannot create collecting dir %s ' % self.subs_dir print >> sys.stderr, 'Error: %s' % msg raise IOError(msg) else: msg = u'Configuration dir %s does not exists' % self.subs_dir print >> sys.stderr, 'Error: %s' % msg raise IOError(msg) self._notifier = Notifier.getInstance() self.registered = True
class SubtitlesHandler(object): __single = None def __init__(self): SubtitlesHandler.__single = self self.languagesUtility = LanguagesProvider.getLanguagesInstance() self.subtitlesDb = None self.registered = False self.subs_dir = None @staticmethod def getInstance(*args, **kw): if SubtitlesHandler.__single is None: SubtitlesHandler(*args, **kw) return SubtitlesHandler.__single def register(self, overlay_bridge, metadataDBHandler, session): self.overlay_bridge = overlay_bridge self.subtitlesDb = metadataDBHandler self.config_dir = os.path.abspath(session.get_state_dir()) subs_path = os.path.join(self.config_dir, session.get_subtitles_collecting_dir()) self.subs_dir = os.path.abspath(session.get_subtitles_collecting_dir()) self._upload_rate = session.get_subtitles_upload_rate() self.max_subs_message_size = MAX_SUBS_MESSAGE_SIZE self._session = session tokenBucket = SimpleTokenBucket(self._upload_rate, self.max_subs_message_size) self._subsMsgHndlr = SubsMessageHandler(self.overlay_bridge, tokenBucket, MAX_SUBTITLE_SIZE) self._subsMsgHndlr.registerListener(self) if os.path.isdir(self.config_dir): if not os.path.isdir(self.subs_dir): try: os.mkdir(self.subs_dir) except: msg = u'Cannot create collecting dir %s ' % self.subs_dir print >> sys.stderr, 'Error: %s' % msg raise IOError(msg) else: msg = u'Configuration dir %s does not exists' % self.subs_dir print >> sys.stderr, 'Error: %s' % msg raise IOError(msg) self._notifier = Notifier.getInstance() self.registered = True def sendSubtitleRequest(self, permid, channel_id, infohash, languages, callback=None, selversion=-1): if DEBUG: print >> sys.stderr, SUBS_LOG_PREFIX + 'preparing to send GET_SUBS to ' + utilities.show_permid_short( permid) if len(languages) == 0: if DEBUG: print >> sys.stderr, SUBS_LOG_PREFIX + ' no subtitles to request.' return requestDetails = dict() requestDetails['channel_id'] = channel_id requestDetails['infohash'] = infohash requestDetails['languages'] = languages self._subsMsgHndlr.sendSubtitleRequest( permid, requestDetails, lambda e, d, c, i, b: self._subsRequestSent(e, d, c, i, b), callback, selversion) def _subsRequestSent(self, exception, dest, channel_id, infohash, bitmask): pass def receivedSubsRequest(self, permid, request, selversion): channel_id, infohash, languages = request allSubtitles = self.subtitlesDb.getAllSubtitles(channel_id, infohash) contentsList = {} for lang in sorted(languages): if lang in allSubtitles.keys(): if allSubtitles[lang].subtitleExists(): content = self._readSubContent(allSubtitles[lang].path) if content is not None: contentsList[lang] = content else: if DEBUG: print >> sys.stderr, SUBS_LOG_PREFIX + 'File not available for channel %s, infohash %s, lang %s' % ( show_permid_short(channel_id), bin2str(infohash), lang) self.subtitlesDb.updateSubtitlePath( channel_id, infohash, lang, None) elif DEBUG: print >> sys.stderr, SUBS_LOG_PREFIX + 'Subtitle not available for channel %s, infohash %s, lang %s' % ( show_permid_short(channel_id), bin2str(infohash), lang) if len(contentsList) == 0: if DEBUG: print >> sys.stderr, SUBS_LOG_PREFIX + 'None of the requested subtitles were available. No answer will be sent to %s' % show_permid_short( permid) return True return self._subsMsgHndlr.sendSubtitleResponse( permid, (channel_id, infohash, contentsList), selversion) def _readSubContent(self, path): try: fileName = path file = open(fileName, 'rb') fileContent = file.read() file.close() except IOError as e: if DEBUG: print >> sys.stderr, SUBS_LOG_PREFIX + 'Error reading from subs file %s: %s' % ( relativeName, e) fileContent = None if fileContent and len(fileContent) <= MAX_SUBTITLE_SIZE: return fileContent print >> sys.stderr, 'Warning: Subtitle %s dropped. Bigger than %d' % ( relativeName, MAX_SUBTITLE_SIZE) def _subs_send_callback(self, exception, permid): if exception is not None: if DEBUG: print >> sys.stderr, SUBS_LOG_PREFIX + 'Failed to send metadata to %s: %s' % ( show_permid_short(permid), str(exception)) def receivedSubsResponse(self, permid, msg, callbacks, selversion): channel_id, infohash, contentsDictionary = msg metadataDTO = self.subtitlesDb.getMetadata(channel_id, infohash) filepaths = dict() somethingToWrite = False for lang, subtitleContent in contentsDictionary.iteritems(): try: filename = self._saveSubOnDisk(channel_id, infohash, lang, subtitleContent) filepaths[lang] = filename except IOError as e: if DEBUG: print >> sys.stderr, SUBS_LOG_PREFIX + 'Unable to save subtitle for channel %s and infohash %s to file: %s' % ( show_permid_short(channel_id), str(infohash), e) continue except Exception as e: if DEBUG: print >> sys.stderr, 'Unexpected error copying subtitle On Disk: ' + str( e) raise e subToUpdate = metadataDTO.getSubtitle(lang) if subToUpdate is None: print >> sys.stderr, 'Warning:' + SUBS_LOG_PREFIX + 'Subtitles database inconsistency.' raise MetadataDBException('Subtitles database inconsistency!') subToUpdate.path = filename if not subToUpdate.verifyChecksum(): if DEBUG: print >> sys.stderr, 'Received a subtitle having invalid checsum from %s' % show_permid_short( permid) subToUpdate.path = None os.remove(filename) continue self.subtitlesDb.updateSubtitlePath(channel_id, infohash, subToUpdate.lang, filename, False) somethingToWrite = True if somethingToWrite: self.subtitlesDb.commit() if DEBUG: print >> sys.stderr, 'Subtitle written on disk and informations on database.' if callbacks: self._scheduleUserCallbacks(callbacks) return True def _scheduleUserCallbacks(self, callbacks): def call_helper(callback, listOfLanguages): self.overlay_bridge.add_task(lambda: callback(listOfLanguages)) for callback, bitmask in callbacks: listOfLanguages = self.languagesUtility.maskToLangCodes(bitmask) call_helper(callback, listOfLanguages) def _saveSubOnDisk(self, channel_id, infohash, lang, subtitleContent): filename = getSubtitleFileRelativeName(channel_id, infohash, lang) filename = os.path.join(self.subs_dir, filename) file = open(filename, 'wb') file.write(subtitleContent) file.close() return filename def _notify_sub_is_in(self, channel_id, infohash, langCode, filename): if DEBUG: print >> sys.stderr, SUBS_LOG_PREFIX + 'Subtitle is in at' + filename if self._notifier is not None: self.notifier.notify(NTFY_SUBTITLE_CONTENTS, NTFY_UPDATE, (channel_id, infohash), langCode, filename) def setUploadRate(self, uploadRate): self._upload_rate = float(uploadRate) self._subsMsgHndlr._tokenBucket.fill_rate = float(uploadRate) def getUploadRate(self): return self._upload_rate def delUploadRate(self): raise RuntimeError('Operation not supported') upload_rate = property( getUploadRate, setUploadRate, delUploadRate, 'Controls the subtitles uploading rate. Expressed in KB/s') def copyToSubtitlesFolder(self, pathToMove, channel_id, infohash, langCode): if not os.path.isfile(pathToMove): raise RichMetadataException('File not found.') if os.path.getsize(pathToMove) >= MAX_SUBTITLE_SIZE: raise RichMetadataException('Subtitle bigger then %d KBs' % (MAX_SUBTITLE_SIZE / 1024)) if not pathToMove.endswith(SUBS_EXTENSION): raise RichMetadataException('Only .srt subtitles are supported') filename = getSubtitleFileRelativeName(channel_id, infohash, langCode) filename = os.path.join(self.subs_dir, filename) copyfile(pathToMove, filename) return filename def getMessageHandler(self): return self._subsMsgHndlr.handleMessage
class SubtitlesHandler(object): __single = None def __init__(self): SubtitlesHandler.__single = self self.languagesUtility = LanguagesProvider.getLanguagesInstance() self.subtitlesDb = None self.registered = False self.subs_dir = None @staticmethod def getInstance(*args, **kw): if SubtitlesHandler.__single is None: SubtitlesHandler(*args, **kw) return SubtitlesHandler.__single def register(self, overlay_bridge, metadataDBHandler, session): self.overlay_bridge = overlay_bridge self.subtitlesDb = metadataDBHandler self.config_dir = os.path.abspath(session.get_state_dir()) subs_path = os.path.join(self.config_dir, session.get_subtitles_collecting_dir()) self.subs_dir = os.path.abspath(session.get_subtitles_collecting_dir()) self._upload_rate = session.get_subtitles_upload_rate() self.max_subs_message_size = MAX_SUBS_MESSAGE_SIZE self._session = session tokenBucket = SimpleTokenBucket(self._upload_rate, self.max_subs_message_size) self._subsMsgHndlr = SubsMessageHandler(self.overlay_bridge, tokenBucket, MAX_SUBTITLE_SIZE) self._subsMsgHndlr.registerListener(self) if os.path.isdir(self.config_dir): if not os.path.isdir(self.subs_dir): try: os.mkdir(self.subs_dir) except: msg = u'Cannot create collecting dir %s ' % self.subs_dir print >> sys.stderr, 'Error: %s' % msg raise IOError(msg) else: msg = u'Configuration dir %s does not exists' % self.subs_dir print >> sys.stderr, 'Error: %s' % msg raise IOError(msg) self._notifier = Notifier.getInstance() self.registered = True def sendSubtitleRequest(self, permid, channel_id, infohash, languages, callback = None, selversion = -1): if DEBUG: print >> sys.stderr, SUBS_LOG_PREFIX + 'preparing to send GET_SUBS to ' + utilities.show_permid_short(permid) if len(languages) == 0: if DEBUG: print >> sys.stderr, SUBS_LOG_PREFIX + ' no subtitles to request.' return requestDetails = dict() requestDetails['channel_id'] = channel_id requestDetails['infohash'] = infohash requestDetails['languages'] = languages self._subsMsgHndlr.sendSubtitleRequest(permid, requestDetails, lambda e, d, c, i, b: self._subsRequestSent(e, d, c, i, b), callback, selversion) def _subsRequestSent(self, exception, dest, channel_id, infohash, bitmask): pass def receivedSubsRequest(self, permid, request, selversion): channel_id, infohash, languages = request allSubtitles = self.subtitlesDb.getAllSubtitles(channel_id, infohash) contentsList = {} for lang in sorted(languages): if lang in allSubtitles.keys(): if allSubtitles[lang].subtitleExists(): content = self._readSubContent(allSubtitles[lang].path) if content is not None: contentsList[lang] = content else: if DEBUG: print >> sys.stderr, SUBS_LOG_PREFIX + 'File not available for channel %s, infohash %s, lang %s' % (show_permid_short(channel_id), bin2str(infohash), lang) self.subtitlesDb.updateSubtitlePath(channel_id, infohash, lang, None) elif DEBUG: print >> sys.stderr, SUBS_LOG_PREFIX + 'Subtitle not available for channel %s, infohash %s, lang %s' % (show_permid_short(channel_id), bin2str(infohash), lang) if len(contentsList) == 0: if DEBUG: print >> sys.stderr, SUBS_LOG_PREFIX + 'None of the requested subtitles were available. No answer will be sent to %s' % show_permid_short(permid) return True return self._subsMsgHndlr.sendSubtitleResponse(permid, (channel_id, infohash, contentsList), selversion) def _readSubContent(self, path): try: fileName = path file = open(fileName, 'rb') fileContent = file.read() file.close() except IOError as e: if DEBUG: print >> sys.stderr, SUBS_LOG_PREFIX + 'Error reading from subs file %s: %s' % (relativeName, e) fileContent = None if fileContent and len(fileContent) <= MAX_SUBTITLE_SIZE: return fileContent print >> sys.stderr, 'Warning: Subtitle %s dropped. Bigger than %d' % (relativeName, MAX_SUBTITLE_SIZE) def _subs_send_callback(self, exception, permid): if exception is not None: if DEBUG: print >> sys.stderr, SUBS_LOG_PREFIX + 'Failed to send metadata to %s: %s' % (show_permid_short(permid), str(exception)) def receivedSubsResponse(self, permid, msg, callbacks, selversion): channel_id, infohash, contentsDictionary = msg metadataDTO = self.subtitlesDb.getMetadata(channel_id, infohash) filepaths = dict() somethingToWrite = False for lang, subtitleContent in contentsDictionary.iteritems(): try: filename = self._saveSubOnDisk(channel_id, infohash, lang, subtitleContent) filepaths[lang] = filename except IOError as e: if DEBUG: print >> sys.stderr, SUBS_LOG_PREFIX + 'Unable to save subtitle for channel %s and infohash %s to file: %s' % (show_permid_short(channel_id), str(infohash), e) continue except Exception as e: if DEBUG: print >> sys.stderr, 'Unexpected error copying subtitle On Disk: ' + str(e) raise e subToUpdate = metadataDTO.getSubtitle(lang) if subToUpdate is None: print >> sys.stderr, 'Warning:' + SUBS_LOG_PREFIX + 'Subtitles database inconsistency.' raise MetadataDBException('Subtitles database inconsistency!') subToUpdate.path = filename if not subToUpdate.verifyChecksum(): if DEBUG: print >> sys.stderr, 'Received a subtitle having invalid checsum from %s' % show_permid_short(permid) subToUpdate.path = None os.remove(filename) continue self.subtitlesDb.updateSubtitlePath(channel_id, infohash, subToUpdate.lang, filename, False) somethingToWrite = True if somethingToWrite: self.subtitlesDb.commit() if DEBUG: print >> sys.stderr, 'Subtitle written on disk and informations on database.' if callbacks: self._scheduleUserCallbacks(callbacks) return True def _scheduleUserCallbacks(self, callbacks): def call_helper(callback, listOfLanguages): self.overlay_bridge.add_task(lambda : callback(listOfLanguages)) for callback, bitmask in callbacks: listOfLanguages = self.languagesUtility.maskToLangCodes(bitmask) call_helper(callback, listOfLanguages) def _saveSubOnDisk(self, channel_id, infohash, lang, subtitleContent): filename = getSubtitleFileRelativeName(channel_id, infohash, lang) filename = os.path.join(self.subs_dir, filename) file = open(filename, 'wb') file.write(subtitleContent) file.close() return filename def _notify_sub_is_in(self, channel_id, infohash, langCode, filename): if DEBUG: print >> sys.stderr, SUBS_LOG_PREFIX + 'Subtitle is in at' + filename if self._notifier is not None: self.notifier.notify(NTFY_SUBTITLE_CONTENTS, NTFY_UPDATE, (channel_id, infohash), langCode, filename) def setUploadRate(self, uploadRate): self._upload_rate = float(uploadRate) self._subsMsgHndlr._tokenBucket.fill_rate = float(uploadRate) def getUploadRate(self): return self._upload_rate def delUploadRate(self): raise RuntimeError('Operation not supported') upload_rate = property(getUploadRate, setUploadRate, delUploadRate, 'Controls the subtitles uploading rate. Expressed in KB/s') def copyToSubtitlesFolder(self, pathToMove, channel_id, infohash, langCode): if not os.path.isfile(pathToMove): raise RichMetadataException('File not found.') if os.path.getsize(pathToMove) >= MAX_SUBTITLE_SIZE: raise RichMetadataException('Subtitle bigger then %d KBs' % (MAX_SUBTITLE_SIZE / 1024)) if not pathToMove.endswith(SUBS_EXTENSION): raise RichMetadataException('Only .srt subtitles are supported') filename = getSubtitleFileRelativeName(channel_id, infohash, langCode) filename = os.path.join(self.subs_dir, filename) copyfile(pathToMove, filename) return filename def getMessageHandler(self): return self._subsMsgHndlr.handleMessage