Changeset 3415875
- Timestamp:
- 12/09/2025 11:21:08 PM (4 months ago)
- Location:
- spinitron-player
- Files:
-
- 14 added
- 6 edited
-
tags/1.0.9 (added)
-
tags/1.0.9/ajax (added)
-
tags/1.0.9/ajax/ajax-handler-today.php (added)
-
tags/1.0.9/app (added)
-
tags/1.0.9/app/plugin-settings.php (added)
-
tags/1.0.9/app/spinitron-api-client.php (added)
-
tags/1.0.9/app/spinitron-get-client.php (added)
-
tags/1.0.9/js (added)
-
tags/1.0.9/js/spinitron-fetch-today.js (added)
-
tags/1.0.9/readme.txt (added)
-
tags/1.0.9/spinitron-player.php (added)
-
tags/1.0.9/style.css (added)
-
tags/1.0.9/ui (added)
-
tags/1.0.9/ui/today.php (added)
-
trunk/ajax/ajax-handler-today.php (modified) (2 diffs)
-
trunk/app/plugin-settings.php (modified) (2 diffs)
-
trunk/app/spinitron-get-client.php (modified) (1 diff)
-
trunk/readme.txt (modified) (2 diffs)
-
trunk/spinitron-player.php (modified) (1 diff)
-
trunk/ui/today.php (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
spinitron-player/trunk/ajax/ajax-handler-today.php
r3278401 r3415875 1 1 <?php 2 2 3 if (!defined('ABSPATH')) exit; // Exit if accessed directly 3 if ( ! defined( 'ABSPATH' ) ) { 4 exit; // Exit if accessed directly 5 } 4 6 5 7 /** … … 7 9 * @return void 8 10 */ 9 function fetch_spinitron_show_today_callback() {10 // Prevent browser and server-side caching11 header("Expires: 0");12 header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0");13 header("Pragma: no-cache");14 header("Content-Type: text/html; charset=utf-8");11 function spinitron_player_fetch_spinitron_show_today_callback() { 12 // Prevent browser and server-side caching 13 header( 'Expires: 0' ); 14 header( 'Cache-Control: no-store, no-cache, must-revalidate, max-age=0' ); 15 header( 'Pragma: no-cache' ); 16 header( 'Content-Type: text/html; charset=utf-8' ); 15 17 16 // Clear any output buffers that might interfere 17 if (ob_get_length()) { 18 ob_clean(); 18 // Clear any output buffers that might interfere 19 if ( ob_get_length() ) { 20 ob_clean(); 21 } 22 23 require plugin_dir_path( __FILE__ ) . '../app/spinitron-get-client.php'; 24 25 global $spinitron_player_client; 26 27 // Retrieve plugin options 28 $options = get_option( 'spinitron_player_options' ); 29 $separate_time_dj = isset( $options['spinitron_player_field_separate_time_dj'] ) ? $options['spinitron_player_field_separate_time_dj'] : 0; 30 $duplicate_show_image = isset( $options['spinitron_player_field_duplicate_show_image'] ) ? $options['spinitron_player_field_duplicate_show_image'] : 0; 31 32 // Ensure the fallback image is set, since it's required 33 $fallback_image = ''; 34 if ( isset( $options['spinitron_player_field_image_fallback'] ) && ! empty( $options['spinitron_player_field_image_fallback'] ) ) { 35 $fallback_image = esc_url( $options['spinitron_player_field_image_fallback'] ); 36 } 37 38 // Try to get the transient 39 $shows = get_transient( 'spinitron_show_today' ); 40 41 if ( false === $shows ) { 42 // If the transient doesn't exist, fetch from API 43 $shows = $spinitron_player_client->search( 44 'shows', 45 array( 46 'count' => 1, 47 'cache_bust' => time(), 48 ) 49 ); 50 51 if ( empty( $shows['items'] ) ) { 52 echo '<p>No shows found. Please check the Spinitron API for available shows.</p>'; 53 wp_die(); 19 54 } 20 55 21 require plugin_dir_path(__FILE__) . '../app/spinitron-get-client.php'; 56 // Calculate the number of seconds until the next multiple of 5 minutes 57 $current_time = time(); 58 $next_cache_time = ceil( $current_time / 300 ) * 300; // 300 seconds = 5 minutes 59 $expiration = $next_cache_time - $current_time; 22 60 23 global $client; 61 // Set the transient 62 set_transient( 'spinitron_show_today', $shows, $expiration ); 63 } 24 64 25 // Retrieve plugin options 26 $options = get_option('spinitron_player_options'); 27 $separate_time_dj = isset($options['spinitron_player_field_separate_time_dj']) ? $options['spinitron_player_field_separate_time_dj'] : 0; 28 $duplicate_show_image = isset($options['spinitron_player_field_duplicate_show_image']) ? $options['spinitron_player_field_duplicate_show_image'] : 0; 65 ob_start(); 29 66 30 // Ensure the fallback image is set, since it's required 31 $fallback_image = ''; 32 if (isset($options['spinitron_player_field_image_fallback']) && !empty($options['spinitron_player_field_image_fallback'])) { 33 $fallback_image = esc_url($options['spinitron_player_field_image_fallback']); 67 foreach ( $shows['items'] as $show ) { 68 $timezone = isset( $show['timezone'] ) ? $show['timezone'] : 'America/Los_Angeles'; 69 $tz = new DateTimeZone( $timezone ); 70 $show_title = esc_html( $show['title'] ); 71 $show_image = esc_url( $show['image'] ); 72 $show_start = ( new DateTime( $show['start'] ) )->setTimezone( $tz ); 73 $show_end = ( new DateTime( $show['end'] ) )->setTimezone( $tz ); 74 $time_now = ( new DateTime( 'now' ) )->setTimezone( $tz ); 75 $persona_url = esc_url( $show['_links']['personas'][0]['href'] ); 76 77 $persona_parts = explode( '/', $persona_url ); 78 $persona_id = end( $persona_parts ); 79 $persona_array = $spinitron_player_client->fetch( 'personas', $persona_id ); 80 $show_dj = esc_html( $persona_array['name'] ); 81 82 // Check if the image URL is valid 83 if ( ! empty( $show_image ) ) { 84 $headers = @get_headers( $show_image ); 85 if ( ! $headers || false === strpos( $headers[0], '200' ) ) { 86 $show_image = $fallback_image; 87 } 88 } else { 89 $show_image = $fallback_image; 34 90 } 35 91 36 // Try to get the transient 37 $shows = get_transient('spinitron_show_today'); 92 $show_status = ( $show_start <= $time_now && $time_now <= $show_end ) ? 'On air' : 'Up next'; 38 93 39 if ($shows === false) { 40 // If the transient doesn't exist, fetch from API 41 $shows = $client->search('shows', array('count' => 1, 'cache_bust' => time())); 94 echo '<div class="spinitron-player">'; 42 95 43 if (empty($shows['items'])) { 44 echo '<p>No shows found. Please check the Spinitron API for available shows.</p>'; 45 wp_die(); 46 } 47 48 // Calculate the number of seconds until the next multiple of 5 minutes 49 $current_time = time(); 50 $next_cache_time = ceil($current_time / 300) * 300; // 300 seconds = 5 minutes 51 $expiration = $next_cache_time - $current_time; 52 53 // Set the transient 54 set_transient('spinitron_show_today', $shows, $expiration); 96 if ( $duplicate_show_image ) { 97 echo '<img class="show-image-outter" src="' . esc_html( $show_image ) . '" alt="' . esc_attr( $show_title ) . '" />'; 55 98 } 56 99 57 ob_start(); 100 echo '<div class="show-image"> 101 <img src="' . esc_html( $show_image ) . '" alt="' . esc_attr( $show_title ) . '" /> 102 </div> 103 <div class="show-details"> 104 <p class="show-status">' . esc_html( $show_status ) . '</p> 105 <p class="show-title">' . esc_html( $show_title ) . '</p>'; 58 106 59 foreach ($shows['items'] as $show) { 60 $show_title = esc_html($show['title']); 61 $show_image = esc_url($show['image']); 62 $show_start = (new DateTime($show['start']))->setTimezone(new DateTimeZone($show['timezone'] ?? 'America/Los_Angeles')); 63 $show_end = (new DateTime($show['end']))->setTimezone(new DateTimeZone($show['timezone'] ?? 'America/Los_Angeles')); 64 $time_now = (new DateTime('now'))->setTimezone(new DateTimeZone($show['timezone'] ?? 'America/Los_Angeles')); 65 66 $persona_url = esc_url($show['_links']['personas'][0]['href']); 67 $persona_parts = explode('/', $persona_url); 68 $persona_id = end($persona_parts); 69 $persona_array = $client->fetch('personas', $persona_id); 70 $show_dj = esc_html($persona_array['name']); 71 72 // Check if the image URL is valid 73 if (!empty($show_image)) { 74 $headers = @get_headers($show_image); 75 if (!$headers || strpos($headers[0], '200') === false) { 76 $show_image = $fallback_image; 77 } 78 } else { 79 error_log('Show image URL is empty. Using fallback image.'); 80 $show_image = $fallback_image; 81 } 82 83 $show_status = ($show_start <= $time_now && $time_now <= $show_end) ? 'On air' : 'Up next'; 84 85 echo '<div class="spinitron-player">'; 86 if ($duplicate_show_image) { 87 echo '<img class="show-image-outter" src="' . esc_html($show_image) . '" alt="' . esc_attr($show_title) . '" />'; 88 } 89 90 echo '<div class="show-image"> 91 <img src="' . esc_html($show_image) . '" alt="' . esc_attr($show_title) . '" /> 92 </div> 93 <div class="show-details"> 94 <p class="show-status">' . esc_html($show_status) . '</p> 95 <p class="show-title">' . esc_html($show_title) . '</p>'; 96 97 if ($separate_time_dj) { 98 echo '<p class="show-time">' . esc_html($show_start->format('g:i A')) . ' - ' . esc_html($show_end->format('g:i A')) . '</p> 99 <p class="show-dj">With ' . esc_html($show_dj) . '</p>'; 100 } else { 101 echo '<p class="show-time-dj">' . esc_html($show_start->format('g:i A')) . ' - ' . esc_html($show_end->format('g:i A')) . ' with ' . esc_html($show_dj) . '</p>'; 102 } 103 104 echo '</div></div>'; 107 if ( $separate_time_dj ) { 108 echo '<p class="show-time">' . esc_html( $show_start->format( 'g:i A' ) ) . ' - ' . esc_html( $show_end->format( 'g:i A' ) ) . '</p> 109 <p class="show-dj">With ' . esc_html( $show_dj ) . '</p>'; 110 } else { 111 echo '<p class="show-time-dj">' . esc_html( $show_start->format( 'g:i A' ) ) . ' - ' . esc_html( $show_end->format( 'g:i A' ) ) . ' with ' . esc_html( $show_dj ) . '</p>'; 105 112 } 106 113 107 $output = ob_get_clean(); 114 echo '</div></div>'; 115 } 108 116 109 echo $output;117 $output = ob_get_clean(); 110 118 111 wp_die(); // This is required to terminate immediately and return a proper response. 119 // $output is composed of escaped HTML fragments above. 120 echo $output; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped 121 122 wp_die(); // This is required to terminate immediately and return a proper response. 112 123 } 113 add_action('wp_ajax_fetch_spinitron_show_today', 'fetch_spinitron_show_today_callback'); 114 add_action('wp_ajax_nopriv_fetch_spinitron_show_today', 'fetch_spinitron_show_today_callback'); 124 125 add_action( 'wp_ajax_fetch_spinitron_show_today', 'spinitron_player_fetch_spinitron_show_today_callback' ); 126 add_action( 'wp_ajax_nopriv_fetch_spinitron_show_today', 'spinitron_player_fetch_spinitron_show_today_callback' ); -
spinitron-player/trunk/app/plugin-settings.php
r3102947 r3415875 8 8 */ 9 9 function Spinitron_Player_Settings_init() { 10 register_setting('spinitron_player', 'spinitron_player_options'); 10 register_setting( 11 'spinitron_player', 12 'spinitron_player_options', 13 array( 14 'sanitize_callback' => 'spinitron_player_sanitize_options', 15 ) 16 ); 11 17 12 18 add_settings_section( … … 83 89 } 84 90 add_action('admin_init', 'Spinitron_Player_Settings_init'); 91 92 /** 93 * Sanitize and validate Spinitron Player options. 94 * 95 * @param array $input Raw input from the settings form. 96 * @return array Sanitized options. 97 */ 98 function spinitron_player_sanitize_options( $input ) { 99 100 $input = is_array( $input ) ? $input : array(); 101 $output = array(); 102 $options = get_option( 'spinitron_player_options', array() ); 103 104 // API Key (text) 105 if ( isset( $input['spinitron_player_field_api_key'] ) ) { 106 $output['spinitron_player_field_api_key'] = sanitize_text_field( 107 $input['spinitron_player_field_api_key'] 108 ); 109 110 // If API key changed, clear transient cache. 111 if ( 112 empty( $options['spinitron_player_field_api_key'] ) || 113 $options['spinitron_player_field_api_key'] !== $output['spinitron_player_field_api_key'] 114 ) { 115 delete_transient( 'spinitron_show_today' ); 116 } 117 } 118 119 // Image Fallback (URL) 120 if ( isset( $input['spinitron_player_field_image_fallback'] ) ) { 121 $output['spinitron_player_field_image_fallback'] = esc_url_raw( 122 $input['spinitron_player_field_image_fallback'] 123 ); 124 } 125 126 // Stream URL (URL) 127 if ( isset( $input['spinitron_player_field_stream_url'] ) ) { 128 $output['spinitron_player_field_stream_url'] = esc_url_raw( 129 $input['spinitron_player_field_stream_url'] 130 ); 131 } 132 133 // Separate Time & DJ (checkbox) 134 $output['spinitron_player_field_separate_time_dj'] = ! empty( $input['spinitron_player_field_separate_time_dj'] ) ? 1 : 0; 135 136 // Duplicate Show Image (checkbox) 137 $output['spinitron_player_field_duplicate_show_image'] = ! empty( $input['spinitron_player_field_duplicate_show_image'] ) ? 1 : 0; 138 139 return $output; 140 } 85 141 86 142 /** -
spinitron-player/trunk/app/spinitron-get-client.php
r3102938 r3415875 17 17 */ 18 18 19 if (!defined('ABSPATH')) exit; // Exit if accessed directly 19 if ( ! defined( 'ABSPATH' ) ) { 20 exit; // Exit if accessed directly 21 } 20 22 21 $spinitron_player_options = get_option( 'spinitron_player_options', '');23 $spinitron_player_options = get_option( 'spinitron_player_options', '' ); 22 24 23 if (!empty($spinitron_player_options) && is_array($spinitron_player_options)) { 24 $api_key = isset($spinitron_player_options['spinitron_player_field_api_key']) ? $spinitron_player_options['spinitron_player_field_api_key'] : ''; 25 if ( ! empty( $spinitron_player_options ) && is_array( $spinitron_player_options ) ) { 25 26 26 if (empty($api_key)) { 27 $spinitron_api_key = isset( $spinitron_player_options['spinitron_player_field_api_key'] ) 28 ? $spinitron_player_options['spinitron_player_field_api_key'] 29 : ''; 30 31 if ( empty( $spinitron_api_key ) ) { 27 32 echo '<p>API key is missing. Please provide a valid API key in the settings.</p>'; 28 33 exit; 29 34 } 30 35 31 if ( !class_exists('SpinitronApiClient')) {32 include plugin_dir_path( __FILE__) . 'spinitron-api-client.php';36 if ( ! class_exists( 'SpinitronApiClient' ) ) { 37 include plugin_dir_path( __FILE__ ) . 'spinitron-api-client.php'; 33 38 } 34 39 35 global $client; 36 if (!isset($client)) { 37 $client = new SpinitronApiClient($api_key); 40 // Global client used throughout the plugin. 41 global $spinitron_player_client; 42 43 if ( ! isset( $spinitron_player_client ) ) { 44 $spinitron_player_client = new SpinitronApiClient( $spinitron_api_key ); 38 45 39 46 // Validate API key by making a test request to the Spinitron API. 40 47 try { 41 $test_response = $client->search('shows', array('count' => 1, 'cache_bust' => time())); 42 if (isset($test_response['error'])) { 43 throw new Exception('Invalid API key'); 48 $spinitron_test_response = $spinitron_player_client->search( 49 'shows', 50 array( 51 'count' => 1, 52 'cache_bust' => time(), 53 ) 54 ); 55 56 if ( isset( $spinitron_test_response['error'] ) ) { 57 throw new Exception( 'Invalid API key' ); 44 58 } 45 if (empty($test_response['items'])) { 46 throw new Exception('No shows found'); 59 60 if ( empty( $spinitron_test_response['items'] ) ) { 61 throw new Exception( 'No shows found' ); 47 62 } 48 } catch ( Exception $e) {49 if ( $e->getMessage() === 'Invalid API key') {63 } catch ( Exception $e ) { 64 if ( 'Invalid API key' === $e->getMessage() ) { 50 65 echo '<p>Invalid API key. Please make sure your Spinitron API key is valid.</p>'; 51 } elseif ( $e->getMessage() === 'No shows found') {66 } elseif ( 'No shows found' === $e->getMessage() ) { 52 67 echo '<p>No shows found. Please check the Spinitron API for available shows.</p>'; 53 68 } else { -
spinitron-player/trunk/readme.txt
r3352329 r3415875 2 2 Contributors: razorfrog 3 3 Tags: spinitron, radio, music, player, stream 4 Requires at least: 5. 05 Tested up to: 6. 8.24 Requires at least: 5.2 5 Tested up to: 6.9 6 6 Requires PHP: 7.2 7 Stable tag: 1.0. 87 Stable tag: 1.0.9 8 8 License: GPLv2 or later 9 License URI: http ://www.gnu.org/licenses/gpl-2.0.html9 License URI: https://www.gnu.org/licenses/gpl-2.0.html 10 10 11 11 A streaming player for radio stations using Spinitron, with live data integration. … … 41 41 42 42 == Changelog == 43 44 = 1.0.9 = 45 - Security updates 46 - Updated for WP Core compatibility 43 47 44 48 = 1.0.8 = -
spinitron-player/trunk/spinitron-player.php
r3352329 r3415875 1 1 <?php 2 2 3 /** 3 * Plugin Name: Spinitron Player 4 * Plugin URI: https://razorfrog.com/spinitron-player/ 5 * Description: A streaming player for radio stations using Spinitron, with live data integration. 6 * Author: Razorfrog Web Design 7 * Version: 1.0.8 8 * Author URI: https://razorfrog.com/ 9 * License: GPLv2 or later 10 * Text Domain: spinitron-player 11 * Requires at least: 6.0 12 * Requires PHP: 7.2 13 * 14 * PHP version 7.2 15 * 16 * @category Media 17 * @package SpinitronPlayer 4 * Plugin Name: Spinitron Player 5 * Plugin URI: https://razorfrog.com/spinitron-player/ 6 * Description: A streaming player for radio stations using Spinitron. 7 * Version: 1.0.9 8 * Requires at least: 5.2 9 * Requires PHP: 7.2 10 * Author: Razorfrog Web Design 11 * Author URI: https://razorfrog.com/ 12 * License: GPLv2 or later 13 * License URI: https://www.gnu.org/licenses/gpl-2.0.html 14 * Text Domain: spinitron-player 18 15 */ 19 16 -
spinitron-player/trunk/ui/today.php
r3102938 r3415875 1 1 <?php 2 if (!defined('ABSPATH')) exit; // Exit if accessed directly 2 if ( ! defined( 'ABSPATH' ) ) { 3 exit; // Exit if accessed directly 4 } 3 5 4 $options = get_option('spinitron_player_options'); 5 $fallback_image = isset($options['spinitron_player_field_image_fallback']) ? esc_url($options['spinitron_player_field_image_fallback']) : ''; 6 $separate_time_dj = isset($options['spinitron_player_field_separate_time_dj']) ? $options['spinitron_player_field_separate_time_dj'] : 0; 7 $duplicate_show_image = isset($options['spinitron_player_field_duplicate_show_image']) ? $options['spinitron_player_field_duplicate_show_image'] : 0; 6 // Prefixed globals to satisfy coding standards. 7 $spinitron_options = get_option( 'spinitron_player_options' ); 8 $spinitron_fallback_image = isset( $spinitron_options['spinitron_player_field_image_fallback'] ) ? $spinitron_options['spinitron_player_field_image_fallback'] : ''; 9 $spinitron_separate_time_dj = isset( $spinitron_options['spinitron_player_field_separate_time_dj'] ) ? $spinitron_options['spinitron_player_field_separate_time_dj'] : 0; 10 $spinitron_duplicate_show_image = isset( $spinitron_options['spinitron_player_field_duplicate_show_image'] ) ? $spinitron_options['spinitron_player_field_duplicate_show_image'] : 0; 8 11 9 12 global $spinitron_instance_count; 10 $ container_id = 'spinitron-show-container-' . $spinitron_instance_count;13 $spinitron_container_id = 'spinitron-show-container-' . $spinitron_instance_count; 11 14 ?> 12 15 13 <div id="<?php echo $container_id; ?>"> 14 <div class="spinitron-player spinitron-player-loading"> 15 <?php if ($duplicate_show_image) : ?> 16 <img class="show-image-outter" src="<?php echo $fallback_image; ?>" alt="Fallback radio cover image" /> 17 <?php endif; ?> 18 <div class="show-image"> 19 <img src="<?php echo $fallback_image; ?>" alt="Fallback radio cover image" /> 20 </div> 21 <div class="show-details"> 22 <p class="show-title">Loading...</p> 23 </div> 16 <div id="<?php echo esc_attr( $spinitron_container_id ); ?>"> 17 <div class="spinitron-player spinitron-player-loading"> 18 <?php if ( $spinitron_duplicate_show_image ) : ?> 19 <img class="show-image-outter" src="<?php echo esc_url( $spinitron_fallback_image ); ?>" alt="Fallback radio cover image" /> 20 <?php endif; ?> 21 <div class="show-image"> 22 <img src="<?php echo esc_url( $spinitron_fallback_image ); ?>" alt="Fallback radio cover image" /> 24 23 </div> 24 <div class="show-details"> 25 <p class="show-title">Loading...</p> 26 </div> 27 </div> 25 28 </div>
Note: See TracChangeset
for help on using the changeset viewer.