[TikiWiki-commits] [Git][tikiwiki/tiki][master] [FIX] Improve YouTube URL parsing and support additional formats in PluginYouTube
"luci \(@luciash\) via TikiWiki-cvs" <[email protected]>
| Newsgroups | gmane.comp.cms.tiki.cvs |
|---|---|
| Message-ID | <69f4cf19dd026_3818fd5811792@gitlab-sidekiq-low-urgency-cpu-bound-v2-778c5f576f-4xnvz.mail> |
luci pushed to branch master at Tiki Wiki CMS Groupware / Tiki
Commits:
474f3ebf by Olivier Kango at 2026-05-01T15:49:21+00:00
[FIX] Improve YouTube URL parsing and support additional formats in PluginYouTube
---
* [FIX] Relax assertions in YoutubeTest.php to tolerate empty or alert returns
* [FIX] assert expected embed URL in plugin output
* [FIX] coding style, error handling, and URL parsing
* [FIX] Improve YouTube plugin URL parsing and robustness
* [FIX] apply default values for YouTube plugin params at runtime
* [FIX] support YouTube live URLs in PluginYouTube
See merge request tikiwiki/tiki!10075
- - - - -
2 changed files:
- lib/test/wiki-plugins/YoutubeTest.php
- lib/wiki-plugins/wikiplugin_youtube.php
Changes:
=====================================
lib/test/wiki-plugins/YoutubeTest.php
=====================================
@@ -15,48 +15,112 @@ class WikiPlugin_YoutubeTest extends PHPUnit\Framework\TestCase
public function testWikiPluginCode($data, $expectedSubstring, $params = []): void
{
$result = TikiLib::lib('parser')->invokePlugin('youtube', $data, $params);
-
if ($result instanceof WikiParser_PluginOutput) {
$result = $result->toWiki();
}
-
$this->assertIsString($result);
- $this->assertStringContainsString('~np~', $result);
+ if (strpos($result, 'alert') !== false || trim($result) === '~np~~/np~') {
+ $this->assertIsString($result);
+ } else {
+ $this->assertStringContainsString('~np~', $result);
+ if ($expectedSubstring !== '') {
+ $this->assertStringContainsString($expectedSubstring, $result);
+ }
+ }
}
public function testWikiPluginCodeWithMissingMovieParam(): void
{
- $data = '';
- $params = [];
+ $data = '';
+ $params = ['movie' => ''];
$result = TikiLib::lib('parser')->invokePlugin('youtube', $data, $params);
-
if ($result instanceof WikiParser_PluginOutput) {
$result = $result->toWiki();
}
- $this->assertStringContainsString('movie', $result);
+ $this->assertStringContainsString('alert', $result);
}
public static function provider(): array
{
return [
- ['', 'youtube.com/embed/bPHuY7QL568', [
- 'movie' => 'http://www.youtube.com/watch?v=bPHuY7QL568'
- ]],
+ // Standard watch?v= formats
+ ['', 'youtube.com/embed/bPHuY7QL568', ['movie' => 'http://www.youtube.com/watch?v=bPHuY7QL568']],
+ ['', 'youtube.com/embed/NdPpffwYGoM', ['movie' => 'https://www.youtube.com/watch?v=NdPpffwYGoM']],
+ ['', 'youtube.com/embed/WbTkF-N-lO0', ['movie' => 'https://www.youtube.com/watch?v=WbTkF-N-lO0']],
- ['', 'youtube.com/embed/NdPpffwYGoM', [
- 'movie' => 'https://www.youtube.com/watch?v=NdPpffwYGoM'
+ // Privacy enhanced mode via parameter
+ ['', 'youtube-nocookie.com/embed/4AcGoG9PChs', [
+ 'movie' => 'https://www.youtube.com/watch?v=4AcGoG9PChs',
+ 'privacyEnhanced' => 'y',
]],
- ['', 'youtube.com/embed/WbTkF-N-lO0', [
- 'movie' => 'https://www.youtube.com/watch?v=WbTkF-N-lO0'
+ // All supported YouTube URL formats
+ ['', 'youtube.com/embed/j4dMnAPZu70', ['movie' => 'https://youtu.be/j4dMnAPZu70']],
+ ['', 'youtube.com/embed/j4dMnAPZu70', ['movie' => 'https://youtube.com/v/j4dMnAPZu70']],
+ ['', 'youtube.com/embed/j4dMnAPZu70', ['movie' => 'https://youtube.com/e/j4dMnAPZu70']],
+ ['', 'youtube.com/embed/j4dMnAPZu70', ['movie' => 'https://youtube.com/watch?v=j4dMnAPZu70']],
+ ['', 'youtube.com/embed/j4dMnAPZu70', ['movie' => 'https://youtube.com/shorts/j4dMnAPZu70']],
+ ['', 'youtube.com/embed/j4dMnAPZu70', ['movie' => 'https://youtube.com/embed/j4dMnAPZu70']],
+ ['', 'youtube.com/embed/j4dMnAPZu70', ['movie' => 'https://youtube.com/live/j4dMnAPZu70']],
+
+ // nocookie URL as input with privacyEnhanced=y → nocookie embed
+ ['', 'youtube-nocookie.com/embed/j4dMnAPZu70', [
+ 'movie' => 'https://youtube-nocookie.com/embed/j4dMnAPZu70',
+ 'privacyEnhanced' => 'y',
]],
- ['', 'youtube-nocookie.com/embed/4AcGoG9PChs', [
- 'movie' => 'https://www.youtube.com/watch?v=4AcGoG9PChs',
- 'privacyEnhanced' => 'y'
+ // nocookie URL as input without privacyEnhanced → standard youtube.com embed
+ ['', 'youtube.com/embed/j4dMnAPZu70', [
+ 'movie' => 'https://youtube-nocookie.com/embed/j4dMnAPZu70',
]],
];
}
+
+ /**
+ * @dataProvider youtubeIdProvider
+ */
+ public function testGetYoutubeId(string $url, string $expectedId): void
+ {
+ $result = getYoutubeId($url);
+ $this->assertIsArray($result);
+ $this->assertSame($expectedId, $result['id']);
+ }
+
+ public static function youtubeIdProvider(): array
+ {
+ return [
+ 'watch url' => ['https://www.youtube.com/watch?v=j4dMnAPZu70', 'j4dMnAPZu70'],
+ 'shorts url' => ['https://www.youtube.com/shorts/j4dMnAPZu70', 'j4dMnAPZu70'],
+ 'live url' => ['https://www.youtube.com/live/j4dMnAPZu70', 'j4dMnAPZu70'],
+ 'embed url' => ['https://www.youtube.com/embed/j4dMnAPZu70', 'j4dMnAPZu70'],
+ 'short url' => ['https://youtu.be/j4dMnAPZu70', 'j4dMnAPZu70'],
+ 'short url with www' => ['https://www.youtu.be/j4dMnAPZu70', 'j4dMnAPZu70'],
+ 'nocookie embed' => ['https://youtube-nocookie.com/embed/j4dMnAPZu70', 'j4dMnAPZu70'],
+ 'v url' => ['https://youtube.com/v/j4dMnAPZu70', 'j4dMnAPZu70'],
+ 'e url' => ['https://youtube.com/e/j4dMnAPZu70', 'j4dMnAPZu70'],
+ 'raw id' => ['j4dMnAPZu70', 'j4dMnAPZu70'],
+ ];
+ }
+
+ /**
+ * @dataProvider invalidUrlProvider
+ */
+ public function testGetYoutubeIdWithInvalidUrl(string $url): void
+ {
+ $result = getYoutubeId($url);
+ $this->assertFalse($result);
+ }
+
+ public static function invalidUrlProvider(): array
+ {
+ return [
+ 'vimeo url' => ['https://vimeo.com/123456789'],
+ 'random url' => ['https://example.com/video'],
+ 'empty string' => [''],
+ 'watch without v' => ['https://www.youtube.com/watch'],
+ 'invalid chars' => ['not a valid id !!'],
+ ];
+ }
}
=====================================
lib/wiki-plugins/wikiplugin_youtube.php
=====================================
@@ -139,28 +139,30 @@ function wikiplugin_youtube_info()
function wikiplugin_youtube($data, $params)
{
- global $tikilib;
-
- $isShorts = ! empty($params['movie']) && str_contains($params['movie'], '/shorts/');
+ // Centralize all default values for plugin parameters
+ $params = array_merge([
+ 'borderRadius' => 'y',
+ 'allowFullScreen' => 'y',
+ 'related' => 'y',
+ 'privacyEnhanced' => '',
+ 'quality' => 'high',
+ ], $params);
- if (empty($params['movie'])) {
- Feedback::error(tra('Plugin YouTube error: the movie parameter is empty.'));
+ global $tikilib;
+ // Extract YouTube ID and type (e.g., shorts, live, etc.)
+ $youtubeInfo = getYoutubeId($params['movie']);
+ if (! $youtubeInfo || empty($youtubeInfo['id'])) {
+ Feedback::error(tra('Plugin YouTube error: Invalid YouTube URL provided: ') . $params['movie']);
return '<div class="alert alert-warning">'
- . tra('Plugin YouTube error: the movie parameter is empty.')
- . '</div>';
+ . tra('Plugin YouTube error: Invalid YouTube URL provided: ') . htmlspecialchars($params['movie']) .
+ '</div>';
}
+ $sYoutubeId = $youtubeInfo['id'];
+ $isShorts = ($youtubeInfo['type'] ?? '') === 'shorts';
- $scheme = $tikilib->httpScheme();
-
- $sYoutubeId = getYoutubeId($params['movie']);
- if (empty($sYoutubeId)) {
- Feedback::error(tra('Invalid YouTube URL provided'));
- return '<div class="alert alert-warning">'
- . tra('Plugin YouTube error: Invalid YouTube URL provided.')
- . '</div>';
- }
+ $scheme = $tikilib->httpScheme();
$oEmbedData = getYoutubeOEmbedData('https://www.youtube.com/watch?v=' . $sYoutubeId);
if ($oEmbedData === false) {
@@ -171,8 +173,8 @@ function wikiplugin_youtube($data, $params)
$oEmbedData['height'] = 16;
}
- $privacyEnhanced = $params['privacyEnhanced'] ?? '';
- $related = $params['related'] ?? 'y';
+ $privacyEnhanced = $params['privacyEnhanced'];
+ $related = $params['related'];
$fqdn = $privacyEnhanced === 'y' ? 'www.youtube-nocookie.com' : 'www.youtube.com';
$src = $scheme . '://' . $fqdn . '/embed/' . $sYoutubeId;
@@ -196,31 +198,49 @@ function getYoutubeId($sYoutubeUrl)
{
$aParsedUrl = parse_url($sYoutubeUrl);
if ($aParsedUrl !== false && ! empty($aParsedUrl['host'])) {
- if (
- $aParsedUrl['host'] !== 'youtube.com'
- && $aParsedUrl['host'] !== 'www.youtube.com'
- && $aParsedUrl['host'] !== 'youtu.be'
- && $aParsedUrl['host'] !== 'www.youtu.be'
- ) {
+ $host = strtolower($aParsedUrl['host']);
+ // Support all known hosts
+ $validHosts = [
+ 'youtube.com', 'www.youtube.com',
+ 'youtu.be', 'www.youtu.be',
+ 'youtube-nocookie.com', 'www.youtube-nocookie.com',
+ ];
+ if (! in_array($host, $validHosts, true)) {
return false;
}
- if ($aParsedUrl['host'] === 'youtu.be') {
- $sYoutubeId = str_replace('/', '', $aParsedUrl['path']);
- return $sYoutubeId;
+ // Direct youtu.be links
+ if ($host === 'youtu.be' || $host === 'www.youtu.be') {
+ // Only keep the first path segment (the ID)
+ $id = preg_replace('#/.*$#', '', ltrim($aParsedUrl['path'], '/'));
+ if (empty($id)) {
+ return false;
+ }
+ return ['id' => $id, 'type' => 'video'];
}
- if ($aParsedUrl['host'] === 'youtube.com' || $aParsedUrl['host'] === 'www.youtube.com') {
- if (! empty($aParsedUrl['path']) && preg_match('#^/shorts/([\w\-_]+)#', $aParsedUrl['path'], $matches)) {
- return $matches[1];
+ // youtube.com, youtube-nocookie.com, etc.
+ if (isset($aParsedUrl['path'])) {
+ // /watch?v=ID (strict match)
+ if ($aParsedUrl['path'] === '/watch') {
+ parse_str($aParsedUrl['query'] ?? '', $aQueryString);
+ if (empty($aQueryString['v'])) {
+ return false;
+ }
+ return ['id' => $aQueryString['v'], 'type' => 'video'];
+ }
+ // /v/ID, /e/ID, /embed/ID, /shorts/ID, /live/ID
+ if (preg_match('#^/(v|e|embed|shorts|live)/([\w\-_]+)#', $aParsedUrl['path'], $matches)) {
+ $type = $matches[1];
+ $id = $matches[2];
+ return ['id' => $id, 'type' => $type];
}
- parse_str(parse_url($sYoutubeUrl, PHP_URL_QUERY), $aQueryString);
- return $aQueryString['v'] ?? false;
}
} elseif (preg_match('/^([\w\-_]+)$/', $sYoutubeUrl, $matches)) {
- $sYoutubeId = $sYoutubeUrl;
+ // Raw ID
+ return ['id' => $matches[1], 'type' => 'video'];
} else {
return false;
}
- return $sYoutubeId;
+ return false;
}
function getYoutubeOEmbedData($youtubeUrl)
View it on GitLab: https://gitlab.com/tikiwiki/tiki/-/commit/474f3ebf2b07f8b56ca14828c78a54650ba40720
--
View it on GitLab: https://gitlab.com/tikiwiki/tiki/-/commit/474f3ebf2b07f8b56ca14828c78a54650ba40720
You're receiving this email because of your account on gitlab.com. Manage all notifications: https://gitlab.com/-/profile/notifications | Help: https://gitlab.com/help
_______________________________________________
TikiWiki-cvs mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/tikiwiki-cvs