get_charset_collate(); require_once ABSPATH . 'wp-admin/includes/upgrade.php'; $sql = "CREATE TABLE {$table_name} ( id bigint(20) unsigned NOT NULL AUTO_INCREMENT, source_hash varchar(64) NOT NULL, audio_src text NOT NULL, track_title varchar(255) NOT NULL DEFAULT '', play_count bigint(20) unsigned NOT NULL DEFAULT 0, last_played datetime NULL DEFAULT NULL, created_at datetime NOT NULL, updated_at datetime NOT NULL, PRIMARY KEY (id), UNIQUE KEY source_hash (source_hash), KEY play_count (play_count), KEY last_played (last_played) ) {$charset_collate};"; dbDelta( $sql ); } /** * Get the analytics table name. * * @return string */ public static function table_name() { global $wpdb; return $wpdb->prefix . 'map_analytics'; } /** * Build a stable source hash. * * @param string $audio_src Audio URL. * @return string */ public static function build_source_hash( $audio_src ) { return hash( 'sha256', esc_url_raw( trim( (string) $audio_src ) ) ); } /** * Record a play. * * Count rule: * One request counts once for the rendered player instance session. The frontend * only sends this call on the first meaningful play event per player rendered on a page. * * @param string $audio_src Audio source URL. * @param string $track_title Track title. * @return bool */ public static function record_play( $audio_src, $track_title = '' ) { global $wpdb; $audio_src = esc_url_raw( $audio_src ); $track_title = sanitize_text_field( $track_title ); $source_hash = self::build_source_hash( $audio_src ); $table_name = self::table_name(); $current_time = current_time( 'mysql', true ); if ( empty( $audio_src ) ) { return false; } $existing_id = $wpdb->get_var( $wpdb->prepare( "SELECT id FROM {$table_name} WHERE source_hash = %s LIMIT 1", $source_hash ) ); if ( $existing_id ) { $result = $wpdb->query( $wpdb->prepare( "UPDATE {$table_name} SET play_count = play_count + 1, track_title = %s, last_played = %s, updated_at = %s WHERE source_hash = %s", $track_title, $current_time, $current_time, $source_hash ) ); return false !== $result; } $result = $wpdb->insert( $table_name, array( 'source_hash' => $source_hash, 'audio_src' => $audio_src, 'track_title' => $track_title, 'play_count' => 1, 'last_played' => $current_time, 'created_at' => $current_time, 'updated_at' => $current_time, ), array( '%s', '%s', '%s', '%d', '%s', '%s', '%s' ) ); return false !== $result; } /** * Fetch top tracks. * * @param int $limit Row limit. * @return array */ public static function get_top_tracks( $limit = 20 ) { global $wpdb; $table_name = self::table_name(); $limit = max( 1, absint( $limit ) ); return $wpdb->get_results( $wpdb->prepare( "SELECT source_hash, audio_src, track_title, play_count, last_played FROM {$table_name} ORDER BY play_count DESC, last_played DESC LIMIT %d", $limit ) ); } /** * Get aggregate stats. * * @return array */ public static function get_summary() { global $wpdb; $table_name = self::table_name(); $row = $wpdb->get_row( "SELECT COUNT(*) AS track_count, COALESCE(SUM(play_count), 0) AS total_plays, MAX(last_played) AS last_played FROM {$table_name}", ARRAY_A ); return array( 'track_count' => isset( $row['track_count'] ) ? (int) $row['track_count'] : 0, 'total_plays' => isset( $row['total_plays'] ) ? (int) $row['total_plays'] : 0, 'last_played' => $row['last_played'] ?? null, ); } }