diff --git a/app.py b/app.py index 1e3a7aa..28f89b5 100644 --- a/app.py +++ b/app.py @@ -2,7 +2,6 @@ import asyncio import aiotieba from aioflask import render_template, request, escape -from flask_caching import Cache from urllib.parse import quote_plus from datetime import datetime @@ -14,6 +13,21 @@ from extra import * ###################################################################### +# Clean a leading part and append the text. +def append_with_leading_clean(orig, content): + if orig.endswith('
'): + return orig[:-4] + content + else: + return orig + content + +# Return the corresponding user name for an id. +async def cache_name_from_id(c, i): + if not cache.get(i): + r = await c.get_user_info(i, require=aiotieba.enums.ReqUInfo.USER_NAME) + cache.set(i, r) + +###################################################################### + # Convert a timestamp to its simpliest readable date format. @app.template_filter('simpledate') def _jinja2_filter_simpledate(ts): @@ -44,28 +58,44 @@ def _jinja2_filter_trim(text): # Format fragments to its equiviant HTML. @app.template_filter('translate') -def _jinja2_filter_translate(frags): +async def _jinja2_filter_translate(frags, reply_id=0): htmlfmt = '' + + if reply_id: + htmlfmt += f'@{ cache.get(reply_id) } ' - for frag in frags: + for i in range(len(frags)): + frag = frags[i] if isinstance(frag, FragText): subfrags = frag.text.split('\n') for subfrag in subfrags: - htmlfmt += '

' + str(escape(subfrag)) + '

' + htmlfmt += str(escape(subfrag)) + '
' elif isinstance(frag, FragImage_p): htmlfmt += \ f'' \ f'' elif isinstance(frag, FragEmoji_p): - clear_leading = False - if htmlfmt.endswith('

'): - clear_leading = True - htmlfmt = htmlfmt.rstrip('

') - htmlfmt += f'[{ frag.desc }]' - if clear_leading: - htmlfmt += '

