Repository navigation
feat(seo): add key moments to video schema from chapters (#1597) - #2194
rudrakshi-gupta merged 3 commits into
Conversation
Output each GoDAM video chapter as a schema.org Clip in the VideoObject's hasPart, so Google can show "key moments" (jump-to links) for the video in search results. - Add Seo::get_key_moment_clips(): reads the chapters from the attachment's rtgodam_meta at output time, so editing chapters updates the schema without re-saving the page. - Each Clip has name, startOffset, endOffset and a deep link of the form <page>?t=<seconds>#godam-video-<attachment id>, which the GoDAM player already uses to seek on page load. - A clip ends at the chapter's own endTime, else the next chapter's start, else the video duration (clamped to the duration when known). - Skip chapters with no title, an unreadable start, a start at or past the end of the video, or zero length; drop duplicate start times; strip HTML from chapter names. - Parse chapter times as plain seconds, mm:ss or hh:mm:ss(.ms), falling back to originalTime when startTime is missing. - Build clip URLs without add_query_arg(): use & on plain permalinks and drop any existing fragment from the page URL. - Only add hasPart when the video has an attachment and a host page (render-time output on archives/404 has nothing to deep-link to). - Add filters godam_video_seo_key_moments_enabled and godam_video_seo_key_moments. - Move ISO 8601 duration parsing to Seo::iso8601_to_seconds(); Video_Sitemap::iso8601_duration_to_seconds() now delegates to it. - Add VideoSeoKeyMomentsTest (27 tests) and a get_permalink() test stub.
🔍 WordPress Plugin Check Report
📊 Report
|
| 📍 Line | 🔖 Check | 💬 Message |
|---|---|---|
620 |
WordPress.DB.SlowDBQuery.slow_db_query_meta_key | Detected usage of meta_key, possible slow query. |
📁 readme.txt (2 warnings)
| 📍 Line | 🔖 Check | 💬 Message |
|---|---|---|
0 |
mismatched_plugin_name | Plugin name "GoDAM - Organize WordPress Media Library & File Manager with Unlimited Folders for Images, Videos & more" is different from the name declared in plugin header "GoDAM". |
0 |
trademarked_term | The plugin name includes a restricted term. Your chosen plugin name - "GoDAM - Organize WordPress Media Library & File Manager with Unlimited Folders for Images, Videos & more" - contains the restricted term "wordpress" which cannot be used at all in your plugin name. |
📁 inc/classes/class-video-sitemap-core-provider.php (1 warning)
| 📍 Line | 🔖 Check | 💬 Message |
|---|---|---|
73 |
WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound | Hook names invoked by a theme/plugin should start with the theme/plugin prefix. Found: "wp_sitemaps_index_entry". |
📁 assets/build/css/main.css (1 warning)
| 📍 Line | 🔖 Check | 💬 Message |
|---|---|---|
0 |
EnqueuedStylesScope | This style is being loaded in all contexts. |
📁 assets/src/libs/analytics.min.js (5 warnings)
| 📍 Line | 🔖 Check | 💬 Message |
|---|---|---|
0 |
EnqueuedScriptsScope | This script is being loaded in all frontend contexts. |
0 |
NonBlockingScripts.NoStrategy | This script on http://localhost:8880 (with handle analytics-library) is loaded in the footer. Consider a defer or async script loading strategy instead. |
0 |
NonBlockingScripts.NoStrategy | This script on http://localhost:8880/2026/10/19/hello-world/ (with handle analytics-library) is loaded in the footer. Consider a defer or async script loading strategy instead. |
0 |
NonBlockingScripts.NoStrategy | This script on http://localhost:8880/sample-page/ (with handle analytics-library) is loaded in the footer. Consider a defer or async script loading strategy instead. |
0 |
NonBlockingScripts.NoStrategy | This script on http://localhost:8880/demo-attachment-post/ (with handle analytics-library) is loaded in the footer. Consider a defer or async script loading strategy instead. |
📁 assets/build/js/main.min.js (5 warnings)
| 📍 Line | 🔖 Check | 💬 Message |
|---|---|---|
0 |
EnqueuedScriptsScope | This script is being loaded in all frontend contexts. |
0 |
NonBlockingScripts.NoStrategy | This script on http://localhost:8880 (with handle rtgodam-script) is loaded in the footer. Consider a defer or async script loading strategy instead. |
0 |
NonBlockingScripts.NoStrategy | This script on http://localhost:8880/2026/10/19/hello-world/ (with handle rtgodam-script) is loaded in the footer. Consider a defer or async script loading strategy instead. |
0 |
NonBlockingScripts.NoStrategy | This script on http://localhost:8880/sample-page/ (with handle rtgodam-script) is loaded in the footer. Consider a defer or async script loading strategy instead. |
0 |
NonBlockingScripts.NoStrategy | This script on http://localhost:8880/demo-attachment-post/ (with handle rtgodam-script) is loaded in the footer. Consider a defer or async script loading strategy instead. |
🤖 Generated by WordPress Plugin Check Action • Learn more about Plugin Check
subodhr258
left a comment
There was a problem hiding this comment.
Reviewed at 2ebc723: read the code, ran PHPUnit (426 pass), ran the clip builder on edge cases, and ran the player's deep-link parsing on the generated URLs. Not tested in a browser or in Google's tools.
Conflict with the base branch: GitHub shows a conflict since #2186 moved to d7368b1. It's only tests/stubs/video-seo-functions.php, where both sides add stubs at the same spot; keep both. class-seo.php and class-video-sitemap.php merge cleanly, and the full suite passes on the merged result (435 tests).
CI: the unit test and POT checks don't run here because the base isn't develop. They'll run once #2186 merges and this is retargeted.
Claims
| Claim | How checked | Result |
|---|---|---|
One Clip per chapter in hasPart |
Code + tests | True |
| Each clip URL opens the page and seeks the player | Player code (applyDeepLink() in frontend.js) run on the URLs |
True while the #godam-video-<id> part is there. ?t= alone doesn't seek, inline |
End from endTime, then the next chapter, then the duration |
Ran it with the editor's stored shape and GoDAM Central's | True, with two edge cases, inline |
| Same output for block, Elementor and WPBakery | Code | True: all three render through inc/templates/godam-player.php, which sets data-id |
| Chapter edits show without a re-save | Code | True: chapters are read at render time |
| Eligible for key moments | Google's Clip guidelines | Needs the 30-second minimum, inline |
Small wording point: the docblocks call #godam-video-<id> an anchor, but no element has that id. The player matches it in JS against data-id. It works; the comment just doesn't match.
Not tested: a real Google result, the Rich Results Test, page caches with ?t= URLs.
subodhr258
left a comment
There was a problem hiding this comment.
Re-reviewed at cb7e9ef: read the changes since 2ebc723, ran PHPUnit (449 pass) and the lightboxTargets JS tests (39 pass), re-ran my earlier chapter cases, and ran applyDeepLink() in Jest with the player mocked. Not run in a browser or in Google's tools.
Earlier comments: all fixed.
| Comment | At cb7e9ef |
|---|---|
?t= without the #godam-video-<id> part doesn't seek |
Fixed: with no fragment, the first inline video on the page is seeked |
| Videos under 30 seconds get key moments | Fixed: skipped when the known duration is under 30 s |
| A clip ends at a chapter that's later dropped as a duplicate | Fixed: Intro 0, Recap 0.4, Main 30 now gives Intro 0 -> 30, Main 30 -> 120 |
| Neighbouring clips overlap by a second | Fixed: a boundary at 40.5 s gives 0 -> 40 and 40 -> 95 |
| Docblocks call the fragment an anchor | Fixed |
The base-branch conflict is gone, and the PR adds no new translatable strings.
One new issue, inline: on category, tag and author archives the clip URLs point at an unrelated post. The cause is the host post ID in #2186, and the one-line fix I suggested there fixes this too.
Worth knowing: any URL with ?t=<number> now seeks the first video on the page and scrolls to it, also when t means something else on that site (in Jest, ?t=5&utm=x seeked the first video to 0:05). Probably fine. applyDeepLink() has no test of its own, so a small one would pin the new no-fragment path.
Not tested: a real Google result, the Rich Results Test, a live archive page.
9b953b5
into
feature/1622-video-seo-sitemap
* feat(seo): fix VideoObject schema and add video sitemap (#1622 v1) Make GoDAM's video schema indexable by Google and add a video sitemap that plugs into Yoast SEO, Rank Math, All in One SEO or the WordPress core sitemap. No new public URLs are created. - Schema contentUrl now uses the HLS (.m3u8) stream, falling back to the original file; the DASH .mpd is never output. Resolved at output time, so pages cached before this change are fixed without a re-save. - Add embedUrl pointing at GoDAM's existing video-embed player page. - Never output an empty description: fall back to a 160-char transcript excerpt (fetched on save only, cached in _godam_seo_transcript_excerpt), then the page excerpt, otherwise omit the property. - Output isFamilyFriendly as a real JSON boolean (string values such as "false" are parsed correctly) and omit an empty uploadDate. - Add Video_Sitemap: /godam-video-sitemap.xml with one <url> per published page and one <video:video> per video, built from the cached video SEO post meta via the shared schema builder so both always agree. - Exclude drafts, private and password-protected posts, pages noindexed in Yoast / Rank Math / AIOSEO, and videos the editor excludes. - List the sitemap in the Yoast, Rank Math and AIOSEO indexes, or via a core sitemap provider when none is active; paginate at 1000 URLs, cache per site with version-based invalidation. - Add GoDAM > Settings > SEO with an "Enable video sitemap" toggle (off by default) and a per-video "Exclude from video sitemap" toggle in Gutenberg, Elementor and WPBakery. - Warn editors in all three editors when a video title looks like a raw file name. - Prefer the HLS URL when prefilling SEO contentUrl in the editors. - Add new filters: godam_video_seo_content_url, godam_video_seo_embed_url, godam_video_seo_description, godam_video_sitemap_enabled, godam_video_sitemap_entries_per_page, godam_video_sitemap_post_types, godam_video_sitemap_exclude_post, godam_video_sitemap_entry. - Add PHPUnit coverage (VideoSeoSchemaTest, VideoSitemapTest) and Jest tests for the filename-title check. * Resolve feedback * Resolve feedbacks and regenrate POT file * Revert the POT file * Resolve Privacy Issue * feat(seo): add key moments to video schema from chapters (#1597) (#2194) Output each GoDAM video chapter as a schema.org Clip in the VideoObject's hasPart, so Google can show "key moments" (jump-to links) for the video in search results. - Add Seo::get_key_moment_clips(): reads the chapters from the attachment's rtgodam_meta at output time, so editing chapters updates the schema without re-saving the page. - Each Clip has name, startOffset, endOffset and a deep link of the form <page>?t=<seconds>#godam-video-<attachment id>, which the GoDAM player already uses to seek on page load. - A clip ends at the chapter's own endTime, else the next chapter's start, else the video duration (clamped to the duration when known). - Skip chapters with no title, an unreadable start, a start at or past the end of the video, or zero length; drop duplicate start times; strip HTML from chapter names. - Parse chapter times as plain seconds, mm:ss or hh:mm:ss(.ms), falling back to originalTime when startTime is missing. - Build clip URLs without add_query_arg(): use & on plain permalinks and drop any existing fragment from the page URL. - Only add hasPart when the video has an attachment and a host page (render-time output on archives/404 has nothing to deep-link to). - Add filters godam_video_seo_key_moments_enabled and godam_video_seo_key_moments. - Move ISO 8601 duration parsing to Seo::iso8601_to_seconds(); Video_Sitemap::iso8601_duration_to_seconds() now delegates to it. - Add VideoSeoKeyMomentsTest (27 tests) and a get_permalink() test stub. * Match WPBakery SEO Settings to Gutenberg Model (#2209)
* Stop the admin menu jumping when a GoDAM page loads (#2207) * Fix: stop the admin menu jumping when a GoDAM page loads pages/style.css restores Tailwind's border reset for every element on the page. It is injected by page-css.min.js after the page has painted, and core's a { transition: border .05s } then animates each admin menu link's border from 3px to 0: every item grows from 34px to 40px and the whole menu jumps by about 186px, then snaps back. Keep the reset off #adminmenumain and #wpadminbar. * Fix: open the Media Editor with the admin menu already folded The editor folds the menu from React after the page has painted, so the menu showed at full width and then collapsed. Add the folded body class on the server when the editor opens a file. * Keep the reset off the menu and admin bar containers too; test the editor fold - pages/style.css also excludes #adminmenumain and #wpadminbar themselves, not only their descendants. - MediaEditorMenuFoldTest: the Media Editor with an id gets 'folded'; its file picker, other GoDAM pages, the Media Library and no screen do not. * Regenerate the POT: 12 references into class-pages.php moved down Line numbers only; no strings added or changed. Built like CI: Node 22.14.0, npm ci, npm run build:prod, composer pot. * feat(seo): fix VideoObject schema and add video sitemap (#2186) * feat(seo): fix VideoObject schema and add video sitemap (#1622 v1) Make GoDAM's video schema indexable by Google and add a video sitemap that plugs into Yoast SEO, Rank Math, All in One SEO or the WordPress core sitemap. No new public URLs are created. - Schema contentUrl now uses the HLS (.m3u8) stream, falling back to the original file; the DASH .mpd is never output. Resolved at output time, so pages cached before this change are fixed without a re-save. - Add embedUrl pointing at GoDAM's existing video-embed player page. - Never output an empty description: fall back to a 160-char transcript excerpt (fetched on save only, cached in _godam_seo_transcript_excerpt), then the page excerpt, otherwise omit the property. - Output isFamilyFriendly as a real JSON boolean (string values such as "false" are parsed correctly) and omit an empty uploadDate. - Add Video_Sitemap: /godam-video-sitemap.xml with one <url> per published page and one <video:video> per video, built from the cached video SEO post meta via the shared schema builder so both always agree. - Exclude drafts, private and password-protected posts, pages noindexed in Yoast / Rank Math / AIOSEO, and videos the editor excludes. - List the sitemap in the Yoast, Rank Math and AIOSEO indexes, or via a core sitemap provider when none is active; paginate at 1000 URLs, cache per site with version-based invalidation. - Add GoDAM > Settings > SEO with an "Enable video sitemap" toggle (off by default) and a per-video "Exclude from video sitemap" toggle in Gutenberg, Elementor and WPBakery. - Warn editors in all three editors when a video title looks like a raw file name. - Prefer the HLS URL when prefilling SEO contentUrl in the editors. - Add new filters: godam_video_seo_content_url, godam_video_seo_embed_url, godam_video_seo_description, godam_video_sitemap_enabled, godam_video_sitemap_entries_per_page, godam_video_sitemap_post_types, godam_video_sitemap_exclude_post, godam_video_sitemap_entry. - Add PHPUnit coverage (VideoSeoSchemaTest, VideoSitemapTest) and Jest tests for the filename-title check. * Resolve feedback * Resolve feedbacks and regenrate POT file * Revert the POT file * Resolve Privacy Issue * feat(seo): add key moments to video schema from chapters (#1597) (#2194) Output each GoDAM video chapter as a schema.org Clip in the VideoObject's hasPart, so Google can show "key moments" (jump-to links) for the video in search results. - Add Seo::get_key_moment_clips(): reads the chapters from the attachment's rtgodam_meta at output time, so editing chapters updates the schema without re-saving the page. - Each Clip has name, startOffset, endOffset and a deep link of the form <page>?t=<seconds>#godam-video-<attachment id>, which the GoDAM player already uses to seek on page load. - A clip ends at the chapter's own endTime, else the next chapter's start, else the video duration (clamped to the duration when known). - Skip chapters with no title, an unreadable start, a start at or past the end of the video, or zero length; drop duplicate start times; strip HTML from chapter names. - Parse chapter times as plain seconds, mm:ss or hh:mm:ss(.ms), falling back to originalTime when startTime is missing. - Build clip URLs without add_query_arg(): use & on plain permalinks and drop any existing fragment from the page URL. - Only add hasPart when the video has an attachment and a host page (render-time output on archives/404 has nothing to deep-link to). - Add filters godam_video_seo_key_moments_enabled and godam_video_seo_key_moments. - Move ISO 8601 duration parsing to Seo::iso8601_to_seconds(); Video_Sitemap::iso8601_duration_to_seconds() now delegates to it. - Add VideoSeoKeyMomentsTest (27 tests) and a get_permalink() test stub. * Match WPBakery SEO Settings to Gutenberg Model (#2209) * ci: run the POT check only on PRs into main (#2188) * ci: run the POT check only on the develop → main merge Feature PRs into develop no longer have to regenerate languages/godam.pot. The release PR from develop into main still fails if the committed POT is stale. PRs into main from any other branch skip the job. * ci: run the POT check on every PR into main Patch releases reach main from fix and hotfix branches, not only from develop, so they need the check too. The branches: [main] trigger already keeps it off feature PRs into develop. * Pass only a numeric form ID to the form layer preview (#2223) Cast the layer id to an integer in the preview template and in the Ninja Forms and MetForm callbacks, and add unit tests for it. * Keep the Gravity Forms recorder working after an AJAX page change (#2217) * Keep the Gravity Forms recorder working after an AJAX page change Gravity Forms replaces a page's markup on every AJAX page change or failed submit. The recorder kept pointing at the replaced container, file input and button, and Uppy had bound its dashboard trigger once to the old button, so Start Recording opened nothing on the new page. On every gform_post_render, an uploader whose markup left the page now finds the new container by its id, reads the input and button from it, binds the new button once and shows the recording it holds again. * Regenerate the translation template for the recorder's moved lines * Stop sending the signed-in viewer's email with video analytics (#2213) * Stop sending the signed-in viewer's email with video analytics The page params that GoDAM prints on every front-end page no longer carry the current user's email address (`emailId`), and the analytics request body no longer has an `email` field. The WordPress user id is still sent. Tests: the request-body helper and the page params are both checked for the absence of the email, including a page cached by an older version. * Remove the viewer's email from the analytics data listed in the readme The analytics.godam.io entry under External services no longer lists Email as data sent. * Keep the signed-in viewer's email out of the player's page data The player script's page data (godamData.currentLoggedInUserData) carried the current user's email address, which nothing in the browser reads. It now holds the display name and whether someone is signed in. The comments routes still read the email on the server. * Keep the fixture lines untouched in the JS test The explanation moves into the test, so the fixture lines this branch leaves alone cannot conflict with the sibling change. * List the page's user fields instead of removing one rtgodam_get_current_user_data_for_page() now returns only the display name and the sign-in type, so a field added later to the server-side user data is not printed into the page by accident. * Regenerate the POT for the new line numbers The new helper in custom-functions.php moves five strings further down the file, so their source references change. Nothing else in the file differs from develop. * Clean up video share popup: remove GoDAM Central/Embed links, use Page link, show share button for all videos (#2211) * Clean up video share popup links and share button gating - Remove the redundant GoDAM Central and Embed copy fields from the share modal footer, keeping only the Page Link field. - Point the social share links at the WordPress page link instead of the GoDAM Central URL, which is private by default and shows a login prompt. - Show the share button for all videos when sharing is enabled in the block settings, instead of only transcoded videos. Closes #2210 * Address review: keep share anchor and gate button on an ID - Encode the share link with encodeURIComponent so the #godam-video-{id} anchor and any query param travel with the social share URL instead of splitting off (encodeURI left #, ? and & intact). - Hide the share button for players with neither a WordPress attachment ID nor a transcoding job ID ("Insert from URL" embeds), since their share link could not target the video. - Remove stale README references to the removed GoDAM Central/Embed links and correct the ID described for share links. --------- Co-authored-by: KMchaudhary <kuldipkumar.chaudhary@rtcamp.com> * Save converted MP3 audio as audio/mpeg, and fix files already saved as audio/mp3 (#2208) * Fix: store transcoded MP3 audio as audio/mpeg, not audio/mp3 WordPress registers MP3 as audio/mpeg. Attachments saved as audio/mp3 were left out of REST media_type=audio, and REST ignored a mime_type=audio/mp3 filter. New conversions are saved as audio/mpeg, and a one-time migration (2.4.1 batch) fixes the ones saved before. * Test the saved type, satisfy the hook-integrity check, retry a failed SELECT - TranscodedAudioMimeTypeTest calls add_transcoded_files() and checks that converted audio is saved as audio/mpeg (video stays video/mp4). With the old audio/mp3 it fails. - add_transcoded_files() starts $transcoded_files as array(), not false; PHP 8.1+ reported every conversion as a deprecated false-to-array use. - The migration's two direct queries carry godam-coverage-ignore: the runner already runs it once per site. - A failed SELECT now returns false so the runner retries, instead of marking the site done with nothing changed. - The runner test checks batch order instead of which batch is last. * Run the MP3 type migration in the 2.5.0 batch 2.4.1 ships without this change but still stores 2.4.1 as each site's migration version, and the runner skips batches at or below the stored version. Keyed 2.4.1, the migration would never run on sites that update through 2.4.1; keyed 2.5.0 it runs for both 2.4.0 and 2.4.1 sites. * Release prep v2.5.0: version bump, changelog and POT refresh (#2231) - Version 2.5.0 in godam.php, readme.txt, README.md, package.json and package-lock.json. - v2.5.0 changelog in readme.txt and CHANGELOG.md; v2.3.2 removed from readme.txt to keep the last three. - Every @SInCE n.e.x.t replaced with 2.5.0 (51 tags in 6 files). - languages/godam.pot regenerated from a production build. * Fix the two new Plugin Check warnings in the Video SEO sitemap code (#2232) - class-video-sitemap.php: the phpcs:ignore named slow_query_meta_key, which is not a sniff, so it ignored nothing. Use slow_db_query_meta_key. - class-video-sitemap-core-provider.php: ignore NonPrefixedHooknameFound on WordPress core's own wp_sitemaps_index_entry filter. * Keep page-builder shortcode text out of the video sitemap description (#2235) * Keep page-builder shortcode text out of the video sitemap description The sitemap is served at parse_request priority 1, before WPBakery and Divi register their shortcodes, so core's strip_shortcodes() left tags such as [vc_row] in the fallback description taken from the page text. Add the tag names found in the text to strip_shortcodes()'s own list so unregistered shortcodes are removed the same way registered ones are, which gives the same description in the sitemap and on the front end. The strip_shortcodes() test stub removed every [tag], which hid the bug. It now follows core: only registered shortcodes are removed. * Only strip builder-style unregistered shortcode names from the fallback description Review of the first version found that it also removed ordinary bracketed text ([sic], [1], [b]bold[/b]) from the front-end description, that one very long bracketed word made core's pattern too large and the description empty, that a tag another filter had taken off the strip list was re-added, and that the filter stayed registered if stripping threw. Only names that look like a builder tag are added now (a letter first, an underscore or hyphen in the name, 64 characters at most), tags that are registered are left to core, and the filter is removed in a finally block. New tests pin the cap, the length limit, the filter removal and the ordinary bracketed text. * Set the 2.5.0 changelog date to October 22, 2026 (#2236) 2.5.0 releases on 22 Oct (release ticket rtCamp/godam-plugin-wp#60), but readme.txt and CHANGELOG.md dated it October 21. --------- Co-authored-by: Kuldip Chaudhary <64731232+KMchaudhary@users.noreply.github.com> Co-authored-by: Rudrakshi Gupta <109859631+rudrakshi-gupta@users.noreply.github.com> Co-authored-by: Vishal Kumar <vishnshiv3@gmail.com> Co-authored-by: KMchaudhary <kuldipkumar.chaudhary@rtcamp.com>
Video SEO: key moments from video chapters
Summary
Google can show key moments for a video in search results: links that jump straight to a point in the video. GoDAM videos already have chapters (video editor → Chapters).
This PR outputs each chapter as a schema.org
Clipin the page'sVideoObjectschema (hasPart). Every clip gets a deep link that opens the page and seeks the GoDAM player to that time.Closes #1597. Part of the Video SEO epic #1622.
Before / after
Data changes
None. This reads existing chapter data (
rtgodam_meta['chapters']) and adds no new meta, options or settings.Out of scope
SeekToAction(automatic key moments) isn't added; chapters are the explicit source.Testing
Automated
composer phpunit: 426 tests pass, including 27 new ones intests/php/VideoSeoKeyMomentsTest.php:endTime, the next chapter or the duration, plus clamping to the duration and no end when the duration is unknown;originalTimefallback;&handling and fragment removal;mm:ss,hh:mm:ss.ms, invalid);hasPartonly when there are chapters, an attachment and a host page;vendor/bin/phpcs: clean.npm run lint:godam-hooks: 0 uncovered call sites.Manual
hasPart.Clipentries with the right names, start/end offsets, and?t=<seconds>#godam-video-<id>URLs.hasPartshows the new name.hasPart..localsite, clip URLs show an optional "Invalid URL" warning because the domain isn't public;embedUrlshows the same. With the domain replaced by a public one, or on staging, the warning is gone.Demo
Screen.Recording.2026-10-15.at.4.mp4