diff --git a/application/static/library.js b/application/static/library.js index 3c6cefe5..45e4dc2d 100644 --- a/application/static/library.js +++ b/application/static/library.js @@ -334,9 +334,11 @@ function SelectCategory(obj, category) { function DoSearchInShared() { var input = $('#search_text'); var txt = input.val(); - if (txt == "#download") { //下载所有的共享RSS - DownAllRssToFile(); + if ((txt == "#download") || (txt == "#downxml") || (txt == "#downjson")) { //下载所有的共享RSS + var fileType = (txt == "#downjson") ? "json" : "xml"; + DownAllRssToFile(fileType); input.val(""); + DoSearchInShared(); return; } @@ -401,39 +403,54 @@ function ReportInvalid(title, feedurl, dbId) { //将内容全部下载到本地一个xml文件内 -function DownAllRssToFile() { +//fileType: 保存的文件格式,xml/json +function DownAllRssToFile(fileType) { if (!g_SharedRss) { return; } - var title, url, ftext, cat, rssd, fmtdate, nowdate, lang; - var elementA = document.createElement('a'); - var aTxt = new Array(); - aTxt.push(""); - aTxt.push(""); - aTxt.push(""); - aTxt.push(" KindleEar.opml"); - aTxt.push(" " + new Date() + ""); - aTxt.push(" " + new Date() + ""); - aTxt.push(" KindleEar"); - aTxt.push(""); - aTxt.push(""); - for (var i = 0; i < g_SharedRss.length; i++) { - title = escapeXml(g_SharedRss[i].t); - url = escapeXml(g_SharedRss[i].u); - cat = escapeXml(g_SharedRss[i].c); - lang = g_SharedRss[i].l || ''; - ftext = (g_SharedRss[i].f == "false") ? "no" : "yes"; - aTxt.push(' ' - .format(title, url, ftext, cat, lang)); + fileType = fileType || 'xml'; + var content, mimeType; + var title, url, ftext, cat, lang; + const now = new Date(); + if (fileType == 'xml') { + var aTxt = new Array(); + aTxt.push(""); + aTxt.push(""); + aTxt.push(""); + aTxt.push(" KindleEar.opml"); + aTxt.push(" " + now + ""); + aTxt.push(" " + now + ""); + aTxt.push(" KindleEar"); + aTxt.push(""); + aTxt.push(""); + for (var i = 0; i < g_SharedRss.length; i++) { + title = escapeXml(g_SharedRss[i].t); + url = escapeXml(g_SharedRss[i].u); + cat = escapeXml(g_SharedRss[i].c); + lang = g_SharedRss[i].l || ''; + ftext = (g_SharedRss[i].f == "false") ? "no" : "yes"; + aTxt.push(' ' + .format(title, url, ftext, cat, lang)); + } + aTxt.push(""); + aTxt.push("\n"); + content = aTxt.join("\n"); + mimeType = "application/xml"; + } else { + var jsonData = g_SharedRss.map(function(item) { + return {t: item.t, u: item.u, c: item.c, l: item.l || '', f: item.f}; + }); + content = JSON.stringify(jsonData, null, 2); + mimeType = "application/json"; } - aTxt.push(""); - aTxt.push("\n"); - - nowdate = new Date(); - fmtdate = "KindleEar_library_" + nowdate.getFullYear() + "_" + ((nowdate.getMonth() + 1)) + "_" + nowdate.getDate() + ".xml"; - elementA.setAttribute("href", "data:text/plain;charset=utf-8," + encodeURIComponent(aTxt.join("\n"))); - elementA.setAttribute("download", fmtdate); + var elementA = document.createElement('a'); + const year = now.getFullYear(); + const month = now.getMonth() + 1; + const date = now.getDate(); + const fmtDate = "KindleEar_" + year + "_" + month + "_" + date + "." + fileType; + elementA.setAttribute("href", "data:" + mimeType + ";charset=utf-8," + encodeURIComponent(content)); + elementA.setAttribute("download", fmtDate); elementA.style.display = 'none'; document.body.appendChild(elementA); elementA.click(); diff --git a/application/templates/adv_delivernow.html b/application/templates/adv_delivernow.html index 27754962..f9535475 100644 --- a/application/templates/adv_delivernow.html +++ b/application/templates/adv_delivernow.html @@ -11,23 +11,30 @@ {% if recipes|length == 0 -%}
{{ _("There are no recipes subscribed") }}
{% endif -%} - {% set userName = session.get('userName', '') -%} {% for item in recipes -%} {% endfor -%} {% if recipes|length > 0 -%}
- {{_("Select all")}} - {{_("Select none")}} + {{_("Select all")}} + {{_("Select none")}}
{% endif -%}
- {{ _('Deliver') }} + {{ _('Deliver') }}
{% endblock -%} +{% block js -%} + +{% endblock -%} \ No newline at end of file diff --git a/application/templates/settings.html b/application/templates/settings.html index f1a0b55f..f01ef6a1 100644 --- a/application/templates/settings.html +++ b/application/templates/settings.html @@ -32,7 +32,7 @@
- +
{% if g.allowReader -%}
@@ -142,7 +142,7 @@ {{_("Custom RSS")}}
- +
diff --git a/application/view/adv.py b/application/view/adv.py index d8d546fa..8f15c334 100644 --- a/application/view/adv.py +++ b/application/view/adv.py @@ -32,6 +32,12 @@ def adv_render_template(tpl, advCurr, **kwargs): @login_required() def AdvDeliverNow(user: KeUser): recipes = user.get_booked_recipe() + if user.cfg('enable_send') != 'all': #添加自定义RSS + rss = [BookedRecipe(recipe_id=r.recipe_id, separated=r.custom.get('separated', False), + user=user.name, title=r.title, description=r.description) + for r in user.all_custom_rss()] + recipes = rss + recipes + deliveryKey = app.config['DELIVERY_KEY'] return adv_render_template('adv_delivernow.html', 'deliverNow', user=user, recipes=recipes, deliveryKey=deliveryKey) diff --git a/application/view/deliver.py b/application/view/deliver.py index 33bfc711..69cf96dd 100644 --- a/application/view/deliver.py +++ b/application/view/deliver.py @@ -78,20 +78,31 @@ def MultiUserDelivery(): #idList: recipe id列表,id格式:custom:xx,upload:xx,builtin:xx,为空则推送指定账号下所有订阅 def SingleUserDelivery(userName: str, idList: list=None): user = KeUser.get_or_none(KeUser.name == userName) - if not user or not user.cfg('kindle_email'): + if not user or ('email' in user.cfg('delivery_mode') and not user.cfg('kindle_email')): return render_template('autoback.html', tips=_('The username does not exist or the email is empty.')) - sent = [] #这里不判断特定账号是否已经订阅了指定的书籍,只要提供就推送 if idList: - recipesToPush = list(filter(bool, map(user.get_booked_recipe, idList))) + toPush = [] + for id_ in idList: + recipeType, dbId = Recipe.type_and_id(id_) + bked = user.get_booked_recipe(id_) + #如果没有启用自定义RSS推送 + r = Recipe.get_by_id_or_none(dbId) if (not bked and (recipeType == 'custom')) else None + if r: + toPush.append({'id_': r.recipe_id, 'separated': r.custom.get('separated', False), + 'title': r.title}) + elif bked: + toPush.append({'id_': bked.recipe_id, 'separated': bked.separated, 'title': bked.title}) else: #推送特定账号所有订阅的书籍 - recipesToPush = user.get_booked_recipe() + toPush = [{'id_': r.recipe_id, 'separated': r.separated, 'title': r.title} + for r in user.get_booked_recipe()] + sent = [] bkQueue = defaultdict(list) - for bkRecipe in recipesToPush: #BookedRecipe实例 - queueOneBook(bkQueue, user, bkRecipe.recipe_id, bkRecipe.separated, reason='manual') - sent.append(f'{bkRecipe.title}') + for bked in toPush: + queueOneBook(bkQueue, user, bked['id_'], bked['separated'], reason='manual') + sent.append(f'{bked["title"]}') flushQueueToPush(bkQueue, reason='manual') if sent: diff --git a/application/view/settings.py b/application/view/settings.py index e2e2d15d..b3b43bac 100644 --- a/application/view/settings.py +++ b/application/view/settings.py @@ -37,47 +37,36 @@ def Settings(user: KeUser): @login_required() def SettingsPost(user: KeUser): form = request.form - keMail = form.get('kindle_email', '').strip(';, ') - myTitle = form.get('rss_title') - - send_mail_service = BuildSmSrvDict(user, form) - if not keMail: - tips = _("Kindle E-mail is requied!") - elif not myTitle: - tips = _("Title is requied!") - elif not send_mail_service: - tips = _("Some parameters are missing or wrong.") - else: - base_config = user.base_config - book_config = user.book_config - - enable_send = form.get('enable_send') - base_config['enable_send'] = enable_send if enable_send in ('all', 'recipes') else '' - base_config['kindle_email'] = keMail - base_config['delivery_mode'] = form.get('delivery_mode', 'email') if app.config['EBOOK_SAVE_DIR'] else 'email' - base_config['webshelf_days'] = str_to_int(form.get('webshelf_days', '7')) - base_config['timezone'] = float(form.get('timezone', '0')) - user.send_time = int(form.get('send_time', '0')) - book_config['type'] = form.get('book_type', 'epub') - book_config['device'] = form.get('device_type', 'kindle') - book_config['title_fmt'] = form.get('title_fmt', '') - allDays = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'] - user.send_days = [weekday for weekday, day in enumerate(allDays) if str_to_bool(form.get(day, ''))] - book_config['mode'] = form.get('book_mode', '') - book_config['rm_links'] = form.get('remove_hyperlinks', '') - book_config['author_fmt'] = form.get('author_format', '') #修正Kindle 5.9.x固件的bug【将作者显示为日期】 - book_config['title'] = myTitle - book_config['language'] = form.get("book_language", "en") - book_config['oldest_article'] = int(form.get('oldest', 7)) - book_config['time_fmt'] = form.get('time_fmt', '') - user.base_config = base_config - user.book_config = book_config - user.send_mail_service = send_mail_service - user.save() - tips = _("Settings Saved!") - - #根据自定义RSS的使能设置,将自定义RSS添加进订阅列表或从订阅列表移除 - UpdateBookedCustomRss(user) + base_config = user.base_config + book_config = user.book_config + + enable_send = form.get('enable_send') + base_config['enable_send'] = enable_send if enable_send in ('all', 'recipes') else '' + base_config['kindle_email'] = form.get('kindle_email', '').strip(';, ') + base_config['delivery_mode'] = form.get('delivery_mode', 'email') if app.config['EBOOK_SAVE_DIR'] else 'email' + base_config['webshelf_days'] = str_to_int(form.get('webshelf_days', '7')) + base_config['timezone'] = float(form.get('timezone', '0')) + user.send_time = int(form.get('send_time', '0')) + book_config['type'] = form.get('book_type', 'epub') + book_config['device'] = form.get('device_type', 'kindle') + book_config['title_fmt'] = form.get('title_fmt', '') + allDays = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'] + user.send_days = [weekday for weekday, day in enumerate(allDays) if str_to_bool(form.get(day, ''))] + book_config['mode'] = form.get('book_mode', '') + book_config['rm_links'] = form.get('remove_hyperlinks', '') + book_config['author_fmt'] = form.get('author_format', '') #修正Kindle 5.9.x固件的bug【将作者显示为日期】 + book_config['title'] = form.get('rss_title') or 'KindleEar' + book_config['language'] = form.get("book_language", "en") + book_config['oldest_article'] = int(form.get('oldest', 7)) + book_config['time_fmt'] = form.get('time_fmt', '') + user.base_config = base_config + user.book_config = book_config + user.send_mail_service = BuildSmSrvDict(user, form) + user.save() + tips = _("Settings Saved!") + + #根据自定义RSS的使能设置,将自定义RSS添加进订阅列表或从订阅列表移除 + UpdateBookedCustomRss(user) sm_services = avaliable_sm_services() return render_template('settings.html', tab='set', user=user, tips=tips, langMap=LangMap(), @@ -85,11 +74,11 @@ def SettingsPost(user: KeUser): #构建发送邮件服务配置字典,返回空字典表示出错 #form: request.form 实例 -def BuildSmSrvDict(user: KeUser, form): +def BuildSmSrvDict(user: KeUser, form) -> dict: srv = user.send_mail_service.copy() - srvType = form.get('sm_service', '') #service==admin 说明和管理员的设置一致 if user.name == os.getenv('ADMIN_NAME') or srv.get('service') != 'admin': + srvType = form.get('sm_service', '') srv['service'] = srvType srv['apikey'] = form.get('sm_apikey', '') srv['secret_key'] = form.get('sm_secret_key', '') @@ -98,7 +87,7 @@ def BuildSmSrvDict(user: KeUser, form): srv['username'] = form.get('sm_username', '') srv['password'] = user.encrypt(form.get('sm_password', '')) srv['save_path'] = form.get('sm_save_path', '') - validations = { + validations = { #根据选择的发送邮件服务类型进行简单校验 'sendgrid': lambda srv: srv['apikey'], 'mailjet': lambda srv: srv['apikey'] and srv['secret_key'], 'smtp': lambda srv: all((srv['host'], srv['port'], srv['password'])), #部分smtp不需要账号名 diff --git a/application/work/worker.py b/application/work/worker.py index f28e1457..e8611ab3 100644 --- a/application/work/worker.py +++ b/application/work/worker.py @@ -157,25 +157,33 @@ def WorkerImpl(userName: str, recipeId: Union[list,str,None]=None, reason='cron' #在已订阅的Recipe或自定义RSS列表创建Recipe源码列表 #返回一个字典,键名为title,元素为 [BookedRecipe, Recipe, src] def GetAllRecipeSrc(user, idList): + bkeds = [] + for id_ in idList: + recipeType, dbId = Recipe.type_and_id(id_) + bked = BookedRecipe.get_or_none(BookedRecipe.recipe_id == id_) + #针对没有启用自定义RSS推送的情况,创建一个临时BookedRecipe对象但不保存到数据库 + recipe = Recipe.get_by_id_or_none(dbId) if (not bked and (recipeType == 'custom')) else None + if recipe: + bked = BookedRecipe(recipe_id=id_, separated=recipe.custom.get('separated', False), + user=user.name, title=recipe.title, description=recipe.description) + bkeds.append({'recipeId': id_, 'recipeType': recipeType, 'dbId': dbId, 'bked': bked, + 'recipe': recipe}) + srcDict = {} - for bked in filter(bool, [BookedRecipe.get_or_none(BookedRecipe.recipe_id == id_) for id_ in idList]): - recipeId = bked.recipe_id - recipeType, dbId = Recipe.type_and_id(recipeId) - if recipeType == 'builtin': - bnInfo = GetBuiltinRecipeInfo(recipeId) - src = GetBuiltinRecipeSource(recipeId) + for item in bkeds: + recipe = item['recipe'] + if item['recipeType'] == 'builtin': + bnInfo = GetBuiltinRecipeInfo(item['recipeId']) + src = GetBuiltinRecipeSource(item['recipeId']) if bnInfo and src: - srcDict[bnInfo.get('title', '')] = [bked, bnInfo, src] - continue - - recipe = Recipe.get_by_id_or_none(dbId) - if recipe: + srcDict[bnInfo.get('title', '')] = [item['bked'], bnInfo, src] + elif recipe: title = recipe.title - if recipeType == 'upload': #上传的Recipe - srcDict[title] = [bked, recipe, recipe.src] + if item['recipeType'] == 'upload': #上传的Recipe + srcDict[title] = [item['bked'], recipe, recipe.src] else: #自定义RSS src = GenerateRecipeSource(title, [(title, recipe.url)], user, isfulltext=recipe.isfulltext) - srcDict[title] = [bked, recipe, src] + srcDict[title] = [item['bked'], recipe, src] return srcDict #返回可用的mp3cat执行文件路径