Media API Widget
This Wordpress plugin allows for youtube and podcast integration on a website through API integration, allowing for syncing and caching, as well as a podcast player and youtube embed players.
by Chris Paschall · github.com/magellan-web-dev/media-api-widget
Install
No release zip yet. The repository archive installs, but the folder name will carry the branch suffix and updates will not flow:
wp plugin install https://github.com/magellan-web-dev/media-api-widget/archive/refs/heads/main.zipAuthor: Chris Paschall
Requires WordPress: 5.0+
Requires PHP: 7.4+
A WordPress plugin that syncs YouTube playlists and podcast RSS feeds to the front end, with full admin-managed configuration — no WPCode constants required.
Table of Contents
- Overview
- Installation
- Admin Panel
- Shortcodes
- Shortcode Attribute Reference
- Admin Shortcode Field References (Dynamic Values)
- Podcast Platform Support
- Caching Architecture
- Runaway Protection
- Tests
- SEO Meta Tags
- Podcast Player (
/podcast/player) - API Statistics
- JavaScript Events
- Developer Hooks / Data Enrichment
- Backward Compatibility
Overview
Media API Widget provides a structured system for embedding YouTube playlists and podcast audio on WordPress pages using shortcodes. Key capabilities:
- YouTube — Fetches playlist items from the YouTube Data API v3, caches results server-side (transients) and client-side (localStorage), and renders clickable thumbnails with a built-in lightbox video player.
- Podcasts — Parses RSS feeds from direct URLs or via Apple/iTunes lookup for Omny, SoundCloud, Buzzsprout, and others. Renders thumbnail-based items that open an embedded audio player.
- Custom Podcast Player — A self-hosted, fully themed podcast player served at
/podcast/playerand embeddable via iframe. - Global Shortcode Fields — Store key/value pairs in the admin and reference them in any shortcode attribute using
{{field_name}}syntax. - SEO — Automatically injects Open Graph and Twitter Card meta tags derived from the media content on each page.
- API Stats — Tracks every external API call in a database table with 24-hour reporting, including when each playlist's fallback backup was last stored and a direct download link for it.
Installation
- Upload the
media-api-widgetfolder to/wp-content/plugins/. - Activate the plugin through Plugins → Installed Plugins.
- Navigate to Media API in the WordPress admin sidebar.
- Add your YouTube playlists and/or podcast feeds under Media Items.
- Place shortcodes on any page or post.
After activation, visit Settings → Permalinks and click Save Changes to flush rewrite rules if the podcast player route (
/podcast/player) does not resolve.
Admin Panel
Access the plugin settings at WordPress Admin → Media API.
Settings — Media Items
Define the YouTube playlists and podcast feeds the plugin should manage. Each item requires:
| Field | Description |
|---|---|
| Type | YouTube or Podcast |
| Name (playlist_name) | A unique slug used to reference this item in shortcodes (e.g. my_show). Must be lowercase, no spaces. |
YouTube-specific fields
| Field | Description |
|---|---|
| Playlist ID | The YouTube playlist ID (found in the YouTube URL after list=). |
| API Key | Your Google/YouTube Data API v3 key. |
| Sort Mode | Normal — preserves YouTube order. Number in title — extracts the leading number from each video title, deduplicates, and sorts descending. Use this for TV/show episodes numbered in their title. |
| Load full playlist | When checked, fetches all videos in the playlist. When unchecked, limits to the first 6. |
Podcast-specific fields
| Field | Description |
|---|---|
| Platform | See Podcast Platform Support. |
| RSS URL / ID / Embed URL | Depends on the platform selected (see below). |
Settings — Shortcode Fields
Shortcode fields are global key/value pairs stored in the database and referenceable inside any shortcode attribute value using {{field_name}} syntax.
Ten default fields are auto-seeded on activation and cannot be deleted (their field names are locked). You can change their values:
| Field | Default Value | Used For |
|---|---|---|
podcast_player_background_color |
#151515 |
Podcast player background / mode color |
podcast_player_text_color |
#ffffff |
Podcast player text color |
podcast_player_play_icon_color |
#ffffff |
Podcast player play button color |
podcast_player_color |
#c7c7c7 |
Podcast player accent color |
podcast_player_progress_bar_color |
#616161 |
Podcast player progress bar color |
podcast_player_selected_color |
#7a7a7a |
Podcast player selected episode highlight color |
podcast_player_font |
Roboto |
Podcast player font (Google Fonts name) |
podcast_player_scrollbar_color |
#c7c7c7 |
Podcast player scrollbar color |
lightbox_playlist_logo |
(empty) | Logo image URL displayed in the lightbox playlist panel header |
lightbox_playlist_border_color |
#ffffff |
Border / theme color of the lightbox playlist panel |
You can add custom fields for any value you want to centrally manage and reuse across shortcodes (e.g. hero_title, brand_color, show_name).
Example — adding a custom field:
- Field:
show_tagline - Value:
All-new episodes every Tuesday
Then use it in a shortcode: [media-api-widget field="show_tagline"]
Caching
Configure how long data is cached to control YouTube API quota usage. Navigate to Media API → Caching.
| Setting | Default | Description |
|---|---|---|
| Media cache transient | 7200 s (2 hrs) | How long fetched YouTube or podcast data is held in WordPress transients. Also sets the client-side cookie duration. |
| YouTube request-in-progress transient | 600 s (10 min) | Prevents duplicate simultaneous API requests. Also sets how long the atomic per-playlist refresh lock stays valid before an abandoned one can be reclaimed. |
| YouTube error transient | 600 s (10 min) | After a failed YouTube API call, blocks retry for this duration. |
| YouTube backup window | 7200 s (2 hrs) | If the last successful fetch was within this window, serves the local JSON backup instead of re-calling the API. |
| YouTube maximum pages per refresh | 20 pages | Hard ceiling on playlistItems requests during a single refresh. Allowed range 1–100. |
| YouTube daily call limit | 200 calls | Circuit breaker on total outbound YouTube requests per quota day. Allowed range 1–10,000. |
Important: The YouTube Data API allows 10,000 units per day. Each playlist fetch costs 1 unit per page of 50 results. Set the cache TTL high enough to avoid exhausting your quota.
The Caching page also shows read-only guard status:
- YouTube calls used today — count against the configured daily limit.
- Quota day / reset — the current quota day and the timezone it resets in.
- Last guard event — why a refresh was last abandoned, if one was.
See Runaway Protection for what the two guards do.
API Stats
Navigate to Media API → API Stats to see a summary of all external API calls made in the last 24 hours, broken down by playlist, media type, endpoint, and hour.
The By Playlist and Endpoint table also reports on the local backup each playlist falls back to when a live API call fails:
| Column | Shows |
|---|---|
| Last Successful Backup | When the playlist's backup JSON was last stored successfully, in the site timezone. Reads the time_stored value written inside the file, falling back to the file's modification time for older files. Shows No backup stored when no file exists yet. |
| Backup File | A Download link that streams the backup JSON file directly, with its size beside it. Shows an em dash when there is no file to download. |
Because a backup is only written after a refresh completes every requested page, the timestamp is the last successful store rather than the last attempt — a failed or partial refresh leaves the previous good backup, and its timestamp, untouched. YouTube playlists always have a backup once one refresh has succeeded. As of 5.0.0 every podcast platform that fetches an RSS feed has one too — direct RSS feeds and Apple-lookup platforms alike, including those warmed by a shortcode. Only embed platforms have no backup file, because they make no API call and store just the embed URL.
Downloads are served through admin-post.php rather than a direct uploads URL, so every request is checked for the manage_options capability and a valid nonce, and the file path is resolved and confirmed to be inside the backup directory before anything is sent. The files are only ever read — nothing on the page can write, replace, or delete a backup.
Logs are automatically pruned after 48 hours.
Shortcodes
[media-api-widget] — Field Output
Outputs the value of a stored shortcode field as plain text. Use this to inject centrally managed text anywhere on a page.
[media-api-widget field="field_name"]
Example:
[media-api-widget field="show_tagline"]
Outputs: All-new episodes every Tuesday
[media-api-widget-render] — Media Item
Renders a clickable media thumbnail. Clicking it opens a lightbox with the YouTube video player or podcast audio player.
The YouTube video whose media item opens the lightbox autoplays. Neighboring carousel videos remain paused, including after they are selected with the navigation arrows. Podcast lightboxes request autoplay for the built-in custom player, Omny, and SoundCloud. Browser autoplay preferences can still block audible playback, Buzzsprout does not support autoplay, and arbitrary embed platform URLs retain their provider-defined behavior. Inline [media-api-podcast-player] embeds remain paused by default.
Also available as the alias [media-api-widget-item].
Minimal example (YouTube):
[media-api-widget-render playlist_name="my_show" media_platform="youtube"]
Minimal example (Podcast):
[media-api-widget-render playlist_name="my_podcast" media_platform="podcast" thumbnail="https://example.com/cover.jpg"]
Grid of all episodes:
[media-api-widget-render playlist_name="my_show" media_platform="youtube" multiplegrid="true" multiplegridtext="title"]
Specific episode by number:
[media-api-widget-render playlist_name="my_show" media_platform="youtube" episodenumber="5"]
Specific episode by title keyword:
[media-api-widget-render playlist_name="my_show" media_platform="youtube" nameselect="pilot"]
Episode by position:
[media-api-widget-render playlist_name="my_show" media_platform="youtube" orderdescending="1"]
Display episode title text only (no thumbnail):
[media-api-widget-render playlist_name="my_show" media_platform="youtube" orderdescending="3" mediatitle="true"]
Display episode description text only:
[media-api-widget-render playlist_name="my_show" media_platform="youtube" orderdescending="3" mediadescription="true" mediadescriptiontextcolor="#333333"]
[media-api-podcast-player] — Podcast Player Embed
Renders the custom podcast player as an inline `
This README is longer than the copy stored here. Read the rest on GitHub →