mirror of https://github.com/zulip/zulip.git
markdown: Rewrite YouTube URL parser without regex spaghetti.
This also adds support for the new YouTube Shorts URLs. Signed-off-by: Anders Kaseorg <anders@zulip.com>
This commit is contained in:
parent
53aa3f6c71
commit
0a1904a6a7
|
@ -26,7 +26,7 @@ from typing import (
|
|||
TypeVar,
|
||||
Union,
|
||||
)
|
||||
from urllib.parse import urlencode, urljoin, urlsplit
|
||||
from urllib.parse import parse_qs, urlencode, urljoin, urlsplit
|
||||
from xml.etree.ElementTree import Element, SubElement
|
||||
|
||||
import ahocorasick
|
||||
|
@ -811,28 +811,30 @@ class InlineInterestingLinkProcessor(markdown.treeprocessors.Treeprocessor):
|
|||
def youtube_id(self, url: str) -> Optional[str]:
|
||||
if not self.zmd.image_preview_enabled:
|
||||
return None
|
||||
# YouTube video id extraction regular expression from https://pastebin.com/KyKAFv1s
|
||||
# Slightly modified to support URLs of the forms
|
||||
# - youtu.be/<id>
|
||||
# - youtube.com/playlist?v=<id>&list=<list-id>
|
||||
# - youtube.com/watch_videos?video_ids=<id1>,<id2>,<id3>
|
||||
# If it matches, match.group(2) is the video id.
|
||||
schema_re = r"(?:https?://)"
|
||||
host_re = r"(?:youtu\.be/|(?:\w+\.)?youtube(?:-nocookie)?\.com/)"
|
||||
param_re = (
|
||||
r"(?:(?:(?:v|embed)/)"
|
||||
r"|(?:(?:(?:watch|playlist)(?:_popup|_videos)?(?:\.php)?)?(?:\?|#!?)(?:.+&)?v(?:ideo_ids)?=))"
|
||||
)
|
||||
id_re = r"([0-9A-Za-z_-]+)"
|
||||
youtube_re = r"^({schema_re}?{host_re}{param_re}?)?{id_re}(?(1).+)?$"
|
||||
youtube_re = youtube_re.format(
|
||||
schema_re=schema_re, host_re=host_re, id_re=id_re, param_re=param_re
|
||||
)
|
||||
match = re.match(youtube_re, url)
|
||||
# URLs of the form youtube.com/playlist?list=<list-id> are incorrectly matched
|
||||
if match is None or match.group(2) == "playlist":
|
||||
return None
|
||||
return match.group(2)
|
||||
|
||||
id = None
|
||||
split_url = urlsplit(url)
|
||||
if split_url.scheme in ("http", "https"):
|
||||
if split_url.hostname in (
|
||||
"m.youtube.com",
|
||||
"www.youtube.com",
|
||||
"www.youtube-nocookie.com",
|
||||
"youtube.com",
|
||||
"youtube-nocookie.com",
|
||||
):
|
||||
query = parse_qs(split_url.query)
|
||||
if split_url.path in ("/watch", "/watch_popup") and "v" in query:
|
||||
id = query["v"][0]
|
||||
elif split_url.path == "/watch_videos" and "video_ids" in query:
|
||||
id = query["video_ids"][0].split(",", 1)[0]
|
||||
elif split_url.path.startswith(("/embed/", "/shorts/", "/v/")):
|
||||
id = split_url.path.split("/", 3)[2]
|
||||
elif split_url.hostname == "youtu.be" and split_url.path.startswith("/"):
|
||||
id = split_url.path[len("/") :]
|
||||
|
||||
if id is not None and re.fullmatch(r"[0-9A-Za-z_-]+", id):
|
||||
return id
|
||||
return None
|
||||
|
||||
def youtube_title(self, extracted_data: UrlEmbedData) -> Optional[str]:
|
||||
if extracted_data.title is not None:
|
||||
|
|
|
@ -537,14 +537,12 @@ class MarkdownTest(ZulipTestCase):
|
|||
'<p><a href="https://www.youtube.com/playlist?list=PL8dPuuaLjXtNlUrzyH5r6jN9ulIgZBpdo">https://www.youtube.com/playlist?list=PL8dPuuaLjXtNlUrzyH5r6jN9ulIgZBpdo</a></p>',
|
||||
)
|
||||
|
||||
msg = (
|
||||
"https://www.youtube.com/playlist?v=O5nskjZ_GoI&list=PL8dPuuaLjXtNlUrzyH5r6jN9ulIgZBpdo"
|
||||
)
|
||||
msg = "https://www.youtube.com/watch?v=O5nskjZ_GoI&list=PL8dPuuaLjXtNlUrzyH5r6jN9ulIgZBpdo"
|
||||
converted = markdown_convert_wrapper(msg)
|
||||
|
||||
self.assertEqual(
|
||||
converted,
|
||||
f"""<p><a href="https://www.youtube.com/playlist?v=O5nskjZ_GoI&list=PL8dPuuaLjXtNlUrzyH5r6jN9ulIgZBpdo">https://www.youtube.com/playlist?v=O5nskjZ_GoI&list=PL8dPuuaLjXtNlUrzyH5r6jN9ulIgZBpdo</a></p>\n<div class="youtube-video message_inline_image"><a data-id="O5nskjZ_GoI" href="https://www.youtube.com/playlist?v=O5nskjZ_GoI&list=PL8dPuuaLjXtNlUrzyH5r6jN9ulIgZBpdo"><img src="{get_camo_url("https://i.ytimg.com/vi/O5nskjZ_GoI/default.jpg")}"></a></div>""",
|
||||
f"""<p><a href="https://www.youtube.com/watch?v=O5nskjZ_GoI&list=PL8dPuuaLjXtNlUrzyH5r6jN9ulIgZBpdo">https://www.youtube.com/watch?v=O5nskjZ_GoI&list=PL8dPuuaLjXtNlUrzyH5r6jN9ulIgZBpdo</a></p>\n<div class="youtube-video message_inline_image"><a data-id="O5nskjZ_GoI" href="https://www.youtube.com/watch?v=O5nskjZ_GoI&list=PL8dPuuaLjXtNlUrzyH5r6jN9ulIgZBpdo"><img src="{get_camo_url("https://i.ytimg.com/vi/O5nskjZ_GoI/default.jpg")}"></a></div>""",
|
||||
)
|
||||
|
||||
msg = "http://www.youtube.com/watch_videos?video_ids=nOJgD4fcZhI,i96UO8-GFvw"
|
||||
|
|
Loading…
Reference in New Issue