register_post_type(); $page = get_page_by_path( 'admin' ); if ( ! $page ) { $page_id = wp_insert_post( array( 'post_title' => 'Admin MYXA', 'post_name' => 'admin', 'post_content' => ' ', 'post_status' => 'publish', 'post_type' => 'page', ) ); if ( ! is_wp_error( $page_id ) ) { update_option( 'myxa_admin_page_id', (int) $page_id ); } } else { update_option( 'myxa_admin_page_id', (int) $page->ID ); if ( '' === trim( (string) $page->post_content ) ) { wp_update_post( array( 'ID' => $page->ID, 'post_content' => ' ', ) ); } } flush_rewrite_rules(); } public static function deactivate() { flush_rewrite_rules(); } public function register_post_type() { register_post_type( self::POST_TYPE, array( 'labels' => array( 'name' => 'Músicas MYXA', 'singular_name' => 'Música MYXA', 'menu_name' => 'Músicas MYXA', ), 'public' => false, 'publicly_queryable' => false, 'show_ui' => true, 'show_in_menu' => true, 'show_in_rest' => false, 'menu_icon' => 'dashicons-format-audio', 'supports' => array( 'title' ), 'capability_type' => 'post', 'map_meta_cap' => true, 'exclude_from_search' => true, ) ); } private function is_admin_page() { $page_id = (int) get_option( 'myxa_admin_page_id', 0 ); return is_page( 'admin' ) || ( $page_id && is_page( $page_id ) ); } private function admin_url( $args = array() ) { $page_id = (int) get_option( 'myxa_admin_page_id', 0 ); $url = $page_id ? get_permalink( $page_id ) : home_url( '/admin/' ); return $args ? add_query_arg( $args, $url ) : $url; } private function redirect_message( $code, $extra = array() ) { wp_safe_redirect( $this->admin_url( array_merge( array( 'myxa_msg' => $code ), $extra ) ) ); exit; } public function handle_front_requests() { if ( ! $this->is_admin_page() || 'POST' !== strtoupper( isset( $_SERVER['REQUEST_METHOD'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_METHOD'] ) ) : '' ) ) { return; } $action = isset( $_POST['myxa_action'] ) ? sanitize_key( wp_unslash( $_POST['myxa_action'] ) ) : ''; if ( 'login' === $action ) { $this->process_login(); } if ( ! is_user_logged_in() || ! current_user_can( 'manage_options' ) ) { $this->redirect_message( 'forbidden' ); } $nonce = isset( $_POST['myxa_nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['myxa_nonce'] ) ) : ''; if ( ! wp_verify_nonce( $nonce, self::NONCE_ACTION ) ) { $this->redirect_message( 'security' ); } switch ( $action ) { case 'save_track': $this->save_track(); break; case 'toggle_track': $this->toggle_track(); break; case 'trash_track': $this->trash_track(); break; } } private function request_fingerprint( $username = '' ) { $ip = isset( $_SERVER['REMOTE_ADDR'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) : 'unknown'; return hash_hmac( 'sha256', strtolower( $username ) . '|' . $ip, wp_salt( 'auth' ) ); } private function process_login() { $nonce = isset( $_POST['myxa_login_nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['myxa_login_nonce'] ) ) : ''; if ( ! wp_verify_nonce( $nonce, 'myxa_front_login' ) ) { $this->redirect_message( 'security' ); } $username = isset( $_POST['myxa_user'] ) ? sanitize_user( wp_unslash( $_POST['myxa_user'] ) ) : ''; $password = isset( $_POST['myxa_pass'] ) ? (string) wp_unslash( $_POST['myxa_pass'] ) : ''; $fingerprint = $this->request_fingerprint( $username ); $key = 'myxa_login_' . substr( $fingerprint, 0, 32 ); $attempts = (int) get_transient( $key ); if ( $attempts >= 5 ) { $this->redirect_message( 'login_blocked' ); } $user = wp_signon( array( 'user_login' => $username, 'user_password' => $password, 'remember' => ! empty( $_POST['myxa_remember'] ), ), is_ssl() ); if ( is_wp_error( $user ) ) { set_transient( $key, $attempts + 1, 15 * MINUTE_IN_SECONDS ); $this->redirect_message( 'login_error' ); } if ( ! user_can( $user, 'manage_options' ) ) { wp_logout(); set_transient( $key, $attempts + 1, 15 * MINUTE_IN_SECONDS ); $this->redirect_message( 'forbidden' ); } delete_transient( $key ); wp_safe_redirect( $this->admin_url() ); exit; } private function get_track( $post_id ) { $post = get_post( $post_id ); return $post && self::POST_TYPE === $post->post_type ? $post : null; } private function posted_text( $key, $default = '' ) { return isset( $_POST[ $key ] ) ? sanitize_text_field( wp_unslash( $_POST[ $key ] ) ) : $default; } private function upload_present( $field ) { return isset( $_FILES[ $field ]['error'] ) && UPLOAD_ERR_NO_FILE !== (int) $_FILES[ $field ]['error']; } private function validate_upload( $field, $kind, $required = false ) { if ( ! $this->upload_present( $field ) ) { return $required ? new WP_Error( 'required', 'Selecione o arquivo de ' . ( 'audio' === $kind ? 'áudio MP3.' : 'imagem do artista.' ) ) : true; } $file = $_FILES[ $field ]; if ( UPLOAD_ERR_OK !== (int) $file['error'] ) { return new WP_Error( 'upload', 'O arquivo não pôde ser enviado. Verifique o limite da hospedagem.' ); } $max = 'audio' === $kind ? self::MAX_AUDIO : self::MAX_IMAGE; if ( (int) $file['size'] > $max ) { return new WP_Error( 'size', 'O ' . ( 'audio' === $kind ? 'MP3 deve ter no máximo 30 MB.' : 'arquivo de imagem deve ter no máximo 5 MB.' ) ); } $allowed = 'audio' === $kind ? array( 'mp3' => 'audio/mpeg' ) : array( 'jpg' => 'image/jpeg', 'jpeg' => 'image/jpeg', 'png' => 'image/png', 'webp' => 'image/webp' ); $checked = wp_check_filetype_and_ext( $file['tmp_name'], $file['name'], $allowed ); if ( empty( $checked['ext'] ) || ! isset( $allowed[ strtolower( $checked['ext'] ) ] ) ) { return new WP_Error( 'type', 'Formato inválido. Envie ' . ( 'audio' === $kind ? 'somente arquivos MP3.' : 'JPG, PNG ou WEBP.' ) ); } if ( 'image' === $kind && ! @getimagesize( $file['tmp_name'] ) ) { // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged return new WP_Error( 'image', 'A imagem enviada não é válida.' ); } return true; } private function artist_exists( $artist, $exclude_id = 0 ) { $ids = get_posts( array( 'post_type' => self::POST_TYPE, 'post_status' => array( 'publish', 'draft', 'pending', 'private' ), 'posts_per_page' => -1, 'fields' => 'ids', ) ); $needle = strtolower( remove_accents( trim( $artist ) ) ); foreach ( $ids as $id ) { if ( (int) $id === (int) $exclude_id ) { continue; } $current = strtolower( remove_accents( trim( (string) get_post_meta( $id, '_myxa_artist', true ) ) ) ); if ( $needle === $current ) { return true; } } return false; } private function unique_artist_count( $exclude_id = 0 ) { $ids = get_posts( array( 'post_type' => self::POST_TYPE, 'post_status' => array( 'publish', 'draft', 'pending', 'private' ), 'posts_per_page' => -1, 'fields' => 'ids', ) ); $artists = array(); foreach ( $ids as $id ) { if ( (int) $id === (int) $exclude_id ) { continue; } $name = strtolower( remove_accents( trim( (string) get_post_meta( $id, '_myxa_artist', true ) ) ) ); if ( $name ) { $artists[ $name ] = true; } } return count( $artists ); } private function save_track() { $post_id = absint( $this->posted_text( 'track_id', '0' ) ); $is_edit = $post_id > 0; $existing = $is_edit ? $this->get_track( $post_id ) : null; $artist = $this->posted_text( 'artist' ); $song = $this->posted_text( 'song' ); $genre = $this->posted_text( 'genre', 'Música autoral' ); $composer = $this->posted_text( 'composer' ); $release_date = $this->posted_text( 'release_date', current_time( 'Y-m-d' ) ); $status = 'draft' === $this->posted_text( 'status', 'publish' ) ? 'draft' : 'publish'; $description = isset( $_POST['description'] ) ? sanitize_textarea_field( wp_unslash( $_POST['description'] ) ) : ''; $featured = ! empty( $_POST['featured'] ) ? '1' : '0'; if ( $is_edit && ! $existing ) { $this->redirect_message( 'not_found' ); } if ( '' === $artist || '' === $song ) { $this->redirect_message( 'required', $is_edit ? array( 'edit_track' => $post_id ) : array() ); } if ( ! $this->artist_exists( $artist, $post_id ) && $this->unique_artist_count( $post_id ) >= self::ARTIST_LIMIT ) { $this->redirect_message( 'artist_limit', $is_edit ? array( 'edit_track' => $post_id ) : array() ); } $audio_check = $this->validate_upload( 'audio_file', 'audio', ! $is_edit ); $image_check = $this->validate_upload( 'artist_image', 'image', ! $is_edit ); if ( is_wp_error( $audio_check ) || is_wp_error( $image_check ) ) { $error = is_wp_error( $audio_check ) ? $audio_check->get_error_message() : $image_check->get_error_message(); set_transient( 'myxa_error_' . get_current_user_id(), $error, 2 * MINUTE_IN_SECONDS ); $this->redirect_message( 'upload_error', $is_edit ? array( 'edit_track' => $post_id ) : array() ); } $new_post = false; if ( ! $is_edit ) { $post_id = wp_insert_post( array( 'post_type' => self::POST_TYPE, 'post_status' => 'draft', 'post_title' => $artist . ' — ' . $song, ), true ); if ( is_wp_error( $post_id ) ) { $this->redirect_message( 'save_error' ); } $new_post = true; } require_once ABSPATH . 'wp-admin/includes/file.php'; require_once ABSPATH . 'wp-admin/includes/media.php'; require_once ABSPATH . 'wp-admin/includes/image.php'; $new_attachments = array(); $image_id = 0; $audio_id = 0; if ( $this->upload_present( 'artist_image' ) ) { $image_id = media_handle_upload( 'artist_image', $post_id ); if ( ! is_wp_error( $image_id ) ) { $new_attachments[] = $image_id; } } if ( ! is_wp_error( $image_id ) && $this->upload_present( 'audio_file' ) ) { $audio_id = media_handle_upload( 'audio_file', $post_id ); if ( ! is_wp_error( $audio_id ) ) { $new_attachments[] = $audio_id; } } if ( is_wp_error( $image_id ) || is_wp_error( $audio_id ) ) { foreach ( $new_attachments as $attachment_id ) { wp_delete_attachment( $attachment_id, true ); } if ( $new_post ) { wp_delete_post( $post_id, true ); } $error = is_wp_error( $image_id ) ? $image_id->get_error_message() : $audio_id->get_error_message(); set_transient( 'myxa_error_' . get_current_user_id(), $error, 2 * MINUTE_IN_SECONDS ); $this->redirect_message( 'upload_error', $is_edit ? array( 'edit_track' => $post_id ) : array() ); } wp_update_post( array( 'ID' => $post_id, 'post_title' => $artist . ' — ' . $song, 'post_status' => $status, ) ); update_post_meta( $post_id, '_myxa_artist', $artist ); update_post_meta( $post_id, '_myxa_song', $song ); update_post_meta( $post_id, '_myxa_genre', $genre ); update_post_meta( $post_id, '_myxa_composer', $composer ); update_post_meta( $post_id, '_myxa_description', $description ); update_post_meta( $post_id, '_myxa_release_date', $release_date ); update_post_meta( $post_id, '_myxa_featured', $featured ); if ( '' === get_post_meta( $post_id, '_myxa_plays', true ) ) { update_post_meta( $post_id, '_myxa_plays', 0 ); } if ( $image_id ) { update_post_meta( $post_id, '_myxa_image_id', (int) $image_id ); } if ( $audio_id ) { update_post_meta( $post_id, '_myxa_audio_id', (int) $audio_id ); } $this->redirect_message( 'saved' ); } private function toggle_track() { $post_id = absint( $this->posted_text( 'track_id', '0' ) ); $post = $this->get_track( $post_id ); if ( ! $post ) { $this->redirect_message( 'not_found' ); } wp_update_post( array( 'ID' => $post_id, 'post_status' => 'publish' === $post->post_status ? 'draft' : 'publish', ) ); $this->redirect_message( 'status_changed' ); } private function trash_track() { $post_id = absint( $this->posted_text( 'track_id', '0' ) ); if ( ! $this->get_track( $post_id ) ) { $this->redirect_message( 'not_found' ); } wp_trash_post( $post_id ); $this->redirect_message( 'trashed' ); } public function register_rest_routes() { register_rest_route( 'myxa/v1', '/tracks', array( 'methods' => WP_REST_Server::READABLE, 'callback' => array( $this, 'rest_tracks' ), 'permission_callback' => '__return_true', 'args' => array( 'limit' => array( 'default' => self::ARTIST_LIMIT, 'sanitize_callback' => 'absint', ), ), ) ); register_rest_route( 'myxa/v1', '/tracks/(?P\d+)/play', array( 'methods' => WP_REST_Server::CREATABLE, 'callback' => array( $this, 'rest_register_play' ), 'permission_callback' => '__return_true', 'args' => array( 'id' => array( 'required' => true, 'sanitize_callback' => 'absint', ), ), ) ); } private function track_payload( $post_id ) { $image_id = (int) get_post_meta( $post_id, '_myxa_image_id', true ); $audio_id = (int) get_post_meta( $post_id, '_myxa_audio_id', true ); return array( 'id' => (string) $post_id, 'artist' => (string) get_post_meta( $post_id, '_myxa_artist', true ), 'title' => (string) get_post_meta( $post_id, '_myxa_song', true ), 'genre' => (string) get_post_meta( $post_id, '_myxa_genre', true ), 'composer' => (string) get_post_meta( $post_id, '_myxa_composer', true ), 'description' => (string) get_post_meta( $post_id, '_myxa_description', true ), 'cover' => $image_id ? (string) wp_get_attachment_url( $image_id ) : '', 'audioUrl' => $audio_id ? (string) wp_get_attachment_url( $audio_id ) : '', 'plays' => (int) get_post_meta( $post_id, '_myxa_plays', true ), 'featured' => '1' === get_post_meta( $post_id, '_myxa_featured', true ), 'createdAt' => (string) get_post_meta( $post_id, '_myxa_release_date', true ), 'publishedAt' => get_post_time( DATE_ATOM, true, $post_id ), ); } public function rest_tracks( WP_REST_Request $request ) { $artist_limit = min( self::ARTIST_LIMIT, max( 1, absint( $request->get_param( 'limit' ) ) ) ); $ids = get_posts( array( 'post_type' => self::POST_TYPE, 'post_status' => 'publish', 'posts_per_page' => -1, 'fields' => 'ids', ) ); $tracks = array_map( array( $this, 'track_payload' ), $ids ); usort( $tracks, function ( $a, $b ) { if ( $a['plays'] === $b['plays'] ) { return strcmp( $b['publishedAt'], $a['publishedAt'] ); } return $b['plays'] <=> $a['plays']; } ); $artists = array(); $output = array(); foreach ( $tracks as $track ) { $key = strtolower( remove_accents( trim( $track['artist'] ) ) ); if ( ! isset( $artists[ $key ] ) && count( $artists ) >= $artist_limit ) { continue; } $artists[ $key ] = true; $output[] = $track; } $response = rest_ensure_response( array( 'tracks' => $output, 'artists' => count( $artists ), 'limit' => $artist_limit ) ); $response->header( 'Cache-Control', 'public, max-age=30, stale-while-revalidate=60' ); return $response; } public function rest_register_play( WP_REST_Request $request ) { $post_id = absint( $request->get_param( 'id' ) ); $post = $this->get_track( $post_id ); if ( ! $post || 'publish' !== $post->post_status ) { return new WP_Error( 'not_found', 'Música não encontrada.', array( 'status' => 404 ) ); } $ip = isset( $_SERVER['REMOTE_ADDR'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) : 'unknown'; $ua = isset( $_SERVER['HTTP_USER_AGENT'] ) ? substr( sanitize_text_field( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ), 0, 180 ) : ''; $key = 'myxa_play_' . substr( hash_hmac( 'sha256', $post_id . '|' . $ip . '|' . $ua, wp_salt( 'nonce' ) ), 0, 32 ); $play = (int) get_post_meta( $post_id, '_myxa_plays', true ); if ( get_transient( $key ) ) { return rest_ensure_response( array( 'plays' => $play, 'counted' => false ) ); } $play++; update_post_meta( $post_id, '_myxa_plays', $play ); set_transient( $key, 1, 30 ); return rest_ensure_response( array( 'plays' => $play, 'counted' => true ) ); } private function message_html() { $code = isset( $_GET['myxa_msg'] ) ? sanitize_key( wp_unslash( $_GET['myxa_msg'] ) ) : ''; if ( ! $code ) { return ''; } $messages = array( 'saved' => array( 'success', 'Música salva e catálogo atualizado.' ), 'status_changed' => array( 'success', 'Status da música atualizado.' ), 'trashed' => array( 'success', 'Música movida para a lixeira.' ), 'login_error' => array( 'error', 'Usuário ou senha incorretos.' ), 'login_blocked' => array( 'error', 'Muitas tentativas. Aguarde 15 minutos.' ), 'forbidden' => array( 'error', 'Acesso permitido somente para administradores.' ), 'security' => array( 'error', 'A sessão expirou. Atualize a página e tente novamente.' ), 'required' => array( 'error', 'Preencha o nome do artista e o nome da música.' ), 'artist_limit' => array( 'error', 'O limite máximo de 110 artistas foi atingido.' ), 'not_found' => array( 'error', 'Música não encontrada.' ), 'save_error' => array( 'error', 'Não foi possível salvar a música.' ), 'upload_error' => array( 'error', 'Não foi possível enviar os arquivos.' ), ); if ( ! isset( $messages[ $code ] ) ) { return ''; } $message = $messages[ $code ][1]; if ( 'upload_error' === $code && is_user_logged_in() ) { $detail = get_transient( 'myxa_error_' . get_current_user_id() ); if ( $detail ) { $message = $detail; delete_transient( 'myxa_error_' . get_current_user_id() ); } } return '
' . esc_html( $message ) . '
'; } private function styles() { return <<<'CSS' CSS; } private function scripts() { return <<<'JS' JS; } private function render_login() { ob_start(); ?> styles() . ob_get_clean() . $this->scripts(); } private function edit_values() { $id = isset( $_GET['edit_track'] ) ? absint( $_GET['edit_track'] ) : 0; $post = $id ? $this->get_track( $id ) : null; if ( ! $post ) { return array( 'id' => 0, 'artist' => '', 'song' => '', 'genre' => '', 'composer' => '', 'description' => '', 'release_date' => current_time( 'Y-m-d' ), 'featured' => false, 'status' => 'publish', 'image' => '', 'audio' => '', ); } $image_id = (int) get_post_meta( $id, '_myxa_image_id', true ); $audio_id = (int) get_post_meta( $id, '_myxa_audio_id', true ); return array( 'id' => $id, 'artist' => (string) get_post_meta( $id, '_myxa_artist', true ), 'song' => (string) get_post_meta( $id, '_myxa_song', true ), 'genre' => (string) get_post_meta( $id, '_myxa_genre', true ), 'composer' => (string) get_post_meta( $id, '_myxa_composer', true ), 'description' => (string) get_post_meta( $id, '_myxa_description', true ), 'release_date' => (string) get_post_meta( $id, '_myxa_release_date', true ), 'featured' => '1' === get_post_meta( $id, '_myxa_featured', true ), 'status' => $post->post_status, 'image' => $image_id ? wp_get_attachment_url( $image_id ) : '', 'audio' => $audio_id ? wp_get_attachment_url( $audio_id ) : '', ); } private function render_dashboard() { $ids = get_posts( array( 'post_type' => self::POST_TYPE, 'post_status' => array( 'publish', 'draft', 'pending', 'private' ), 'posts_per_page' => -1, 'orderby' => 'date', 'order' => 'DESC', 'fields' => 'ids', ) ); $artists = $this->unique_artist_count(); $plays = 0; $published = 0; foreach ( $ids as $id ) { $plays += (int) get_post_meta( $id, '_myxa_plays', true ); if ( 'publish' === get_post_status( $id ) ) { $published++; } } $user = wp_get_current_user(); $edit = $this->edit_values(); ob_start(); ?>
MYXA SMART STREAMING

Painel de músicas

Abrir página Home ↗
message_html() ); ?>
Artistas cadastradosde permitidos
Músicas no paineltotal cadastrado
Músicas publicadasvisíveis na Home
Total de execuçõesranking automático

Os dados publicados aparecem automaticamente na Home MYXA Play.

Cancelar edição

Catálogo cadastrado

Edite, oculte ou remova as músicas da plataforma.

Nenhuma música cadastrada ainda.
Música e artistaGêneroExecuçõesStatusDataAções

post_status ? 'Publicada' : 'Rascunho'; ?>
Editar
styles() . ob_get_clean() . $this->scripts(); } public function render_shortcode() { if ( ! is_user_logged_in() ) { return $this->render_login(); } if ( ! current_user_can( 'manage_options' ) ) { return $this->styles() . ''; } return $this->render_dashboard(); } } MYXA_Play_Admin::instance(); register_activation_hook( __FILE__, array( 'MYXA_Play_Admin', 'activate' ) ); register_deactivation_hook( __FILE__, array( 'MYXA_Play_Admin', 'deactivate' ) );