diff --git a/src/app/Markdown/MediaUrlResolver.php b/src/app/Markdown/MediaUrlResolver.php
index 1fd476d..89f5ff6 100644
--- a/src/app/Markdown/MediaUrlResolver.php
+++ b/src/app/Markdown/MediaUrlResolver.php
@@ -4,11 +4,31 @@
class MediaUrlResolver
{
+ private const VIDEO_EXT = ['mp4', 'webm', 'ogv', 'mov', 'm4v'];
+
public function resolve(string $url): ?string
{
if ($url === '') {
return null;
}
- return null;
+ return $this->detectVideo($url);
+ }
+
+ private function detectVideo(string $url): ?string
+ {
+ if (!in_array($this->getPathExtension($url), self::VIDEO_EXT, true)) {
+ return null;
+ }
+ $safe = htmlspecialchars($url, ENT_QUOTES, 'UTF-8');
+ return "";
+ }
+
+ private function getPathExtension(string $url): string
+ {
+ $path = parse_url($url, PHP_URL_PATH);
+ if ($path === null || $path === false) {
+ return '';
+ }
+ return strtolower(pathinfo($path, PATHINFO_EXTENSION));
}
}
diff --git a/src/tests/Unit/Markdown/MediaUrlResolverTest.php b/src/tests/Unit/Markdown/MediaUrlResolverTest.php
index b28446c..c24411a 100644
--- a/src/tests/Unit/Markdown/MediaUrlResolverTest.php
+++ b/src/tests/Unit/Markdown/MediaUrlResolverTest.php
@@ -35,4 +35,36 @@ public static function nonMediaUrls(): array
'youtu.be lookalike host' => ['https://example.com/youtu.be-fake/abc'],
];
}
+
+ #[DataProvider('videoUrls')]
+ public function test_video_urls_produce_video_tag(string $url): void
+ {
+ $html = $this->resolver->resolve($url);
+ $this->assertNotNull($html);
+ $this->assertStringStartsWith('