Skip to content

Commit 9971542

Browse files
author
OpenMontage Review
committed
Merge reviewed stock cohort member PR OpenMontageInc#779
# Conflicts: # tests/tools/test_stock_source_adapters.py
2 parents aa28ab6 + 9ecb7b3 commit 9971542

2 files changed

Lines changed: 66 additions & 2 deletions

File tree

‎tools/video/stock_sources/coverr.py‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ def search(self, query: str, filters: SearchFilters) -> list[Candidate]:
6161
"query": query,
6262
"page_size": max(1, min(filters.per_page, 25)),
6363
"page": max(1, filters.page),
64+
# Without this the API omits download URLs and every hit is dropped.
65+
"urls": "true",
6466
}
6567

6668
r = requests.get(
@@ -93,8 +95,8 @@ def search(self, query: str, filters: SearchFilters) -> list[Candidate]:
9395
if not download_url:
9496
continue
9597

96-
width = int(v.get("width") or 1920)
97-
height = int(v.get("height") or 1080)
98+
width = int(v.get("width") or v.get("max_width") or 1920)
99+
height = int(v.get("height") or v.get("max_height") or 1080)
98100
if filters.min_width and width < filters.min_width:
99101
continue
100102

‎tools/video/stock_sources/mixkit.py‎

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,20 @@ def _content_url_from_json_ld(html: str) -> str:
8585
return ""
8686

8787

88+
def _ld_dimension(value: Any) -> int:
89+
"""Pixel size from a schema.org width/height (number, "1920", "1920 px",
90+
or a QuantitativeValue). Returns 0 when absent or unparseable."""
91+
if isinstance(value, dict):
92+
value = value.get("value")
93+
if isinstance(value, (int, float)):
94+
return max(0, int(value))
95+
if isinstance(value, str):
96+
match = re.match(r"\s*(\d+)", value)
97+
if match:
98+
return int(match.group(1))
99+
return 0
100+
101+
88102
class MixkitSource:
89103
"""Mixkit video adapter. Satisfies `StockSource`."""
90104

@@ -193,6 +207,54 @@ def search(self, query: str, filters: SearchFilters) -> list[Candidate]:
193207
)
194208
)
195209

210+
if not out:
211+
out = self._parse_json_ld(soup, query, filters)
212+
return out
213+
214+
def _parse_json_ld(self, soup, query: str, filters: SearchFilters) -> list[Candidate]:
215+
"""Read results from the page's schema.org VideoObject entries.
216+
217+
Mixkit's current markup no longer exposes the card classes above, but
218+
every result is still described in JSON-LD with a direct contentUrl.
219+
"""
220+
import json
221+
222+
out: list[Candidate] = []
223+
for script in soup.select('script[type="application/ld+json"]'):
224+
try:
225+
data = json.loads(script.string or "")
226+
except (ValueError, TypeError):
227+
continue
228+
nodes = data.get("@graph", [data]) if isinstance(data, dict) else data
229+
for node in nodes if isinstance(nodes, list) else []:
230+
if not isinstance(node, dict) or node.get("@type") != "VideoObject":
231+
continue
232+
content_url = node.get("contentUrl") or ""
233+
if not content_url.lower().endswith(".mp4"):
234+
continue
235+
href = (node.get("@id") or "").split("#", 1)[0]
236+
clip_id = href.rstrip("/").rsplit("-", 1)[-1] if href else content_url
237+
out.append(
238+
Candidate(
239+
source=self.name,
240+
source_id=f"mixkit_{clip_id}",
241+
source_url=href or content_url,
242+
download_url=content_url,
243+
kind="video",
244+
# Only trust declared dimensions; the "-1080" filename suffix
245+
# says nothing about orientation. 0 = unknown until probed.
246+
width=_ld_dimension(node.get("width")),
247+
height=_ld_dimension(node.get("height")),
248+
duration=0.0,
249+
creator="Mixkit",
250+
license=_LICENSE,
251+
source_tags=f"{node.get('name', '')} {node.get('description', '')} {query}".strip(),
252+
thumbnail_url=node.get("thumbnailUrl", ""),
253+
extra={"detail_url": content_url, "preview_url": node.get("embedUrl", "")},
254+
)
255+
)
256+
if len(out) >= filters.per_page:
257+
return out
196258
return out
197259

198260
def download(self, candidate: Candidate, out_path: Path) -> Path:

0 commit comments

Comments
 (0)