'sg7g_media', 'post_status' => array( 'publish', 'draft', 'pending', 'private', 'future' ), 'posts_per_page' => 50, 'orderby' => 'ID', 'order' => 'ASC', // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- One-time backfill query for missing cover metadata. 'meta_query' => array( 'relation' => 'OR', array( 'key' => '_thumbnail_id', 'compare' => 'NOT EXISTS', ), array( 'key' => '_thumbnail_id', 'value' => '0', 'compare' => '=', ), ), ) ); foreach ( $posts as $post ) { if ( $post instanceof WP_Post ) { self::ensure( $post->ID, $post, true ); } } if ( count( $posts ) < 50 ) { update_option( 'sg7g_cover_backfill_version', self::BACKFILL_VERSION, false ); } } public static function ensure( int $post_id, WP_Post $post, bool $update ): void { unset( $update ); if ( class_exists( 'SG7G_Transfer' ) && SG7G_Transfer::is_importing() ) { return; } if ( wp_is_post_autosave( $post_id ) || wp_is_post_revision( $post_id ) ) { return; } if ( in_array( $post->post_status, array( 'auto-draft', 'trash', 'inherit' ), true ) ) { return; } if ( 'sg7g_media' !== $post->post_type ) { return; } $lock_time = (int) get_post_meta( $post_id, self::LOCK_META, true ); if ( $lock_time && ( time() - $lock_time ) < 300 ) { return; } if ( $lock_time ) { delete_post_meta( $post_id, self::LOCK_META ); } if ( ! add_post_meta( $post_id, self::LOCK_META, time(), true ) ) { return; } try { $default_id = absint( get_post_meta( $post_id, '_sg7g_default_cover_id', true ) ); $thumbnail_id = absint( get_post_thumbnail_id( $post_id ) ); // A user-selected cover always wins. Remove the now-unused generated // attachment so automatic covers do not accumulate in the Media Library. if ( $thumbnail_id && $thumbnail_id !== $default_id ) { if ( $default_id && get_post_meta( $default_id, '_sg7g_generated_cover', true ) ) { wp_delete_attachment( $default_id, true ); delete_post_meta( $post_id, '_sg7g_default_cover_id' ); delete_post_meta( $post_id, '_sg7g_default_cover_hash' ); } return; } $type = get_post_meta( $post_id, '_sg7g_media_type', true ); $type = 'audio' === $type ? 'audio' : 'video'; $hash = md5( $post->post_title . '|' . $type ); if ( $default_id && get_post( $default_id ) ) { $file = get_attached_file( $default_id ); if ( $file && file_exists( $file ) ) { if ( $hash !== get_post_meta( $post_id, '_sg7g_default_cover_hash', true ) && self::write_cover( $file, $post->post_title, $type ) ) { self::refresh_attachment_metadata( $default_id, $file ); update_post_meta( $post_id, '_sg7g_default_cover_hash', $hash ); } update_post_meta( $default_id, '_wp_attachment_image_alt', $post->post_title ?: __( 'Gallery default cover image', '7sgaleri' ) ); wp_update_post( array( 'ID' => $default_id, 'post_title' => sprintf( /* translators: %s: Content title. */ __( '%s – Default Cover', '7sgaleri' ), $post->post_title ?: __( 'Gallery Content', '7sgaleri' ) ), ) ); if ( $thumbnail_id !== $default_id ) { set_post_thumbnail( $post_id, $default_id ); } return; } } $attachment_id = self::create_attachment( $post_id, $post->post_title, $type ); if ( ! $attachment_id ) { return; } update_post_meta( $post_id, '_sg7g_default_cover_id', $attachment_id ); update_post_meta( $post_id, '_sg7g_default_cover_hash', $hash ); set_post_thumbnail( $post_id, $attachment_id ); } finally { delete_post_meta( $post_id, self::LOCK_META ); } } public static function cleanup_on_delete( int $post_id, WP_Post $post ): void { if ( 'sg7g_media' !== $post->post_type ) { return; } $default_id = absint( get_post_meta( $post_id, '_sg7g_default_cover_id', true ) ); if ( $default_id && get_post_meta( $default_id, '_sg7g_generated_cover', true ) ) { wp_delete_attachment( $default_id, true ); } } private static function create_attachment( int $post_id, string $title, string $type ): int { $uploads = wp_upload_dir(); if ( ! empty( $uploads['error'] ) ) { return 0; } $directory = trailingslashit( $uploads['path'] ) . '7sgaleri'; if ( ! wp_mkdir_p( $directory ) ) { return 0; } $filename = '7sgaleri-varsayilan-kapak-' . $post_id . '.png'; $filepath = trailingslashit( $directory ) . sanitize_file_name( $filename ); if ( ! self::write_cover( $filepath, $title, $type ) ) { return 0; } $filetype = wp_check_filetype( basename( $filepath ), null ); $attachment_id = wp_insert_attachment( array( 'post_mime_type' => $filetype['type'] ?: 'image/png', 'post_title' => sprintf( /* translators: %s: Content title. */ __( '%s – Default Cover', '7sgaleri' ), $title ?: __( 'Gallery Content', '7sgaleri' ) ), 'post_content' => '', 'post_status' => 'inherit', ), $filepath, $post_id, true ); if ( is_wp_error( $attachment_id ) ) { if ( file_exists( $filepath ) ) { wp_delete_file( $filepath ); } return 0; } self::refresh_attachment_metadata( (int) $attachment_id, $filepath ); update_post_meta( (int) $attachment_id, '_wp_attachment_image_alt', $title ?: __( 'Gallery default cover image', '7sgaleri' ) ); update_post_meta( (int) $attachment_id, '_sg7g_generated_cover', 1 ); return (int) $attachment_id; } private static function refresh_attachment_metadata( int $attachment_id, string $filepath ): void { require_once ABSPATH . 'wp-admin/includes/image.php'; $metadata = wp_generate_attachment_metadata( $attachment_id, $filepath ); if ( is_array( $metadata ) ) { wp_update_attachment_metadata( $attachment_id, $metadata ); } } private static function write_cover( string $filepath, string $title, string $type ): bool { try { if ( function_exists( 'imagecreatetruecolor' ) && function_exists( 'imagepng' ) ) { return self::write_with_gd( $filepath, $title, $type ); } $fallback = SG7G_DIR . 'assets/images/default-' . ( 'audio' === $type ? 'audio' : 'video' ) . '.png'; if ( ! file_exists( $fallback ) ) { return false; } require_once ABSPATH . 'wp-admin/includes/file.php'; global $wp_filesystem; return WP_Filesystem() && $wp_filesystem instanceof WP_Filesystem_Base && $wp_filesystem->copy( $fallback, $filepath, true, FS_CHMOD_FILE ); } catch ( Throwable $error ) { unset( $error ); return false; } } private static function write_with_gd( string $filepath, string $title, string $type ): bool { $image = imagecreatetruecolor( self::WIDTH, self::HEIGHT ); if ( ! $image ) { return false; } imagealphablending( $image, true ); imagesavealpha( $image, true ); $palette = 'audio' === $type ? array( array( 6, 23, 35 ), array( 15, 92, 88 ), array( 58, 230, 194 ) ) : array( array( 9, 12, 21 ), array( 63, 15, 91 ), array( 255, 76, 139 ) ); for ( $y = 0; $y < self::HEIGHT; $y++ ) { $ratio = $y / max( 1, self::HEIGHT - 1 ); $r = (int) round( $palette[0][0] + ( $palette[1][0] - $palette[0][0] ) * $ratio ); $g = (int) round( $palette[0][1] + ( $palette[1][1] - $palette[0][1] ) * $ratio ); $b = (int) round( $palette[0][2] + ( $palette[1][2] - $palette[0][2] ) * $ratio ); $color = imagecolorallocate( $image, $r, $g, $b ); imageline( $image, 0, $y, self::WIDTH, $y, $color ); } $accent_soft = imagecolorallocatealpha( $image, $palette[2][0], $palette[2][1], $palette[2][2], 88 ); $accent = imagecolorallocate( $image, $palette[2][0], $palette[2][1], $palette[2][2] ); $white = imagecolorallocate( $image, 248, 249, 252 ); $muted = imagecolorallocatealpha( $image, 248, 249, 252, 36 ); $dark = imagecolorallocatealpha( $image, 0, 0, 0, 58 ); imagefilledellipse( $image, 1420, 120, 620, 620, $accent_soft ); imagefilledellipse( $image, 1360, 780, 760, 760, $accent_soft ); imagefilledrectangle( $image, 0, 742, self::WIDTH, self::HEIGHT, $dark ); imagefilledrectangle( $image, 110, 105, 220, 113, $accent ); if ( 'audio' === $type ) { self::draw_waveform( $image, 1170, 380, $accent, $muted ); } else { self::draw_play_symbol( $image, 1290, 385, $accent, $white ); } self::draw_text( $image, '7SGALERI', 110, 150, 28, $muted ); $media_label = 'audio' === $type ? __( 'Audio / Music', '7sgaleri' ) : __( 'Video', '7sgaleri' ); self::draw_text( $image, strtoupper( remove_accents( $media_label ) ), 110, 215, 34, $accent ); $clean_title = trim( wp_strip_all_tags( $title ) ); if ( '' === $clean_title ) { $clean_title = __( 'Gallery Content', '7sgaleri' ); } self::draw_wrapped_title( $image, $clean_title, 110, 360, 990, $white ); $result = imagepng( $image, $filepath, 8 ); imagedestroy( $image ); return (bool) $result; } private static function draw_play_symbol( $image, int $center_x, int $center_y, int $accent, int $white ): void { imagefilledellipse( $image, $center_x, $center_y, 230, 230, $accent ); imagefilledpolygon( $image, array( $center_x - 28, $center_y - 54, $center_x - 28, $center_y + 54, $center_x + 66, $center_y, ), $white ); } private static function draw_waveform( $image, int $center_x, int $center_y, int $accent, int $muted ): void { $heights = array( 80, 160, 250, 340, 230, 390, 300, 190, 290, 145, 90 ); $start_x = $center_x - 275; foreach ( $heights as $index => $height ) { $x = $start_x + ( $index * 52 ); $color = 5 === $index ? $accent : $muted; imagefilledrectangle( $image, $x, $center_y - (int) ( $height / 2 ), $x + 20, $center_y + (int) ( $height / 2 ), $color ); } } private static function draw_wrapped_title( $image, string $title, int $x, int $y, int $max_width, int $color ): void { $font = self::font_path(); if ( $font && function_exists( 'imagettftext' ) ) { $size = 64; $lines = self::wrap_ttf( $title, $font, $size, $max_width, 3 ); foreach ( $lines as $index => $line ) { imagettftext( $image, $size, 0, $x, $y + ( $index * 84 ), $color, $font, $line ); } return; } $plain = strtoupper( remove_accents( $title ) ); $lines = explode( "\n", wordwrap( $plain, 44, "\n", true ) ); $lines = array_slice( $lines, 0, 3 ); foreach ( $lines as $index => $line ) { imagestring( $image, 5, $x, $y + ( $index * 34 ), $line, $color ); } } private static function draw_text( $image, string $text, int $x, int $y, int $size, int $color ): void { $font = self::font_path(); if ( $font && function_exists( 'imagettftext' ) ) { imagettftext( $image, $size, 0, $x, $y, $color, $font, $text ); return; } imagestring( $image, 5, $x, $y - 20, $text, $color ); } private static function wrap_ttf( string $text, string $font, int $size, int $max_width, int $max_lines ): array { $words = preg_split( '/\s+/u', trim( $text ) ) ?: array(); $lines = array(); $line = ''; foreach ( $words as $word ) { $candidate = '' === $line ? $word : $line . ' ' . $word; $box = imagettfbbox( $size, 0, $font, $candidate ); $width = $box ? abs( $box[2] - $box[0] ) : 0; if ( $width > $max_width && '' !== $line ) { $lines[] = $line; $line = $word; if ( count( $lines ) >= $max_lines - 1 ) { break; } } else { $line = $candidate; } } if ( '' !== $line && count( $lines ) < $max_lines ) { $lines[] = $line; } if ( count( $lines ) === $max_lines && count( $words ) > 0 ) { $last = array_pop( $lines ); $lines[] = rtrim( $last, " .…" ) . '…'; } return $lines ?: array( $text ); } private static function font_path(): string { $candidates = array( '/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf', '/usr/share/fonts/dejavu/DejaVuSans-Bold.ttf', '/usr/share/fonts/truetype/liberation2/LiberationSans-Bold.ttf', '/usr/share/fonts/truetype/freefont/FreeSansBold.ttf', ); foreach ( $candidates as $candidate ) { if ( is_readable( $candidate ) ) { return $candidate; } } return ''; } }