Added better support for PWA manifest detection and a default /manifest.json
This commit is contained in:
parent
bc11db3647
commit
463e846448
2 changed files with 119 additions and 47 deletions
|
@ -1,84 +1,125 @@
|
||||||
import os
|
import os
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from flask import request, Response, send_from_directory as send_from_directory_, render_template
|
from flask import (
|
||||||
|
jsonify,
|
||||||
|
request,
|
||||||
|
Response,
|
||||||
|
send_from_directory as send_from_directory_,
|
||||||
|
render_template,
|
||||||
|
)
|
||||||
|
|
||||||
from .app import app
|
from .app import app
|
||||||
from .config import config
|
from .config import config
|
||||||
from ._sorters import PagesSortByTimeGroupedByFolder
|
from ._sorters import PagesSortByTimeGroupedByFolder
|
||||||
|
|
||||||
|
|
||||||
def send_from_directory(path: str, file: str, alternative_path: Optional[str] = None, *args, **kwargs):
|
def send_from_directory(
|
||||||
|
path: str, file: str, alternative_path: Optional[str] = None, *args, **kwargs
|
||||||
|
):
|
||||||
if not os.path.exists(os.path.join(path, file)) and alternative_path:
|
if not os.path.exists(os.path.join(path, file)) and alternative_path:
|
||||||
path = alternative_path
|
path = alternative_path
|
||||||
return send_from_directory_(path, file, *args, **kwargs)
|
return send_from_directory_(path, file, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/', methods=['GET'])
|
@app.route("/", methods=["GET"])
|
||||||
def home_route():
|
def home_route():
|
||||||
return render_template(
|
return render_template(
|
||||||
'index.html',
|
"index.html",
|
||||||
pages=app.get_pages(sorter=PagesSortByTimeGroupedByFolder),
|
pages=app.get_pages(sorter=PagesSortByTimeGroupedByFolder),
|
||||||
config=config
|
config=config,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/img/<img>', methods=['GET'])
|
@app.route("/img/<img>", methods=["GET"])
|
||||||
def img_route(img: str):
|
def img_route(img: str):
|
||||||
return send_from_directory(app.img_dir, img, config.default_img_dir)
|
return send_from_directory(app.img_dir, img, config.default_img_dir)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/favicon.ico', methods=['GET'])
|
@app.route("/favicon.ico", methods=["GET"])
|
||||||
def favicon_route():
|
def favicon_route():
|
||||||
return img_route('favicon.ico')
|
return img_route("favicon.ico")
|
||||||
|
|
||||||
|
|
||||||
@app.route('/js/<file>', methods=['GET'])
|
@app.route("/js/<file>", methods=["GET"])
|
||||||
def js_route(file: str):
|
def js_route(file: str):
|
||||||
return send_from_directory(app.js_dir, file, config.default_js_dir)
|
return send_from_directory(app.js_dir, file, config.default_js_dir)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/pwabuilder-sw.js', methods=['GET'])
|
@app.route("/pwabuilder-sw.js", methods=["GET"])
|
||||||
def pwa_builder_route():
|
def pwa_builder_route():
|
||||||
return send_from_directory(app.js_dir, 'pwabuilder-sw.js', config.default_js_dir)
|
return send_from_directory(app.js_dir, "pwabuilder-sw.js", config.default_js_dir)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/pwabuilder-sw-register.js', methods=['GET'])
|
@app.route("/pwabuilder-sw-register.js", methods=["GET"])
|
||||||
def pwa_builder_register_route():
|
def pwa_builder_register_route():
|
||||||
return send_from_directory(app.js_dir, 'pwabuilder-sw-register.js', config.default_js_dir)
|
return send_from_directory(
|
||||||
|
app.js_dir, "pwabuilder-sw-register.js", config.default_js_dir
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/css/<style>', methods=['GET'])
|
@app.route("/css/<style>", methods=["GET"])
|
||||||
def css_route(style: str):
|
def css_route(style: str):
|
||||||
return send_from_directory(app.css_dir, style, config.default_css_dir)
|
return send_from_directory(app.css_dir, style, config.default_css_dir)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/fonts/<file>', methods=['GET'])
|
@app.route("/fonts/<file>", methods=["GET"])
|
||||||
def fonts_route(file: str):
|
def fonts_route(file: str):
|
||||||
return send_from_directory(app.fonts_dir, file, config.default_fonts_dir)
|
return send_from_directory(app.fonts_dir, file, config.default_fonts_dir)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/manifest.json', methods=['GET'])
|
@app.route("/manifest.json", methods=["GET"])
|
||||||
def manifest_route():
|
def manifest_route():
|
||||||
return send_from_directory(config.content_dir, 'manifest.json')
|
# If there is a manifest.json in the content directory, use it
|
||||||
|
manifest_file = os.path.join(config.content_dir, "manifest.json")
|
||||||
|
if os.path.isfile(manifest_file):
|
||||||
|
return send_from_directory(config.content_dir, "manifest.json")
|
||||||
|
|
||||||
|
# Otherwise, generate a default manifest.json
|
||||||
|
return jsonify(
|
||||||
|
{
|
||||||
|
"name": config.title,
|
||||||
|
"short_name": config.title,
|
||||||
|
"icons": [
|
||||||
|
{"src": "/img/icon-48.png", "sizes": "48x48", "type": "image/png"},
|
||||||
|
{"src": "/img/icon-72.png", "sizes": "72x72", "type": "image/png"},
|
||||||
|
{"src": "/img/icon-96.png", "sizes": "96x96", "type": "image/png"},
|
||||||
|
{"src": "/img/icon-144.png", "sizes": "144x144", "type": "image/png"},
|
||||||
|
{"src": "/img/icon-168.png", "sizes": "168x168", "type": "image/png"},
|
||||||
|
{"src": "/img/icon-192.png", "sizes": "192x192", "type": "image/png"},
|
||||||
|
{"src": "/img/icon-256.png", "sizes": "256x256", "type": "image/png"},
|
||||||
|
{"src": "/img/icon-512.png", "sizes": "512x512", "type": "image/png"},
|
||||||
|
],
|
||||||
|
"gcm_sender_id": "",
|
||||||
|
"gcm_user_visible_only": True,
|
||||||
|
"start_url": "/",
|
||||||
|
"permissions": ["gcm"],
|
||||||
|
"scope": "",
|
||||||
|
"orientation": "portrait",
|
||||||
|
"display": "standalone",
|
||||||
|
"theme_color": "#ffffff",
|
||||||
|
"background_color": "#000000",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/article/<path:path>/<article>', methods=['GET'])
|
@app.route("/article/<path:path>/<article>", methods=["GET"])
|
||||||
def article_with_path_route(path: str, article: str):
|
def article_with_path_route(path: str, article: str):
|
||||||
return app.get_page(os.path.join(path, article))
|
return app.get_page(os.path.join(path, article))
|
||||||
|
|
||||||
|
|
||||||
@app.route('/article/<article>', methods=['GET'])
|
@app.route("/article/<article>", methods=["GET"])
|
||||||
def article_route(article: str):
|
def article_route(article: str):
|
||||||
return article_with_path_route('', article)
|
return article_with_path_route("", article)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/rss', methods=['GET'])
|
@app.route("/rss", methods=["GET"])
|
||||||
def rss_route():
|
def rss_route():
|
||||||
pages = app.get_pages(with_content=True, skip_header=True, skip_html_head=True)
|
pages = app.get_pages(with_content=True, skip_header=True, skip_html_head=True)
|
||||||
short_description = 'short' in request.args
|
short_description = "short" in request.args
|
||||||
|
|
||||||
return Response('''<?xml version="1.0" encoding="UTF-8" ?>
|
return Response(
|
||||||
|
"""<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
<rss version="2.0" xmlns:media="http://search.yahoo.com/mrss/">
|
<rss version="2.0" xmlns:media="http://search.yahoo.com/mrss/">
|
||||||
<channel>
|
<channel>
|
||||||
<title>{title}</title>
|
<title>{title}</title>
|
||||||
|
@ -95,18 +136,20 @@ def rss_route():
|
||||||
|
|
||||||
{items}
|
{items}
|
||||||
</channel>
|
</channel>
|
||||||
</rss>'''.format(
|
</rss>""".format(
|
||||||
title=config.title,
|
title=config.title,
|
||||||
description=config.description,
|
description=config.description,
|
||||||
link=config.link,
|
link=config.link,
|
||||||
categories=','.join(config.categories),
|
categories=",".join(config.categories),
|
||||||
language=config.language,
|
language=config.language,
|
||||||
last_pub_date=(
|
last_pub_date=(
|
||||||
pages[0][1]['published'].strftime('%a, %d %b %Y %H:%M:%S GMT')
|
pages[0][1]["published"].strftime("%a, %d %b %Y %H:%M:%S GMT")
|
||||||
if pages else ''
|
if pages
|
||||||
),
|
else ""
|
||||||
items='\n\n'.join([
|
),
|
||||||
'''
|
items="\n\n".join(
|
||||||
|
[
|
||||||
|
"""
|
||||||
<item>
|
<item>
|
||||||
<title>{title}</title>
|
<title>{title}</title>
|
||||||
<link>{base_link}{link}</link>
|
<link>{base_link}{link}</link>
|
||||||
|
@ -114,17 +157,26 @@ def rss_route():
|
||||||
<description><![CDATA[{content}]]></description>
|
<description><![CDATA[{content}]]></description>
|
||||||
<media:content medium="image" url="{base_link}{image}" width="200" height="150" />
|
<media:content medium="image" url="{base_link}{image}" width="200" height="150" />
|
||||||
</item>
|
</item>
|
||||||
'''.format(
|
""".format(
|
||||||
base_link=config.link,
|
base_link=config.link,
|
||||||
title=page.get('title', '[No Title]'),
|
title=page.get("title", "[No Title]"),
|
||||||
link=page.get('uri', ''),
|
link=page.get("uri", ""),
|
||||||
published=page['published'].strftime('%a, %d %b %Y %H:%M:%S GMT') if 'published' in page else '',
|
published=page["published"].strftime(
|
||||||
content=page.get('description', '') if short_description else page.get('content', ''),
|
"%a, %d %b %Y %H:%M:%S GMT"
|
||||||
image=page.get('image', ''),
|
)
|
||||||
)
|
if "published" in page
|
||||||
for _, page in pages
|
else "",
|
||||||
]),
|
content=page.get("description", "")
|
||||||
), mimetype='application/xml')
|
if short_description
|
||||||
|
else page.get("content", ""),
|
||||||
|
image=page.get("image", ""),
|
||||||
|
)
|
||||||
|
for _, page in pages
|
||||||
|
]
|
||||||
|
),
|
||||||
|
),
|
||||||
|
mimetype="application/xml",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# vim:sw=4:ts=4:et:
|
# vim:sw=4:ts=4:et:
|
||||||
|
|
|
@ -2,12 +2,32 @@
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
|
||||||
|
<!-- PWA & Viewport -->
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
|
||||||
|
<meta name="viewport" content="uc-fitscreen=yes"/>
|
||||||
|
<meta name="description" content="{{ config.description }}">
|
||||||
|
<link rel="manifest" href="/manifest.json">
|
||||||
|
<!-- Android PWA -->
|
||||||
|
<meta name="theme-color" content="white">
|
||||||
|
<meta name="mobile-web-app-capable" content="yes">
|
||||||
|
<meta name="application-name" content="{{ config.title }}">
|
||||||
|
<!-- iOS PWA -->
|
||||||
|
<meta name="apple-mobile-web-app-title" content="{{ config.title }}">
|
||||||
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||||
|
<meta name="apple-mobile-web-app-status-bar-style" content="default">
|
||||||
|
<!-- Layout mode -->
|
||||||
|
<meta name="layoutmode" content="fitscreen/standard">
|
||||||
|
<!-- Orientation -->
|
||||||
|
<meta name="screen-orientation" content="portrait">
|
||||||
|
<!-- RSS feed -->
|
||||||
<link rel="alternate" type="application/rss+xml" title="{{ config.title }}" href="/rss" />
|
<link rel="alternate" type="application/rss+xml" title="{{ config.title }}" href="/rss" />
|
||||||
|
<!-- Fonts & Styles -->
|
||||||
<link rel="stylesheet" href="/fonts/poppins.css">
|
<link rel="stylesheet" href="/fonts/poppins.css">
|
||||||
<link rel="stylesheet" href="/fonts/fira-sans.css">
|
<link rel="stylesheet" href="/fonts/fira-sans.css">
|
||||||
<link rel="stylesheet" href="/css/common.css">
|
<link rel="stylesheet" href="/css/common.css">
|
||||||
<link rel="manifest" href="/manifest.json">
|
<!-- PWA builder -->
|
||||||
<script type="module" src="/pwabuilder-sw-register.js"></script>
|
<script type="module" src="/pwabuilder-sw-register.js"></script>
|
||||||
|
|
||||||
{% if styles %}
|
{% if styles %}
|
||||||
|
|
Loading…
Reference in a new issue