Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 139 additions & 0 deletions website_sale_product_video_html5/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
.. image:: https://odoo-community.org/readme-banner-image
:target: https://odoo-community.org/get-involved?utm_source=readme
:alt: Odoo Community Association

=============================
eCommerce Product Video HTML5
=============================

..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:ef92162bf8a78a8ace1cc240924bd890473dc59e63844aac70be3c3e5c98a6f9
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status
:alt: Beta
.. |badge2| image:: https://img.shields.io/badge/license-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fe--commerce-lightgray.png?logo=github
:target: https://github.com/OCA/e-commerce/tree/19.0/website_sale_product_video_html5
:alt: OCA/e-commerce
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/e-commerce-19-0/e-commerce-19-0-website_sale_product_video_html5
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
:target: https://runboat.odoo-community.org/builds?repo=OCA/e-commerce&target_branch=19.0
:alt: Try me on Runboat

|badge1| |badge2| |badge3| |badge4| |badge5|

This module lets you showcase products with video files hosted on your
own server or on a CDN, next to the YouTube, Vimeo, Dailymotion,
Instagram and Facebook videos supported by Odoo in the eCommerce media
of products.

Such videos are played with the browser's native HTML5 ``<video>``
player, so no third-party player or cookies are involved. By default,
the image of the media is shown as a poster with a play button, and
nothing is downloaded until the visitor starts the video.

**Table of contents**

.. contents::
:local:

Configuration
=============

Video URLs ending with a video file extension (``.mp4``, ``.m4v``,
``.webm``, ``.ogv``, ``.ogg``) are accepted without configuration.

Many CDNs serve videos from URLs without a file extension. To allow
them, go to *Website > Configuration > Settings*, and list the hosts
serving these videos in *Self-hosted product videos*, separated by
commas (e.g. ``res.cloudinary.com``). Subdomains of these hosts are
allowed as well.

Usage
=====

1. Open a product, and add a media in *eCommerce Media* of the *Sales*
tab.
2. Fill in the *Video URL* with the URL of the video file, e.g.
``https://cdn.example.com/videos/product.mp4``.
3. Upload an image: it is used as thumbnail in the product page carousel
and as poster of the video.

The player can be tuned by adding parameters to the video URL:

- ``autoplay=1``: start playing on page load, muted (browsers block
autoplay with sound);
- ``loop=1``: play the video in a loop;
- ``controls=0``: hide the player controls.

These parameters are removed from the URL the browser loads, so signed
CDN URLs keep working.

As the computation only depends on the URL, video media can also be
created by imports or through the API.

Known issues / Roadmap
======================

- Generate the thumbnail from the video, as done for the other
platforms.
- Support HTTP Live Streaming (``.m3u8``) playlists, which need a
JavaScript player outside of Safari.

Bug Tracker
===========

Bugs are tracked on `GitHub Issues <https://github.com/OCA/e-commerce/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
`feedback <https://github.com/OCA/e-commerce/issues/new?body=module:%20website_sale_product_video_html5%0Aversion:%2019.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.

Do not contact contributors directly about support or help with technical issues.

Credits
=======

Authors
-------

* bosd

Contributors
------------

- Bosd

Maintainers
-----------

This module is maintained by the OCA.

.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org

OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.

.. |maintainer-bosd| image:: https://github.com/bosd.png?size=40px
:target: https://github.com/bosd
:alt: bosd

Current `maintainer <https://odoo-community.org/page/maintainer-role>`__:

|maintainer-bosd|

