array(
'local_video' => __( 'WordPress video file', '7sgaleri' ),
'youtube' => __( 'YouTube', '7sgaleri' ),
'vimeo' => __( 'Vimeo', '7sgaleri' ),
'external_video'=> __( 'External video link', '7sgaleri' ),
),
'audio' => array(
'local_audio' => __( 'WordPress audio file', '7sgaleri' ),
'spotify' => __( 'Spotify', '7sgaleri' ),
'apple_music' => __( 'Apple Music', '7sgaleri' ),
'soundcloud' => __( 'SoundCloud', '7sgaleri' ),
'deezer' => __( 'Deezer', '7sgaleri' ),
'youtube_music' => __( 'YouTube Music', '7sgaleri' ),
'external_audio'=> __( 'External audio link', '7sgaleri' ),
),
);
}
public static function source_label( string $source ): string {
foreach ( self::sources() as $group ) {
if ( isset( $group[ $source ] ) ) {
return $group[ $source ];
}
}
return $source ?: '—';
}
public static function render( WP_Post $post ): void {
wp_nonce_field( 'sg7g_save_media', 'sg7g_media_nonce' );
$type = (string) get_post_meta( $post->ID, '_sg7g_media_type', true );
$type = in_array( $type, array( 'video', 'audio' ), true ) ? $type : 'video';
$sources = self::sources();
$source = (string) get_post_meta( $post->ID, '_sg7g_source', true );
if ( ! isset( $sources[ $type ][ $source ] ) ) {
$source = 'video' === $type ? 'youtube' : 'spotify';
}
$url = get_post_meta( $post->ID, '_sg7g_source_url', true );
$file_id = absint( get_post_meta( $post->ID, '_sg7g_file_id', true ) );
$file_url = $file_id ? wp_get_attachment_url( $file_id ) : '';
?>
get_error_message() );
} else {
delete_post_meta( $post_id, '_sg7g_source_error' );
}
$duplicate_id = self::find_duplicate_source( $post_id, $url, $file_id, $source );
if ( $duplicate_id ) {
update_post_meta( $post_id, '_sg7g_duplicate_source_id', $duplicate_id );
} else {
delete_post_meta( $post_id, '_sg7g_duplicate_source_id' );
}
}
private static function find_duplicate_source( int $post_id, string $url, int $file_id, string $source ): int {
$meta_query = array();
if ( in_array( $source, array( 'local_video', 'local_audio' ), true ) && $file_id > 0 ) {
$meta_query[] = array(
'key' => '_sg7g_file_id',
'value' => $file_id,
'type' => 'NUMERIC',
);
} elseif ( '' !== $url ) {
$meta_query[] = array(
'key' => '_sg7g_source_url',
'value' => $url,
);
}
if ( empty( $meta_query ) ) {
return 0;
}
$matches = get_posts(
array(
'post_type' => 'sg7g_media',
'post_status' => array( 'publish', 'draft', 'pending', 'private', 'future' ),
'posts_per_page' => 1,
// phpcs:ignore WordPressVIPMinimum.Performance.WPQueryParams.PostNotIn_post__not_in -- Single-ID exclusion for duplicate detection.
'post__not_in' => array( $post_id ),
'fields' => 'ids',
'orderby' => 'ID',
'order' => 'ASC',
// phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- Required duplicate lookup, limited to one result.
'meta_query' => $meta_query,
'no_found_rows' => true,
'update_post_meta_cache' => false,
'update_post_term_cache' => false,
)
);
return empty( $matches ) ? 0 : absint( $matches[0] );
}
/**
* Detect source and media type from a public URL.
*
* @return array{type:string,source:string}|array{}
*/
public static function detect_source( string $url ): array {
$url = html_entity_decode( $url, ENT_QUOTES, 'UTF-8' );
$host = strtolower( (string) wp_parse_url( $url, PHP_URL_HOST ) );
$path = strtolower( (string) wp_parse_url( $url, PHP_URL_PATH ) );
$host = preg_replace( '/^www\./', '', $host );
if ( ! $host ) {
return array();
}
if ( 'music.youtube.com' === $host ) {
return array( 'type' => 'audio', 'source' => 'youtube_music' );
}
if ( in_array( $host, array( 'youtube.com', 'm.youtube.com', 'youtu.be', 'youtube-nocookie.com' ), true ) || str_ends_with( $host, '.youtube.com' ) ) {
return array( 'type' => 'video', 'source' => 'youtube' );
}
if ( 'vimeo.com' === $host || str_ends_with( $host, '.vimeo.com' ) ) {
return array( 'type' => 'video', 'source' => 'vimeo' );
}
if ( 'open.spotify.com' === $host ) {
return array( 'type' => 'audio', 'source' => 'spotify' );
}
if ( 'music.apple.com' === $host || 'embed.music.apple.com' === $host ) {
return array( 'type' => 'audio', 'source' => 'apple_music' );
}
if ( 'soundcloud.com' === $host || str_ends_with( $host, '.soundcloud.com' ) ) {
return array( 'type' => 'audio', 'source' => 'soundcloud' );
}
if ( 'deezer.com' === $host || str_ends_with( $host, '.deezer.com' ) ) {
return array( 'type' => 'audio', 'source' => 'deezer' );
}
$extension = strtolower( pathinfo( $path, PATHINFO_EXTENSION ) );
if ( in_array( $extension, array( 'mp4', 'webm', 'ogv', 'm3u8' ), true ) ) {
return array( 'type' => 'video', 'source' => 'external_video' );
}
if ( in_array( $extension, array( 'mp3', 'm4a', 'wav', 'ogg', 'oga', 'aac' ), true ) ) {
return array( 'type' => 'audio', 'source' => 'external_audio' );
}
return array();
}
/**
* Validate the selected local file or platform URL without blocking publication.
*
* @return true|WP_Error
*/
public static function validate_source( string $type, string $source, string $url, int $file_id ) {
if ( in_array( $source, array( 'local_video', 'local_audio' ), true ) ) {
if ( ! $file_id || 'attachment' !== get_post_type( $file_id ) ) {
return new WP_Error( 'sg7g_missing_file', __( 'No file has been selected from the Media Library.', '7sgaleri' ) );
}
$mime = (string) get_post_mime_type( $file_id );
$expected = 'local_audio' === $source ? 'audio/' : 'video/';
if ( ! str_starts_with( $mime, $expected ) ) {
return new WP_Error( 'sg7g_wrong_file', __( 'The selected file type does not match the content type.', '7sgaleri' ) );
}
return true;
}
$url = html_entity_decode( $url, ENT_QUOTES, 'UTF-8' );
if ( ! $url || ! wp_http_validate_url( $url ) ) {
return new WP_Error( 'sg7g_invalid_url', __( 'No valid source link was entered.', '7sgaleri' ) );
}
$detected = self::detect_source( $url );
if ( ! $detected ) {
return new WP_Error( 'sg7g_unsupported_url', __( 'The link does not belong to a supported platform or direct media file.', '7sgaleri' ) );
}
if ( $detected['type'] !== $type || $detected['source'] !== $source ) {
return new WP_Error( 'sg7g_source_mismatch', __( 'The link does not match the selected content type or source.', '7sgaleri' ) );
}
$path = trim( (string) wp_parse_url( $url, PHP_URL_PATH ), '/' );
if ( in_array( $source, array( 'youtube', 'youtube_music' ), true ) && ! self::youtube_id( $url ) && ! self::youtube_playlist_id( $url ) ) {
return new WP_Error( 'sg7g_youtube_url', __( 'No valid video or playlist ID was found in the YouTube link.', '7sgaleri' ) );
}
if ( 'vimeo' === $source && ! self::vimeo_id( $url ) ) {
return new WP_Error( 'sg7g_vimeo_url', __( 'Enter a valid Vimeo video link.', '7sgaleri' ) );
}
if ( 'spotify' === $source ) {
$segments = self::normalized_segments( $path );
if ( isset( $segments[0] ) && 'embed' === strtolower( $segments[0] ) ) {
array_shift( $segments );
}
if ( count( $segments ) < 2 || ! in_array( $segments[0], array( 'track', 'album', 'playlist', 'episode', 'show', 'artist' ), true ) ) {
return new WP_Error( 'sg7g_spotify_url', __( 'Enter a valid Spotify track, album, playlist, artist, or podcast link.', '7sgaleri' ) );
}
}
if ( 'apple_music' === $source && '' === $path ) {
return new WP_Error( 'sg7g_apple_url', __( 'Enter a valid Apple Music content link.', '7sgaleri' ) );
}
if ( 'soundcloud' === $source && '' === $path ) {
return new WP_Error( 'sg7g_soundcloud_url', __( 'Enter a valid SoundCloud content link.', '7sgaleri' ) );
}
if ( 'deezer' === $source ) {
$segments = self::normalized_segments( $path );
if ( isset( $segments[0], $segments[1] ) && 'widget' === $segments[0] && 'auto' === $segments[1] ) {
$segments = array_slice( $segments, 2 );
}
if ( count( $segments ) < 2 || ! in_array( $segments[0], array( 'track', 'album', 'playlist', 'artist', 'show', 'episode' ), true ) ) {
return new WP_Error( 'sg7g_deezer_url', __( 'Enter a valid Deezer content link.', '7sgaleri' ) );
}
}
return true;
}
private static function normalized_segments( string $path ): array {
$segments = array_values( array_filter( explode( '/', trim( $path, '/' ) ) ) );
if ( isset( $segments[0] ) && ( preg_match( '/^intl-[a-z]{2}$/i', $segments[0] ) || preg_match( '/^[a-z]{2}(?:-[a-z]{2})?$/i', $segments[0] ) ) ) {
array_shift( $segments );
}
return $segments;
}
public static function youtube_id( string $url ): string {
$host = strtolower( (string) wp_parse_url( $url, PHP_URL_HOST ) );
$path = trim( (string) wp_parse_url( $url, PHP_URL_PATH ), '/' );
parse_str( (string) wp_parse_url( $url, PHP_URL_QUERY ), $query );
$candidate = '';
if ( str_contains( $host, 'youtu.be' ) ) {
$candidate = (string) strtok( $path, '/' );
} elseif ( isset( $query['v'] ) ) {
$candidate = (string) $query['v'];
} elseif ( preg_match( '~(?:embed|shorts|live)/([^/?]+)~', $path, $matches ) ) {
$candidate = $matches[1];
}
$candidate = sanitize_text_field( $candidate );
return preg_match( '/^[A-Za-z0-9_-]{6,20}$/', $candidate ) ? $candidate : '';
}
public static function youtube_playlist_id( string $url ): string {
parse_str( (string) wp_parse_url( $url, PHP_URL_QUERY ), $query );
$candidate = isset( $query['list'] ) ? sanitize_text_field( (string) $query['list'] ) : '';
return preg_match( '/^[A-Za-z0-9_-]{10,80}$/', $candidate ) ? $candidate : '';
}
public static function vimeo_id( string $url ): string {
$host = strtolower( (string) wp_parse_url( $url, PHP_URL_HOST ) );
$path = trim( (string) wp_parse_url( $url, PHP_URL_PATH ), '/' );
if ( ! str_contains( $host, 'vimeo.com' ) ) {
return '';
}
$segments = array_values( array_filter( explode( '/', $path ) ) );
$video_id = '';
foreach ( $segments as $index => $segment ) {
if ( 'video' === strtolower( $segment ) && isset( $segments[ $index + 1 ] ) && ctype_digit( $segments[ $index + 1 ] ) ) {
$video_id = $segments[ $index + 1 ];
} elseif ( ctype_digit( $segment ) ) {
$video_id = $segment;
}
}
return sanitize_text_field( $video_id );
}
/**
* Return the privacy hash used by Vimeo unlisted links when available.
*/
public static function vimeo_hash( string $url ): string {
$path = trim( (string) wp_parse_url( $url, PHP_URL_PATH ), '/' );
parse_str( (string) wp_parse_url( $url, PHP_URL_QUERY ), $query );
if ( isset( $query['h'] ) ) {
$candidate = sanitize_text_field( (string) $query['h'] );
return preg_match( '/^[A-Za-z0-9]+$/', $candidate ) ? $candidate : '';
}
$video_id = self::vimeo_id( $url );
$segments = array_values( array_filter( explode( '/', $path ) ) );
$id_index = array_search( $video_id, $segments, true );
if ( false !== $id_index && isset( $segments[ $id_index + 1 ] ) ) {
$candidate = sanitize_text_field( (string) $segments[ $id_index + 1 ] );
if ( preg_match( '/^[A-Za-z0-9]+$/', $candidate ) && ! ctype_digit( $candidate ) && 'video' !== strtolower( $candidate ) ) {
return $candidate;
}
}
return '';
}
public static function source_notice(): void {
$screen = get_current_screen();
if ( ! $screen || 'sg7g_media' !== $screen->post_type || 'post' !== $screen->base ) {
return;
}
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only post ID on the authorized edit screen.
$post_id = isset( $_GET['post'] ) ? absint( $_GET['post'] ) : 0;
if ( ! $post_id ) {
return;
}
if ( 0 === (int) get_post_meta( $post_id, '_sg7g_source_valid', true ) ) {
$message = (string) get_post_meta( $post_id, '_sg7g_source_error', true );
if ( '' !== $message ) {
printf(
'',
esc_html__( 'Media source warning:', '7sgaleri' ),
esc_html( $message )
);
}
}
$duplicate_id = absint( get_post_meta( $post_id, '_sg7g_duplicate_source_id', true ) );
if ( $duplicate_id && 'sg7g_media' === get_post_type( $duplicate_id ) && 'trash' !== get_post_status( $duplicate_id ) ) {
$edit_url = get_edit_post_link( $duplicate_id, 'raw' );
printf(
'',
esc_html__( 'Duplicate media source:', '7sgaleri' ),
esc_html__( 'Another item using the same media source was found.', '7sgaleri' ),
$edit_url ? '' . esc_html__( 'Review content', '7sgaleri' ) . '' : ''
);
}
}
}