logger = $logger; $this->cache = $cache; } /** * Register hooks. */ public function init(): void { add_action( 'wp_dashboard_setup', [ $this, 'register_widget' ] ); add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_styles' ] ); } /** * Register the dashboard widget. */ public function register_widget(): void { wp_add_dashboard_widget( $this->widget_id, __( 'Piperless — Audio Storage', 'piperless' ), [ $this, 'render' ] ); } /** * Render the dashboard widget. */ public function render(): void { $file_stats = $this->get_file_stats(); $without = $this->get_posts_without_audio(); $disk = $this->get_disk_usage(); $total_files = ( $file_stats['mp3']['count'] ?? 0 ) + ( $file_stats['wav']['count'] ?? 0 ) + ( $file_stats['opus']['count'] ?? 0 ); ?>

render_file_card( __( 'Opus', 'piperless' ), $file_stats['opus'] ?? [ 'count' => 0, 'bytes' => 0 ], '#1565c0' ); ?> render_file_card( __( 'MP3', 'piperless' ), $file_stats['mp3'] ?? [ 'count' => 0, 'bytes' => 0 ], '#008a20' ); ?> render_file_card( __( 'WAV', 'piperless' ), $file_stats['wav'] ?? [ 'count' => 0, 'bytes' => 0 ], '#f56e28' ); ?>
0 ) : ?>
human_size( $disk['free'] ) ), esc_html( $this->human_size( $disk['total'] ) ) ); ?>
int, 'bytes' => int]. * @param string $color Accent color hex. */ private function render_file_card( string $label, array $stats, string $color ): void { $count = $stats['count'] ?? 0; $size = $this->human_size( $stats['bytes'] ?? 0 ); ?>
*/ private function get_file_stats(): array { $cache_dir = $this->cache->dir(); $stats = [ 'mp3' => [ 'count' => 0, 'bytes' => 0 ], 'wav' => [ 'count' => 0, 'bytes' => 0 ], 'opus' => [ 'count' => 0, 'bytes' => 0 ], ]; if ( ! is_dir( $cache_dir ) ) { return $stats; } foreach ( [ 'mp3', 'wav', 'opus' ] as $ext ) { $files = (array) glob( $cache_dir . '/*.' . $ext ); foreach ( $files as $file ) { $size = @filesize( $file ); if ( false !== $size ) { $stats[ $ext ]['count']++; $stats[ $ext ]['bytes'] += $size; } } } return $stats; } /** * Count published posts / pages without an audio transcript. * * @return int */ private function get_posts_without_audio(): int { global $wpdb; $types = $this->supported_post_types(); $placeholders = implode( ',', array_fill( 0, count( $types ), '%s' ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery $count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$wpdb->posts} p WHERE p.post_type IN ( {$placeholders} ) AND p.post_status = 'publish' AND NOT EXISTS ( SELECT 1 FROM {$wpdb->postmeta} pm WHERE pm.post_id = p.ID AND pm.meta_key = '_piperless_audio_url' AND pm.meta_value != '' )", ...$types ) ); return (int) $count; } /** * Get disk usage stats for the uploads directory volume. * * @return array{total:float,free:float,used:float,pct:float} */ private function get_disk_usage(): array { $dir = $this->cache->dir(); // Walk up until we find a directory that exists. while ( ! is_dir( $dir ) && '/' !== $dir ) { $dir = dirname( $dir ); } $total = @disk_total_space( $dir ); $free = @disk_free_space( $dir ); $fallback = [ 'total' => 0.0, 'free' => 0.0, 'used' => 0.0, 'pct' => 0.0 ]; if ( false === $total || null === $total || false === $free || null === $free || $total <= 0.0 ) { return $fallback; } $used = $total - $free; $pct = ( $used / $total ) * 100.0; return [ 'total' => (float) $total, 'free' => (float) $free, 'used' => (float) $used, 'pct' => (float) $pct, ]; } /** * Format bytes as a human-readable string. * * @param float $bytes Raw byte count. * @return string e.g. "24.5 MB" */ private function human_size( float $bytes ): string { if ( $bytes <= 0.0 ) { return '0 B'; } $units = [ 'B', 'KB', 'MB', 'GB', 'TB' ]; $exp = (int) floor( log( $bytes, 1024 ) ); $exp = min( $exp, count( $units ) - 1 ); $val = $bytes / ( 1024 ** $exp ); return round( $val, 1 ) . ' ' . $units[ $exp ]; } /** * Enqueue dashboard-specific styles on the dashboard page only. * * @param string $hook_suffix Current admin page hook. */ public function enqueue_styles( string $hook_suffix ): void { if ( 'index.php' !== $hook_suffix ) { return; } wp_enqueue_style( 'piperless-dashboard', PIPERLESS_PLUGIN_URL . 'assets/css/dashboard.css', [], PIPERLESS_VERSION ); } /** * Supported post types for audio transcripts. * * @return string[] */ private function supported_post_types(): array { $types = apply_filters( 'piperless_post_types', [ 'post', 'page' ] ); return is_array( $types ) ? $types : [ 'post' ]; } }