From 14b7c51267287e77c42d762f67b85410fd31106e Mon Sep 17 00:00:00 2001 From: Mariotaku Date: Mon, 10 Jan 2022 01:15:44 +0900 Subject: [PATCH] updating site generator --- .github/workflows/repo-sitegen.yml | 2 +- packages/com.crunchyroll.webos.yml | 32 +++++++-------- pelicanconf.py | 20 ++++++---- repogen/__init__.py | 5 +++ repogen/__main__.py | 14 ++++++- repogen/apppage.py | 13 ++++-- repogen/common.py | 35 ++++++++++++----- repogen/plugin.py | 42 ++++++++++++++++++++ repogen/templates/apps/detail.md | 2 +- theme/static/styles/app.scss | 63 ++++++++++++++++++++++++++++-- theme/templates/app.html | 29 ++++++++++---- theme/templates/apps.html | 5 +-- website | 2 +- 13 files changed, 206 insertions(+), 58 deletions(-) create mode 100644 repogen/__init__.py create mode 100644 repogen/plugin.py diff --git a/.github/workflows/repo-sitegen.yml b/.github/workflows/repo-sitegen.yml index f21f66a..9117aef 100644 --- a/.github/workflows/repo-sitegen.yml +++ b/.github/workflows/repo-sitegen.yml @@ -34,7 +34,7 @@ jobs: run: python3 -m pip install -r ./requirements.txt - name: Generate repository data - run: python3 -m repogen -i ./packages -o ./content + run: python3 -m repogen -i ./packages -o ./content -d false - name: Generate site content run: pelican content diff --git a/packages/com.crunchyroll.webos.yml b/packages/com.crunchyroll.webos.yml index 9d4d146..2b0722a 100644 --- a/packages/com.crunchyroll.webos.yml +++ b/packages/com.crunchyroll.webos.yml @@ -1,16 +1,16 @@ -title: Crunchyroll -iconUri: https://raw.githubusercontent.com/mateussouzaweb/crunchyroll-webos/master/src/img/130px.png -manifestUrl: https://github.com/mateussouzaweb/crunchyroll-webos/releases/latest/download/webosbrew.manifest.json -category: multimedia -description: | - Unofficial WebOS TV App for Crunchyroll. - - The last Crunchyroll app you will ever need! - - ## Support - - Support [author](https://github.com/mateussouzaweb) for future versions for Samsung, Android TV, ChromeCast and Others. - - ## Known Bugs - - - Select dropdown does not trigger with LG TV Controller in arrow navigation mode. +title: Crunchyroll +iconUri: https://raw.githubusercontent.com/mateussouzaweb/crunchyroll-webos/master/src/img/130px.png +manifestUrl: https://github.com/mateussouzaweb/crunchyroll-webos/releases/latest/download/webosbrew.manifest.json +category: multimedia +description: | + Unofficial WebOS TV App for Crunchyroll. + + The last Crunchyroll app you will ever need! + + ## Support + + Support [author](https://github.com/mateussouzaweb) for future versions for Samsung, Android TV, ChromeCast and Others. + + ## Known Bugs + + - Select dropdown does not trigger with LG TV Controller in arrow navigation mode. diff --git a/pelicanconf.py b/pelicanconf.py index d13b0ac..8d49aff 100644 --- a/pelicanconf.py +++ b/pelicanconf.py @@ -3,6 +3,9 @@ import datetime from os.path import join, dirname, abspath +import repogen +from pelican.plugins import webassets + AUTHOR = 'webOS Homebrew Project' SITENAME = 'webOS Homebrew Project' SITEURL = '' @@ -10,13 +13,16 @@ THEME = './theme' THEME_STATIC_PATHS = [join(dirname(abspath(__file__)), 'website/theme/static')] THEME_TEMPLATES_OVERRIDES = ['./website/theme/templates'] + +PLUGINS = [webassets, repogen] + WEBASSETS_SOURCE_PATHS = ['static'] PATH = 'content' STATIC_PATHS = ['api', 'extra/CNAME', 'styles'] ARTICLE_EXCLUDES = ['api'] -PAGE_PATHS = ['pages', 'apps'] +PAGE_PATHS = ['pages', 'apps', '../packages'] EXTRA_PATH_METADATA = { 'extra/CNAME': {'path': 'CNAME'}, @@ -45,23 +51,21 @@ TRANSLATION_FEED_ATOM = None AUTHOR_FEED_ATOM = None AUTHOR_FEED_RSS = None +CACHE_CONTENT = False +LOAD_CONTENT_CACHE = False MENUITEMS = ( ('Applications', '/apps'), ('Submit', '/submit'), ) -# Blogroll LINKS = ( - ('webOS Homebrew', 'https://github.com/webosbrew/'), + ('Github Organization', 'https://github.com/webosbrew/'), + ('Join us on Discord', 'https://discord.gg/xWqRVEm'), + ('RootMy.TV', 'https://rootmy.tv/'), ('openlgtv', 'https://openlgtv.github.io/'), ) -# Social widget -SOCIAL = ( - ('openlgtv Discord', 'https://discord.gg/xWqRVEm'), -) - DEFAULT_PAGINATION = 30 # Uncomment following line if you want document-relative URLs when developing diff --git a/repogen/__init__.py b/repogen/__init__.py new file mode 100644 index 0000000..2ec9721 --- /dev/null +++ b/repogen/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- +__title__ = 'repogen' +__version__ = '1.0.0' + +from .plugin import * diff --git a/repogen/__main__.py b/repogen/__main__.py index 814661b..756228a 100644 --- a/repogen/__main__.py +++ b/repogen/__main__.py @@ -8,8 +8,18 @@ parser = argparse.ArgumentParser() parser.add_argument('-i', '--input-dir', required=True) parser.add_argument('-o', '--output-dir', required=True) + +parser.add_argument('-A', '--no-gen-api', dest='gen_api', action='store_false') +parser.add_argument('-D', '--no-gen-details', dest='gen_details', action='store_false') +parser.add_argument('-L', '--no-gen-list', dest='gen_list', action='store_false') + +parser.set_defaults(gen_api=True, gen_details=True, gen_list=True) + args = parser.parse_args() packages = list_packages(args.input_dir) -apidata.generate(packages, path.join(args.output_dir, 'api')) -apppage.generate(packages, path.join(args.output_dir, 'apps')) + +if args.gen_api: + apidata.generate(packages, path.join(args.output_dir, 'api')) + +apppage.generate(packages, path.join(args.output_dir, 'apps'), gen_details=args.gen_details, gen_list=args.gen_list) diff --git a/repogen/apppage.py b/repogen/apppage.py index bf5dab8..715ee14 100644 --- a/repogen/apppage.py +++ b/repogen/apppage.py @@ -32,6 +32,7 @@ def _gen_page(self, outdir, items, pagination): def _nav_path(p: int): return 'apps' if p == 1 else 'apps/page/%d' % p + prev_href = _nav_path(prevp) if prevp else None next_href = _nav_path(nextp) if nextp else None @@ -45,17 +46,19 @@ def _nav_path(p: int): def _nav_item(p: int): return {'page': p, 'path': _nav_path(p), 'current': p == page} + page_links = list(map(_nav_item, range( nav_center_start, nav_center_end + 1))) if page > 4: page_links = page_links[1:] - page_links = [_nav_item(1), None] + page_links + page_links = [_nav_item(1), None] + page_links if page < maxp - 3: page_links = page_links[:-1] page_links = page_links + [None, _nav_item(maxp)] def _page_path(p: int): return 'apps/index.html' if p == 1 else 'apps/page/%d.html' % p + with open(join(outdir, ('apps-page-%d.html' % page)), 'w', encoding='utf-8') as f: f.write(pystache.render(self.index_template, { 'packages': items, 'pagePath': _page_path(page), @@ -81,14 +84,16 @@ def gen_list(self, outdir): self._gen_page(outdir, items, pagination) -def generate(packages, outdir): +def generate(packages, outdir, gen_details=True, gen_list=True): generator = AppListingGenerator(packages) if not exists(outdir): makedirs(outdir) - generator.gen_details(outdir) - generator.gen_list(outdir) + if gen_details: + generator.gen_details(outdir) + if gen_list: + generator.gen_list(outdir) print('Generated application page for %d packages.' % len(generator.packages)) diff --git a/repogen/common.py b/repogen/common.py index 165fcaa..220ddc0 100644 --- a/repogen/common.py +++ b/repogen/common.py @@ -6,6 +6,7 @@ import urllib from datetime import datetime from email.utils import parsedate_to_datetime +from json import JSONDecodeError from os import listdir, mkdir, path from os.path import basename, isfile, join from urllib.parse import urljoin @@ -27,15 +28,25 @@ def url_fixup(u): return u -def obtain_manifest(pkgid, type, url: str): +def url_size(u): + content_length = requests.head(u, allow_redirects=True).headers.get('content-length', None) + if not content_length: + return 0 + return int(content_length) + + +def obtain_manifest(pkgid, type, url: str, offline=False): if not path.exists('cache'): mkdir('cache') cache_file = path.join('cache', f'manifest_{pkgid}_{type}.json') try: + if offline: + raise requests.exceptions.ConnectionError('Offline') url = url_fixup(url) resp = requests.get(url=url, allow_redirects=True) manifest = resp.json() manifest['ipkUrl'] = urljoin(url, manifest['ipkUrl']) + manifest['ipkSize'] = url_size(manifest['ipkUrl']) with open(cache_file, 'w', encoding='utf-8') as f: json.dump(manifest, f) last_modified = datetime.now() @@ -44,14 +55,19 @@ def obtain_manifest(pkgid, type, url: str): resp.headers['last-modified']) os.utime(cache_file, (last_modified.timestamp(), last_modified.timestamp())) return manifest, last_modified - except requests.exceptions.RequestException: + except requests.exceptions.RequestException as e: if path.exists(cache_file): - with open(cache_file, encoding='utf-8') as f: - return json.load(f), datetime.fromtimestamp(os.stat(cache_file).st_mtime) + try: + with open(cache_file, encoding='utf-8') as f: + return json.load(f), datetime.fromtimestamp(os.stat(cache_file).st_mtime) + except IOError or JSONDecodeError: + os.unlink(cache_file) + else: + raise e return None -def parse_package_info(path: str): +def parse_package_info(path: str, offline=False): extension = os.path.splitext(path)[1] if extension == '.yml': content = parse_yml_package(path) @@ -71,7 +87,7 @@ def parse_package_info(path: str): 'category': content['category'], 'description': bleach.clean(content.get('description', '')), } - manifest, lastmodified_r = obtain_manifest(pkgid, 'release', manifest_url) + manifest, lastmodified_r = obtain_manifest(pkgid, 'release', manifest_url, offline) if manifest: pkginfo['manifest'] = manifest lastmodified_b = None @@ -80,7 +96,8 @@ def parse_package_info(path: str): if manifest_b: pkginfo['manifestBeta'] = manifest_b lastmodified = lastmodified_r, lastmodified_b - pkginfo['lastmodified'] = max(d for d in lastmodified if d is not None).strftime('%Y/%m/%d %H:%M:%S %Z') + pkginfo['lastmodified'] = max(d for d in lastmodified if d is not None) + pkginfo['lastmodified_str'] = pkginfo['lastmodified'].strftime('%Y/%m/%d %H:%M:%S %Z') return pkginfo @@ -99,7 +116,7 @@ def load_py_package(path): return module.load() -def list_packages(pkgdir): +def list_packages(pkgdir, offline=False): paths = [join(pkgdir, f) for f in listdir(pkgdir) if isfile(join(pkgdir, f))] - return sorted(filter(lambda x: x, map(parse_package_info, paths)), key=lambda x: x['title']) + return sorted(filter(lambda x: x, map(lambda p: parse_package_info(p, offline), paths)), key=lambda x: x['title']) diff --git a/repogen/plugin.py b/repogen/plugin.py new file mode 100644 index 0000000..3d758ea --- /dev/null +++ b/repogen/plugin.py @@ -0,0 +1,42 @@ +import logging +import os + +from markdown import Markdown +from pelican import signals, Readers +from pelican.readers import BaseReader + +from repogen.common import parse_package_info + +log = logging.getLogger(__name__) + + +class PackageInfoReader(BaseReader): + enabled = True + + file_extensions = ['yml', 'py'] + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self._md = Markdown(**self.settings['MARKDOWN']) + + def read(self, filename): + info = parse_package_info(filename, offline='CI' not in os.environ) + metadata = { + 'title': info['title'], + 'override_save_as': f'apps/{info["id"]}.html', + 'template': 'app', + 'status': 'hidden', + 'modified': info['lastmodified'], + 'manifest': info['manifest'], + } + return self._md.convert(info['description']), metadata + + +def readers_init(readers: Readers): + readers.reader_classes['yml'] = PackageInfoReader + readers.reader_classes['py'] = PackageInfoReader + + +def register(): + signals.readers_init.connect(readers_init) + pass diff --git a/repogen/templates/apps/detail.md b/repogen/templates/apps/detail.md index bee39a5..0e28df4 100644 --- a/repogen/templates/apps/detail.md +++ b/repogen/templates/apps/detail.md @@ -9,7 +9,7 @@ Version: {{manifest.version}} {{#manifest.sourceUrl}}Source: [{{manifest.sourceUrl}}]({{manifest.sourceUrl}}){{/manifest.sourceUrl}} -{{#lastmodified}}Last Updated: {{lastmodified}}{{/lastmodified}} +{{#lastmodified_str}}Last Updated: {{lastmodified_str}}{{/lastmodified_str}} --- diff --git a/theme/static/styles/app.scss b/theme/static/styles/app.scss index e31c9fb..5216beb 100644 --- a/theme/static/styles/app.scss +++ b/theme/static/styles/app.scss @@ -1,11 +1,66 @@ +@import "../../../website/theme/static/styles/variables"; + +$icon-size: 160px; + #content .main { img { max-width: 100%; } - img.app-icon { - max-width: 80px; - margin-top: -20px; - float: right; + .app-icon { + width: $icon-size; + height: $icon-size; + float: left; + } + + .app-info { + display: flex; + flex-flow: column; + min-height: $icon-size; + + .app-title { + margin-top: 10px; + } + + .app-actions { + margin: auto 10px 10px auto; + padding-top: 10px; + + .app-action { + height: 48px; + padding: 5px 10px; + box-sizing: border-box; + + overflow: hidden; + + background-color: $color-accent; + border-radius: 5px; + color: white; + } + + .download-ipk { + .title { + font-size: 15px; + } + + .version { + display: inline-block; + font-size: 12px; + float: left; + } + + .size { + display: inline-block; + font-size: 12px; + float: right; + } + + hr { + margin: 2px 0; + border-color: rgba(255, 255, 255, 0.5); + border-width: 1px; + } + } + } } } diff --git a/theme/templates/app.html b/theme/templates/app.html index 09bdf77..6639d91 100644 --- a/theme/templates/app.html +++ b/theme/templates/app.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "page.html" %} {% block html_lang %}{{ page.lang }}{% endblock %} {% block title %}{{ page.title|striptags }} | {{ SITENAME }}{%endblock%} @@ -16,18 +16,31 @@ {% endblock %} {% block content %} -