' - + htmlfmt = append_with_leading_clean(htmlfmt, + f'[{ frag.desc }]') + if i+1 < len(frags) and isinstance(frags[i+1], FragImage_p): + htmlfmt += '
' + elif isinstance(frag, FragLink): + markup = '{ frag.title }' + htmlfmt = append_with_leading_clean(htmlfmt, markup) + elif isinstance(frag, FragAt): + htmlfmt = append_with_leading_clean(htmlfmt, + f'{ frag.text }') + else: + print('Unhandled: ', type(frag)) + print(frag) + return htmlfmt ###################################################################### @@ -77,23 +107,39 @@ async def thread_view(tid): async with aiotieba.Client() as tieba: # Default to 15 posts per page, confirm to tieba.baidu.com - thread_info = await tieba.get_posts(tid, rn=15, pn=pn) + thread_info = await tieba.get_posts(tid, rn=15, pn=pn, + with_comments=should_fetch_comments) - for post in thread_info: - print(post.comments) - + available_users = [] + for floor in thread_info: + for comment in floor.comments: + available_users.append(comment.author_id) + cache.set(comment.author_id, comment.user.user_name) + + all_users = {} + for floor in thread_info: + for comment in floor.comments: + if not comment.reply_to_id in available_users: + all_users[comment.reply_to_id] = '' + all_users.pop(0, None) + all_users = list(all_users.keys()) + + await asyncio.gather(*(cache_name_from_id(tieba, i) for i in all_users)) + + return await render_template('thread.html', info=thread_info) @app.route('/f') async def forum_view(): fname = request.args['kw'] pn = int(request.args.get('pn') or 1) + sort = int(request.args.get('sort') or 0) async with aiotieba.Client() as tieba: forum_info, threads = await asyncio.gather(awaitify(find_tieba_info)(fname), - tieba.get_threads(fname, rn=50, pn=pn)) + tieba.get_threads(fname, rn=50, pn=pn, sort=sort)) - return await render_template('bar.html', info=forum_info, threads=threads) + return await render_template('bar.html', info=forum_info, threads=threads, sort=sort) if __name__ == '__main__': app.run(debug=True) diff --git a/extra.py b/extra.py index 2bfa4e1..9cd533b 100644 --- a/extra.py +++ b/extra.py @@ -6,9 +6,13 @@ import re from shared import * +# TODO: known bug, can't extract from super old editor images. def extract_image_name(url): match = re.search(r'/(\w+)\.jpg', url) - return match.group(1) + '.jpg' + try: + return match.group(1) + '.jpg' + except: + return '404.jpg' @cache.cached(timeout=60, key_prefix='tieba_info') def find_tieba_info(tname): diff --git a/main.py b/main.py index a02b03c..aaa0056 100644 --- a/main.py +++ b/main.py @@ -138,11 +138,12 @@ class ReverseProxyResource(Resource): # To start this function for testing: python -c 'import main; main.twisted_start()' def twisted_start(): - flask_res = proxy.ReverseProxyResource('127.0.0.1', 5000, b'') + flask_port = int(app.config['SERVER_NAME'].split(':')[1]) + flask_res = proxy.ReverseProxyResource('127.0.0.1', flask_port, b'') flask_res.putChild(b'proxy', ReverseProxyResource(b'/proxy')) flask_res.putChild(b'static', File('static')) - flask_port = int(app.config['SERVER_NAME'].split(':')[1]) + print(f' *** SERVER IS RUNNING ON PORT {flask_port-1} ***') site = server.Site(flask_res) reactor.listenTCP(flask_port-1, site) @@ -154,11 +155,7 @@ def flask_start(): # If we're executed directly, also start the flask daemon. if __name__ == '__main__': - flask_port = int(app.config['SERVER_NAME'].split(':')[1]) - print(f' *** SERVER IS RUNNING ON PORT {flask_port-1} ***') - - twisted_start() - flask_task = multiprocessing.Process(target=flask_start) flask_task.daemon = True # Exit the child if the parent was killed :-( flask_task.start() + twisted_start() diff --git a/shared.py b/shared.py index 87adbef..f0b0f98 100644 --- a/shared.py +++ b/shared.py @@ -12,7 +12,13 @@ def awaitify(sync_func): app = Flask(__name__) -app.config['SERVER_NAME'] = ':6666' +###################################################################### + +app.config['SERVER_NAME'] = '127.0.0.1:8886' + +should_fetch_comments = True + +###################################################################### app.config['CACHE_TYPE'] = 'SimpleCache' cache = Cache(app) diff --git a/static/css/main.css b/static/css/main.css index a85c19a..6b188a2 100644 --- a/static/css/main.css +++ b/static/css/main.css @@ -2,6 +2,21 @@ max-width: 5% !important; } +.vlist { + display: flex; + justify-content: space-between; +} + +.vlist > div { + flex: 1; + text-align: center; + margin: 0.5em; +} + +.current-sel { + color: inherit !important; +} + /* global styling */ :root { --bg-color: #eeeecc; diff --git a/templates/bar.html b/templates/bar.html index 73c38c2..ae3e047 100644 --- a/templates/bar.html +++ b/templates/bar.html @@ -21,6 +21,16 @@ +
+
+
时下热门
+
最新发布
+
最新回复
+
+
{% for t in threads %}
@@ -34,9 +44,12 @@ %}置顶{% endif %}{% if t.is_good %}{% endif - %}{{ t.title }} + %}{{ t.title if t.title else t.text|trim }}
+ + {% if t.title %}
{{ t.text[(t.title|length):]|trim }}
+ {% endif %}
🧑{{ t.user.user_name }}
@@ -47,13 +60,13 @@
{% if threads.page.current_page > 1 %} - 首页 + 首页 {% endif %} {% for i in range(5) %} {% set np = threads.page.current_page - 5 + i %} {% if np > 0 %} - {{ np }} + {{ np }} {% endif %} {% endfor %} @@ -62,12 +75,12 @@ {% for i in range(5) %} {% set np = threads.page.current_page + 1 + i %} {% if np <= threads.page.total_page %} - {{ np }} + {{ np }} {% endif %} {% endfor %} {% if threads.page.current_page < threads.page.total_page %} - 尾页 + 尾页 {% endif %}
diff --git a/templates/thread.html b/templates/thread.html index 2e5be92..fc06ed5 100644 --- a/templates/thread.html +++ b/templates/thread.html @@ -30,6 +30,24 @@ {{ p.create_time|date }} {{ p.floor }} + {% if p.comments %} +
+ {% for comment in p.comments %} +
+ +
+ +
+ {{ comment.contents|translate(comment.reply_to_id)|safe }} +
+ {{ comment.create_time|date }} +
+
+ {% endfor %} +
+ {% endif %} {% endfor %}