This module is part of the `OCA/e-commerce <https://github.com/OCA/e-commerce/tree/19.0/website_sale_product_video_html5>`_ project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
1 change: 1 addition & 0 deletions website_sale_product_video_html5/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import models
17 changes: 17 additions & 0 deletions website_sale_product_video_html5/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Copyright 2026 Bosd
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
{
"name": "eCommerce Product Video HTML5",
"summary": "Show self-hosted and CDN video files in the product images",
"version": "19.0.1.0.0",
"category": "Website/Website",
"author": "bosd, Odoo Community Association (OCA)",
"maintainers": ["bosd"],
"website": "https://github.com/OCA/e-commerce",
"license": "AGPL-3",
"depends": ["website_sale"],
"data": [
"views/res_config_settings_views.xml",
],
"installable": True,
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * website_sale_product_video_html5
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 19.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-14 09:12+0000\n"
"PO-Revision-Date: 2026-10-14 09:12+0000\n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"

#. module: website_sale_product_video_html5
#: model:ir.model.fields,help:website_sale_product_video_html5.field_res_config_settings__product_video_allowed_hosts
msgid ""
"Comma-separated list of hosts serving product videos from URLs without a "
"file extension, e.g. res.cloudinary.com. Subdomains are included."
msgstr ""

#. module: website_sale_product_video_html5
#: model:ir.model,name:website_sale_product_video_html5.model_res_config_settings
msgid "Config Settings"
msgstr ""

#. module: website_sale_product_video_html5
#: model:ir.model.fields,field_description:website_sale_product_video_html5.field_product_image__display_name
#: model:ir.model.fields,field_description:website_sale_product_video_html5.field_res_config_settings__display_name
msgid "Display Name"
msgstr ""

#. module: website_sale_product_video_html5
#: model:ir.model.fields,field_description:website_sale_product_video_html5.field_product_image__id
#: model:ir.model.fields,field_description:website_sale_product_video_html5.field_res_config_settings__id
msgid "ID"
msgstr ""

#. module: website_sale_product_video_html5
#: model:ir.model,name:website_sale_product_video_html5.model_product_image
msgid "Product Image"
msgstr ""

#. module: website_sale_product_video_html5
#: model:ir.model.fields,field_description:website_sale_product_video_html5.field_res_config_settings__product_video_allowed_hosts
msgid "Product Video Hosts"
msgstr ""

#. module: website_sale_product_video_html5
#: model_terms:ir.ui.view,arch_db:website_sale_product_video_html5.res_config_settings_view_form
msgid ""
"Product video URLs ending in .mp4, .webm, .ogv or .m4v are always accepted. "
"List here the hosts serving videos from URLs without a file extension."
msgstr ""

#. module: website_sale_product_video_html5
#: model_terms:ir.ui.view,arch_db:website_sale_product_video_html5.res_config_settings_view_form
msgid "Self-hosted product videos"
msgstr ""

#. module: website_sale_product_video_html5
#: model_terms:ir.ui.view,arch_db:website_sale_product_video_html5.res_config_settings_view_form
msgid "res.cloudinary.com, cdn.example.com"
msgstr ""
2 changes: 2 additions & 0 deletions website_sale_product_video_html5/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from . import product_image
from . import res_config_settings
60 changes: 60 additions & 0 deletions website_sale_product_video_html5/models/product_image.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Copyright 2026 Bosd
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from markupsafe import Markup

from odoo import api, models

from odoo.addons.html_editor.tools import get_video_source_data

from ..tools import get_allowed_hosts, is_html5_video_url, split_video_url


class ProductImage(models.Model):
_inherit = "product.image"

@api.depends("video_url")
def _compute_embed_code(self):
allowed_hosts = get_allowed_hosts(self.env)
html5_images = self.filtered(
lambda image: is_html5_video_url(image.video_url, allowed_hosts)
and not get_video_source_data(image.video_url)
)
for image in html5_images:
image.embed_code = image._get_html5_video_embed_code()
return super(ProductImage, self - html5_images)._compute_embed_code()

def _get_html5_video_poster_url(self):
"""URL of the image shown before the video is played."""
self.ensure_one()
if not self._origin.id or not self.with_context(bin_size=True).image_1024:
return False
website = self.env["website"].get_current_website()
url = website.image_url(self._origin, "image_1024")
if website.cdn_activated:
url = website.get_cdn_url(url)
return url

def _get_html5_video_embed_code(self):
self.ensure_one()
src, options = split_video_url(self.video_url.strip())
attributes = {
"class": "o_product_video_html5 w-100 h-100",
"src": src,
"playsinline": "playsinline",
# Nothing is downloaded until the visitor starts the video.
"preload": "none",
}
if not options.get("hide_controls"):
attributes["controls"] = "controls"
if options.get("autoplay"):
# Browsers only allow autoplay for muted videos.
attributes.update(autoplay="autoplay", muted="muted", preload="auto")
if options.get("loop"):
attributes["loop"] = "loop"
poster = self._get_html5_video_poster_url()
if poster:
attributes["poster"] = poster
return Markup("<video %s></video>") % Markup(" ").join(
Markup('%s="%s"') % item for item in attributes.items()
)
16 changes: 16 additions & 0 deletions website_sale_product_video_html5/models/res_config_settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Copyright 2026 Bosd
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from odoo import fields, models


class ResConfigSettings(models.TransientModel):
_inherit = "res.config.settings"

product_video_allowed_hosts = fields.Char(
string="Product Video Hosts",
config_parameter="website_sale_product_video_html5.allowed_hosts",
help="Comma-separated list of hosts serving product videos from URLs "
"without a file extension, e.g. res.cloudinary.com. Subdomains are "
"included.",
)
3 changes: 3 additions & 0 deletions website_sale_product_video_html5/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[build-system]
requires = ["whool"]
build-backend = "whool.buildapi"
7 changes: 7 additions & 0 deletions website_sale_product_video_html5/readme/CONFIGURE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Video URLs ending with a video file extension (`.mp4`, `.m4v`, `.webm`,
`.ogv`, `.ogg`) are accepted without configuration.

Many CDNs serve videos from URLs without a file extension. To allow them, go
to *Website \> Configuration \> Settings*, and list the hosts serving these
videos in *Self-hosted product videos*, separated by commas (e.g.
`res.cloudinary.com`). Subdomains of these hosts are allowed as well.
1 change: 1 addition & 0 deletions website_sale_product_video_html5/readme/CONTRIBUTORS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Bosd
8 changes: 8 additions & 0 deletions website_sale_product_video_html5/readme/DESCRIPTION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
This module lets you showcase products with video files hosted on your own
server or on a CDN, next to the YouTube, Vimeo, Dailymotion, Instagram and
Facebook videos supported by Odoo in the eCommerce media of products.

Such videos are played with the browser's native HTML5 `<video>` player, so
no third-party player or cookies are involved. By default, the image of the
media is shown as a poster with a play button, and nothing is downloaded
until the visitor starts the video.
3 changes: 3 additions & 0 deletions website_sale_product_video_html5/readme/ROADMAP.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
- Generate the thumbnail from the video, as done for the other platforms.
- Support HTTP Live Streaming (`.m3u8`) playlists, which need a JavaScript
player outside of Safari.
19 changes: 19 additions & 0 deletions website_sale_product_video_html5/readme/USAGE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
1. Open a product, and add a media in *eCommerce Media* of the
*Sales* tab.
2. Fill in the *Video URL* with the URL of the video file, e.g.
`https://cdn.example.com/videos/product.mp4`.
3. Upload an image: it is used as thumbnail in the product page carousel and
as poster of the video.

The player can be tuned by adding parameters to the video URL:

- `autoplay=1`: start playing on page load, muted (browsers block autoplay
with sound);
- `loop=1`: play the video in a loop;
- `controls=0`: hide the player controls.

These parameters are removed from the URL the browser loads, so signed CDN
URLs keep working.

As the computation only depends on the URL, video media can also be created
by imports or through the API.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading