Detect Vimeo URLs and emit iframe with dnt=1
Recognizes vimeo.com/{id} and player.vimeo.com/video/{id}. Preserves
timestamps from #t=30s and ?t=30s as #t=30s on the embed URL (Vimeo
convention).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -15,7 +15,8 @@ public function resolve(string $url): ?string
|
|||||||
}
|
}
|
||||||
return $this->detectVideo($url)
|
return $this->detectVideo($url)
|
||||||
?? $this->detectAudio($url)
|
?? $this->detectAudio($url)
|
||||||
?? $this->detectYouTube($url);
|
?? $this->detectYouTube($url)
|
||||||
|
?? $this->detectVimeo($url);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function detectVideo(string $url): ?string
|
private function detectVideo(string $url): ?string
|
||||||
@@ -98,6 +99,31 @@ private function parseTimestamp(string $t): ?int
|
|||||||
return $matched ? $total : null;
|
return $matched ? $total : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function detectVimeo(string $url): ?string
|
||||||
|
{
|
||||||
|
if (!preg_match('~^https?://(?:www\.|player\.)?vimeo\.com/(?:video/)?(\d+)~', $url, $m)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
$videoId = $m[1];
|
||||||
|
$src = "https://player.vimeo.com/video/{$videoId}?dnt=1";
|
||||||
|
$hash = $this->extractVimeoHash($url);
|
||||||
|
if ($hash !== null) {
|
||||||
|
$src .= '#' . $hash;
|
||||||
|
}
|
||||||
|
return $this->iframeHtml($src, 'vimeo');
|
||||||
|
}
|
||||||
|
|
||||||
|
private function extractVimeoHash(string $url): ?string
|
||||||
|
{
|
||||||
|
if (preg_match('/#(t=[^&]+)/', $url, $m)) {
|
||||||
|
return $m[1];
|
||||||
|
}
|
||||||
|
if (preg_match('/[?&](t=[^&#]+)/', $url, $m)) {
|
||||||
|
return $m[1];
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private function iframeHtml(string $src, string $provider): string
|
private function iframeHtml(string $src, string $provider): string
|
||||||
{
|
{
|
||||||
$safe = htmlspecialchars($src, ENT_QUOTES, 'UTF-8');
|
$safe = htmlspecialchars($src, ENT_QUOTES, 'UTF-8');
|
||||||
|
|||||||
@@ -145,4 +145,45 @@ public static function youtubeTimestampUrls(): array
|
|||||||
'start=45' => ['https://www.youtube.com/watch?v=dQw4w9WgXcQ&start=45', 45],
|
'start=45' => ['https://www.youtube.com/watch?v=dQw4w9WgXcQ&start=45', 45],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[DataProvider('vimeoUrls')]
|
||||||
|
public function test_vimeo_urls_produce_iframe(string $url): void
|
||||||
|
{
|
||||||
|
$html = $this->resolver->resolve($url);
|
||||||
|
$this->assertNotNull($html);
|
||||||
|
$this->assertStringStartsWith('<iframe', $html);
|
||||||
|
$this->assertStringContainsString('player.vimeo.com/video/123456789', $html);
|
||||||
|
$this->assertStringContainsString('dnt=1', $html);
|
||||||
|
$this->assertStringContainsString('class="kb-embed kb-embed-vimeo"', $html);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function vimeoUrls(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'vimeo.com' => ['https://vimeo.com/123456789'],
|
||||||
|
'www.vimeo.com' => ['https://www.vimeo.com/123456789'],
|
||||||
|
'player.vimeo.com' => ['https://player.vimeo.com/video/123456789'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
#[DataProvider('vimeoTimestampUrls')]
|
||||||
|
public function test_vimeo_timestamp_preserved_as_hash(string $url): void
|
||||||
|
{
|
||||||
|
$html = $this->resolver->resolve($url);
|
||||||
|
$this->assertNotNull($html);
|
||||||
|
$this->assertStringContainsString('#t=30s', $html);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function vimeoTimestampUrls(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'hash form' => ['https://vimeo.com/123456789#t=30s'],
|
||||||
|
'query form' => ['https://vimeo.com/123456789?t=30s'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_vimeo_invalid_id_returns_null(): void
|
||||||
|
{
|
||||||
|
$this->assertNull($this->resolver->resolve('https://vimeo.com/notanumber'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user