def add_profile(self, clicked): if self.stacks.currentIndex() == 0: src, title = self.options_to_profile() try: compile_recipe(src) except Exception as err: error_dialog(self, _('Invalid input'), _('<p>Could not create recipe. Error:<br>%s')%str(err)).exec_() return profile = src else: src = unicode(self.source_code.toPlainText()) try: title = compile_recipe(src).title except Exception as err: error_dialog(self, _('Invalid input'), _('<p>Could not create recipe. Error:<br>%s')%str(err)).exec_() return profile = src.replace('BasicUserRecipe', 'AdvancedUserRecipe') if self._model.has_title(title): if question_dialog(self, _('Replace recipe?'), _('A custom recipe named %s already exists. Do you want to ' 'replace it?')%title): self._model.replace_by_title(title, profile) else: return else: self.model.add(title, profile) self.clear()
def validate(self): src = self.recipe_source try: compile_recipe(src) except Exception as err: error_dialog(self, _('Invalid recipe'), _( 'Failed to compile the recipe, with syntax error: %s' % err), show=True) return False return True
def load(self): files = choose_files( self, "recipe loader dialog", _("Choose a recipe file"), filters=[(_("Recipes"), [".py", ".recipe"])], all_files=False, select_only_single_file=True, ) if files: file = files[0] try: profile = open(file, "rb").read().decode("utf-8") title = compile_recipe(profile).title except Exception as err: error_dialog(self, _("Invalid input"), _("<p>Could not create recipe. Error:<br>%s") % str(err)).exec_() return if self._model.has_title(title): if question_dialog( self, _("Replace recipe?"), _("A custom recipe named %s already exists. Do you want to " "replace it?") % title, ): self._model.replace_by_title(title, profile) else: return else: self.model.add(title, profile) self.clear()
def validate(self): title = self.title.text().strip() if not title: error_dialog(self, _('Title required'), _( 'You must give your news source a title'), show=True) return False if self.feeds.count() < 1: error_dialog(self, _('Feed required'), _( 'You must add at least one feed to your news source'), show=True) return False try: compile_recipe(self.recipe_source) except Exception as err: error_dialog(self, _('Invalid recipe'), _( 'Failed to compile the recipe, with syntax error: %s' % err), show=True) return False return True
def opml_import(self): from calibre.gui2.dialogs.opml import ImportOPML d = ImportOPML(parent=self) if d.exec_() != d.Accepted: return oldest_article, max_articles_per_feed, replace_existing = d.oldest_article, d.articles_per_feed, d.replace_existing failed_recipes, replace_recipes, add_recipes = {}, {}, {} for group in d.recipes: title = base_title = group.title or _('Unknown') if not replace_existing: c = 0 while self._model.has_title(title): c += 1 title = u'%s %d' % (base_title, c) src, title = self.options_to_profile(**{ 'title':title, 'feeds':group.feeds, 'oldest_article':oldest_article, 'max_articles':max_articles_per_feed, }) try: compile_recipe(src) except Exception: import traceback failed_recipes[title] = traceback.format_exc() continue if replace_existing and self._model.has_title(title): replace_recipes[title] = src else: add_recipes[title] = src if add_recipes: self.model.add_many(add_recipes) if replace_recipes: self.model.replace_many_by_title(replace_recipes) if failed_recipes: det_msg = '\n'.join('%s\n%s\n' % (title, tb) for title, tb in failed_recipes.iteritems()) error_dialog(self, _('Failed to create recipes'), _( 'Failed to create some recipes, click "Show details" for details'), show=True, det_msg=det_msg) self.clear()
def import_recipes(self, outlines): nr = 0 #recipe_model = CustomRecipeModel(RecipeModel()) for outline in outlines: src, title = self.options_to_profile(dict( nr=nr, title=unicode(outline.get('title')), feeds=outline.get('xmlUrl'), oldest_article=self.oldest_article, max_articles=self.max_articles, base_class='AutomaticNewsRecipe' )) try: compile_recipe(src) add_custom_recipe(title, src) except Exception as err: # error dialog should be placed somewhere where it can have a parent # Left it here as this way only failing feeds will silently fail error_dialog(None, _('Invalid input'), _('<p>Could not create recipe. Error:<br>%s')%str(err)).exec_() nr+=1
def download_builtin_recipe(urn): from calibre.utils.config_base import prefs from calibre.utils.https import get_https_resource_securely import bz2 recipe_source = bz2.decompress(get_https_resource_securely( 'https://code.calibre-ebook.com/recipe-compressed/'+urn, headers={'CALIBRE-INSTALL-UUID':prefs['installation_uuid']})) from calibre.web.feeds.recipes import compile_recipe recipe = compile_recipe(recipe_source) # ensure the downloaded recipe is at least compile-able if recipe is None: raise ValueError('Failed to find recipe object in downloaded recipe: ' + urn) if recipe.requires_version > numeric_version: raise ValueError('Downloaded recipe for {} requires calibre >= {}'.format(urn, recipe.requires_version)) return recipe_source
def serialize_builtin_recipes(): from calibre.web.feeds.recipes import compile_recipe recipe_mapping = {} for rid, f in iterate_over_builtin_recipe_files(): with open(f, 'rb') as stream: try: recipe_class = compile_recipe(stream.read()) except: print ('Failed to compile: %s'%f) raise if recipe_class is not None: recipe_mapping['builtin:'+rid] = recipe_class return serialize_collection(recipe_mapping)
def import_opml(self): from calibre.gui2.dialogs.opml import ImportOPML d = ImportOPML(parent=self) if d.exec_() != d.Accepted: return oldest_article, max_articles_per_feed, replace_existing = d.oldest_article, d.articles_per_feed, d.replace_existing failed_recipes, replace_recipes, add_recipes = {}, {}, {} for group in d.recipes: title = base_title = group.title or _('Unknown') if not replace_existing: c = 0 while self.recipe_list.has_title(title): c += 1 title = '%s %d' % (base_title, c) try: src = options_to_recipe_source(title, oldest_article, max_articles_per_feed, group.feeds) compile_recipe(src) except Exception: import traceback failed_recipes[title] = traceback.format_exc() continue if replace_existing and self.recipe_list.has_title(title): replace_recipes[title] = src else: add_recipes[title] = src if add_recipes: self.recipe_list.add_many(add_recipes) if replace_recipes: self.recipe_list.replace_many_by_title(replace_recipes) if failed_recipes: det_msg = '\n'.join('%s\n%s\n' % (title, tb) for title, tb in iteritems(failed_recipes)) error_dialog(self, _('Failed to create recipes'), _( 'Failed to create some recipes, click "Show details" for details'), show=True, det_msg=det_msg)
def editing_finished(self): w = self.stack.currentWidget() if not w.validate(): return src = w.recipe_source if not isinstance(src, bytes): src = src.encode('utf-8') recipe = compile_recipe(src) row = self.editing_row if row is None: # Adding a new recipe self.recipe_list.add(recipe.title, src) else: self.recipe_list.update(row, recipe.title, src) self.stack.setCurrentIndex(0)
def fset(self, src): self.feeds.clear() self.feed_title.clear() self.feed_url.clear() if src is None: self.title.setText(_('My News Source')) self.oldest_article.setValue(7) self.max_articles.setValue(100) else: recipe = compile_recipe(src) self.title.setText(recipe.title) self.oldest_article.setValue(recipe.oldest_article) self.max_articles.setValue(recipe.max_articles_per_feed) for x in (recipe.feeds or ()): title, url = ('', x) if len(x) == 1 else x QListWidgetItem('%s - %s' % (title, url), self.feeds).setData(Qt.UserRole, (title, url))
def current_changed(self, current, previous): if not current.isValid(): return src = self._model.script(current) if src is None: return if 'class BasicUserRecipe' in src: recipe = compile_recipe(src) self.populate_options(recipe) self.stacks.setCurrentIndex(0) self.toggle_mode_button.setText(_('Switch to Advanced mode')) self.source_code.setPlainText('') else: self.source_code.setPlainText(src) self.highlighter = PythonHighlighter(self.source_code.document()) self.stacks.setCurrentIndex(1) self.toggle_mode_button.setText(_('Switch to Basic mode'))
def get_custom_recipe_collection(*args): from calibre.web.feeds.recipes import compile_recipe, \ custom_recipes bdir = os.path.dirname(custom_recipes.file_path) rmap = {} for id_, x in custom_recipes.iteritems(): title, fname = x recipe = os.path.join(bdir, fname) try: recipe = open(recipe, 'rb').read().decode('utf-8') recipe_class = compile_recipe(recipe) if recipe_class is not None: rmap['custom:%s'%id_] = recipe_class except: print 'Failed to load recipe from: %r'%fname import traceback traceback.print_exc() continue return etree.fromstring(serialize_collection(rmap))
def load(self): files = choose_files(self, 'recipe loader dialog', _('Choose a recipe file'), filters=[(_('Recipes'), ['.py', '.recipe'])], all_files=False, select_only_single_file=True) if files: file = files[0] try: profile = open(file, 'rb').read().decode('utf-8') title = compile_recipe(profile).title except Exception as err: error_dialog(self, _('Invalid input'), _('<p>Could not create recipe. Error:<br>%s')%str(err)).exec_() return if self._model.has_title(title): if question_dialog(self, _('Replace recipe?'), _('A custom recipe named %s already exists. Do you want to ' 'replace it?')%title): self._model.replace_by_title(title, profile) else: return else: self.model.add(title, profile) self.clear()
def convert(self, recipe_or_file, opts, file_ext, log, accelerators): from calibre.web.feeds.recipes import compile_recipe opts.output_profile.flow_size = 0 if file_ext == 'downloaded_recipe': from calibre.utils.zipfile import ZipFile zf = ZipFile(recipe_or_file, 'r') zf.extractall() zf.close() self.recipe_source = open(u'download.recipe', 'rb').read() recipe = compile_recipe(self.recipe_source) recipe.needs_subscription = False self.recipe_object = recipe(opts, log, self.report_progress) else: if os.access(recipe_or_file, os.R_OK): self.recipe_source = open(recipe_or_file, 'rb').read() recipe = compile_recipe(self.recipe_source) log('Using custom recipe') else: from calibre.web.feeds.recipes.collection import \ get_builtin_recipe_by_title title = getattr(opts, 'original_recipe_input_arg', recipe_or_file) title = os.path.basename(title).rpartition('.')[0] raw = get_builtin_recipe_by_title(title, log=log, download_recipe=not opts.dont_download_recipe) builtin = False try: recipe = compile_recipe(raw) self.recipe_source = raw if recipe.requires_version > numeric_version: log.warn( 'Downloaded recipe needs calibre version at least: %s' % \ ('.'.join(recipe.requires_version))) builtin = True except: log.exception('Failed to compile downloaded recipe. Falling ' 'back to builtin one') builtin = True if builtin: log('Using bundled builtin recipe') raw = get_builtin_recipe_by_title(title, log=log, download_recipe=False) if raw is None: raise ValueError('Failed to find builtin recipe: '+title) recipe = compile_recipe(raw) self.recipe_source = raw else: log('Using downloaded builtin recipe') if recipe is None: raise ValueError('%r is not a valid recipe file or builtin recipe' % recipe_or_file) disabled = getattr(recipe, 'recipe_disabled', None) if disabled is not None: raise RecipeDisabled(disabled) ro = recipe(opts, log, self.report_progress) ro.download() self.recipe_object = ro for key, val in self.recipe_object.conversion_options.items(): setattr(opts, key, val) for f in os.listdir(u'.'): if f.endswith('.opf'): return os.path.abspath(f) for f in walk(u'.'): if f.endswith('.opf'): return os.path.abspath(f)
def convert(self, recipe_or_file, opts, file_ext, log, accelerators): from calibre.web.feeds.recipes import compile_recipe opts.output_profile.flow_size = 0 if file_ext == 'downloaded_recipe': from calibre.utils.zipfile import ZipFile zf = ZipFile(recipe_or_file, 'r') zf.extractall() zf.close() self.recipe_source = open(u'download.recipe', 'rb').read() recipe = compile_recipe(self.recipe_source) recipe.needs_subscription = False self.recipe_object = recipe(opts, log, self.report_progress) else: if os.access(recipe_or_file, os.R_OK): self.recipe_source = open(recipe_or_file, 'rb').read() recipe = compile_recipe(self.recipe_source) log('Using custom recipe') else: from calibre.web.feeds.recipes.collection import ( get_builtin_recipe_by_title, get_builtin_recipe_titles) title = getattr(opts, 'original_recipe_input_arg', recipe_or_file) title = os.path.basename(title).rpartition('.')[0] titles = frozenset(get_builtin_recipe_titles()) if title not in titles: title = getattr(opts, 'original_recipe_input_arg', recipe_or_file) title = title.rpartition('.')[0] raw = get_builtin_recipe_by_title( title, log=log, download_recipe=not opts.dont_download_recipe) builtin = False try: recipe = compile_recipe(raw) self.recipe_source = raw if recipe.requires_version > numeric_version: log.warn( 'Downloaded recipe needs calibre version at least: %s' % ('.'.join(recipe.requires_version))) builtin = True except: log.exception( 'Failed to compile downloaded recipe. Falling ' 'back to builtin one') builtin = True if builtin: log('Using bundled builtin recipe') raw = get_builtin_recipe_by_title(title, log=log, download_recipe=False) if raw is None: raise ValueError('Failed to find builtin recipe: ' + title) recipe = compile_recipe(raw) self.recipe_source = raw else: log('Using downloaded builtin recipe') if recipe is None: raise ValueError( '%r is not a valid recipe file or builtin recipe' % recipe_or_file) disabled = getattr(recipe, 'recipe_disabled', None) if disabled is not None: raise RecipeDisabled(disabled) ro = recipe(opts, log, self.report_progress) ro.download() self.recipe_object = ro for key, val in self.recipe_object.conversion_options.items(): setattr(opts, key, val) for f in os.listdir(u'.'): if f.endswith('.opf'): return os.path.abspath(f) for f in walk(u'.'): if f.endswith('.opf'): return os.path.abspath(f)
def convert(self, recipe_or_file, opts, file_ext, log, accelerators): from calibre.web.feeds.recipes import compile_recipe opts.output_profile.flow_size = 0 if file_ext == 'downloaded_recipe': from calibre.utils.zipfile import ZipFile zf = ZipFile(recipe_or_file, 'r') zf.extractall() zf.close() with lopen('download.recipe', 'rb') as f: self.recipe_source = f.read() recipe = compile_recipe(self.recipe_source) recipe.needs_subscription = False self.recipe_object = recipe(opts, log, self.report_progress) else: if os.environ.get('CALIBRE_RECIPE_URN'): from calibre.web.feeds.recipes.collection import get_custom_recipe, get_builtin_recipe_by_id urn = os.environ['CALIBRE_RECIPE_URN'] log('Downloading recipe urn: ' + urn) rtype, recipe_id = urn.partition(':')[::2] if not recipe_id: raise ValueError('Invalid recipe urn: ' + urn) if rtype == 'custom': self.recipe_source = get_custom_recipe(recipe_id) else: self.recipe_source = get_builtin_recipe_by_id(urn, log=log, download_recipe=True) if not self.recipe_source: raise ValueError('Could not find recipe with urn: ' + urn) if not isinstance(self.recipe_source, bytes): self.recipe_source = self.recipe_source.encode('utf-8') recipe = compile_recipe(self.recipe_source) elif os.access(recipe_or_file, os.R_OK): with lopen(recipe_or_file, 'rb') as f: self.recipe_source = f.read() recipe = compile_recipe(self.recipe_source) log('Using custom recipe') else: from calibre.web.feeds.recipes.collection import ( get_builtin_recipe_by_title, get_builtin_recipe_titles) title = getattr(opts, 'original_recipe_input_arg', recipe_or_file) title = os.path.basename(title).rpartition('.')[0] titles = frozenset(get_builtin_recipe_titles()) if title not in titles: title = getattr(opts, 'original_recipe_input_arg', recipe_or_file) title = title.rpartition('.')[0] raw = get_builtin_recipe_by_title(title, log=log, download_recipe=not opts.dont_download_recipe) builtin = False try: recipe = compile_recipe(raw) self.recipe_source = raw if recipe.requires_version > numeric_version: log.warn( 'Downloaded recipe needs calibre version at least: %s' % ('.'.join(recipe.requires_version))) builtin = True except: log.exception('Failed to compile downloaded recipe. Falling ' 'back to builtin one') builtin = True if builtin: log('Using bundled builtin recipe') raw = get_builtin_recipe_by_title(title, log=log, download_recipe=False) if raw is None: raise ValueError('Failed to find builtin recipe: '+title) recipe = compile_recipe(raw) self.recipe_source = raw else: log('Using downloaded builtin recipe') if recipe is None: raise ValueError('%r is not a valid recipe file or builtin recipe' % recipe_or_file) disabled = getattr(recipe, 'recipe_disabled', None) if disabled is not None: raise RecipeDisabled(disabled) ro = recipe(opts, log, self.report_progress) ro.download() self.recipe_object = ro for key, val in self.recipe_object.conversion_options.items(): setattr(opts, key, val) for f in os.listdir('.'): if f.endswith('.opf'): return os.path.abspath(f) for f in walk('.'): if f.endswith('.opf'): return os.path.abspath(f)