markdown: allow videos with size and positioning
Same options as for images, except for [pixelated]. Supported sources are standard videos and YouTube, and there is basic auto-detection which avoids the need to set the [video] attribute.
This commit is contained in:
parent
48d6c1c03c
commit
610fe6f1fd
|
@ -120,15 +120,15 @@ input[type="submit"]:focus {
|
|||
.bg-warn:active {
|
||||
background: var(--warn-active);
|
||||
}
|
||||
img.align-left {
|
||||
.align-left {
|
||||
text-align: left;
|
||||
}
|
||||
img.align-center {
|
||||
.align-center {
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
img.align-right {
|
||||
.align-right {
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
|
|
@ -117,15 +117,15 @@ section {
|
|||
}
|
||||
}
|
||||
|
||||
img.align-left {
|
||||
.align-left {
|
||||
text-align: left;
|
||||
}
|
||||
img.align-center {
|
||||
.align-center {
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
img.align-right {
|
||||
.align-right {
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
|
|
@ -10,7 +10,8 @@ markdown_tags = [
|
|||
"sub", "sup",
|
||||
"table", "thead", "tbody", "tr", "th", "td",
|
||||
"form", "fieldset", "input", "textarea",
|
||||
"label", "progress"
|
||||
"label", "progress",
|
||||
"video", "source", "iframe",
|
||||
]
|
||||
|
||||
markdown_attrs = {
|
||||
|
@ -21,4 +22,7 @@ markdown_attrs = {
|
|||
"input": ["id", "name", "type", "value"],
|
||||
"label": ["for"],
|
||||
"progress": ["value", "min", "max"],
|
||||
"video": ["controls", "width", "height"],
|
||||
"source": ["src"],
|
||||
"iframe": ["src", "width", "height", "frameborder", "allowfullscreen"],
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
from markdown.extensions import Extension
|
||||
from markdown.inlinepatterns import LinkInlineProcessor
|
||||
import xml.etree.ElementTree as etree
|
||||
import urllib
|
||||
import re
|
||||
|
||||
class MediaExtension(Extension):
|
||||
def __init__(self, **kwargs):
|
||||
|
@ -49,6 +51,19 @@ class AttrDict:
|
|||
except ValueError:
|
||||
return None, None
|
||||
|
||||
def getAlignmentClass(self):
|
||||
if self.has("left"):
|
||||
return "align-left"
|
||||
if self.has("center"):
|
||||
return "align-center"
|
||||
elif self.has("right"):
|
||||
return "align-right"
|
||||
elif self.has("float-left"):
|
||||
return "float-left"
|
||||
elif self.has("float-right"):
|
||||
return "float-right"
|
||||
return ""
|
||||
|
||||
class AttributeLinkInlineProcessor(LinkInlineProcessor):
|
||||
"""
|
||||
A LinkInlineProcessor which additionally supports attributes after links,
|
||||
|
@ -100,8 +115,11 @@ class MediaInlineProcessor(AttributeLinkInlineProcessor):
|
|||
""" Return a media element from the given match. """
|
||||
|
||||
def isVideo(self, url):
|
||||
# TODO: YouTube integration
|
||||
return url.endswith(".mp4") or url.endswith(".webm")
|
||||
if url.endswith(".mp4") or url.endswith(".webm"):
|
||||
return True
|
||||
url = urllib.parse.urlparse(url)
|
||||
# TODO: Better detect YouTube URLs
|
||||
return url.hostname in ["youtu.be", "www.youtube.com"]
|
||||
|
||||
def isAudio(self, url):
|
||||
return url.endswith(".mp3") or url.endswith(".ogg")
|
||||
|
@ -120,29 +138,20 @@ class MediaInlineProcessor(AttributeLinkInlineProcessor):
|
|||
return None, None, None
|
||||
|
||||
kind = "image"
|
||||
if attrs.has("audio") or self.isAudio(src):
|
||||
if attrs.has("image"):
|
||||
kind = "image"
|
||||
elif attrs.has("audio") or self.isAudio(src):
|
||||
kind = "audio"
|
||||
elif attrs.has("video") or self.isVideo(src):
|
||||
kind = "video"
|
||||
|
||||
# Images
|
||||
|
||||
if kind == "image":
|
||||
w, h = attrs.getSize("size")
|
||||
class_ = ""
|
||||
# TODO: Media converter: Find a way to clear atfer a float
|
||||
if attrs.has("pixelated"):
|
||||
class_ += " pixelated"
|
||||
if attrs.has("left"):
|
||||
class_ += " align-left"
|
||||
if attrs.has("center"):
|
||||
class_ += " align-center"
|
||||
elif attrs.has("right"):
|
||||
class_ += " align-right"
|
||||
elif attrs.has("float-left"):
|
||||
class_ += " float-left"
|
||||
elif attrs.has("float-right"):
|
||||
class_ += " float-right"
|
||||
class_ += " " + attrs.getAlignmentClass()
|
||||
|
||||
el = etree.Element("img")
|
||||
el.set("src", src)
|
||||
|
@ -160,4 +169,52 @@ class MediaInlineProcessor(AttributeLinkInlineProcessor):
|
|||
el.set('alt', self.unescape(text))
|
||||
return el, m.start(0), index
|
||||
|
||||
elif kind == "audio":
|
||||
# TODO: Media converter: support audio files
|
||||
pass
|
||||
|
||||
elif kind == "video":
|
||||
w, h = attrs.getSize("size")
|
||||
class_ = attrs.getAlignmentClass()
|
||||
url = urllib.parse.urlparse(src)
|
||||
args = urllib.parse.parse_qs(url.query)
|
||||
youtube_source = None
|
||||
|
||||
if url.hostname == "youtu.be" and \
|
||||
re.fullmatch(r'\/[a-zA-Z0-9_-]+', url.path):
|
||||
youtube_source = url.path[1:]
|
||||
elif url.hostname == "www.youtube.com" and "v" in args and \
|
||||
re.fullmatch(r'[a-zA-Z0-9_-]+', args["v"][0]):
|
||||
youtube_source = args["v"][0]
|
||||
|
||||
if youtube_source:
|
||||
if w is None and h is None:
|
||||
w, h = (470, 300) if attrs.has("tiny") else (560, 340)
|
||||
|
||||
el = etree.Element("iframe")
|
||||
el.set("src",f"https://www.youtube.com/embed/{youtube_source}")
|
||||
el.set("frameborder", "0")
|
||||
el.set("allowfullscreen", "")
|
||||
# <iframe
|
||||
# src="https://www.youtube.com/embed/{url.path}"
|
||||
# frameborder="0" allowfullscreen></iframe>
|
||||
pass
|
||||
else:
|
||||
el = etree.Element("video")
|
||||
el.set("controls", "")
|
||||
source = etree.Element("source")
|
||||
source.set("src", src)
|
||||
el.append(source)
|
||||
# <video controls
|
||||
# <source src="{url}">
|
||||
# </video>
|
||||
|
||||
el.set("class", class_)
|
||||
if w is not None:
|
||||
el.set("width", str(min(w, 560)))
|
||||
if h is not None:
|
||||
el.set("height", str(min(h, 340)))
|
||||
|
||||
return el, m.start(0), index
|
||||
|
||||
return None, None, None
|
||||
|
|
Loading…
Reference in New Issue