#!/usr/bin/env python # -*- coding: utf-8 -*- # Taken from sphinx-contrib # https://bitbucket.org/birkenfeld/sphinx-contrib/src/a3d904f8ab24/youtube # If not otherwise noted, the extensions in this package are licensed # under the following license. # # Copyright (c) 2009 by the contributors (see AUTHORS file). # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are # met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. from __future__ import division import re from docutils import nodes from docutils.parsers.rst import directives from sphinx.util.compat import Directive CONTROL_HEIGHT = 30 def get_size(d, key): if key not in d: return None m = re.match("(\d+)(|%|px)$", d[key]) if not m: raise ValueError("invalid size %r" % d[key]) return int(m.group(1)), m.group(2) or "px" def css(d): return "; ".join(sorted("%s: %s" % kv for kv in d.iteritems())) class youtube(nodes.General, nodes.Element): pass def visit_youtube_node(self, node): aspect = node["aspect"] width = node["width"] height = node["height"] if aspect is None: aspect = 16, 9 if (height is None) and (width is not None) and (width[1] == "%"): style = { "padding-top": "%dpx" % CONTROL_HEIGHT, "padding-bottom": "%f%%" % (width[0] * aspect[1] / aspect[0]), "width": "%d%s" % width, "position": "relative", } self.body.append(self.starttag(node, "div", style=css(style))) style = { "position": "absolute", "top": "0", "left": "0", "width": "100%", "height": "100%", "border": "0", } attrs = { "src": "http://www.youtube.com/embed/%s" % node["id"], "style": css(style), } self.body.append(self.starttag(node, "iframe", **attrs)) self.body.append("") else: if width is None: if height is None: width = 560, "px" else: width = height[0] * aspect[0] / aspect[1], "px" if height is None: height = width[0] * aspect[1] / aspect[0], "px" style = { "width": "%d%s" % width, "height": "%d%s" % (height[0] + CONTROL_HEIGHT, height[1]), "border": "0", } attrs = { "src": "http://www.youtube.com/embed/%s" % node["id"], "style": css(style), } self.body.append(self.starttag(node, "iframe", **attrs)) self.body.append("") def depart_youtube_node(self, node): pass class YouTube(Directive): has_content = True required_arguments = 1 optional_arguments = 0 final_argument_whitespace = False option_spec = { "width": directives.unchanged, "height": directives.unchanged, "aspect": directives.unchanged, } def run(self): if "aspect" in self.options: aspect = self.options.get("aspect") m = re.match("(\d+):(\d+)", aspect) if m is None: raise ValueError("invalid aspect ratio %r" % aspect) aspect = tuple(int(x) for x in m.groups()) else: aspect = None width = get_size(self.options, "width") height = get_size(self.options, "height") return [youtube(id=self.arguments[0], aspect=aspect, width=width, height=height)] def setup(app): app.add_node(youtube, html=(visit_youtube_node, depart_youtube_node)) app.add_directive("youtube", YouTube)