Skip to content

Commit fd2197b

Browse files
committed
feat(stock): add Openverse image source (keyless, CC-licensed)
Openverse federates openly licensed media (Flickr, Wikimedia, Met, NYPL, ~90 providers) behind one keyless API. It indexes images and audio only, so the adapter declares video unsupported and short-circuits video queries without a network call. Defaults to license_type=commercial,modification so candidates permit monetised, edited video; OPENVERSE_LICENSE_TYPE overrides. Anonymous access is 20 req/min and 200/day; optional client credentials raise it. Downloads send a non-"Bot" User-Agent with an image Accept header because Flickr's CDN 403s the bot string, and retry 403/429 with backoff. A live corpus run went from 1/4 to 4/4 candidates accepted. Tests: 43 passed, 3 xfailed (the existing contract tests cover the new adapter automatically).
1 parent e01dbf4 commit fd2197b

2 files changed

Lines changed: 383 additions & 0 deletions

File tree

‎tests/tools/test_stock_source_adapters.py‎

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@
44
import pytest
55

66
from tools.video.stock_sources import SearchFilters, all_sources, get_source
7+
from tools.video.stock_sources.openverse import (
8+
_license_label,
9+
_matches_orientation,
10+
_result_to_candidate,
11+
)
712
from tools.video.stock_sources.unsplash import _build_download_url, _orientation_for_unsplash
813
from tools.video.stock_sources.wikimedia import (
914
_build_search_queries,
@@ -16,6 +21,7 @@ def test_stock_source_autodiscovery_includes_new_sources():
1621
names = {source.name for source in all_sources()}
1722
assert "wikimedia" in names
1823
assert "unsplash" in names
24+
assert "openverse" in names
1925

2026

2127
def test_wikimedia_search_query_respects_kind():
@@ -77,6 +83,80 @@ def test_unsplash_helpers_preserve_query_params():
7783
assert "fm=jpg" in url
7884

7985

86+
def test_openverse_is_image_only():
87+
# Openverse has no video endpoint; a video query must short-circuit
88+
# without touching the network.
89+
source = get_source("openverse")
90+
assert source.supports == {"video": False, "image": True}
91+
assert source.search("rain city", SearchFilters(kind="video", per_page=5)) == []
92+
93+
94+
def test_openverse_maps_result_to_candidate():
95+
cand = _result_to_candidate(
96+
{
97+
"id": "b6829a2c",
98+
"title": "Illuminated London – The Rain City",
99+
"url": "https://live.staticflickr.com/5098/5584905187_b.jpg",
100+
"foreign_landing_url": "https://www.flickr.com/photos/x/5584905187",
101+
"width": 680,
102+
"height": 1024,
103+
"creator": "Sprengben",
104+
"license": "by-sa",
105+
"license_version": "2.0",
106+
"license_url": "https://creativecommons.org/licenses/by-sa/2.0/",
107+
"source": "flickr",
108+
"tags": [{"name": "rain"}, {"name": "london"}],
109+
},
110+
SearchFilters(kind="image"),
111+
)
112+
113+
assert cand is not None
114+
assert cand.clip_id == "openverse_b6829a2c"
115+
assert cand.kind == "image"
116+
assert cand.width == 680 and cand.height == 1024
117+
assert cand.creator == "Sprengben"
118+
assert cand.license == "CC BY-SA 2.0"
119+
assert cand.source_url.startswith("https://www.flickr.com/")
120+
assert "rain" in cand.source_tags and "london" in cand.source_tags
121+
assert cand.extra["provider"] == "flickr"
122+
123+
124+
def test_openverse_drops_results_without_a_file_url():
125+
assert _result_to_candidate({"id": "x"}, SearchFilters(kind="image")) is None
126+
127+
128+
def test_openverse_respects_min_width_and_orientation():
129+
narrow = {"url": "https://x/y.jpg", "width": 320, "height": 200}
130+
assert (
131+
_result_to_candidate(narrow, SearchFilters(kind="image", min_width=1200))
132+
is None
133+
)
134+
landscape = {"url": "https://x/y.jpg", "width": 1920, "height": 1080}
135+
assert (
136+
_result_to_candidate(landscape, SearchFilters(kind="image", orientation="portrait"))
137+
is None
138+
)
139+
assert (
140+
_result_to_candidate(landscape, SearchFilters(kind="image", orientation="landscape"))
141+
is not None
142+
)
143+
144+
145+
def test_openverse_license_labels():
146+
assert _license_label({"license": "cc0", "license_version": "1.0"}) == "CC0 1.0"
147+
assert _license_label({"license": "by-nc", "license_version": "4.0"}) == "CC BY-NC 4.0"
148+
assert _license_label({"license": "pdm"}) == "Public Domain Mark"
149+
assert _license_label({"license_url": "https://example.org/lic"}) == "https://example.org/lic"
150+
151+
152+
def test_openverse_orientation_helper():
153+
assert _matches_orientation("landscape", 1920, 1080)
154+
assert _matches_orientation("portrait", 1080, 1920)
155+
assert _matches_orientation("square", 1000, 1000)
156+
assert not _matches_orientation("portrait", 1920, 1080)
157+
158+
159+
80160
# ---------------------------------------------------------------------
81161
# Transport-error contract (issue #511)
82162
#

0 commit comments

Comments
 (0)