string, * 'title' => string, * 'station' => string, // display name e.g. "DEF CON Radio" * 'station_id' => string, // e.g. "soma-defcon" * 'at' => int, // unix timestamp * ) */ if ( ! defined( 'ABSPATH' ) ) { exit; } const RADIO_HISTORY_KEY = 'radio_history'; const RADIO_FAVOURITES_KEY = 'radio_favourites'; const RADIO_HISTORY_CAP = 500; /** Current user's track history (oldest first). */ function radio_get_history( $user_id = 0 ) { $user_id = $user_id ? (int) $user_id : get_current_user_id(); if ( ! $user_id ) { return array(); } $h = get_user_meta( $user_id, RADIO_HISTORY_KEY, true ); return is_array( $h ) ? $h : array(); } /** Current user's favourited tracks (oldest first). */ function radio_get_favourites( $user_id = 0 ) { $user_id = $user_id ? (int) $user_id : get_current_user_id(); if ( ! $user_id ) { return array(); } $f = get_user_meta( $user_id, RADIO_FAVOURITES_KEY, true ); return is_array( $f ) ? $f : array(); } /** Normalise raw POSTed track data; returns null on junk input. */ function radio_sanitize_entry( $entry ) { if ( ! is_array( $entry ) ) { return null; } $artist = isset( $entry['artist'] ) ? sanitize_text_field( wp_unslash( $entry['artist'] ) ) : ''; $title = isset( $entry['title'] ) ? sanitize_text_field( wp_unslash( $entry['title'] ) ) : ''; $station = isset( $entry['station'] ) ? sanitize_text_field( wp_unslash( $entry['station'] ) ) : ''; $station_id = isset( $entry['station_id'] ) ? sanitize_key( wp_unslash( $entry['station_id'] ) ) : ''; if ( ! $artist || ! $title ) { return null; } if ( strtolower( $artist ) === '(unknown)' ) { return null; } // SomaFM promo / dead-air placeholder return array( 'artist' => mb_substr( $artist, 0, 200 ), 'title' => mb_substr( $title, 0, 200 ), 'station' => mb_substr( $station, 0, 100 ), 'station_id' => $station_id, 'at' => time(), ); } /** Dedup signature — artist|title|station_id, lowercased + trimmed. */ function radio_entry_signature( $entry ) { $a = isset( $entry['artist'] ) ? $entry['artist'] : ''; $t = isset( $entry['title'] ) ? $entry['title'] : ''; $s = isset( $entry['station_id'] ) ? $entry['station_id'] : ''; return strtolower( trim( $a . '|' . $t . '|' . $s ) ); } /** Append a track to the user's history, deduped against the last entry * and capped at RADIO_HISTORY_CAP. Returns true if appended. */ function radio_log_track( $entry, $user_id = 0 ) { $user_id = $user_id ? (int) $user_id : get_current_user_id(); if ( ! $user_id ) { return false; } $clean = radio_sanitize_entry( $entry ); if ( ! $clean ) { return false; } $history = radio_get_history( $user_id ); if ( ! empty( $history ) ) { $last_sig = radio_entry_signature( $history[ count( $history ) - 1 ] ); if ( radio_entry_signature( $clean ) === $last_sig ) { return false; } } $history[] = $clean; if ( count( $history ) > RADIO_HISTORY_CAP ) { $history = array_slice( $history, -RADIO_HISTORY_CAP ); } update_user_meta( $user_id, RADIO_HISTORY_KEY, $history ); return true; } /** Toggle whether an entry is favourited. Returns the new state * (true = now favourited, false = now unfavourited). */ function radio_toggle_favourite( $entry, $user_id = 0 ) { $user_id = $user_id ? (int) $user_id : get_current_user_id(); if ( ! $user_id ) { return false; } $clean = radio_sanitize_entry( $entry ); if ( ! $clean ) { return false; } $sig = radio_entry_signature( $clean ); $favs = radio_get_favourites( $user_id ); $found = -1; foreach ( $favs as $i => $f ) { if ( radio_entry_signature( $f ) === $sig ) { $found = $i; break; } } if ( $found >= 0 ) { array_splice( $favs, $found, 1 ); update_user_meta( $user_id, RADIO_FAVOURITES_KEY, $favs ); return false; } $favs[] = $clean; update_user_meta( $user_id, RADIO_FAVOURITES_KEY, $favs ); return true; } /** Clear the user's history (favourites preserved). */ function radio_clear_history_all( $user_id = 0 ) { $user_id = $user_id ? (int) $user_id : get_current_user_id(); if ( ! $user_id ) { return false; } delete_user_meta( $user_id, RADIO_HISTORY_KEY ); return true; } /** URL-build helpers for the four search providers. */ function radio_search_urls( $artist, $title ) { $enc = rawurlencode( trim( $artist . ' ' . $title ) ); return array( 'spotify' => 'https://open.spotify.com/search/' . $enc, 'youtube' => 'https://www.youtube.com/results?search_query=' . $enc, 'apple' => 'https://music.apple.com/search?term=' . $enc, 'bandcamp' => 'https://bandcamp.com/search?q=' . $enc, ); } /** Render the History admin page (tabs: History / Favourites). */ function radio_render_history_page() { if ( ! current_user_can( 'read' ) ) { wp_die( esc_html__( 'You do not have permission to view this page.', 'radio' ) ); } $tab = isset( $_GET['tab'] ) ? sanitize_key( wp_unslash( $_GET['tab'] ) ) : 'history'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- tab-switch only, no state change if ( ! in_array( $tab, array( 'history', 'favourites' ), true ) ) { $tab = 'history'; } $all_history = radio_get_history(); $all_favourites = radio_get_favourites(); $entries = ( $tab === 'favourites' ) ? $all_favourites : $all_history; $entries = array_reverse( $entries ); // newest first // Set of favourite signatures for fast lookup in the row render. $fav_sigs = array(); foreach ( $all_favourites as $f ) { $fav_sigs[ radio_entry_signature( $f ) ] = true; } // Stations present in the current tab → filter dropdown options. $stations_in_list = array(); foreach ( $entries as $e ) { if ( ! empty( $e['station_id'] ) && ! isset( $stations_in_list[ $e['station_id'] ] ) ) { $stations_in_list[ $e['station_id'] ] = $e['station']; } } asort( $stations_in_list ); $base_url = admin_url( 'admin.php?page=radio-history' ); $hist_url = $base_url . '&tab=history'; $fav_url = $base_url . '&tab=favourites'; $nonce = wp_create_nonce( 'radio_history' ); ?>