feat(0.5.0): track history + favourites with four search providers
Quietly log every track that scrolls past the player to a per-user
history (capped 500). A star button promotes the good ones to a
separate Favourites tab that doesn't age out. Each row has four
search-provider deep links so you can find the track on whichever
service you use.
Storage
Two new user_meta keys (separate from radio_state so frequent
inserts don't churn the player-state blob):
- radio_history — capped FIFO of last 500 played tracks
- radio_favourites — uncapped user-starred list
New page: Radio → History
- History tab: capped FIFO list, newest first.
- Favourites tab: starred tracks (uncapped).
- Each row: when (relative + full timestamp on hover) / station /
artist — title / four search links / favourite-star toggle.
- Filter by artist/title (live, client-side) + by station (dropdown).
- Clear history button (favourites preserved).
- Search providers: Spotify, YouTube, Apple Music, Bandcamp.
Deep-link URLs only — no API keys, no third-party JS.
Auto-logging during playback
fetchTrack (existing 30s SomaFM poll loop) now hands new tracks to
logTrackIfNew → POST to wp_ajax_radio_log_track. Dedup client-side
(lastLoggedSig) AND server-side (against last entry in user_meta).
Junk filtered server-side: (unknown) artist, missing artist/title.
New AJAX endpoints
- radio_log_track (nonce radio_save_state — player pages)
- radio_toggle_favourite (nonce radio_history — history page only)
- radio_clear_history (nonce radio_history — history page only)
Files
- radio.php (version, require new include, submenu page, asset
enqueue hook adds radio_page_radio-history, three AJAX endpoints,
three new localized strings)
- inc/history.php (NEW — storage helpers + admin page renderer)
- assets/css/radio.css (history table, toolbar, search-link pills,
favourite star, dark-theme overrides)
- assets/js/radio.js (logTrackIfNew wired into fetchTrack;
bindHistoryPage handles filter/favourite/clear)
- inc/about.php (history entry)
- CHANGELOG.md (full entry)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
* Plugin Name: Radio
|
||||
* Plugin URI: https://icanhelp.ie/radio
|
||||
* Description: A small, focused, free radio player for your WordPress admin. 44 SomaFM stations grouped by 10 genres — ambient, electronic, lounge, rock, metal, jazz, world, reggae, holiday, specials. Plays via HTML5 audio; volume + station choice persist per-user.
|
||||
* Version: 0.4.0
|
||||
* Version: 0.5.0
|
||||
* Requires at least: 5.0
|
||||
* Requires PHP: 7.4
|
||||
* Author: David Keane
|
||||
@@ -20,7 +20,7 @@
|
||||
if ( ! defined( 'ABSPATH' ) ) { exit; }
|
||||
|
||||
// Plugin coordinates.
|
||||
if ( ! defined( 'RADIO_VERSION' ) ) { define( 'RADIO_VERSION', '0.4.0' ); }
|
||||
if ( ! defined( 'RADIO_VERSION' ) ) { define( 'RADIO_VERSION', '0.5.0' ); }
|
||||
if ( ! defined( 'RADIO_FILE' ) ) { define( 'RADIO_FILE', __FILE__ ); }
|
||||
if ( ! defined( 'RADIO_PATH' ) ) { define( 'RADIO_PATH', plugin_dir_path( __FILE__ ) ); }
|
||||
if ( ! defined( 'RADIO_URL' ) ) { define( 'RADIO_URL', plugin_dir_url( __FILE__ ) ); }
|
||||
@@ -34,6 +34,7 @@ require_once RADIO_PATH . 'inc/dashboard-widget.php'; // the compact mini-player
|
||||
require_once RADIO_PATH . 'inc/admin-page.php'; // dedicated Radio admin page (larger player)
|
||||
require_once RADIO_PATH . 'inc/about.php'; // About page
|
||||
require_once RADIO_PATH . 'inc/settings.php'; // Settings page
|
||||
require_once RADIO_PATH . 'inc/history.php'; // Track history + favourites (v0.5.0)
|
||||
require_once RADIO_PATH . 'inc/updater.php'; // self-hosted update checker against Gitea
|
||||
|
||||
/**
|
||||
@@ -75,6 +76,15 @@ function radio_register_admin_menu() {
|
||||
'radio_render_settings_page'
|
||||
);
|
||||
|
||||
add_submenu_page(
|
||||
'radio',
|
||||
__( 'Track history', 'radio' ),
|
||||
__( 'History', 'radio' ),
|
||||
'read',
|
||||
'radio-history',
|
||||
'radio_render_history_page'
|
||||
);
|
||||
|
||||
add_submenu_page(
|
||||
'radio',
|
||||
__( 'About', 'radio' ),
|
||||
@@ -92,10 +102,11 @@ function radio_register_admin_menu() {
|
||||
add_action( 'admin_enqueue_scripts', 'radio_enqueue_admin_assets' );
|
||||
function radio_enqueue_admin_assets( $hook ) {
|
||||
$radio_hooks = array(
|
||||
'index.php', // WP Dashboard (the widget lives here)
|
||||
'toplevel_page_radio', // Radio main page
|
||||
'radio_page_radio-settings', // Settings
|
||||
'radio_page_radio-about', // About
|
||||
'index.php', // WP Dashboard (the widget lives here)
|
||||
'toplevel_page_radio', // Radio main page
|
||||
'radio_page_radio-settings', // Settings
|
||||
'radio_page_radio-about', // About
|
||||
'radio_page_radio-history', // History + Favourites (v0.5.0)
|
||||
);
|
||||
if ( ! in_array( $hook, $radio_hooks, true ) ) { return; }
|
||||
|
||||
@@ -121,16 +132,19 @@ function radio_enqueue_admin_assets( $hook ) {
|
||||
'ajaxUrl' => admin_url( 'admin-ajax.php' ),
|
||||
'nonce' => wp_create_nonce( 'radio_save_state' ),
|
||||
'strings' => array(
|
||||
'play' => __( 'Play', 'radio' ),
|
||||
'pause' => __( 'Pause', 'radio' ),
|
||||
'loading' => __( 'Loading…', 'radio' ),
|
||||
'error' => __( 'Stream error — try another station.', 'radio' ),
|
||||
'saveError' => __( 'Preferences not saved — check your connection.', 'radio' ),
|
||||
'mute' => __( 'Mute', 'radio' ),
|
||||
'unmute' => __( 'Unmute', 'radio' ),
|
||||
'nowPlaying' => __( 'Now Playing', 'radio' ),
|
||||
'volume' => __( 'Volume', 'radio' ),
|
||||
'station' => __( 'Station', 'radio' ),
|
||||
'play' => __( 'Play', 'radio' ),
|
||||
'pause' => __( 'Pause', 'radio' ),
|
||||
'loading' => __( 'Loading…', 'radio' ),
|
||||
'error' => __( 'Stream error — try another station.', 'radio' ),
|
||||
'saveError' => __( 'Preferences not saved — check your connection.', 'radio' ),
|
||||
'mute' => __( 'Mute', 'radio' ),
|
||||
'unmute' => __( 'Unmute', 'radio' ),
|
||||
'nowPlaying' => __( 'Now Playing', 'radio' ),
|
||||
'volume' => __( 'Volume', 'radio' ),
|
||||
'station' => __( 'Station', 'radio' ),
|
||||
'addFav' => __( 'Add to favourites', 'radio' ),
|
||||
'removeFav' => __( 'Remove from favourites', 'radio' ),
|
||||
'clearConfirm' => __( 'Clear all track history? (Favourites are kept.)', 'radio' ),
|
||||
),
|
||||
) );
|
||||
}
|
||||
@@ -162,6 +176,51 @@ function radio_ajax_save_state() {
|
||||
wp_send_json_success( radio_get_state() );
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX: log a played track to the user's history (v0.5.0).
|
||||
* Nonce: same `radio_save_state` token used for state saves — both come
|
||||
* from the same `wp_localize_script` config available on player pages.
|
||||
*/
|
||||
add_action( 'wp_ajax_radio_log_track', 'radio_ajax_log_track' );
|
||||
function radio_ajax_log_track() {
|
||||
if ( ! is_user_logged_in() ) { wp_send_json_error( 'Not logged in.', 401 ); }
|
||||
check_ajax_referer( 'radio_save_state', 'nonce' );
|
||||
$logged = radio_log_track( array(
|
||||
'artist' => $_POST['artist'] ?? '',
|
||||
'title' => $_POST['title'] ?? '',
|
||||
'station' => $_POST['station'] ?? '',
|
||||
'station_id' => $_POST['station_id'] ?? '',
|
||||
) );
|
||||
wp_send_json_success( array( 'logged' => (bool) $logged ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX: toggle whether a track is favourited.
|
||||
* Nonce: `radio_history` — created fresh per History-page render so the
|
||||
* AJAX is gated to users who actually loaded the page.
|
||||
*/
|
||||
add_action( 'wp_ajax_radio_toggle_favourite', 'radio_ajax_toggle_favourite' );
|
||||
function radio_ajax_toggle_favourite() {
|
||||
if ( ! is_user_logged_in() ) { wp_send_json_error( 'Not logged in.', 401 ); }
|
||||
check_ajax_referer( 'radio_history', 'nonce' );
|
||||
$new_state = radio_toggle_favourite( array(
|
||||
'artist' => $_POST['artist'] ?? '',
|
||||
'title' => $_POST['title'] ?? '',
|
||||
'station' => $_POST['station'] ?? '',
|
||||
'station_id' => $_POST['station_id'] ?? '',
|
||||
) );
|
||||
wp_send_json_success( array( 'favourite' => (bool) $new_state ) );
|
||||
}
|
||||
|
||||
/** AJAX: clear the current user's history (favourites preserved). */
|
||||
add_action( 'wp_ajax_radio_clear_history', 'radio_ajax_clear_history' );
|
||||
function radio_ajax_clear_history() {
|
||||
if ( ! is_user_logged_in() ) { wp_send_json_error( 'Not logged in.', 401 ); }
|
||||
check_ajax_referer( 'radio_history', 'nonce' );
|
||||
radio_clear_history_all();
|
||||
wp_send_json_success();
|
||||
}
|
||||
|
||||
/**
|
||||
* Surface the user's theme choice (auto/light/dark) to CSS as a body
|
||||
* class. `radio-theme-dark` forces dark; `radio-theme-auto` lets the
|
||||
|
||||
Reference in New Issue
Block a user