'int', 'duration_seconds' => 'int', 'play_count' => 'int', 'is_favorite' => 'bool', 'release_date' => 'date', 'last_played_at' => 'datetime' ]; public function source() { $stmt = $this->pdo->prepare("SELECT * FROM sources WHERE id = :source_id"); $stmt->execute(['source_id' => $this->source_id]); $sourceData = $stmt->fetch(\PDO::FETCH_ASSOC); return $sourceData ? new Source($this->pdo, $sourceData) : null; } /** * Get the artist for this track */ public function artist() { $stmt = $this->pdo->prepare("SELECT * FROM music_artists WHERE id = :artist_id"); $stmt->execute(['artist_id' => $this->artist_id]); $artistData = $stmt->fetch(\PDO::FETCH_ASSOC); return $artistData ? new MusicArtist($this->pdo, $artistData) : null; } /** * Get the album for this track */ public function album() { if (!$this->album_id) { return null; } $stmt = $this->pdo->prepare("SELECT * FROM music_albums WHERE id = :album_id"); $stmt->execute(['album_id' => $this->album_id]); $albumData = $stmt->fetch(\PDO::FETCH_ASSOC); return $albumData ? new MusicAlbum($this->pdo, $albumData) : null; } /** * Toggle favorite status */ public function toggleFavorite(): bool { return $this->update($this->id, [ 'is_favorite' => !$this->is_favorite ]); } /** * Increment play count and update last played time */ public function markAsPlayed(): bool { return $this->update($this->id, [ 'play_count' => $this->play_count + 1, 'last_played_at' => date('Y-m-d H:i:s') ]); } /** * Get total count of tracks with optional filters */ public static function getTotalCount(\PDO $pdo, string $search = '', array $genres = [], array $artists = [], array $albums = []): int { $where = []; $params = []; $sql = "SELECT COUNT(*) as count FROM music_tracks mt JOIN sources s ON mt.source_id = s.id"; if (!empty($search)) { $where[] = "(mt.title LIKE :search OR mt.artist_name LIKE :search OR mt.album_name LIKE :search)"; $params[':search'] = "%$search%"; } if (!empty($genres)) { $genreConditions = []; foreach ($genres as $i => $genre) { $param = ":genre_$i"; $genreConditions[] = "mt.genre LIKE $param"; $params[$param] = "%$genre%"; } $where[] = "(" . implode(' OR ', $genreConditions) . ")"; } if (!empty($artists)) { $artistConditions = []; foreach ($artists as $i => $artist) { $param = ":artist_$i"; $artistConditions[] = "mt.artist_name LIKE $param"; $params[$param] = "%$artist%"; } $where[] = "(" . implode(' OR ', $artistConditions) . ")"; } if (!empty($albums)) { $albumConditions = []; foreach ($albums as $i => $album) { $param = ":album_$i"; $albumConditions[] = "mt.album_name LIKE $param"; $params[$param] = "%$album%"; } $where[] = "(" . implode(' OR ', $albumConditions) . ")"; } if (!empty($where)) { $sql .= " WHERE " . implode(' AND ', $where); } $stmt = $pdo->prepare($sql); $stmt->execute($params); return (int)$stmt->fetchColumn(); } /** * Get paginated tracks with optional filters */ public static function getAllWithPagination( \PDO $pdo, int $page = 1, int $perPage = 20, string $search = '', array $genres = [], array $artists = [], array $albums = [], string $sort = 'title_asc' ): array { $offset = ($page - 1) * $perPage; $where = []; $params = []; if (!empty($search)) { $where[] = "(title LIKE :search OR artist_name LIKE :search OR album_name LIKE :search)"; $params[':search'] = "%$search%"; } if (!empty($genres)) { $genreConditions = []; foreach ($genres as $i => $genre) { $param = ":genre$i"; $genreConditions[] = "genre LIKE $param"; $params[$param] = "%$genre%"; } $where[] = "(" . implode(' OR ', $genreConditions) . ")"; } if (!empty($artists)) { $artistConditions = []; foreach ($artists as $i => $artist) { $param = ":artist$i"; $artistConditions[] = "artist_name LIKE $param"; $params[$param] = "%$artist%"; } $where[] = "(" . implode(' OR ', $artistConditions) . ")"; } if (!empty($albums)) { $albumConditions = []; foreach ($albums as $i => $album) { $param = ":album$i"; $albumConditions[] = "album_name LIKE $param"; $params[$param] = "%$album%"; } $where[] = "(" . implode(' OR ', $albumConditions) . ")"; } // Determine sort order $orderBy = 'title ASC'; switch ($sort) { case 'title_desc': $orderBy = 'title DESC'; break; case 'artist_asc': $orderBy = 'artist_name ASC'; break; case 'artist_desc': $orderBy = 'artist_name DESC'; break; case 'album_asc': $orderBy = 'album_name ASC'; break; case 'album_desc': $orderBy = 'album_name DESC'; break; case 'duration_asc': $orderBy = 'duration_seconds ASC'; break; case 'duration_desc': $orderBy = 'duration_seconds DESC'; break; case 'play_count_desc': $orderBy = 'play_count DESC'; break; case 'last_played_desc': $orderBy = 'last_played_at DESC NULLS LAST'; break; } $sql = "SELECT mt.*, s.display_name as source_name FROM music_tracks mt JOIN sources s ON mt.source_id = s.id"; if (!empty($where)) { $sql .= " WHERE " . implode(' AND ', $where); } $sql .= " ORDER BY $orderBy LIMIT :limit OFFSET :offset"; $stmt = $pdo->prepare($sql); // Bind parameters foreach ($params as $key => $value) { $stmt->bindValue($key, $value); } $stmt->bindValue(':limit', $perPage, \PDO::PARAM_INT); $stmt->bindValue(':offset', $offset, \PDO::PARAM_INT); $stmt->execute(); return $stmt->fetchAll(\PDO::FETCH_ASSOC); } /** * Get all unique genres from tracks */ public static function getAvailableGenres(\PDO $pdo): array { $stmt = $pdo->query("SELECT DISTINCT genre FROM music_tracks WHERE genre IS NOT NULL AND genre != '' ORDER BY genre"); return $stmt->fetchAll(\PDO::FETCH_COLUMN); } /** * Get all unique artists from tracks */ public static function getAvailableArtists(\PDO $pdo): array { $stmt = $pdo->query("SELECT DISTINCT artist_name FROM music_tracks WHERE artist_name IS NOT NULL AND artist_name != '' ORDER BY artist_name"); return $stmt->fetchAll(\PDO::FETCH_COLUMN); } /** * Get all unique albums from tracks */ public static function getAvailableAlbums(\PDO $pdo): array { $stmt = $pdo->query("SELECT DISTINCT album_name FROM music_tracks WHERE album_name IS NOT NULL AND album_name != '' ORDER BY album_name"); return $stmt->fetchAll(\PDO::FETCH_COLUMN); } /** * Get track statistics */ public static function getStats(\PDO $pdo): array { $stmt = $pdo->query(" SELECT COUNT(*) as total_tracks, COUNT(CASE WHEN is_favorite = 1 THEN 1 END) as favorite_tracks, SUM(play_count) as total_plays, SUM(duration_seconds) as total_duration, AVG(duration_seconds) as avg_duration FROM music_tracks "); return $stmt->fetch(\PDO::FETCH_ASSOC); } /** * Get recently played tracks */ public static function getRecentlyPlayed(\PDO $pdo, int $limit = 10): array { $stmt = $pdo->prepare(" SELECT mt.*, s.display_name as source_name FROM music_tracks mt JOIN sources s ON mt.source_id = s.id WHERE mt.last_played_at IS NOT NULL ORDER BY mt.last_played_at DESC LIMIT :limit "); $stmt->execute(['limit' => $limit]); return $stmt->fetchAll(\PDO::FETCH_ASSOC); } /** * Get most played tracks */ public static function getMostPlayed(\PDO $pdo, int $limit = 10): array { $stmt = $pdo->prepare(" SELECT mt.*, s.display_name as source_name FROM music_tracks mt JOIN sources s ON mt.source_id = s.id ORDER BY mt.play_count DESC, mt.title ASC LIMIT :limit "); $stmt->execute(['limit' => $limit]); return $stmt->fetchAll(\PDO::FETCH_ASSOC); } }