SoulBook/soulbook/views/novels_blueprint.py
2024-08-01 19:38:07 +08:00

404 lines
17 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python
import time
from jinja2 import Environment, PackageLoader, select_autoescape
from operator import itemgetter
from sanic import Blueprint
from sanic.response import redirect, html, text, json
from soulbook.config import RULES, LOGGER, REPLACE_RULES, ENGINE_PRIORITY, CONFIG
from soulbook.database.mongodb import MotorBase
from soulbook.fetcher.cache import cache_owllook_novels_content, cache_owllook_novels_chapter, \
cache_owllook_search_ranking
from soulbook.fetcher.function import get_time, get_netloc, get_scheme, get_timestamp
from soulbook.fetcher.novels_tools import get_novels_info
from soulbook.utils import ver_question
novels_bp = Blueprint('novels_blueprint')
novels_bp.static('/static/novels', CONFIG.BASE_DIR + '/static/novels')
@novels_bp.listener('before_server_start')
def setup_db(novels_bp, loop):
global motor_base
motor_base = MotorBase()
@novels_bp.listener('after_server_stop')
def close_connection(novels_bp, loop):
motor_base = None
# jinjia2 config
env = Environment(
loader=PackageLoader('soulbook.views.novels_blueprint', '../templates/novels'),
autoescape=select_autoescape(['html', 'xml', 'tpl']))
def template(tpl, **kwargs):
template = env.get_template(tpl)
return html(template.render(kwargs))
@novels_bp.route("/chapter")
async def chapter(request):
"""
返回小说章节目录页
: content_url 这决定当前U页面url的生成方式
: url 章节目录页源url
: novels_name 小说名称
:return: 小说章节内容页
"""
url = request.args.get('url', None)
novels_name = request.args.get('novels_name', None)
netloc = get_netloc(url)
# if netloc not in RULES.keys():
# return redirect(url)
if netloc in REPLACE_RULES.keys():
url = url.replace(REPLACE_RULES[netloc]['old'], REPLACE_RULES[netloc]['new'])
if RULES.get(netloc):
content_url = RULES[netloc].content_url
else:
content_url = "0"
content = await cache_owllook_novels_chapter(url=url, netloc=netloc)
if content:
content = str(content).strip('[],, Jjs').replace(', ', '').replace('onerror', '').replace('js', '').replace(
'加入书架', '').replace('target="_blank"', '').replace('"_blank"', '')
return template(
'chapter.html', novels_name=novels_name, url=url, content_url=content_url, soup=content)
else:
return text('解析失败,请将失败页面反馈给本站,请重新刷新一次,或者访问源网页:{url}'.format(url=url))
@novels_bp.route("/owllook_donate")
async def donate(request):
return template('donate.html')
@novels_bp.route("/owllook_feedback")
async def feedback(request):
return template('feedback.html')
@novels_bp.route("/")
async def index(request):
user = request['session'].get('user', None)
search_ranking = await cache_owllook_search_ranking()
if user:
return template('index.html', title='SoulBook - 网络小说搜索引擎', is_login=1, user=user,
search_ranking=search_ranking[:25])
else:
return template('index.html', title='soulBook - 网络小说搜索引擎', is_login=0, search_ranking=search_ranking[:25])
@novels_bp.route("/owllook_content")
async def owllook_content(request):
"""
返回小说章节内容页
: content_url 这决定当前U页面url的生成方式
: url 章节内容页源url
: chapter_url 小说目录源url
: novels_name 小说名称
:return: 小说章节内容页
"""
url = request.args.get('url', None)
chapter_url = request.args.get('chapter_url', None)
novels_name = request.args.get('novels_name', None)
name = request.args.get('name', '')
is_ajax = request.args.get('is_ajax', '')
# 拼接链接
if not url.startswith("http"):
# # 协议
# get_scheme(chapter_url)
# # 域名
# get_netloc(chapter_url)
if url.startswith("//"):
url = get_scheme(chapter_url) + ":" + url
elif url.startswith("/"):
url = get_scheme(chapter_url) + "://" + get_netloc(chapter_url) + url
else:
url = chapter_url + "/" + url
print("url: %s" % url)
# 当小说内容url不在解析规则内 跳转到原本url
netloc = get_netloc(url)
# if netloc not in RULES.keys():
# return redirect(url)
user = request['session'].get('user', None)
# 拼接小说目录url
book_url = "/chapter?url={chapter_url}&novels_name={novels_name}".format(
chapter_url=chapter_url,
novels_name=novels_name)
motor_db = motor_base.get_db()
if url == chapter_url:
# 阅读到最后章节时候 在数据库中保存最新阅读章节
if user and is_ajax == "owl_cache":
owl_referer = request.headers.get('Referer', '').split('owllook_content')[1]
if owl_referer:
latest_read = "/owllook_content" + owl_referer
await motor_db.user_message.update_one(
{'user': user, 'books_url.book_url': book_url},
{'$set': {'books_url.$.last_read_url': latest_read,
'books_url.$.set_time': time.time(),
"books_url.$.set_date": get_time()
}})
return redirect(book_url)
if RULES.get(netloc):
content_url = RULES[netloc].content_url
else:
content_url = "0"
content_data = await cache_owllook_novels_content(url=url, chapter_url=chapter_url,netloc=netloc)
if content_data:
try:
content = content_data.get('content', '获取失败')
next_chapter = content_data.get('next_chapter', [])
title = content_data.get('title', '').replace(novels_name, '')
name = title if title else name
# 拼接小说书签url
bookmark_url = "{path}?url={url}&name={name}&chapter_url={chapter_url}&novels_name={novels_name}".format(
path=request.path,
url=url,
name=name,
chapter_url=chapter_url,
novels_name=novels_name
)
# 破坏广告链接
content = str(content).strip('[]Jjs,').replace('http', 'hs').replace('.js', '').replace('();', '')
# content += """欢迎关注公众号【粮草小说】,享受精品书籍推荐以及实体书赠送福利!"""
# content += """欢迎关注公众号【Soul】享受精品书籍推荐以及实体书赠送福利"""
if "网页版章节内容慢请下载爱阅小说app阅读最新内容" in content:
list_split = content.rsplit("网页版章节内容慢请下载爱阅小说app阅读最新内容", 1)
content = list_split[0] + "</div>" + list_split[-1].split("</div>", 1)[-1]
if user:
bookmark = await motor_db.user_message.find_one({'user': user, 'bookmarks.bookmark': bookmark_url})
book = await motor_db.user_message.find_one({'user': user, 'books_url.book_url': book_url})
bookmark = 1 if bookmark else 0
if book:
# 当书架中存在该书源
book = 1
# 保存最后一次阅读记录
if is_ajax == "owl_cache":
owl_referer = request.headers.get('Referer', bookmark_url).split(
'owllook_content', 1)[-1]
latest_read = "/owllook_content" + owl_referer
await motor_db.user_message.update_one(
{'user': user, 'books_url.book_url': book_url},
{'$set': {'books_url.$.last_read_url': latest_read,
"books_url.$.set_time": get_timestamp(),
"books_url.$.set_date": get_time(),
# "books_url.$.name": title
}})
elif chapter_url and "owllook_content" in request.url:
owl_referer = request.url.split('owllook_content', 1)[-1]
latest_read = "/owllook_content" + owl_referer
await motor_db.user_message.update_one(
{'user': user, 'books_url.book_url': book_url},
{'$set': {'books_url.$.last_read_url': latest_read,
"books_url.$.set_time": get_timestamp(),
"books_url.$.set_date": get_time(),
"books_url.$.name_now": title
}})
else:
book = 0
if is_ajax == "owl_cache":
owl_cache_dict = dict(
is_login=1,
user=user,
name=name,
url=url,
bookmark=bookmark,
book=book,
content_url=content_url,
chapter_url=chapter_url,
novels_name=novels_name,
next_chapter=next_chapter,
soup=content
)
return json(owl_cache_dict)
return template(
'content.html',
is_login=1,
user=user,
name=name,
url=url,
bookmark=bookmark,
book=book,
content_url=content_url,
chapter_url=chapter_url,
novels_name=novels_name,
next_chapter=next_chapter,
soup=content)
else:
if is_ajax == "owl_cache":
owl_cache_dict = dict(
is_login=0,
name=name,
url=url,
bookmark=0,
book=0,
content_url=content_url,
chapter_url=chapter_url,
novels_name=novels_name,
next_chapter=next_chapter,
soup=content
)
return json(owl_cache_dict)
return template(
'content.html',
is_login=0,
name=name,
url=url,
bookmark=0,
book=0,
content_url=content_url,
chapter_url=chapter_url,
novels_name=novels_name,
next_chapter=next_chapter,
soup=content)
except Exception as e:
LOGGER.exception(e)
return redirect(book_url)
else:
if user:
is_login = 1
user = user
return template('parse_error.html', url=url, is_login=is_login, user=user)
else:
is_login = 0
return template('parse_error.html', url=url, is_login=is_login)
@novels_bp.route("/register")
async def owllook_register(request):
"""
用户登录
:param request:
:return:
: -1 用户名或密码不能为空
: 0 用户名或密码错误
: 1 登陆成功
"""
user = request['session'].get('user', None)
if user:
return redirect('/')
else:
ver_que_ans = ver_question()
if ver_que_ans:
request['session']['index'] = ver_que_ans
return template(
'register.html',
title='soulbook - 注册 - 网络小说搜索引擎',
question=ver_que_ans[1]
)
else:
return redirect('/')
@novels_bp.route("/search", methods=['GET'])
async def owllook_search(request):
start = time.time()
name = str(request.args.get('wd', '')).strip()
novels_keyword = name.split(' ')[0]
motor_db = motor_base.get_db()
if not name:
return redirect('/')
else:
# 记录搜索小说名
try:
await motor_db.search_records.update_one({'keyword': name}, {'$inc': {'count': 1}}, upsert=True)
except Exception as e:
LOGGER.exception(e)
# 通过搜索引擎获取检索结果
parse_result = None
if name.startswith('!baidu'):
novels_keyword = name.split('baidu')[1].strip()
novels_name = 'intitle:{name} 小说 阅读'.format(name=novels_keyword)
parse_result = await get_novels_info(class_name='baidu', novels_name=novels_name)
elif name.startswith('!360'):
novels_keyword = name.split('360')[1].strip()
novels_name = "{name} 小说 最新章节".format(name=novels_keyword)
parse_result = await get_novels_info(class_name='so', novels_name=novels_name)
elif name.startswith('!bing'):
novels_keyword = name.split('bing')[1].strip()
novels_name = "{name} 小说 阅读 最新章节".format(name=novels_keyword)
parse_result = await get_novels_info(class_name='bing', novels_name=novels_name)
elif name.startswith('!duck_go'):
novels_keyword = name.split('duck_go')[1].strip()
novels_name = '{name} 小说 阅读 最新章节'.format(name=novels_keyword)
parse_result = await get_novels_info(class_name='duck_go', novels_name=novels_name)
else:
for each_engine in ENGINE_PRIORITY:
# for bing
if each_engine == "bing":
novels_name = "{name} 小说 阅读 最新章节".format(name=name)
parse_result = await get_novels_info(class_name='bing', novels_name=novels_name)
if parse_result:
break
# for 360 so
if each_engine == "360":
novels_name = "{name} 小说 最新章节".format(name=name)
parse_result = await get_novels_info(class_name='so', novels_name=novels_name)
if parse_result:
break
# for baidu
if each_engine == "baidu":
novels_name = 'intitle:{name} 小说 阅读'.format(name=name)
parse_result = await get_novels_info(class_name='baidu', novels_name=novels_name)
if parse_result:
break
# for duckduckgo
if each_engine == "duck_go":
novels_name = '{name} 小说 阅读 最新章节'.format(name=name)
parse_result = await get_novels_info(class_name='duck_go', novels_name=novels_name)
if parse_result:
break
if parse_result:
# result_sorted = sorted(
# parse_result, reverse=True, key=lambda res: res['timestamp']) if ':baidu' not in name else parse_result
# 优先依靠是否解析进行排序 其次以更新时间进行排序
result_sorted = sorted(
parse_result,
reverse=True,
key=itemgetter('is_recommend', 'is_parse', 'timestamp'))
user = request['session'].get('user', None)
if user:
try:
time_current = get_time()
res = await motor_db.user_message.update_one({'user': user},
{'$set': {'last_update_time': time_current}},
upsert=True)
# 此处语法操作过多 下次看一遍mongo再改
if res:
is_ok = await motor_db.user_message.update_one(
{'user': user, 'search_records.keyword': {'$ne': novels_keyword}},
{'$push': {'search_records': {'keyword': novels_keyword, 'counts': 1}}},
)
if is_ok:
await motor_db.user_message.update_one(
{'user': user, 'search_records.keyword': novels_keyword},
{'$inc': {'search_records.$.counts': 1}}
)
except Exception as e:
LOGGER.exception(e)
return template(
'result.html',
is_login=1,
user=user,
name=novels_keyword,
time='%.2f' % (time.time() - start),
result=result_sorted,
count=len(parse_result))
else:
return template(
'result.html',
is_login=0,
name=novels_keyword,
time='%.2f' % (time.time() - start),
result=result_sorted,
count=len(parse_result))
else:
return html("No Result请将小说名反馈给本站谢谢")