views.py 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. # Copyright (C) 2023 MikuInvidious Team
  2. #
  3. # MikuInvidious is free software; you can redistribute it and/or
  4. # modify it under the terms of the GNU General Public License as
  5. # published by the Free Software Foundation; either version 3 of
  6. # the License, or (at your option) any later version.
  7. #
  8. # MikuInvidious is distributed in the hope that it will be useful,
  9. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. # General Public License for more details.
  12. #
  13. # You should have received a copy of the GNU General Public License
  14. # along with MikuInvidious. If not, see <http://www.gnu.org/licenses/>.
  15. import asyncio, requests
  16. from flask import render_template, request
  17. from bilibili_api import user, video, article, comment, search, homepage, video_zone
  18. from shared import *
  19. from extra import video_get_src_for_qn, video_get_dash_for_qn, bv2av, av2bv, article_to_html, article_to_any
  20. @app.route('/licenses')
  21. async def static_licenses_view():
  22. return await render_template_with_theme('licenses.html')
  23. @app.route('/')
  24. async def home_view():
  25. return await render_template_with_theme('home.html', i=await homepage.get_videos())
  26. @app.route('/vv/<zid>')
  27. @app.route('/vv/<zid>/')
  28. async def zone_id_view(zid):
  29. pn = request.args.get('i') or 1
  30. info = await video_zone.get_zone_new_videos(zid, pn)
  31. return await render_template_with_theme('zone.html', info=info)
  32. @app.route('/search')
  33. async def search_view():
  34. q = request.args.get('q')
  35. i = request.args.get('i') or 1
  36. if not q:
  37. return await render_template_with_theme('error.html',
  38. status='无法搜索',
  39. desc='没有发送搜索关键字。',
  40. sg='请设置搜索关键字后重试。'), 400
  41. order_map = {
  42. 'rank': search.OrderVideo.TOTALRANK,
  43. 'click': search.OrderVideo.CLICK,
  44. 'pubdate': search.OrderVideo.PUBDATE,
  45. 'dm': search.OrderVideo.DM,
  46. 'stow': search.OrderVideo.STOW,
  47. 'scores': search.OrderVideo.SCORES,
  48. 'attention': search.OrderArticle.ATTENTION,
  49. 'fans': search.OrderUser.FANS,
  50. 'level': search.OrderUser.LEVEL,
  51. }
  52. if request.args.get('t') == 'article':
  53. search_type = search.SearchObjectType.ARTICLE
  54. tmpl = 'search_article.html'
  55. elif request.args.get('t') == 'user':
  56. search_type = search.SearchObjectType.USER
  57. tmpl = 'search_user.html'
  58. else:
  59. search_type = search.SearchObjectType.VIDEO
  60. tmpl = 'search.html'
  61. sinfo = await search.search_by_type(q, page=i, search_type=search_type,
  62. order_type=order_map.get(request.args.get('sort')))
  63. return await render_template_with_theme(tmpl, q=q, sinfo=sinfo,
  64. rs=sinfo.get('result'),
  65. sort=request.args.get('sort'))
  66. @app.route('/space/<mid>')
  67. @app.route('/space/<mid>/')
  68. async def space_view(mid):
  69. u = user.User(mid)
  70. uinfo, uvids = await asyncio.gather(u.get_user_info(),
  71. u.get_videos(pn=request.args.get('i') or 1, ps=28))
  72. return await render_template_with_theme('space.html', uinfo=uinfo, uvids=uvids)
  73. @app.route('/author/<mid>')
  74. @app.route('/author/<mid>/')
  75. async def author_view(mid):
  76. u = user.User(mid)
  77. uinfo, uarticles = await asyncio.gather(u.get_user_info(),
  78. u.get_articles(pn=request.args.get('i') or 1, ps=28))
  79. return await render_template_with_theme('author.html', uinfo=uinfo, uarts=uarticles)
  80. @app.route('/read/<cid>')
  81. @app.route('/read/<cid>/')
  82. @app.route('/read/mobile/<cid>')
  83. @app.route('/read/mobile/<cid>/')
  84. async def read_view(cid):
  85. req = requests.get(f'https://www.bilibili.com/read/{cid}',
  86. headers={'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)'
  87. 'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0'
  88. 'Safari/537.36 Edg/111.0.1661.62'})
  89. if req.status_code != 200:
  90. st = '服务器错误'
  91. sg = None
  92. desc = '后端服务器发送了无效的回复'
  93. if req.status_code == 404:
  94. st = '没有找到文章'
  95. sg = '这很可能说明您访问的文章不存在,请检查您的请求。' \
  96. '如果您认为这是站点的问题,请联系网站管理员。'
  97. return await render_template_with_theme('error.html',
  98. status = st,
  99. desc = desc,
  100. suggest = sg), req.status_code
  101. if appconf['render']['use_pandoc'] and \
  102. request.args.get('format') in appconf['render']['article_allowed_formats']:
  103. return article_to_any(req.text, request.args.get('format'))
  104. else:
  105. ar = article.Article(cid[2:])
  106. try:
  107. return await render_template_with_theme('read.html', cid=cid, arinfo = (await ar.get_all())['readInfo'], article_content=article_to_html(req.text))
  108. except TypeError:
  109. return await render_template_with_theme('error.html',
  110. status='没有找到文章',
  111. desc='文章不存在',
  112. sg = '这很可能说明您访问的文章不存在,请检查您的请求。'), 404
  113. @app.route('/video_listen/<vid>')
  114. @app.route('/video_listen/<vid>:<idx>')
  115. @app.route('/video_listen/<vid>/')
  116. @app.route('/video_listen/<vid>:<idx>/')
  117. async def video_listen_view(vid, idx=0):
  118. ato = request.args.get('ato') == '1'
  119. # Convert ids to bvid for peace of mind.
  120. vid = av2bv(vid[2:]) if vid.startswith('av') else vid
  121. v = video.Video(bvid=vid, credential=appcred)
  122. vinfo, vtags, vrelated, vcomments, vset = \
  123. await asyncio.gather(v.get_info(), v.get_tags(idx), v.get_related(),
  124. comment.get_comments(vid, comment.CommentResourceType.VIDEO, 1, comment.OrderType.LIKE), v.get_pages())
  125. # Store the download urls for proxies to use.
  126. if not appredis.exists(f'mikuinv_{vid}_{idx}_0'):
  127. vsrc = await video_get_dash_for_qn(v, idx)
  128. appredis.setex(f'mikuinv_{vid}_{idx}_0', 1800, vsrc['dash']['audio'][0]['baseUrl'])
  129. return await render_template_with_theme('video_listen.html', vid=vid, vinfo=vinfo, vrelated=vrelated[:10], vcomments=vcomments,
  130. keywords = ','.join(map(lambda x: x['tag_name'], vtags)), ato=ato, idx=idx, vset=vset)
  131. @app.route('/video/<vid>')
  132. @app.route('/video/<vid>/')
  133. @app.route('/video/<vid>:<idx>')
  134. @app.route('/video/<vid>:<idx>/')
  135. async def video_view(vid, idx=0):
  136. idx = int(idx)
  137. ato = request.args.get('ato') == '1'
  138. if request.args.get('listen') == '1':
  139. return await video_listen_view(vid, idx)
  140. # Convert ids to bvid for peace of mind.
  141. vid = av2bv(vid[2:]) if vid.startswith('av') else vid
  142. v = video.Video(bvid=vid, credential=appcred)
  143. # Fetch the download urls ahead of time to avoid blocking.
  144. v_supported_src, vinfo, vtags, vrelated, vcomments, vset = \
  145. await asyncio.gather(video_get_src_for_qn(v, idx), v.get_info(), v.get_tags(idx), v.get_related(),
  146. comment.get_comments(vid, comment.CommentResourceType.VIDEO, 1, comment.OrderType.LIKE), v.get_pages())
  147. v_supported_src = v_supported_src['support_formats']
  148. # Store the download urls for proxies to use.
  149. if not appredis.exists(f'mikuinv_{vid}_{idx}_16'):
  150. for vsrc in await asyncio.gather(*[video_get_src_for_qn(v, idx, fmt['quality']) for fmt in v_supported_src]):
  151. qn = vsrc['quality']
  152. appredis.setex(f'mikuinv_{vid}_{idx}_{qn}', 1800, vsrc['durl'][0]['url'])
  153. for vsrc in v_supported_src:
  154. qn = vsrc['quality']
  155. if not appredis.exists(f'mikuinv_{vid}_{idx}_{qn}'):
  156. appredis.setex(f'mikuinv_{vid}_{idx}_{qn}', 1800, appredis.get(f'mikuinv_{vid}_{idx}_16'))
  157. return await render_template_with_theme('video.html', vid=vid, vinfo=vinfo, vcomments=vcomments, vrelated=vrelated[:15],
  158. keywords = ','.join(map(lambda x: x['tag_name'], vtags)),
  159. supported_src=v_supported_src, ato=ato, idx=idx, vset=vset)