From eff38c5d20aa8a3547fa7699fc4999d425f79f0a Mon Sep 17 00:00:00 2001 From: Om Gate Date: Thu, 19 Mar 2026 19:15:24 +0530 Subject: [PATCH 1/4] feat: add `get_embed_code` functionality --- videodb/__init__.py | 3 +- videodb/_utils/_video.py | 67 ++++++++++++++++++++++++++ videodb/editor.py | 36 ++++++++++++++ videodb/rtstream.py | 101 ++++++++++++++++++++++++++++++++++++++- videodb/search.py | 37 +++++++++++++- videodb/shot.py | 37 +++++++++++++- videodb/timeline.py | 36 ++++++++++++++ videodb/video.py | 41 +++++++++++++++- 8 files changed, 352 insertions(+), 6 deletions(-) diff --git a/videodb/__init__.py b/videodb/__init__.py index a5adc9c..9f89725 100644 --- a/videodb/__init__.py +++ b/videodb/__init__.py @@ -4,7 +4,7 @@ import logging from typing import Optional -from videodb._utils._video import play_stream +from videodb._utils._video import play_stream, build_embed_code from videodb._constants import ( VIDEO_DB_API, IndexType, @@ -55,6 +55,7 @@ "IndexType", "SearchError", "play_stream", + "build_embed_code", "MediaType", "SearchType", "SubtitleAlignment", diff --git a/videodb/_utils/_video.py b/videodb/_utils/_video.py index 9cdb012..143c50d 100644 --- a/videodb/_utils/_video.py +++ b/videodb/_utils/_video.py @@ -1,7 +1,74 @@ import webbrowser as web +from urllib.parse import urlparse, urlunparse, parse_qs, urlencode + PLAYER_URL: str = "https://console.videodb.io/player" +def player_url_to_embed_url(player_url: str) -> str: + """Convert a /watch player URL to an /embed URL. + + :param str player_url: The player URL (e.g., https://player.videodb.io/watch?v=slug) + :return: The embed URL (e.g., https://player.videodb.io/embed?v=slug) + :rtype: str + :raises ValueError: If the URL format is invalid or missing the 'v' parameter + """ + if not player_url: + raise ValueError("player_url is required to generate embed URL") + + parsed = urlparse(player_url) + query_params = parse_qs(parsed.query) + + if "v" not in query_params: + raise ValueError("player_url must contain a 'v' query parameter") + + embed_params = {"v": query_params["v"][0]} + + embed_url = urlunparse(( + parsed.scheme, + parsed.netloc, + "/embed", + "", + urlencode(embed_params), + "", + )) + + return embed_url + + +def build_embed_code( + player_url: str, + width: str = "100%", + height: int = 405, + title: str = "VideoDB Player", + allow_fullscreen: bool = True, +) -> str: + """Build an iframe embed HTML string from a player URL. + + :param str player_url: The player URL to embed + :param str width: Width of the iframe (default: "100%") + :param int height: Height of the iframe in pixels (default: 405) + :param str title: Title attribute for the iframe (default: "VideoDB Player") + :param bool allow_fullscreen: Whether to allow fullscreen (default: True) + :return: HTML iframe string + :rtype: str + :raises ValueError: If player_url is empty or height is not positive + """ + if not player_url: + raise ValueError("player_url is required to generate embed code") + + if height <= 0: + raise ValueError("height must be a positive integer") + + embed_url = player_url_to_embed_url(player_url) + fullscreen_attr = " allowfullscreen" if allow_fullscreen else "" + + return ( + f'' + ) + + def play_stream(url: str): """Play a stream url in the browser/ notebook diff --git a/videodb/editor.py b/videodb/editor.py index 5c40282..e54d957 100644 --- a/videodb/editor.py +++ b/videodb/editor.py @@ -5,6 +5,7 @@ from enum import Enum from videodb._constants import ApiPath +from videodb._utils._video import build_embed_code from videodb.exceptions import InvalidRequestError @@ -1164,3 +1165,38 @@ def download_stream(self, stream_url: str) -> dict: return self.connection.post( path=f"{ApiPath.editor}/{ApiPath.download}", data={"stream_url": stream_url} ) + + def get_embed_code( + self, + width: str = "100%", + height: int = 405, + title: str = "VideoDB Player", + allow_fullscreen: bool = True, + auto_generate: bool = True, + ) -> str: + """Generate an HTML iframe embed code for the timeline. + + :param str width: Width of the iframe (default: "100%") + :param int height: Height of the iframe in pixels (default: 405) + :param str title: Title attribute for the iframe (default: "VideoDB Player") + :param bool allow_fullscreen: Whether to allow fullscreen (default: True) + :param bool auto_generate: If True and player_url is missing, auto-generate it (default: True) + :return: HTML iframe string + :rtype: str + :raises ValueError: If player_url is not available + """ + if not self.player_url and auto_generate: + self.generate_stream() + + if not self.player_url: + raise ValueError( + "player_url not available. Call generate_stream() first or set auto_generate=True." + ) + + return build_embed_code( + player_url=self.player_url, + width=width, + height=height, + title=title, + allow_fullscreen=allow_fullscreen, + ) diff --git a/videodb/rtstream.py b/videodb/rtstream.py index 540844f..5de92e5 100644 --- a/videodb/rtstream.py +++ b/videodb/rtstream.py @@ -5,7 +5,7 @@ SceneExtractionType, Segmenter, ) -from videodb._utils._video import play_stream +from videodb._utils._video import play_stream, build_embed_code class RTStreamSearchResult: @@ -71,6 +71,36 @@ def __repr__(self) -> str: f"duration={self.duration})" ) + def get_embed_code( + self, + width: str = "100%", + height: int = 405, + title: str = "VideoDB Player", + allow_fullscreen: bool = True, + ) -> str: + """Generate an HTML iframe embed code for the exported recording. + + :param str width: Width of the iframe (default: "100%") + :param int height: Height of the iframe in pixels (default: 405) + :param str title: Title attribute for the iframe (default: "VideoDB Player") + :param bool allow_fullscreen: Whether to allow fullscreen (default: True) + :return: HTML iframe string + :rtype: str + :raises ValueError: If player_url is not available + """ + if not self.player_url: + raise ValueError( + "player_url not available. Export may have failed or returned audio-only content." + ) + + return build_embed_code( + player_url=self.player_url, + width=width, + height=height, + title=title, + allow_fullscreen=allow_fullscreen, + ) + class RTStreamShot: """RTStreamShot class for rtstream search results @@ -159,6 +189,41 @@ def play(self) -> str: self.generate_stream() return play_stream(self.stream_url) + def get_embed_code( + self, + width: str = "100%", + height: int = 405, + title: str = "VideoDB Player", + allow_fullscreen: bool = True, + auto_generate: bool = True, + ) -> str: + """Generate an HTML iframe embed code for the rtstream shot. + + :param str width: Width of the iframe (default: "100%") + :param int height: Height of the iframe in pixels (default: 405) + :param str title: Title attribute for the iframe (default: "VideoDB Player") + :param bool allow_fullscreen: Whether to allow fullscreen (default: True) + :param bool auto_generate: If True and player_url is missing, auto-generate it (default: True) + :return: HTML iframe string + :rtype: str + :raises ValueError: If player_url is not available + """ + if not self.player_url and auto_generate: + self.generate_stream() + + if not self.player_url: + raise ValueError( + "player_url not available. Call generate_stream() first or set auto_generate=True." + ) + + return build_embed_code( + player_url=self.player_url, + width=width, + height=height, + title=title, + allow_fullscreen=allow_fullscreen, + ) + class RTStreamSceneIndex: """RTStreamSceneIndex class to interact with the rtstream scene index @@ -463,6 +528,40 @@ def generate_stream( self.player_url = stream_data.get("player_url") return self.player_url + def get_embed_code( + self, + width: str = "100%", + height: int = 405, + title: str = "VideoDB Player", + allow_fullscreen: bool = True, + ) -> str: + """Generate an HTML iframe embed code for the rtstream. + + Note: Unlike other objects, RTStream does not support auto_generate + because generate_stream() requires start and end parameters. + Call generate_stream(start, end) first to populate player_url. + + :param str width: Width of the iframe (default: "100%") + :param int height: Height of the iframe in pixels (default: 405) + :param str title: Title attribute for the iframe (default: "VideoDB Player") + :param bool allow_fullscreen: Whether to allow fullscreen (default: True) + :return: HTML iframe string + :rtype: str + :raises ValueError: If player_url is not available + """ + if not self.player_url: + raise ValueError( + "player_url not available. Call generate_stream(start, end) first to generate a stream." + ) + + return build_embed_code( + player_url=self.player_url, + width=width, + height=height, + title=title, + allow_fullscreen=allow_fullscreen, + ) + def index_scenes( self, extraction_type=SceneExtractionType.time_based, diff --git a/videodb/search.py b/videodb/search.py index 94730ec..4cfcc6c 100644 --- a/videodb/search.py +++ b/videodb/search.py @@ -1,5 +1,5 @@ from abc import ABC, abstractmethod -from videodb._utils._video import play_stream +from videodb._utils._video import play_stream, build_embed_code from videodb._constants import ( IndexType, SearchType, @@ -100,6 +100,41 @@ def play(self) -> str: self.compile() return play_stream(self.stream_url) + def get_embed_code( + self, + width: str = "100%", + height: int = 405, + title: str = "VideoDB Player", + allow_fullscreen: bool = True, + auto_generate: bool = True, + ) -> str: + """Generate an HTML iframe embed code for the search result. + + :param str width: Width of the iframe (default: "100%") + :param int height: Height of the iframe in pixels (default: 405) + :param str title: Title attribute for the iframe (default: "VideoDB Player") + :param bool allow_fullscreen: Whether to allow fullscreen (default: True) + :param bool auto_generate: If True and player_url is missing, auto-compile it (default: True) + :return: HTML iframe string + :rtype: str + :raises ValueError: If player_url is not available + """ + if not self.player_url and auto_generate: + self.compile() + + if not self.player_url: + raise ValueError( + "player_url not available. Call compile() first or set auto_generate=True." + ) + + return build_embed_code( + player_url=self.player_url, + width=width, + height=height, + title=title, + allow_fullscreen=allow_fullscreen, + ) + class Search(ABC): """Search interface inside video or collection""" diff --git a/videodb/shot.py b/videodb/shot.py index b261077..56ebb63 100644 --- a/videodb/shot.py +++ b/videodb/shot.py @@ -1,5 +1,5 @@ from typing import Optional -from videodb._utils._video import play_stream +from videodb._utils._video import play_stream, build_embed_code from videodb._constants import ( ApiPath, ) @@ -105,3 +105,38 @@ def play(self) -> str: """ self.generate_stream() return play_stream(self.stream_url) + + def get_embed_code( + self, + width: str = "100%", + height: int = 405, + title: str = "VideoDB Player", + allow_fullscreen: bool = True, + auto_generate: bool = True, + ) -> str: + """Generate an HTML iframe embed code for the shot. + + :param str width: Width of the iframe (default: "100%") + :param int height: Height of the iframe in pixels (default: 405) + :param str title: Title attribute for the iframe (default: "VideoDB Player") + :param bool allow_fullscreen: Whether to allow fullscreen (default: True) + :param bool auto_generate: If True and player_url is missing, auto-generate it (default: True) + :return: HTML iframe string + :rtype: str + :raises ValueError: If player_url is not available + """ + if not self.player_url and auto_generate: + self.generate_stream() + + if not self.player_url: + raise ValueError( + "player_url not available. Call generate_stream() first or set auto_generate=True." + ) + + return build_embed_code( + player_url=self.player_url, + width=width, + height=height, + title=title, + allow_fullscreen=allow_fullscreen, + ) diff --git a/videodb/timeline.py b/videodb/timeline.py index f1d1887..a8b6d67 100644 --- a/videodb/timeline.py +++ b/videodb/timeline.py @@ -1,6 +1,7 @@ from typing import Union from videodb._constants import ApiPath +from videodb._utils._video import build_embed_code from videodb.asset import VideoAsset, AudioAsset, ImageAsset, TextAsset @@ -71,3 +72,38 @@ def generate_stream(self) -> str: self.stream_url = stream_data.get("stream_url") self.player_url = stream_data.get("player_url") return stream_data.get("stream_url", None) + + def get_embed_code( + self, + width: str = "100%", + height: int = 405, + title: str = "VideoDB Player", + allow_fullscreen: bool = True, + auto_generate: bool = True, + ) -> str: + """Generate an HTML iframe embed code for the timeline. + + :param str width: Width of the iframe (default: "100%") + :param int height: Height of the iframe in pixels (default: 405) + :param str title: Title attribute for the iframe (default: "VideoDB Player") + :param bool allow_fullscreen: Whether to allow fullscreen (default: True) + :param bool auto_generate: If True and player_url is missing, auto-generate it (default: True) + :return: HTML iframe string + :rtype: str + :raises ValueError: If player_url is not available + """ + if not self.player_url and auto_generate: + self.generate_stream() + + if not self.player_url: + raise ValueError( + "player_url not available. Call generate_stream() first or set auto_generate=True." + ) + + return build_embed_code( + player_url=self.player_url, + width=width, + height=height, + title=title, + allow_fullscreen=allow_fullscreen, + ) diff --git a/videodb/video.py b/videodb/video.py index 3c7126d..c1e1719 100644 --- a/videodb/video.py +++ b/videodb/video.py @@ -1,5 +1,5 @@ from typing import Optional, Union, List, Dict, Tuple, Any -from videodb._utils._video import play_stream +from videodb._utils._video import play_stream, build_embed_code from videodb._constants import ( ApiPath, IndexType, @@ -137,7 +137,9 @@ def generate_stream( "length": self.length, }, ) - return stream_data.get("stream_url", None) + self.stream_url = stream_data.get("stream_url") + self.player_url = stream_data.get("player_url") + return self.stream_url def generate_thumbnail(self, time: Optional[float] = None) -> Union[str, Image]: """Generate the thumbnail of the video. @@ -770,6 +772,41 @@ def play(self) -> str: """ return play_stream(self.stream_url) + def get_embed_code( + self, + width: str = "100%", + height: int = 405, + title: str = "VideoDB Player", + allow_fullscreen: bool = True, + auto_generate: bool = True, + ) -> str: + """Generate an HTML iframe embed code for the video. + + :param str width: Width of the iframe (default: "100%") + :param int height: Height of the iframe in pixels (default: 405) + :param str title: Title attribute for the iframe (default: "VideoDB Player") + :param bool allow_fullscreen: Whether to allow fullscreen (default: True) + :param bool auto_generate: If True and player_url is missing, auto-generate it (default: True) + :return: HTML iframe string + :rtype: str + :raises ValueError: If player_url is not available + """ + if not self.player_url and auto_generate: + self.generate_stream() + + if not self.player_url: + raise ValueError( + "player_url not available. Call generate_stream() first or set auto_generate=True." + ) + + return build_embed_code( + player_url=self.player_url, + width=width, + height=height, + title=title, + allow_fullscreen=allow_fullscreen, + ) + def get_meeting(self): """Get meeting information associated with the video. From 44bd4831ed3f7c3627b447a1949ff3d3ce7a286a Mon Sep 17 00:00:00 2001 From: Om Gate Date: Thu, 19 Mar 2026 19:22:39 +0530 Subject: [PATCH 2/4] feat: naming of function --- videodb/__init__.py | 4 ++-- videodb/_utils/_video.py | 2 +- videodb/editor.py | 4 ++-- videodb/rtstream.py | 8 ++++---- videodb/search.py | 4 ++-- videodb/shot.py | 4 ++-- videodb/timeline.py | 4 ++-- videodb/video.py | 4 ++-- 8 files changed, 17 insertions(+), 17 deletions(-) diff --git a/videodb/__init__.py b/videodb/__init__.py index 9f89725..59d2a1b 100644 --- a/videodb/__init__.py +++ b/videodb/__init__.py @@ -4,7 +4,7 @@ import logging from typing import Optional -from videodb._utils._video import play_stream, build_embed_code +from videodb._utils._video import play_stream, build_iframe_embed_code from videodb._constants import ( VIDEO_DB_API, IndexType, @@ -55,7 +55,7 @@ "IndexType", "SearchError", "play_stream", - "build_embed_code", + "build_iframe_embed_code", "MediaType", "SearchType", "SubtitleAlignment", diff --git a/videodb/_utils/_video.py b/videodb/_utils/_video.py index 143c50d..098b697 100644 --- a/videodb/_utils/_video.py +++ b/videodb/_utils/_video.py @@ -35,7 +35,7 @@ def player_url_to_embed_url(player_url: str) -> str: return embed_url -def build_embed_code( +def build_iframe_embed_code( player_url: str, width: str = "100%", height: int = 405, diff --git a/videodb/editor.py b/videodb/editor.py index e54d957..6c87d53 100644 --- a/videodb/editor.py +++ b/videodb/editor.py @@ -5,7 +5,7 @@ from enum import Enum from videodb._constants import ApiPath -from videodb._utils._video import build_embed_code +from videodb._utils._video import build_iframe_embed_code from videodb.exceptions import InvalidRequestError @@ -1193,7 +1193,7 @@ def get_embed_code( "player_url not available. Call generate_stream() first or set auto_generate=True." ) - return build_embed_code( + return build_iframe_embed_code( player_url=self.player_url, width=width, height=height, diff --git a/videodb/rtstream.py b/videodb/rtstream.py index 5de92e5..0cf35c6 100644 --- a/videodb/rtstream.py +++ b/videodb/rtstream.py @@ -5,7 +5,7 @@ SceneExtractionType, Segmenter, ) -from videodb._utils._video import play_stream, build_embed_code +from videodb._utils._video import play_stream, build_iframe_embed_code class RTStreamSearchResult: @@ -93,7 +93,7 @@ def get_embed_code( "player_url not available. Export may have failed or returned audio-only content." ) - return build_embed_code( + return build_iframe_embed_code( player_url=self.player_url, width=width, height=height, @@ -216,7 +216,7 @@ def get_embed_code( "player_url not available. Call generate_stream() first or set auto_generate=True." ) - return build_embed_code( + return build_iframe_embed_code( player_url=self.player_url, width=width, height=height, @@ -554,7 +554,7 @@ def get_embed_code( "player_url not available. Call generate_stream(start, end) first to generate a stream." ) - return build_embed_code( + return build_iframe_embed_code( player_url=self.player_url, width=width, height=height, diff --git a/videodb/search.py b/videodb/search.py index 4cfcc6c..0106985 100644 --- a/videodb/search.py +++ b/videodb/search.py @@ -1,5 +1,5 @@ from abc import ABC, abstractmethod -from videodb._utils._video import play_stream, build_embed_code +from videodb._utils._video import play_stream, build_iframe_embed_code from videodb._constants import ( IndexType, SearchType, @@ -127,7 +127,7 @@ def get_embed_code( "player_url not available. Call compile() first or set auto_generate=True." ) - return build_embed_code( + return build_iframe_embed_code( player_url=self.player_url, width=width, height=height, diff --git a/videodb/shot.py b/videodb/shot.py index 56ebb63..643267f 100644 --- a/videodb/shot.py +++ b/videodb/shot.py @@ -1,5 +1,5 @@ from typing import Optional -from videodb._utils._video import play_stream, build_embed_code +from videodb._utils._video import play_stream, build_iframe_embed_code from videodb._constants import ( ApiPath, ) @@ -133,7 +133,7 @@ def get_embed_code( "player_url not available. Call generate_stream() first or set auto_generate=True." ) - return build_embed_code( + return build_iframe_embed_code( player_url=self.player_url, width=width, height=height, diff --git a/videodb/timeline.py b/videodb/timeline.py index a8b6d67..9bbeb70 100644 --- a/videodb/timeline.py +++ b/videodb/timeline.py @@ -1,7 +1,7 @@ from typing import Union from videodb._constants import ApiPath -from videodb._utils._video import build_embed_code +from videodb._utils._video import build_iframe_embed_code from videodb.asset import VideoAsset, AudioAsset, ImageAsset, TextAsset @@ -100,7 +100,7 @@ def get_embed_code( "player_url not available. Call generate_stream() first or set auto_generate=True." ) - return build_embed_code( + return build_iframe_embed_code( player_url=self.player_url, width=width, height=height, diff --git a/videodb/video.py b/videodb/video.py index c1e1719..4dcd632 100644 --- a/videodb/video.py +++ b/videodb/video.py @@ -1,5 +1,5 @@ from typing import Optional, Union, List, Dict, Tuple, Any -from videodb._utils._video import play_stream, build_embed_code +from videodb._utils._video import play_stream, build_iframe_embed_code from videodb._constants import ( ApiPath, IndexType, @@ -799,7 +799,7 @@ def get_embed_code( "player_url not available. Call generate_stream() first or set auto_generate=True." ) - return build_embed_code( + return build_iframe_embed_code( player_url=self.player_url, width=width, height=height, From 3397dde9f34175e574282757d38e3273a336a196 Mon Sep 17 00:00:00 2001 From: Om Gate Date: Thu, 19 Mar 2026 19:22:46 +0530 Subject: [PATCH 3/4] feat: test suite --- tests/test_embed_code.py | 175 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 175 insertions(+) create mode 100644 tests/test_embed_code.py diff --git a/tests/test_embed_code.py b/tests/test_embed_code.py new file mode 100644 index 0000000..1fb72e1 --- /dev/null +++ b/tests/test_embed_code.py @@ -0,0 +1,175 @@ +import pytest +from videodb._utils._video import player_url_to_embed_url, build_iframe_embed_code + + +class TestPlayerUrlToEmbedUrl: + """Tests for player_url_to_embed_url utility function.""" + + def test_basic_conversion(self): + """Test basic /watch to /embed conversion.""" + player_url = "https://player.videodb.io/watch?v=abc123" + expected = "https://player.videodb.io/embed?v=abc123" + assert player_url_to_embed_url(player_url) == expected + + def test_preserves_scheme_and_host(self): + """Test that scheme and host are preserved.""" + player_url = "http://localhost:3000/watch?v=test-slug" + expected = "http://localhost:3000/embed?v=test-slug" + assert player_url_to_embed_url(player_url) == expected + + def test_strips_extra_query_params(self): + """Test that only 'v' param is kept in embed URL.""" + player_url = "https://player.videodb.io/watch?v=abc123&other=param&foo=bar" + expected = "https://player.videodb.io/embed?v=abc123" + assert player_url_to_embed_url(player_url) == expected + + def test_empty_url_raises_error(self): + """Test that empty URL raises ValueError.""" + with pytest.raises(ValueError, match="player_url is required"): + player_url_to_embed_url("") + + def test_none_url_raises_error(self): + """Test that None URL raises ValueError.""" + with pytest.raises(ValueError, match="player_url is required"): + player_url_to_embed_url(None) + + def test_missing_v_param_raises_error(self): + """Test that URL without 'v' param raises ValueError.""" + with pytest.raises(ValueError, match="must contain a 'v' query parameter"): + player_url_to_embed_url("https://player.videodb.io/watch?other=param") + + +class TestBuildIframeEmbedCode: + """Tests for build_iframe_embed_code utility function.""" + + def test_basic_embed_code(self): + """Test basic embed code generation.""" + player_url = "https://player.videodb.io/watch?v=abc123" + result = build_iframe_embed_code(player_url) + + assert '" in result - - def test_custom_dimensions(self): - """Test custom width and height.""" - player_url = "https://player.videodb.io/watch?v=abc123" - result = build_iframe_embed_code(player_url, width="800px", height=600) - - assert 'width="800px"' in result - assert 'height="600"' in result - - def test_custom_title(self): - """Test custom title attribute.""" - player_url = "https://player.videodb.io/watch?v=abc123" - result = build_iframe_embed_code(player_url, title="My Custom Player") - - assert 'title="My Custom Player"' in result - - def test_fullscreen_disabled(self): - """Test disabling fullscreen.""" - player_url = "https://player.videodb.io/watch?v=abc123" - result = build_iframe_embed_code(player_url, allow_fullscreen=False) - - assert "allowfullscreen" not in result - - def test_fullscreen_enabled(self): - """Test enabling fullscreen (default).""" - player_url = "https://player.videodb.io/watch?v=abc123" - result = build_iframe_embed_code(player_url, allow_fullscreen=True) - - assert "allowfullscreen" in result - - def test_empty_url_raises_error(self): - """Test that empty URL raises ValueError.""" - with pytest.raises(ValueError, match="player_url is required"): - build_iframe_embed_code("") - - def test_none_url_raises_error(self): - """Test that None URL raises ValueError.""" - with pytest.raises(ValueError, match="player_url is required"): - build_iframe_embed_code(None) - - def test_zero_height_raises_error(self): - """Test that zero height raises ValueError.""" - with pytest.raises(ValueError, match="height must be a positive integer"): - build_iframe_embed_code("https://player.videodb.io/watch?v=abc", height=0) - - def test_negative_height_raises_error(self): - """Test that negative height raises ValueError.""" - with pytest.raises(ValueError, match="height must be a positive integer"): - build_iframe_embed_code("https://player.videodb.io/watch?v=abc", height=-100) - - def test_all_custom_params(self): - """Test with all custom parameters.""" - player_url = "https://player.videodb.io/watch?v=my-video" - result = build_iframe_embed_code( - player_url, - width="640px", - height=480, - title="Search Highlights", - allow_fullscreen=True, - ) - - assert 'src="https://player.videodb.io/embed?v=my-video"' in result - assert 'width="640px"' in result - assert 'height="480"' in result - assert 'title="Search Highlights"' in result - assert "allowfullscreen" in result - - -class TestBuildIframeEmbedCodeIntegration: - """Integration tests for build_iframe_embed_code with videodb module.""" - - def test_import_from_videodb(self): - """Test that build_iframe_embed_code can be imported from videodb.""" - from videodb import build_iframe_embed_code as imported_func - - assert imported_func is not None - assert callable(imported_func) - - def test_usage_with_dict_response(self): - """Test typical usage pattern with dict response (like CaptureSession.export).""" - # Simulate a dict response from an API - response = { - "video_id": "v123", - "player_url": "https://player.videodb.io/watch?v=exported-recording", - "stream_url": "https://stream.videodb.io/abc.m3u8", - } - - from videodb import build_iframe_embed_code - - embed_html = build_iframe_embed_code(response["player_url"]) - - assert "embed?v=exported-recording" in embed_html - assert "