mirror of
https://github.com/squidfunk/mkdocs-material.git
synced 2024-11-23 23:21:00 +01:00
Removed readtime and lxml dependencies
This commit is contained in:
parent
1cb61a9a6f
commit
0d3cd8a433
@ -47,16 +47,12 @@ RUN \
|
||||
git \
|
||||
git-fast-import \
|
||||
jpeg-dev \
|
||||
libxml2 \
|
||||
libxslt \
|
||||
openssh \
|
||||
zlib-dev \
|
||||
&& \
|
||||
apk add --no-cache --virtual .build \
|
||||
gcc \
|
||||
libffi-dev \
|
||||
libxml2-dev \
|
||||
libxslt-dev \
|
||||
musl-dev \
|
||||
&& \
|
||||
pip install --no-cache-dir --upgrade pip \
|
||||
|
@ -23,7 +23,6 @@ from __future__ import annotations
|
||||
import logging
|
||||
import os
|
||||
import posixpath
|
||||
import readtime
|
||||
import yaml
|
||||
|
||||
from babel.dates import format_date
|
||||
@ -43,6 +42,7 @@ from yaml import SafeLoader
|
||||
|
||||
from .author import Authors
|
||||
from .config import BlogConfig
|
||||
from .readtime import readtime
|
||||
from .structure import Archive, Category, Excerpt, Post, View
|
||||
from .templates import url_filter
|
||||
|
||||
@ -260,17 +260,6 @@ class BlogPlugin(BasePlugin[BlogConfig]):
|
||||
# Append to list of authors
|
||||
page.authors.append(self.authors[name])
|
||||
|
||||
# Compute readtime of post, if enabled and not explicitly set
|
||||
if self.config.post_readtime:
|
||||
rate = self.config.post_readtime_words_per_minute
|
||||
|
||||
# There's a bug in the readtime library which causes it to fail if
|
||||
# the input string contains emojis - see https://t.ly/qEoHq
|
||||
if not page.config.readtime:
|
||||
data = markdown.encode("unicode_escape")
|
||||
read = readtime.of_markdown(data, rate)
|
||||
page.config.readtime = read.minutes
|
||||
|
||||
# Extract settings for excerpts
|
||||
separator = self.config.post_excerpt_separator
|
||||
max_authors = self.config.post_excerpt_max_authors
|
||||
@ -295,6 +284,22 @@ class BlogPlugin(BasePlugin[BlogConfig]):
|
||||
page.excerpt.authors = page.authors[:max_authors]
|
||||
page.excerpt.categories = page.categories[:max_categories]
|
||||
|
||||
# Process posts
|
||||
def on_page_content(self, html, *, page, config, files):
|
||||
if not self.config.enabled:
|
||||
return
|
||||
|
||||
# Skip if page is not a post managed by this instance - this plugin has
|
||||
# support for multiple instances, which is why this check is necessary
|
||||
if page not in self.blog.posts:
|
||||
return
|
||||
|
||||
# Compute readtime of post, if enabled and not explicitly set
|
||||
if self.config.post_readtime:
|
||||
words_per_minute = self.config.post_readtime_words_per_minute
|
||||
if not page.config.readtime:
|
||||
page.config.readtime = readtime(html, words_per_minute)
|
||||
|
||||
# Register template filters for plugin
|
||||
def on_env(self, env, *, config, files):
|
||||
if not self.config.enabled:
|
||||
|
74
material/plugins/blog/readtime/__init__.py
Normal file
74
material/plugins/blog/readtime/__init__.py
Normal file
@ -0,0 +1,74 @@
|
||||
# Copyright (c) 2016-2023 Martin Donath <martin.donath@squidfunk.com>
|
||||
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to
|
||||
# deal in the Software without restriction, including without limitation the
|
||||
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
# sell copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
# IN THE SOFTWARE.
|
||||
|
||||
import re
|
||||
|
||||
from html.parser import HTMLParser
|
||||
from math import ceil
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Functions
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
# Compute readtime - we first used the original readtime library, but the list
|
||||
# of dependencies it brings with it increased the size of the Docker image by
|
||||
# 20 MB (packed), which is an increase of 50%. For this reason, we adapt the
|
||||
# original readtime algorithm to our needs - see https://t.ly/fPZ7L
|
||||
def readtime(html: str, words_per_minute: int):
|
||||
parser = ReadtimeParser()
|
||||
parser.feed(html)
|
||||
parser.close()
|
||||
|
||||
# Extract words from text and compute readtime in seconds
|
||||
words = len(re.split(r"\W+", "".join(parser.text)))
|
||||
seconds = ceil(words / words_per_minute * 60)
|
||||
|
||||
# Account for additional images
|
||||
delta = 12
|
||||
for _ in range(parser.images):
|
||||
seconds += delta
|
||||
if delta > 3: delta -= 1
|
||||
|
||||
# Return readtime in minutes
|
||||
return ceil(seconds / 60)
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Classes
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
# Readtime parser
|
||||
class ReadtimeParser(HTMLParser):
|
||||
|
||||
# Initialize parser
|
||||
def __init__(self):
|
||||
super().__init__(convert_charrefs = True)
|
||||
|
||||
# Keep track of text and images
|
||||
self.text = []
|
||||
self.images = 0
|
||||
|
||||
# Collect images
|
||||
def handle_starttag(self, tag, attrs):
|
||||
if tag == "img":
|
||||
self.images += 1
|
||||
|
||||
# Collect text
|
||||
def handle_data(self, data):
|
||||
self.text.append(data)
|
@ -29,8 +29,6 @@ pymdown-extensions>=9.9.1
|
||||
# Requirements for plugins
|
||||
babel>=2.10.3
|
||||
colorama>=0.4
|
||||
lxml>=4.6
|
||||
paginate>=0.5.6
|
||||
readtime>=2.0
|
||||
regex>=2022.4.24
|
||||
requests>=2.26
|
||||
|
@ -23,7 +23,6 @@ from __future__ import annotations
|
||||
import logging
|
||||
import os
|
||||
import posixpath
|
||||
import readtime
|
||||
import yaml
|
||||
|
||||
from babel.dates import format_date
|
||||
@ -43,6 +42,7 @@ from yaml import SafeLoader
|
||||
|
||||
from .author import Authors
|
||||
from .config import BlogConfig
|
||||
from .readtime import readtime
|
||||
from .structure import Archive, Category, Excerpt, Post, View
|
||||
from .templates import url_filter
|
||||
|
||||
@ -260,17 +260,6 @@ class BlogPlugin(BasePlugin[BlogConfig]):
|
||||
# Append to list of authors
|
||||
page.authors.append(self.authors[name])
|
||||
|
||||
# Compute readtime of post, if enabled and not explicitly set
|
||||
if self.config.post_readtime:
|
||||
rate = self.config.post_readtime_words_per_minute
|
||||
|
||||
# There's a bug in the readtime library which causes it to fail if
|
||||
# the input string contains emojis - see https://t.ly/qEoHq
|
||||
if not page.config.readtime:
|
||||
data = markdown.encode("unicode_escape")
|
||||
read = readtime.of_markdown(data, rate)
|
||||
page.config.readtime = read.minutes
|
||||
|
||||
# Extract settings for excerpts
|
||||
separator = self.config.post_excerpt_separator
|
||||
max_authors = self.config.post_excerpt_max_authors
|
||||
@ -295,6 +284,22 @@ class BlogPlugin(BasePlugin[BlogConfig]):
|
||||
page.excerpt.authors = page.authors[:max_authors]
|
||||
page.excerpt.categories = page.categories[:max_categories]
|
||||
|
||||
# Process posts
|
||||
def on_page_content(self, html, *, page, config, files):
|
||||
if not self.config.enabled:
|
||||
return
|
||||
|
||||
# Skip if page is not a post managed by this instance - this plugin has
|
||||
# support for multiple instances, which is why this check is necessary
|
||||
if page not in self.blog.posts:
|
||||
return
|
||||
|
||||
# Compute readtime of post, if enabled and not explicitly set
|
||||
if self.config.post_readtime:
|
||||
words_per_minute = self.config.post_readtime_words_per_minute
|
||||
if not page.config.readtime:
|
||||
page.config.readtime = readtime(html, words_per_minute)
|
||||
|
||||
# Register template filters for plugin
|
||||
def on_env(self, env, *, config, files):
|
||||
if not self.config.enabled:
|
||||
|
74
src/plugins/blog/readtime/__init__.py
Normal file
74
src/plugins/blog/readtime/__init__.py
Normal file
@ -0,0 +1,74 @@
|
||||
# Copyright (c) 2016-2023 Martin Donath <martin.donath@squidfunk.com>
|
||||
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to
|
||||
# deal in the Software without restriction, including without limitation the
|
||||
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
# sell copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
# IN THE SOFTWARE.
|
||||
|
||||
import re
|
||||
|
||||
from html.parser import HTMLParser
|
||||
from math import ceil
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Functions
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
# Compute readtime - we first used the original readtime library, but the list
|
||||
# of dependencies it brings with it increased the size of the Docker image by
|
||||
# 20 MB (packed), which is an increase of 50%. For this reason, we adapt the
|
||||
# original readtime algorithm to our needs - see https://t.ly/fPZ7L
|
||||
def readtime(html: str, words_per_minute: int):
|
||||
parser = ReadtimeParser()
|
||||
parser.feed(html)
|
||||
parser.close()
|
||||
|
||||
# Extract words from text and compute readtime in seconds
|
||||
words = len(re.split(r"\W+", "".join(parser.text)))
|
||||
seconds = ceil(words / words_per_minute * 60)
|
||||
|
||||
# Account for additional images
|
||||
delta = 12
|
||||
for _ in range(parser.images):
|
||||
seconds += delta
|
||||
if delta > 3: delta -= 1
|
||||
|
||||
# Return readtime in minutes
|
||||
return ceil(seconds / 60)
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Classes
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
# Readtime parser
|
||||
class ReadtimeParser(HTMLParser):
|
||||
|
||||
# Initialize parser
|
||||
def __init__(self):
|
||||
super().__init__(convert_charrefs = True)
|
||||
|
||||
# Keep track of text and images
|
||||
self.text = []
|
||||
self.images = 0
|
||||
|
||||
# Collect images
|
||||
def handle_starttag(self, tag, attrs):
|
||||
if tag == "img":
|
||||
self.images += 1
|
||||
|
||||
# Collect text
|
||||
def handle_data(self, data):
|
||||
self.text.append(data)
|
Loading…
Reference in New Issue
Block a user