mirror of
https://github.com/zulip/zulip.git
synced 2025-11-09 16:37:23 +00:00
bugdown: Add preview for vimeo videos.
This also amends a commit from Brock Whittaker <brock@zulipchat.com> that merges two separate functions for YouTube videos and Vimeo videos into a generic video recall function. Fixes #7550.
This commit is contained in:
committed by
showell
parent
9fe284b442
commit
b0fb7aa6b2
@@ -53,19 +53,28 @@ function display_image(payload, options) {
|
|||||||
$(".image-actions .open, .image-actions .download").attr("href", payload.source);
|
$(".image-actions .open, .image-actions .download").attr("href", payload.source);
|
||||||
}
|
}
|
||||||
|
|
||||||
function display_youtube_video(payload) {
|
function display_video(payload) {
|
||||||
render_lightbox_list_images(payload.preview);
|
render_lightbox_list_images(payload.preview);
|
||||||
|
|
||||||
$("#lightbox_overlay .image-preview, .image-description, .download, .lightbox-canvas-trigger").hide();
|
$("#lightbox_overlay .image-preview, .image-description, .download, .lightbox-canvas-trigger").hide();
|
||||||
|
|
||||||
|
var source;
|
||||||
|
if (payload.type === "youtube-video") {
|
||||||
|
source = "https://www.youtube.com/embed/" + payload.source;
|
||||||
|
} else if (payload.type === "vimeo-video") {
|
||||||
|
source = "https://player.vimeo.com/video/" + payload.source;
|
||||||
|
}
|
||||||
|
|
||||||
var iframe = $("<iframe></iframe>", {
|
var iframe = $("<iframe></iframe>", {
|
||||||
src: "https://www.youtube.com/embed/" + payload.source,
|
src: source,
|
||||||
frameborder: 0,
|
frameborder: 0,
|
||||||
allowfullscreen: true,
|
allowfullscreen: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#lightbox_overlay .player-container").html(iframe).show();
|
$("#lightbox_overlay .player-container").html(iframe).show();
|
||||||
$(".image-actions .open").attr("href", "https://youtu.be/" + payload.source);
|
|
||||||
|
var url = (payload.type === "youtube-video" ? "https://youtu.be/" : "https://vimeo.com/") + payload.source;
|
||||||
|
$(".image-actions .open").attr("href", url);
|
||||||
}
|
}
|
||||||
|
|
||||||
// the image param is optional, but required on the first preview of an image.
|
// the image param is optional, but required on the first preview of an image.
|
||||||
@@ -84,6 +93,7 @@ exports.open = function (image, options) {
|
|||||||
// if wrapped in the .youtube-video class, it will be length = 1, and therefore
|
// if wrapped in the .youtube-video class, it will be length = 1, and therefore
|
||||||
// cast to true.
|
// cast to true.
|
||||||
var is_youtube_video = !!$image.closest(".youtube-video").length;
|
var is_youtube_video = !!$image.closest(".youtube-video").length;
|
||||||
|
var is_vimeo_video = !!$image.closest(".vimeo-video").length;
|
||||||
|
|
||||||
var payload;
|
var payload;
|
||||||
// if the asset_map already contains the metadata required to display the
|
// if the asset_map already contains the metadata required to display the
|
||||||
@@ -94,20 +104,32 @@ exports.open = function (image, options) {
|
|||||||
} else {
|
} else {
|
||||||
var $parent = $image.parent();
|
var $parent = $image.parent();
|
||||||
var $message = $parent.closest("[zid]");
|
var $message = $parent.closest("[zid]");
|
||||||
|
var $type;
|
||||||
|
var $source;
|
||||||
|
if (is_youtube_video) {
|
||||||
|
$type = "youtube-video";
|
||||||
|
$source = $parent.attr("data-id");
|
||||||
|
} else if (is_vimeo_video) {
|
||||||
|
$type = "vimeo-video";
|
||||||
|
$source = $parent.attr("data-id");
|
||||||
|
} else {
|
||||||
|
$type = "image";
|
||||||
|
$source = $image.attr("src");
|
||||||
|
}
|
||||||
|
|
||||||
payload = {
|
payload = {
|
||||||
user: message_store.get($message.attr("zid")).sender_full_name,
|
user: message_store.get($message.attr("zid")).sender_full_name,
|
||||||
title: $image.parent().attr("title"),
|
title: $image.parent().attr("title"),
|
||||||
type: is_youtube_video ? "youtube-video" : "image",
|
type: $type,
|
||||||
preview: $image.attr("src"),
|
preview: $image.attr("src"),
|
||||||
source: is_youtube_video ? $parent.attr("data-id") : $image.attr("src"),
|
source: $source,
|
||||||
};
|
};
|
||||||
|
|
||||||
asset_map[payload.preview] = payload;
|
asset_map[payload.preview] = payload;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (payload.type === "youtube-video") {
|
if (payload.type.match("-video")) {
|
||||||
display_youtube_video(payload);
|
display_video(payload);
|
||||||
} else if (payload.type === "image") {
|
} else if (payload.type === "image") {
|
||||||
display_image(payload, options);
|
display_image(payload, options);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -253,13 +253,31 @@ def add_embed(root: Element, link: Text, extracted_data: Dict[Text, Any]) -> Non
|
|||||||
a.set("target", "_blank")
|
a.set("target", "_blank")
|
||||||
a.set("title", title)
|
a.set("title", title)
|
||||||
a.text = title
|
a.text = title
|
||||||
|
|
||||||
description = extracted_data.get('description')
|
description = extracted_data.get('description')
|
||||||
if description:
|
if description:
|
||||||
description_elm = markdown.util.etree.SubElement(data_container, "div")
|
description_elm = markdown.util.etree.SubElement(data_container, "div")
|
||||||
description_elm.set("class", "message_embed_description")
|
description_elm.set("class", "message_embed_description")
|
||||||
description_elm.text = description
|
description_elm.text = description
|
||||||
|
|
||||||
|
def add_vimeo_preview(root: Element, link: Text, extracted_data: Dict[Text, Any], vm_id: Text) -> None:
|
||||||
|
container = markdown.util.etree.SubElement(root, "div")
|
||||||
|
container.set("class", "vimeo-video message_inline_image")
|
||||||
|
|
||||||
|
img_link = extracted_data.get('image')
|
||||||
|
if img_link:
|
||||||
|
parsed_img_link = urllib.parse.urlparse(img_link)
|
||||||
|
# Append domain where relative img_link url is given
|
||||||
|
if not parsed_img_link.netloc:
|
||||||
|
parsed_url = urllib.parse.urlparse(link)
|
||||||
|
domain = '{url.scheme}://{url.netloc}/'.format(url=parsed_url)
|
||||||
|
img_link = urllib.parse.urljoin(domain, img_link)
|
||||||
|
anchor = markdown.util.etree.SubElement(container, "a")
|
||||||
|
anchor.set("href", link)
|
||||||
|
anchor.set("target", "_blank")
|
||||||
|
anchor.set("data-id", vm_id)
|
||||||
|
anchor.set("title", link)
|
||||||
|
img = markdown.util.etree.SubElement(anchor, "img")
|
||||||
|
img.set("src", img_link)
|
||||||
|
|
||||||
@cache_with_key(lambda tweet_id: tweet_id, cache_name="database", with_statsd_key="tweet_data")
|
@cache_with_key(lambda tweet_id: tweet_id, cache_name="database", with_statsd_key="tweet_data")
|
||||||
def fetch_tweet_data(tweet_id: Text) -> Optional[Dict[Text, Any]]:
|
def fetch_tweet_data(tweet_id: Text) -> Optional[Dict[Text, Any]]:
|
||||||
@@ -327,13 +345,11 @@ def fetch_open_graph_image(url: Text) -> Optional[Dict[str, Any]]:
|
|||||||
# a closing tag if it has not been closed yet.
|
# a closing tag if it has not been closed yet.
|
||||||
last_closed = True
|
last_closed = True
|
||||||
head = []
|
head = []
|
||||||
|
|
||||||
# TODO: What if response content is huge? Should we get headers first?
|
# TODO: What if response content is huge? Should we get headers first?
|
||||||
try:
|
try:
|
||||||
content = requests.get(url, timeout=1).text
|
content = requests.get(url, timeout=1).text
|
||||||
except Exception:
|
except Exception:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# Extract the head and meta tags
|
# Extract the head and meta tags
|
||||||
# All meta tags are self closing, have no children or are closed
|
# All meta tags are self closing, have no children or are closed
|
||||||
# automatically.
|
# automatically.
|
||||||
@@ -529,6 +545,27 @@ class InlineInterestingLinkProcessor(markdown.treeprocessors.Treeprocessor):
|
|||||||
return "https://i.ytimg.com/vi/%s/default.jpg" % (yt_id,)
|
return "https://i.ytimg.com/vi/%s/default.jpg" % (yt_id,)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def vimeo_id(self, url: Text) -> Optional[Text]:
|
||||||
|
if not image_preview_enabled_for_realm():
|
||||||
|
return None
|
||||||
|
#(http|https)?:\/\/(www\.)?vimeo.com\/(?:channels\/(?:\w+\/)?|groups\/([^\/]*)\/videos\/|)(\d+)(?:|\/\?)
|
||||||
|
# If it matches, match.group('id') is the video id.
|
||||||
|
|
||||||
|
vimeo_re = r'^((http|https)?:\/\/(www\.)?vimeo.com\/' + \
|
||||||
|
r'(?:channels\/(?:\w+\/)?|groups\/' + \
|
||||||
|
r'([^\/]*)\/videos\/|)(\d+)(?:|\/\?))$'
|
||||||
|
match = re.match(vimeo_re, url)
|
||||||
|
if match is None:
|
||||||
|
return None
|
||||||
|
return match.group(5)
|
||||||
|
|
||||||
|
def vimeo_image(self, url: Text) -> Optional[Text]:
|
||||||
|
vm_id = self.vimeo_id(url)
|
||||||
|
|
||||||
|
if vm_id is not None:
|
||||||
|
return "http://i.vimeocdn.com/video/%s.jpg" % (vm_id,)
|
||||||
|
return None
|
||||||
|
|
||||||
def twitter_text(self, text: Text,
|
def twitter_text(self, text: Text,
|
||||||
urls: List[Dict[Text, Text]],
|
urls: List[Dict[Text, Text]],
|
||||||
user_mentions: List[Dict[Text, Any]],
|
user_mentions: List[Dict[Text, Any]],
|
||||||
@@ -841,7 +878,13 @@ class InlineInterestingLinkProcessor(markdown.treeprocessors.Treeprocessor):
|
|||||||
except NotFoundInCache:
|
except NotFoundInCache:
|
||||||
current_message.links_for_preview.add(url)
|
current_message.links_for_preview.add(url)
|
||||||
continue
|
continue
|
||||||
|
vimeo = self.vimeo_image(url)
|
||||||
if extracted_data:
|
if extracted_data:
|
||||||
|
if vimeo is not None:
|
||||||
|
vm_id = self.vimeo_id(url)
|
||||||
|
add_vimeo_preview(root, url, extracted_data, vm_id)
|
||||||
|
continue
|
||||||
|
else:
|
||||||
add_embed(root, url, extracted_data)
|
add_embed(root, url, extracted_data)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -290,6 +290,17 @@ class BugdownTest(ZulipTestCase):
|
|||||||
|
|
||||||
self.assertEqual(converted, '<p><a href="http://www.youtube.com/watch?v=hx1mjT73xYE" target="_blank" title="http://www.youtube.com/watch?v=hx1mjT73xYE">http://www.youtube.com/watch?v=hx1mjT73xYE</a></p>\n<div class="youtube-video message_inline_image"><a data-id="hx1mjT73xYE" href="http://www.youtube.com/watch?v=hx1mjT73xYE" target="_blank" title="http://www.youtube.com/watch?v=hx1mjT73xYE"><img src="https://i.ytimg.com/vi/hx1mjT73xYE/default.jpg"></a></div>')
|
self.assertEqual(converted, '<p><a href="http://www.youtube.com/watch?v=hx1mjT73xYE" target="_blank" title="http://www.youtube.com/watch?v=hx1mjT73xYE">http://www.youtube.com/watch?v=hx1mjT73xYE</a></p>\n<div class="youtube-video message_inline_image"><a data-id="hx1mjT73xYE" href="http://www.youtube.com/watch?v=hx1mjT73xYE" target="_blank" title="http://www.youtube.com/watch?v=hx1mjT73xYE"><img src="https://i.ytimg.com/vi/hx1mjT73xYE/default.jpg"></a></div>')
|
||||||
|
|
||||||
|
def test_inline_vimeo(self) -> None:
|
||||||
|
msg = 'Check out the debate: https://vimeo.com/246979354'
|
||||||
|
converted = bugdown_convert(msg)
|
||||||
|
|
||||||
|
self.assertEqual(converted, '<p>Check out the debate: <a href="https://vimeo.com/246979354" target="_blank" title="https://vimeo.com/246979354">https://vimeo.com/246979354</a></p>')
|
||||||
|
|
||||||
|
msg = 'https://vimeo.com/246979354'
|
||||||
|
converted = bugdown_convert(msg)
|
||||||
|
|
||||||
|
self.assertEqual(converted, '<p><a href="https://vimeo.com/246979354" target="_blank" title="https://vimeo.com/246979354">https://vimeo.com/246979354</a></p>')
|
||||||
|
|
||||||
@override_settings(INLINE_IMAGE_PREVIEW=True)
|
@override_settings(INLINE_IMAGE_PREVIEW=True)
|
||||||
def test_inline_image_preview(self) -> None:
|
def test_inline_image_preview(self) -> None:
|
||||||
with_preview = '<p>Test: <a href="http://cdn.wallpapersafari.com/13/6/16eVjx.jpeg" target="_blank" title="http://cdn.wallpapersafari.com/13/6/16eVjx.jpeg">http://cdn.wallpapersafari.com/13/6/16eVjx.jpeg</a></p>\n<div class="message_inline_image"><a href="http://cdn.wallpapersafari.com/13/6/16eVjx.jpeg" target="_blank" title="http://cdn.wallpapersafari.com/13/6/16eVjx.jpeg"><img src="https://external-content.zulipcdn.net/389b5d7148a0cbc7475ed564e1b03ceb476bdacb/687474703a2f2f63646e2e77616c6c70617065727361666172692e636f6d2f31332f362f313665566a782e6a706567"></a></div>'
|
with_preview = '<p>Test: <a href="http://cdn.wallpapersafari.com/13/6/16eVjx.jpeg" target="_blank" title="http://cdn.wallpapersafari.com/13/6/16eVjx.jpeg">http://cdn.wallpapersafari.com/13/6/16eVjx.jpeg</a></p>\n<div class="message_inline_image"><a href="http://cdn.wallpapersafari.com/13/6/16eVjx.jpeg" target="_blank" title="http://cdn.wallpapersafari.com/13/6/16eVjx.jpeg"><img src="https://external-content.zulipcdn.net/389b5d7148a0cbc7475ed564e1b03ceb476bdacb/687474703a2f2f63646e2e77616c6c70617065727361666172692e636f6d2f31332f362f313665566a782e6a706567"></a></div>'
|
||||||
|
|||||||
Reference in New Issue
Block a user