Step 1: inventory what you have
WP-CLI can list attachments by MIME type. The wp post list command filters with --post_type and --post_mime_type, limits output with --fields, and can render as CSV. A starting command is wp post list --post_type=attachment --post_mime_type=video/mp4 --fields=ID,post_title,guid --format=csv, repeated for other video types you use.
Then find where each file is used. Search lesson content for each file name and record lesson ID, lesson title, and how the video is embedded. That last column decides how you will swap it.
| Embed style you find | What it looks like | Swap method |
|---|---|---|
| Video shortcode | A video shortcode with src pointing at an uploaded file | Replace the whole shortcode with the new platform URL or embed |
| Video block | A block editor video block with a file src | Replace the block with an embed block |
| Bare file URL on its own line | A media file URL alone on a line | Replace the URL with the platform URL |
| LMS lesson video field | A URL stored in the lesson settings, not the content | Edit the field in the LMS, not the post body |
Step 2: upload and build a mapping sheet
Upload every video to your chosen platform and record its new URL beside the old file URL. WordPress's Embeds documentation lists the services it can embed from by URL; Vimeo and YouTube are on the list, and for YouTube only public and unlisted videos and playlists can be embedded, not private ones. If your platform is not on the list, you will paste its iframe embed code rather than a URL.
The mapping sheet has four columns: attachment ID, old file URL, new platform URL or embed code, and lessons using it. Every later step reads from this sheet, and it doubles as your rollback record.
Step 3: back up, then preview every bulk change
Export the database before any replacement. wp db export writes the database to a file, and its --tables option exports only listed tables if you want a smaller targeted backup of posts and post meta as well as a full one.
The wp search-replace command has the controls a careful bulk change needs. --dry-run runs the whole operation and shows a report without saving. --export writes the transformed data as an SQL file instead of changing the database, so you can review it. --include-columns limits the change to specific columns. --report-changed-only trims the report. The command handles PHP serialized data and does not change primary keys. --regex is available but the documentation warns it takes about 15 to 20 times longer.
Why a simple URL swap is not enough: the WordPress video shortcode plays a media file and supports mp4, m4v, webm, ogv, wmv and flv sources. Put a Vimeo page URL in its src and you get a broken player. Replace the whole shortcode or block, not just the URL inside it.
- Run wp db export before each batch and name the file with the batch number.
- Run each replacement with
--dry-runand compare the reported count with your mapping sheet. - For larger batches, run with
--exportand read the SQL before applying. - Apply one batch, open three affected lessons as a student, then continue.
Step 4: choose manual or bulk by course size
Worked example: 120 lessons, 120 MP4 files, all as video blocks. At an estimated 3 minutes per lesson by hand, that is 6 hours. Scripted per-video replacements with a dry run and spot checks might take 2 to 3 hours including review. These are planning estimates; time ten lessons before choosing.
| Library size | Approach | Reason |
|---|---|---|
| Up to about 30 lessons | Edit each lesson by hand | Faster than writing and checking patterns |
| 30 to 300 lessons with one consistent embed style | search-replace per video, previewed with --dry-run | Exact strings are safe and auditable |
| Mixed embed styles or video URLs in lesson settings | Batch by style; edit LMS fields separately | One pattern will not fit all |
| Hundreds of lessons with shortcodes to replace | Regex replacement exported to SQL and reviewed | Slow but reviewable before it touches data |
Step 5: clean up storage last
Wait at least one full billing or enrolment cycle before deleting anything. Students with lessons open in a browser tab, cached pages and email links can still request old file URLs for a while.
Before deleting, search content again for each old file URL from the mapping sheet. Only attachments with zero matches are safe to remove. Delete them through WordPress rather than removing files over SFTP, so the media library records and the files go together, then take a fresh backup.
- Re-run the inventory command and confirm it lists only attachments you intend to delete.
- Search post content and post meta for each old URL; zero matches required.
- Delete in batches, checking a few lessons after each.
- Record the storage freed and the date in your migration log.
Questions before you decide
Can I just search and replace the MP4 URL with the Vimeo URL?
Not inside a video shortcode or block. WordPress documents the video shortcode as playing media files such as mp4 and webm, so a platform page URL there will not play; replace the whole embed.
How do I preview a bulk change before it happens?
WP-CLI search-replace has a --dry-run flag that runs the operation and reports without saving, and an --export flag that writes the changes to an SQL file instead.
Can WordPress embed private YouTube videos?
No. WordPress's Embeds documentation says only public and unlisted YouTube videos and playlists can be embedded; private videos cannot.
Does search-replace break serialized data?
WP-CLI documents that search-replace handles PHP serialized data and does not change primary key values; --precise forces PHP rather than SQL processing for all columns.
Sources & verification
Product details and prices can change. Check the linked provider before buying.
- WP-CLI: wp post list Accessed 30 Oct 2026
- WP-CLI: wp search-replace Accessed 30 Oct 2026
- WP-CLI: wp db export Accessed 30 Oct 2026
- WordPress.org Documentation: Embeds Accessed 30 Oct 2026
- WordPress.org Documentation: Video shortcode Accessed 30 Oct 2026
Sources link directly to providers. Product buttons may use separately labeled affiliate links. Read our disclosure.