{{ page.title }}

- {% for app in REPO_APPS %} - {{app}} - {% endfor %} {% import 'translations.html' as translations with context %} {{ translations.translations_for(page) }} + {{title}} Icon +
+

{{ page.title }}

+ + Source: {{ page.metadata.manifest.sourceUrl }} + + +
+
{{ page.content }} +
{% if page.modified %} -

- Last updated: {{ page.locale_modified }} -

+

Last Updated: {{ page.modified.strftime('%Y/%m/%d %H:%M:%S %Z') }}

{% endif %} {% endblock %} diff --git a/theme/templates/apps.html b/theme/templates/apps.html index 8675f2c..e65cf17 100644 --- a/theme/templates/apps.html +++ b/theme/templates/apps.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "page.html" %} {% block html_lang %}{{ page.lang }}{% endblock %} {% block title %}{{ page.title|striptags }} | {{ SITENAME }}{%endblock%} @@ -17,9 +17,6 @@ {% block content %}

{{ page.title }}

- {% for app in REPO_APPS %} - {{app}} - {% endfor %} {% import 'translations.html' as translations with context %} {{ translations.translations_for(page) }} diff --git a/website b/website index 6ee6182..48e6cc1 160000 --- a/website +++ b/website @@ -1 +1 @@ -Subproject commit 6ee6182dc4fc934ea826d93ce301b4e605ba7b2f +Subproject commit 48e6cc1b5933eea437f5b5161b2d17bd143b5c67