pax_global_header00006660000000000000000000000064145032731410014512gustar00rootroot0000000000000052 comment=fff4ab29a8606676fe5d64850a2f50114d8be3eb sphinxcontrib-youtube-1.4.1/000077500000000000000000000000001450327314100161015ustar00rootroot00000000000000sphinxcontrib-youtube-1.4.1/.github/000077500000000000000000000000001450327314100174415ustar00rootroot00000000000000sphinxcontrib-youtube-1.4.1/.github/workflows/000077500000000000000000000000001450327314100214765ustar00rootroot00000000000000sphinxcontrib-youtube-1.4.1/.github/workflows/ci.yml000066400000000000000000000020321450327314100226110ustar00rootroot00000000000000name: CI on: push: branches: [master] tags: - "v[0-9]+.[0-9]+.[0-9]+*" pull_request: jobs: lint: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: actions/setup-python@v4 with: python-version: "3.11" - uses: pre-commit/action@v3.0.0 build: needs: [lint] strategy: fail-fast: true matrix: os: [ubuntu-latest] python-version: ["3.8", "3.9", "3.10", "3.11"] include: - os: macos-latest # macos test python-version: "3.11" - os: windows-latest # windows test python-version: "3.11" runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - name: Install dependencies run: pip install .[test] - name: test with pytest run: pytest --color=yes --cov --cov-report=xml tests sphinxcontrib-youtube-1.4.1/.gitignore000066400000000000000000000035011450327314100200700ustar00rootroot00000000000000# Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class # C extensions *.so # Distribution / packaging .Python build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ wheels/ pip-wheel-metadata/ share/python-wheels/ *.egg-info/ .installed.cfg *.egg MANIFEST # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .nox/ .coverage .coverage.* .cache nosetests.xml coverage.xml *.cover *.py,cover .hypothesis/ .pytest_cache/ .ruff_cache/ # Translations *.mo *.pot # Django stuff: *.log local_settings.py db.sqlite3 db.sqlite3-journal # Flask stuff: instance/ .webassets-cache # Scrapy stuff: .scrapy # Sphinx documentation docs/_build/ # PyBuilder target/ # Jupyter Notebook .ipynb_checkpoints # IPython profile_default/ ipython_config.py # pyenv .python-version # pipenv # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. # However, in case of collaboration, if having platform-specific dependencies or dependencies # having no cross-platform support, pipenv may install dependencies that don't work, or not # install all needed dependencies. #Pipfile.lock # PEP 582; used by e.g. github.com/David-OConnor/pyflow __pypackages__/ # Celery stuff celerybeat-schedule celerybeat.pid # SageMath parsed files *.sage.py # Environments .env .venv env/ venv/ ENV/ env.bak/ venv.bak/ # Spyder project settings .spyderproject .spyproject # Visual Studio Code settings .vscode # Rope project settings .ropeproject # mkdocs documentation /site # mypy .mypy_cache/ .dmypy.json dmypy.json # Pyre type checker .pyre/ .idea sphinxcontrib-youtube-1.4.1/.pre-commit-config.yaml000066400000000000000000000007371450327314100223710ustar00rootroot00000000000000default_install_hook_types: [pre-commit] repos: - repo: https://github.com/psf/black rev: 22.6.0 hooks: - id: black - repo: "https://github.com/pre-commit/mirrors-prettier" rev: v2.7.1 hooks: - id: prettier exclude: tests\/test_.+\. - repo: https://github.com/asottile/pyupgrade rev: v3.3.1 hooks: - id: pyupgrade - repo: https://github.com/charliermarsh/ruff-pre-commit rev: v0.0.259 hooks: - id: ruff sphinxcontrib-youtube-1.4.1/.readthedocs.yaml000066400000000000000000000010351450327314100213270ustar00rootroot00000000000000# Read the Docs configuration file # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details # Required version: 2 build: os: ubuntu-20.04 tools: python: "3.9" # Build documentation in the docs/ directory with Sphinx sphinx: configuration: docs/conf.py # Optionally build your docs in additional formats such as PDF formats: - pdf # Optionally set the version of Python and requirements required to build your docs python: install: - method: pip path: . extra_requirements: - doc sphinxcontrib-youtube-1.4.1/CONTRIBUTE.rst000066400000000000000000000060561450327314100203200ustar00rootroot00000000000000Contribute ========== Thank you for your help improving **sphinxcontrib-youtube**! **sphinxcontrib-youtube** uses `nox `__ to automate several development-related tasks. Currently, the project uses four automation processes (called sessions) in ``noxfile.py``: - ``test``: to run the test with pytest; - ``docs``: to build the documentation in the ``build`` folder; - ``lint``: to run the pre-commits in an isolated environment Every nox session is run in its own virtual environment, and the dependencies are installed automatically. To run a specific nox automation process, use the following command: .. code-block:: console nox -s For example: ``nox -s test`` or ``nox -s docs``. Workflow for contributing changes ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ We follow a typical GitHub workflow of: - Create a personal fork of this repo - Create a branch - Open a pull request - Fix findings of various linters and checks - Work through code review See the following sections for more details. Clone the repository ^^^^^^^^^^^^^^^^^^^^ First off, you'll need your own copy of the **sphinxcontrib-youtube** codebase. Fork the repository so you have your own copy on GitHub. See the `GitHub forking guide for more information `__. Then, clone the repository locally so that you have a local copy to work on: .. code-block:: console git clone https://github.com//youtube cd youtube Then install the development version of the extension: .. code-block:: console pip install -e .[dev] This will install the **sphinxcontrib-youtube** library, together with two additional tools: - `pre-commit `__ for automatically enforcing code standards and quality checks before commits. - `nox `__, for automating common development tasks. Lastly, activate the pre-commit hooks by running: .. code-block:: console pre-commit install This will install the necessary dependencies to run pre-commit every time you make a commit with Git. Contribute to the codebase ^^^^^^^^^^^^^^^^^^^^^^^^^^ Any larger updates to the codebase should include tests and documentation. The tests are located in the ``tests`` folder, and the documentation is located in the ``docs`` folder. To run the tests locally, use the following command: .. code-block:: console nox -s test See :ref:`below ` for more information on how to update the documentation. .. _contributing-docs: Contribute to the docs ^^^^^^^^^^^^^^^^^^^^^^ `__ and deployed to `Read the Docs The documentation is built using `Sphinx `__. To build the documentation locally, use the following command: .. code-block:: console nox -s docs For each pull request, the documentation is built and deployed to make it easier to review the changes in the PR. To access the docs build from a PR, click on the "Read the Docs" preview in the CI/CD jobs.sphinxcontrib-youtube-1.4.1/LICENCE000066400000000000000000000030201450327314100170610ustar00rootroot00000000000000Copyright (c) 2011-2021, Dr David Ham, Chris Pickel and others 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. * Neither the name of sphinx-contrib/youtube nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 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. sphinxcontrib-youtube-1.4.1/README.rst000066400000000000000000000020511450327314100175660ustar00rootroot00000000000000sphinxcontrib.youtube ===================== .. image:: https://img.shields.io/badge/License-BSD_3--Clause-orange.svg :alt: license :target: LICENCE .. image:: https://badge.fury.io/py/sphinxcontrib-youtube.svg :target: https://badge.fury.io/py/sphinxcontrib-youtube :alt: PyPi version Overview -------- This module provides support for including YouTube, Vimeo and Peertube videos in Sphinx :code:`rst` documents. This module defines directives, :code:`youtube`, :code:`vimeo` and :code: `peertube` which insert videos from the respective platforms. They take a single, required argument, which is the video ID: .. code-block:: rst .. youtube:: dQw4w9WgXcQ .. code-block:: rst .. vimeo:: 148751763 .. code-block:: rst .. peertube:: 327a21b3-374e-4373-8b2c-494c9f5e1f19 Custom Server for peertube instances: .. code-block:: rst .. peertube:: 327a21b3-374e-4373-8b2c-494c9f5e1f19 :instance: peertube.tv For full usage information, please see the `web documentation `__. sphinxcontrib-youtube-1.4.1/docs/000077500000000000000000000000001450327314100170315ustar00rootroot00000000000000sphinxcontrib-youtube-1.4.1/docs/conf.py000066400000000000000000000043621450327314100203350ustar00rootroot00000000000000"""Configuration file for the Sphinx documentation builder. This file only contains a selection of the most common options. For a full list see the documentation: https://www.sphinx-doc.org/en/master/usage/configuration.html """ # -- Path setup ---------------------------------------------------------------- from datetime import datetime from sphinxcontrib.youtube import __version__ # -- Project information ------------------------------------------------------- project = "sphinxcontrib-youtube" author = "David A. Ham, Chris Pickel and others" copyright = f"2011-{datetime.now().year}, {author}" release = __version__ # -- General configuration ----------------------------------------------------- extensions = ["sphinx_copybutton", "sphinxcontrib.youtube", "sphinx_design"] templates_path = ["_templates"] exclude_patterns = ["**.ipynb_checkpoints"] # -- Options for HTML output --------------------------------------------------- html_theme = "pydata_sphinx_theme" html_theme_options = { "logo": { "text": project, }, "use_edit_page_button": True, "icon_links": [ { "name": "GitHub", "url": "https://github.com/sphinx-contrib/youtube", "icon": "fa-brands fa-github", }, { "name": "Pypi", "url": "https://pypi.org/project/sphinxcontrib-youtube/", "icon": "fa-brands fa-python", }, ], } html_context = { "github_user": "sphinx-contrib", "github_repo": "youtube", "github_version": "main", "doc_path": "docs", } # -- Option for Latex output --------------------------------------------------- # create a custom sphinx output for the youtube, vimeo and peertube video youtube_cmd = ( r"\newcommand{\sphinxcontribyoutube}[3]" r"{\begin{figure}\sphinxincludegraphics{{#2}.jpg}\caption{\url{#1#2#3}}\end{figure}}" "\n" ) vimeo_cmd = ( r"\newcommand{\sphinxcontribvimeo}[3]" r"{\begin{figure}\sphinxincludegraphics{{#2}.jpg}\caption{\url{#1#2#3}}\end{figure}}" "\n" ) peertube_cmd = ( r"\newcommand{\sphinxcontribpeertube}[3]" r"{\begin{figure}\sphinxincludegraphics{{#2}.jpg}\caption{\url{#1#2#3}}\end{figure}}" "\n" ) latex_elements = {"preamble": youtube_cmd + vimeo_cmd + peertube_cmd} sphinxcontrib-youtube-1.4.1/docs/contribute.rst000066400000000000000000000001101450327314100217310ustar00rootroot00000000000000Contribute ========== .. include:: ../CONTRIBUTE.rst :start-line: 3sphinxcontrib-youtube-1.4.1/docs/index.rst000066400000000000000000000014451450327314100206760ustar00rootroot00000000000000:html_theme.sidebar_secondary.remove: sphinxcontrib-youtube ===================== .. toctree:: :hidden: usage contribute Overview -------- This module provides support for including YouTube, Vimeo and PeerTube videos in Sphinx rst documents. This module defines directives, youtube, vimeo and peertube which insert videos from the respective platforms. They take a single, required argument, which is the video ID: .. code-block:: rst .. youtube:: dQw4w9WgXcQ .. youtube:: dQw4w9WgXcQ Documentation contents ---------------------- .. grid:: 1 2 2 2 .. grid-item:: .. card:: Getting started :link: usage.html Usage and installation .. grid-item:: .. card:: Contribute :link: contribute.html Help us improve the library. sphinxcontrib-youtube-1.4.1/docs/usage.rst000066400000000000000000000123701450327314100206720ustar00rootroot00000000000000Getting started =============== Demo ---- This module provides support for including YouTube, Vimeo and PeerTube videos in Sphinx :code:`rst` documents. This module defines directives, :code:`youtube`, :code:`vimeo` :code:`peertube` which insert videos from the respective platforms. They take a single, required argument, wich is the video ID: .. code-block:: rst .. youtube:: dQw4w9WgXcQ .. youtube:: dQw4w9WgXcQ :align: center :aspect: 16:9 .. code-block:: rst .. vimeo:: 148751763 .. vimeo:: 486557682 :align: center :aspect: 16:9 .. code-block:: rst .. peertube:: 327a21b3-374e-4373-8b2c-494c9f5e1f19 .. peertube:: 327a21b3-374e-4373-8b2c-494c9f5e1f19 :align: center :aspect: 16:9 To link to a different instance than the default :code:`peertube.tv` of the peertube platform, use the :code:`instance` keyword: .. code-block:: rst .. peertube:: 9732a818-9fed-4bb2-8469-9502a695cb4d :instance: tube.kockatoo.org .. peertube:: 9732a818-9fed-4bb2-8469-9502a695cb4d :align: center :aspect: 16:9 :instance: tube.kockatoo.org Usage ----- The referenced video will be embedded into HTML and LaTeX outputs, the behaviour will be different as LaTeX cannot display videos. Installation ^^^^^^^^^^^^ The package is avalaible on `pipy `__ and can be installed with pip: .. code-block:: console pip install sphinxcontrib-youtube Configuration ^^^^^^^^^^^^^ Add `"sphinxcontrib.youtube"` to the `extensions` list in :file:`conf.py`. For example: .. code-block:: python3 extensions = [ 'sphinx.ext.intersphinx', 'sphinxcontrib.youtube' ] HTML ^^^^ By default, the embedded video will be sized for 720p content. To control this, the parameters :code:`aspect`, :code:`width`, and :code:`height` may optionally be provided: .. code-block:: rst .. youtube:: dQw4w9WgXcQ :width: 640 :height: 480 .. code-block:: rst .. youtube:: dQw4w9WgXcQ :aspect: 4:3 .. code-block:: rst .. youtube:: dQw4w9WgXcQ :width: 100% .. code-block:: rst .. youtube:: dQw4w9WgXcQ :height: 200px To set the alignment of the embedded video's iframe in the HTML output, an optional :code:`align` parameter can be specified, similar to the rst :code:`image` directive: .. code-block:: rst .. youtube:: dQw4w9WgXcQ :align: center For YouTube "privacy mode", use the directive option :privacy_mode: (and for vimeo, :url_parameters: ?dnt=1): .. code-block:: rst .. youtube:: dQw4w9WgXcQ :privacy_mode: To start the video at a specific time the parameter "url_parameters" may be used (quotes required for Vimeo videos): .. code-block:: rst .. youtube:: dQw4w9WgXcQ :url_parameters: ?start=43 .. code-block:: rst .. vimeo:: 486557682 :url_parameters: "#t=0m43s" .. code-block:: rst .. peertube:: 327a21b3-374e-4373-8b2c-494c9f5e1f19 :url_parameters: "?start=0s" When generating the EPUB output, the videos will not be embedded. Instead, a link to the video will be added. LaTeX ^^^^^ In LaTeX output, the following code will be emitted for the videos: .. code-block:: latex \sphinxcontribyoutube{https://youtu.be/}{dQw4w9WgXcQ}{?start=43} .. code-block:: latex \sphinxcontribvimeo{https://player.vimeo.com/video/}{148751763}{"#t=0m43s"} .. code-block:: latex \sphinxcontribpeertube{https://peertube.tv/w/}{327a21b3-374e-4373-8b2c-494c9f5e1f19}{?start=43} The user may customise the rendering of the URL by defining this command in the preamble. The captions will be downloaded to the latex folder and can thus be used as images in the :code:`.pdf` document. We are using the `Vumbnail `__ (vimeo) and `get-youtube-thumbnail `__ (youtube) web services to retrieve them. Here is an example of custom command for both the vimeo and the yoututbe output. This needs to be added in the :code:`conf.py` file: .. code-block:: python # conf.py # ... # -- Option for LaTeX output --------------------------------------------------- # create a custom sphinx output for the youtube and vimeo video youtube_cmd = r"\newcommand{\sphinxcontribyoutube}[3]{\begin{figure}\sphinxincludegraphics{{#2}.jpg}\caption{\url{#1#2#3}}\end{figure}}" + "\n" vimeo_cmd = r"\newcommand{\sphinxcontribvimeo}[3]{\begin{figure}\sphinxincludegraphics{{#2}.jpg}\caption{\url{#1#2#3}}\end{figure}}" + "\n" latex_elements = {"preamble": youtube_cmd + vimeo_cmd} This example will show the video as a figure using the thumbnail as image and the url as caption (clickable link). This is the one we use for this very documentation. remember that the argument of your command are the following: - :code:`#1`: the platform url - :code:`#2`: the video ID (it's also the name of the image: :code:`#2.jpg` - :code:`#3`: the options of the url If no custom command is set in :code:`conf.py`, then the default definition is used: .. code-block:: latex \newcommand{\sphinxcontribyoutube}[3]{\begin{quote}\begin{center}\fbox{\url{#1#2#3}}\end{center}\end{quote}} This prints a simple link to the video, enclosed in a box. LaTeX support for Vimeo is similar, except that the macro is named :code:`\sphinxcontribvimeo`. .. toctree:: :maxdepth: 2 :caption: Contents: sphinxcontrib-youtube-1.4.1/noxfile.py000066400000000000000000000016441450327314100201240ustar00rootroot00000000000000"""All the process that can be run using nox. The nox run are build in isolated environment that will be stored in .nox. to force the venv update, remove the .nox/xxx folder. """ import nox @nox.session(reuse_venv=True) def lint(session): """Apply the pre-commits.""" session.install("pre-commit") session.run("pre-commit", "run", "--a", *session.posargs) @nox.session(reuse_venv=True) def test(session): """Run all the test using the environment varialbe of the running machine.""" session.install(".[test]") test_files = session.posargs or ["tests"] session.run("pytest", "--color=yes", "--cov", "--cov-report=html", *test_files) @nox.session(reuse_venv=True) def docs(session): """Build the documentation.""" session.install(".[doc]") build = session.posargs[0] if len(session.posargs) else "html" session.run("sphinx-build", "-v", "-b", build, "docs", f"docs/_build/{build}") sphinxcontrib-youtube-1.4.1/pyproject.toml000066400000000000000000000031451450327314100210200ustar00rootroot00000000000000[build-system] requires = ["flit_core >=3.5,<4"] build-backend = "flit_core.buildapi" [tool.flit.module] name = "sphinxcontrib.youtube" [project] name = "sphinxcontrib-youtube" dynamic = ["version", "description"] authors = [{name = "Chris Pickel", email = "sfiera@gmail.com"}] maintainers = [{name = "David A. Ham", email = "david.ham@imperial.ac.uk"}] license = {file = "LICENCE"} classifiers = [ "Development Status :: 5 - Production/Stable", "Environment :: Console", "Environment :: Web Environment", "Framework :: Sphinx :: Extension", "Intended Audience :: Developers", "License :: OSI Approved :: BSD License", "Operating System :: OS Independent", "Programming Language :: Python", "Topic :: Documentation", "Topic :: Utilities", ] dependencies = ["Sphinx>=6.1", "requests"] [project.readme] content-type = "text/plain" text = ''' This package contains the youtube Sphinx extension. The extension defines the directives, "youtube" and "vimeo", for embedding YouTube, Vimeo and Peertube videos, respectively.''' [project.urls] Homepage = "https://github.com/sphinx-contrib/youtube" [project.optional-dependencies] dev = ["nox"] doc = ["sphinx-copybutton", "pydata-sphinx-theme", "sphinx-design"] test = ["pytest", "beautifulsoup4", "pytest-regressions", "pytest-cov"] [tool.ruff] ignore-init-module-imports = true fix = true select = ["E", "F", "W", "I", "D", "RUF"] ignore = ["E501"] # line too long | Black take care of it [tool.ruff.flake8-quotes] docstring-quotes = "double" [tool.ruff.pydocstyle] convention = "google" [tool.coverage.run] source = ["sphinxcontrib.youtube"] sphinxcontrib-youtube-1.4.1/sphinxcontrib/000077500000000000000000000000001450327314100207735ustar00rootroot00000000000000sphinxcontrib-youtube-1.4.1/sphinxcontrib/youtube/000077500000000000000000000000001450327314100224675ustar00rootroot00000000000000sphinxcontrib-youtube-1.4.1/sphinxcontrib/youtube/__init__.py000066400000000000000000000014241450327314100246010ustar00rootroot00000000000000"""Sphinx "youtube" extension.""" from . import peertube, utils, vimeo, youtube __version__ = "1.4.1" def setup(app): """Setup Sphinx application.""" app.add_node(youtube.youtube, **youtube._NODE_VISITORS) app.add_directive("youtube", youtube.YouTube) app.add_node(vimeo.vimeo, **utils._NODE_VISITORS) app.add_directive("vimeo", vimeo.Vimeo) app.add_node(peertube.peertube, **peertube._NODE_VISITORS) app.add_directive("peertube", peertube.PeerTube) app.connect("builder-inited", utils.configure_image_download) app.connect("env-merge-info", utils.merge_download_images) app.connect("env-updated", utils.download_images) return { "version": __version__, "parallel_read_safe": True, "parallel_write_safe": True, } sphinxcontrib-youtube-1.4.1/sphinxcontrib/youtube/peertube.py000066400000000000000000000031011450327314100246470ustar00rootroot00000000000000"""Directive dedicated to the peertube platform.""" from docutils.parsers.rst import directives from . import utils # https://docs.joinpeertube.org/api/embed-player class peertube(utils.video): """Empty video node class.""" pass class PeerTube(utils.Video): """Custom version of the Video Directive.""" _node = peertube _thumbnail_url = "{}.jpg" _platform = "peertube" _platform_url = "peertube.tv" # optional options available option_spec = { "width": directives.unchanged, "height": directives.unchanged, "aspect": directives.unchanged, "align": directives.unchanged, "url_parameters": directives.unchanged, "instance": directives.unchanged, } def visit_peertube_node_html(self, node): """Custom html visit node.""" node["platform_url"] = f"https://{node['instance']}/videos/embed/" return utils.visit_video_node_html(self, node) def visit_video_node_epub(self, node): """Custom epub visit node.""" node["platform_url"] = f"https://{node['instance']}/w/" return utils.visit_video_node_epub(self, node) def visit_video_node_latex(self, node): """Custom epub visit node.""" node["platform_url"] = f"https://{node['instance']}/w/" return utils.visit_video_node_latex(self, node) _NODE_VISITORS = utils._NODE_VISITORS.copy() _NODE_VISITORS.update(html=(visit_peertube_node_html, utils.depart_video_node)) _NODE_VISITORS.update(epub=(visit_video_node_epub, utils.depart_video_node)) _NODE_VISITORS.update(latex=(visit_video_node_latex, utils.depart_video_node)) sphinxcontrib-youtube-1.4.1/sphinxcontrib/youtube/utils.py000066400000000000000000000214621450327314100242060ustar00rootroot00000000000000"""Skeleton of the video directive ready to be extended for specific providers.""" import re from pathlib import Path import requests from docutils import nodes from docutils.parsers.rst import Directive, directives from sphinx.util import logging from sphinx.util.console import brown from sphinx.util.display import status_iterator logger = logging.getLogger(__name__) CONTROL_HEIGHT = 30 THUMBNAIL_DIR = "_video_thumbnail" # -- helper methods ------------------------------------------------------------ def get_size(d, key): """Return a valid css size and unit.""" if key not in d: return None m = re.match(r"(\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 a valid css style string.""" return "; ".join(sorted("%s: %s" % kv for kv in d.items())) # -- node and directive definition --------------------------------------------- class video(nodes.General, nodes.Element): """Video node.""" pass class Video(Directive): """Abstract Video directive.""" _node = None "Subclasses should replace with node class." _thumbnail_url = "{}" "url to retrieve thumbnail images" _platform = "" "name of the platform" _platform_url = "" "url of the platform video provider" _platform_url_privacy = "" "the aleternative url to provide the video privately" has_content = True required_arguments = 1 optional_arguments = 0 final_argument_whitespace = False option_spec = { "width": directives.unchanged, "height": directives.unchanged, "aspect": directives.unchanged, "align": directives.unchanged, "url_parameters": directives.unchanged, "privacy_mode": directives.unchanged, } def run(self): """Run the directive.""" env = self.state.document.settings.env video_id = self.arguments[0] url = self._thumbnail_url.format(video_id) env.video_remote_images[url] = Path(THUMBNAIL_DIR, f"{video_id}.jpg") env.images.add_file("", env.video_remote_images[url]) if "aspect" in self.options: aspect = self.options.get("aspect") m = re.match(r"(\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 alignment = ["left", "center", "right"] if "align" in self.options: align = self.options.get("align") if align not in alignment: raise ValueError(f"invalid alignment choices are: {alignment}") else: align = None # custom platform url for peertube instance = self._platform_url if "instance" in self.options: instance = self.options.get("instance") return [ self._node( id=self.arguments[0], aspect=aspect, width=get_size(self.options, "width"), height=get_size(self.options, "height"), align=align, url_parameters=self.options.get("url_parameters", ""), privacy_mode=self.options.get("privacy_mode"), platform=self._platform, platform_url=self._platform_url, platform_url_privacy=self._platform_url_privacy, instance=instance, ) ] # -- builder specific methods -------------------------------------------------- def visit_video_node_html(self, node, platform_url_privacy=None): """Visit html video node.""" aspect = node["aspect"] width = node["width"] height = node["height"] url_parameters = node["url_parameters"] platform_url = node["platform_url"] platform_url_privacy = node["platform_url_privacy"] if node.get("privacy_mode") and platform_url_privacy: platform_url = platform_url_privacy if aspect is None: aspect = 16, 9 div_style = {} if (height is None) and (width is not None) and (width[1] == "%"): div_style = { "padding-top": "%dpx" % CONTROL_HEIGHT, "padding-bottom": "%f%%" % (width[0] * aspect[1] / aspect[0]), "width": "%d%s" % width, "position": "relative", } style = { "position": "absolute", "top": "0", "left": "0", "width": "100%", "height": "100%", "border": "0", } attrs = { "src": "{}{}{}".format(platform_url, node["id"], url_parameters), "style": css(style), } 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": "{}{}{}".format(platform_url, node["id"], url_parameters), "style": css(style), } if node["align"] is not None: div_style["text-align"] = node["align"] attrs["allowfullscreen"] = "true" div_attrs = { "CLASS": "video_wrapper", "style": css(div_style), } if node["align"] is not None: div_attrs["CLASS"] += " align-%s" % node["align"] self.body.append(self.starttag(node, "div", **div_attrs)) self.body.append(self.starttag(node, "iframe", **attrs)) self.body.append("") def visit_video_node_epub(self, node): """Visit epub video node.""" url_parameters = node["url_parameters"] link_url = "{}{}{}".format(node["platform_url"], node["id"], url_parameters) self.body.append(self.starttag(node, "a", CLASS="video_link_url", href=link_url)) self.body.append(link_url) self.body.append("") def visit_video_node_latex(self, node): """Visit latex video node.""" folder = r"\graphicspath{ {./%s/}{./} }" % THUMBNAIL_DIR if folder not in self.elements["preamble"]: self.elements["preamble"] += folder + "\n" macro = f"\\sphinxcontrib{node['platform']}" if macro not in self.elements["preamble"]: cmd = ( r"\newcommand{%s}[3]{\begin{quote}\begin{center}\fbox{\url{#1#2#3}}\end{center}\end{quote}}" % macro ) self.elements["preamble"] += cmd + "\n" self.body.append( "{}{{{}}}{{{}}}{{{}}}\n".format( macro, node["platform_url"], node["id"], node["url_parameters"] ) ) def visit_video_node_unsupported(self, node): """Visit unsuported video node.""" logger.warning(f"{node['platform']}: unsupported output format (node skipped)") raise nodes.SkipNode def depart_video_node(self, node): """Depart any video node.""" pass _NODE_VISITORS = { "html": (visit_video_node_html, depart_video_node), "epub": (visit_video_node_epub, depart_video_node), "latex": (visit_video_node_latex, depart_video_node), "man": (visit_video_node_unsupported, depart_video_node), "texinfo": (visit_video_node_unsupported, depart_video_node), "text": (visit_video_node_unsupported, depart_video_node), } # -- manage dowloaded images --------------------------------------------------- def merge_download_images(app, env, docnames, other): """Merge remote images, when using parallel processing.""" env.video_remote_images.update(other.video_remote_images) def download_images(app, env): """Download thumbnails for the latex build.""" # images should only be downloaded if the builder is Latex related if "latex" not in app.builder.name: return iterator = ( app.builder.status_iterator if hasattr(app.builder, "status_iterator") else status_iterator ) msg = "Downloading remote images..." nb_images = len(env.video_remote_images) for src in iterator(env.video_remote_images, msg, brown, nb_images): dst = Path(app.outdir) / env.video_remote_images[src] if not dst.is_file(): logger.info(f"{src} -> {dst} (downloading)") with open(dst, "wb") as f: try: f.write(requests.get(src).content) except requests.ConnectionError: logger.info(f'Cannot download "{src}"') else: logger.info(f"{src} -> {dst} (already in cache)") def configure_image_download(app): """Configure Sphinx to download video thumbnails.""" app.env.video_remote_images = {} output_dir = Path(app.outdir) / THUMBNAIL_DIR output_dir.mkdir(exist_ok=True) app.config.html_static_path.append(str(output_dir)) sphinxcontrib-youtube-1.4.1/sphinxcontrib/youtube/vimeo.py000066400000000000000000000005571450327314100241670ustar00rootroot00000000000000"""Directive dedicated to the vimeo platform.""" from . import utils class vimeo(utils.video): """Empty video node class.""" pass class Vimeo(utils.Video): """Custom version of the Video Directive.""" _node = vimeo _thumbnail_url = "https://vumbnail.com/{}.jpg" _platform = "vimeo" _platform_url = "https://player.vimeo.com/video/" sphinxcontrib-youtube-1.4.1/sphinxcontrib/youtube/youtube.py000066400000000000000000000014431450327314100245370ustar00rootroot00000000000000"""Directive dedicated to the youtube platform.""" from . import utils class youtube(utils.video): """Empty video node class.""" pass class YouTube(utils.Video): """Custom version of the Video Directive.""" _node = youtube _thumbnail_url = "https://i3.ytimg.com/vi/{}/maxresdefault.jpg" _platform = "youtube" _platform_url = "https://youtu.be/" def visit_youtube_node(self, node): """Custom html visit node.""" privacy = "https://www.youtube-nocookie.com/embed/" embed = "https://www.youtube.com/embed/" node["platform_url"] = embed if node["privacy_mode"] is None else privacy return utils.visit_video_node_html(self, node) _NODE_VISITORS = utils._NODE_VISITORS.copy() _NODE_VISITORS.update(html=(visit_youtube_node, utils.depart_video_node)) sphinxcontrib-youtube-1.4.1/tests/000077500000000000000000000000001450327314100172435ustar00rootroot00000000000000sphinxcontrib-youtube-1.4.1/tests/__init__.py000066400000000000000000000000771450327314100213600ustar00rootroot00000000000000"""Make tests a package to allow coverage to retrieve data.""" sphinxcontrib-youtube-1.4.1/tests/conftest.py000066400000000000000000000004401450327314100214400ustar00rootroot00000000000000"""Pytest configuration.""" import pytest from sphinx.testing.path import path pytest_plugins = "sphinx.testing.fixtures" @pytest.fixture(scope="session") def rootdir(): """Get the root directory for the whole test session.""" return path(__file__).parent.abspath() / "roots" sphinxcontrib-youtube-1.4.1/tests/roots/000077500000000000000000000000001450327314100204115ustar00rootroot00000000000000sphinxcontrib-youtube-1.4.1/tests/roots/test-video/000077500000000000000000000000001450327314100224745ustar00rootroot00000000000000sphinxcontrib-youtube-1.4.1/tests/roots/test-video/conf.py000066400000000000000000000002611450327314100237720ustar00rootroot00000000000000"""Configuration file for the Sphinx documentation builder.""" project = "test-video" extensions = ["sphinxcontrib.youtube"] exclude_patterns = ["_build"] html_theme = "basic" sphinxcontrib-youtube-1.4.1/tests/roots/test-video/index.rst000066400000000000000000000000751450327314100243370ustar00rootroot00000000000000index ===== .. toctree:: vimeo youtube peertubesphinxcontrib-youtube-1.4.1/tests/roots/test-video/peertube.rst000066400000000000000000000002251450327314100250400ustar00rootroot00000000000000peertube ======== .. peertube:: 327a21b3-374e-4373-8b2c-494c9f5e1f19 .. peertube:: 327a21b3-374e-4373-8b2c-494c9f5e1f19 :instance: "peertube.tv"sphinxcontrib-youtube-1.4.1/tests/roots/test-video/vimeo.rst000066400000000000000000000000411450327314100243400ustar00rootroot00000000000000vimeo ===== .. vimeo:: 148751763sphinxcontrib-youtube-1.4.1/tests/roots/test-video/youtube.rst000066400000000000000000000000521450327314100247170ustar00rootroot00000000000000youtube ======= .. youtube:: dQw4w9WgXcQsphinxcontrib-youtube-1.4.1/tests/test_build.py000066400000000000000000000100331450327314100217500ustar00rootroot00000000000000"""Test sphinxcontrib.video extension.""" import pytest from bs4 import BeautifulSoup, formatter fmt = formatter.HTMLFormatter(indent=2, void_element_close_prefix=" /") # -- HTML related tests -------------------------------------------------------- @pytest.mark.sphinx(testroot="video") def test_youtube_html(app, status, warning, file_regression): """Test a youtube video in html build.""" app.builder.build_all() html = (app.outdir / "youtube.html").read_text(encoding="utf8") html = BeautifulSoup(html, "html.parser") video = html.select(".video_wrapper")[0].prettify(formatter=fmt) file_regression.check(video, basename="youtube", extension=".html") @pytest.mark.sphinx(testroot="video") def test_vimeo_html(app, status, warning, file_regression): """Test a vimeo video in html build.""" app.builder.build_all() html = (app.outdir / "vimeo.html").read_text(encoding="utf8") html = BeautifulSoup(html, "html.parser") video = html.select(".video_wrapper")[0].prettify(formatter=fmt) file_regression.check(video, basename="vimeo", extension=".html") @pytest.mark.sphinx(testroot="video") def test_peertube_html(app, status, warning, file_regression): """Test a peertube video in html build.""" app.builder.build_all() html = (app.outdir / "peertube.html").read_text(encoding="utf8") html = BeautifulSoup(html, "html.parser") video = html.select(".video_wrapper")[0].prettify(formatter=fmt) file_regression.check(video, basename="peertube", extension=".html") # -- Latex related tests ------------------------------------------------------- @pytest.mark.sphinx("latex", testroot="video") def test_latex(app, status, warning): """Test a youtube video in latex build.""" app.builder.build_all() result = (app.outdir / "test-video.tex").read_text(encoding="utf8") assert r"\newcommand{\sphinxcontribyoutube}" in result assert r"\newcommand{\sphinxcontribvimeo}" in result assert r"\newcommand{\sphinxcontribpeertube}" in result assert r"\sphinxcontribyoutube{https://youtu.be/}{dQw4w9WgXcQ}{}" in result assert ( r"\sphinxcontribvimeo{https://player.vimeo.com/video/}{148751763}{}" in result ) assert ( r"\sphinxcontribpeertube{https://peertube.tv/w/}{327a21b3-374e-4373-8b2c-494c9f5e1f19}{}" in result ) # -- Epub related tests -------------------------------------------------------- @pytest.mark.sphinx("epub", testroot="video") def test_youtube_epub(app, status, warning, file_regression): """Test a youtube video in epub build.""" app.builder.build_all() xhtml = (app.outdir / "youtube.xhtml").read_text(encoding="utf8") xhtml = BeautifulSoup(xhtml, "html.parser") video = xhtml.select(".video_link_url")[0].prettify(formatter=fmt) file_regression.check(video, basename="youtube", extension=".xhtml") @pytest.mark.sphinx("epub", testroot="video") def test_vimeo_epub(app, status, warning, file_regression): """Test a vimeo video in epub build.""" app.builder.build_all() xhtml = (app.outdir / "vimeo.xhtml").read_text(encoding="utf8") xhtml = BeautifulSoup(xhtml, "html.parser") video = xhtml.select(".video_link_url")[0].prettify(formatter=fmt) file_regression.check(video, basename="vimeo", extension=".xhtml") @pytest.mark.sphinx("epub", testroot="video") def test_peertube_epub(app, status, warning, file_regression): """Test a peertube video in epub build.""" app.builder.build_all() xhtml = (app.outdir / "peertube.xhtml").read_text(encoding="utf8") xhtml = BeautifulSoup(xhtml, "html.parser") video = xhtml.select(".video_link_url")[0].prettify(formatter=fmt) file_regression.check(video, basename="peertube", extension=".xhtml") # -- Unsuported builders ------------------------------------------------------- @pytest.mark.sphinx("text", testroot="video") def test_youtube_unsupported(app, status, warning): """Test a youtube video in unsupported build.""" app.builder.build_all() assert "unsupported output format (node skipped)" in warning.getvalue() sphinxcontrib-youtube-1.4.1/tests/test_build/000077500000000000000000000000001450327314100214015ustar00rootroot00000000000000sphinxcontrib-youtube-1.4.1/tests/test_build/peertube.html000066400000000000000000000003241450327314100241010ustar00rootroot00000000000000
sphinxcontrib-youtube-1.4.1/tests/test_build/peertube.xhtml000066400000000000000000000002361450327314100242730ustar00rootroot00000000000000 https://peertube.tv/w/327a21b3-374e-4373-8b2c-494c9f5e1f19 sphinxcontrib-youtube-1.4.1/tests/test_build/vimeo.html000066400000000000000000000002671450327314100234130ustar00rootroot00000000000000
sphinxcontrib-youtube-1.4.1/tests/test_build/vimeo.xhtml000066400000000000000000000001721450327314100235760ustar00rootroot00000000000000 https://player.vimeo.com/video/148751763 sphinxcontrib-youtube-1.4.1/tests/test_build/youtube.html000066400000000000000000000002701450327314100237620ustar00rootroot00000000000000
sphinxcontrib-youtube-1.4.1/tests/test_build/youtube.xhtml000066400000000000000000000001421450327314100241500ustar00rootroot00000000000000 https://youtu.be/dQw4w9WgXcQ