diff --git a/static/js/lightbox.js b/static/js/lightbox.js index 99f5f106e8..e435d7b3fa 100644 --- a/static/js/lightbox.js +++ b/static/js/lightbox.js @@ -53,19 +53,28 @@ function display_image(payload, options) { $(".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); $("#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 = $("", { - src: "https://www.youtube.com/embed/" + payload.source, + src: source, frameborder: 0, allowfullscreen: true, }); $("#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. @@ -84,6 +93,7 @@ exports.open = function (image, options) { // if wrapped in the .youtube-video class, it will be length = 1, and therefore // cast to true. var is_youtube_video = !!$image.closest(".youtube-video").length; + var is_vimeo_video = !!$image.closest(".vimeo-video").length; var payload; // if the asset_map already contains the metadata required to display the @@ -94,20 +104,32 @@ exports.open = function (image, options) { } else { var $parent = $image.parent(); 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 = { user: message_store.get($message.attr("zid")).sender_full_name, title: $image.parent().attr("title"), - type: is_youtube_video ? "youtube-video" : "image", + type: $type, preview: $image.attr("src"), - source: is_youtube_video ? $parent.attr("data-id") : $image.attr("src"), + source: $source, }; asset_map[payload.preview] = payload; } - if (payload.type === "youtube-video") { - display_youtube_video(payload); + if (payload.type.match("-video")) { + display_video(payload); } else if (payload.type === "image") { display_image(payload, options); } diff --git a/zerver/lib/bugdown/__init__.py b/zerver/lib/bugdown/__init__.py index 1aff8e69e4..a0e2e83195 100644 --- a/zerver/lib/bugdown/__init__.py +++ b/zerver/lib/bugdown/__init__.py @@ -253,13 +253,31 @@ def add_embed(root: Element, link: Text, extracted_data: Dict[Text, Any]) -> Non a.set("target", "_blank") a.set("title", title) a.text = title - description = extracted_data.get('description') if description: description_elm = markdown.util.etree.SubElement(data_container, "div") description_elm.set("class", "message_embed_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") 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. last_closed = True head = [] - # TODO: What if response content is huge? Should we get headers first? try: content = requests.get(url, timeout=1).text except Exception: return None - # Extract the head and meta tags # All meta tags are self closing, have no children or are closed # automatically. @@ -529,6 +545,27 @@ class InlineInterestingLinkProcessor(markdown.treeprocessors.Treeprocessor): return "https://i.ytimg.com/vi/%s/default.jpg" % (yt_id,) 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, urls: List[Dict[Text, Text]], user_mentions: List[Dict[Text, Any]], @@ -841,8 +878,14 @@ class InlineInterestingLinkProcessor(markdown.treeprocessors.Treeprocessor): except NotFoundInCache: current_message.links_for_preview.add(url) continue + vimeo = self.vimeo_image(url) if extracted_data: - add_embed(root, url, 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) class Avatar(markdown.inlinepatterns.Pattern): diff --git a/zerver/tests/test_bugdown.py b/zerver/tests/test_bugdown.py index 7e93489f77..38a703d320 100644 --- a/zerver/tests/test_bugdown.py +++ b/zerver/tests/test_bugdown.py @@ -290,6 +290,17 @@ class BugdownTest(ZulipTestCase): self.assertEqual(converted, '
http://www.youtube.com/watch?v=hx1mjT73xYE
\n') + def test_inline_vimeo(self) -> None: + msg = 'Check out the debate: https://vimeo.com/246979354' + converted = bugdown_convert(msg) + + self.assertEqual(converted, 'Check out the debate: https://vimeo.com/246979354
') + + msg = 'https://vimeo.com/246979354' + converted = bugdown_convert(msg) + + self.assertEqual(converted, '') + @override_settings(INLINE_IMAGE_PREVIEW=True) def test_inline_image_preview(self) -> None: with_preview = '\n'