mirror of
https://github.com/ceratic/MediaCollectorLibary.git
synced 2026-05-13 23:56:46 +02:00
- Deleted test scripts: test_jellyfin_execution.php, test_stash.php, test_xbvr.php, test_xbvr_sync.php, vite.config.js - Added DashboardController for fetching dashboard statistics and recent activity - Added GameController for managing games, including fetching all games, game details, and games by category - Introduced various check scripts to validate database structures and data integrity for adult videos, games, gender data, posters, and TV show actors
536 lines
18 KiB
PHP
536 lines
18 KiB
PHP
<?php
|
|
|
|
namespace App\Models;
|
|
|
|
class Actor extends Model
|
|
{
|
|
protected string $table = 'actors';
|
|
protected array $fillable = [
|
|
'name',
|
|
'thumbnail_path',
|
|
'metadata'
|
|
];
|
|
|
|
protected array $casts = [
|
|
'metadata' => 'array'
|
|
];
|
|
|
|
/**
|
|
* Get all actors with filtering and pagination
|
|
*/
|
|
public function findAll(array $filters = [], int $limit = null, int $offset = 0): array
|
|
{
|
|
$sql = "
|
|
SELECT a.*,
|
|
COUNT(DISTINCT am.movie_id) as movie_count,
|
|
COUNT(DISTINCT te.tv_show_id) as tv_show_count,
|
|
COUNT(DISTINCT aav.adult_video_id) as adult_video_count
|
|
FROM actors a
|
|
LEFT JOIN actor_movie am ON a.id = am.actor_id
|
|
LEFT JOIN actor_tv_episode ate ON a.id = ate.actor_id
|
|
LEFT JOIN tv_episodes te ON ate.tv_episode_id = te.id
|
|
LEFT JOIN actor_adult_video aav ON a.id = aav.actor_id
|
|
";
|
|
$params = [];
|
|
$whereClauses = [];
|
|
|
|
// Search filter
|
|
if (!empty($filters['search'])) {
|
|
$whereClauses[] = "a.name LIKE :search";
|
|
$params['search'] = "%{$filters['search']}%";
|
|
}
|
|
|
|
// Gender filter
|
|
if (!empty($filters['gender'])) {
|
|
$whereClauses[] = "JSON_UNQUOTE(JSON_EXTRACT(a.metadata, '$.gender')) = :gender";
|
|
$params['gender'] = strtoupper($filters['gender']);
|
|
}
|
|
|
|
// Adult filter
|
|
if (isset($filters['adult'])) {
|
|
if ($filters['adult']) {
|
|
$whereClauses[] = "aav.adult_video_id IS NOT NULL";
|
|
} else {
|
|
$whereClauses[] = "aav.adult_video_id IS NULL";
|
|
}
|
|
}
|
|
|
|
// Combine WHERE clauses
|
|
if (!empty($whereClauses)) {
|
|
$sql .= " WHERE " . implode(' AND ', $whereClauses);
|
|
}
|
|
|
|
$sql .= " GROUP BY a.id";
|
|
|
|
// Add sorting
|
|
$sortBy = $filters['sort'] ?? 'name';
|
|
$sortOrder = $filters['order'] ?? 'asc';
|
|
|
|
switch ($sortBy) {
|
|
case 'name':
|
|
$sql .= " ORDER BY a.name {$sortOrder}";
|
|
break;
|
|
case 'age':
|
|
$sql .= " ORDER BY JSON_EXTRACT(a.metadata, '$.age') {$sortOrder}";
|
|
break;
|
|
case 'media_count':
|
|
$sql .= " ORDER BY (COUNT(DISTINCT am.movie_id) + COUNT(DISTINCT te.tv_show_id) + COUNT(DISTINCT aav.adult_video_id)) {$sortOrder}";
|
|
break;
|
|
case 'movie_count':
|
|
$sql .= " ORDER BY COUNT(DISTINCT am.movie_id) {$sortOrder}";
|
|
break;
|
|
case 'tv_show_count':
|
|
$sql .= " ORDER BY COUNT(DISTINCT te.tv_show_id) {$sortOrder}";
|
|
break;
|
|
case 'adult_count':
|
|
$sql .= " ORDER BY COUNT(DISTINCT aav.adult_video_id) {$sortOrder}";
|
|
break;
|
|
default:
|
|
$sql .= " ORDER BY a.name ASC";
|
|
}
|
|
|
|
if ($limit) {
|
|
$sql .= " LIMIT :limit OFFSET :offset";
|
|
$params['limit'] = $limit;
|
|
$params['offset'] = $offset;
|
|
}
|
|
|
|
$stmt = $this->pdo->prepare($sql);
|
|
$stmt->execute($params);
|
|
return $stmt->fetchAll(\PDO::FETCH_ASSOC);
|
|
}
|
|
|
|
/**
|
|
* Count actors with filters
|
|
*/
|
|
public function count(array $filters = []): int
|
|
{
|
|
$sql = "
|
|
SELECT COUNT(DISTINCT a.id) as total
|
|
FROM actors a
|
|
LEFT JOIN actor_movie am ON a.id = am.actor_id
|
|
LEFT JOIN actor_tv_episode ate ON a.id = ate.actor_id
|
|
LEFT JOIN tv_episodes te ON ate.tv_episode_id = te.id
|
|
LEFT JOIN actor_adult_video aav ON a.id = aav.actor_id
|
|
";
|
|
$params = [];
|
|
$whereClauses = [];
|
|
|
|
// Search filter
|
|
if (!empty($filters['search'])) {
|
|
$whereClauses[] = "a.name LIKE :search";
|
|
$params['search'] = "%{$filters['search']}%";
|
|
}
|
|
|
|
// Gender filter
|
|
if (!empty($filters['gender'])) {
|
|
$whereClauses[] = "JSON_UNQUOTE(JSON_EXTRACT(a.metadata, '$.gender')) = :gender";
|
|
$params['gender'] = strtoupper($filters['gender']);
|
|
}
|
|
|
|
// Adult filter
|
|
if (isset($filters['adult'])) {
|
|
if ($filters['adult']) {
|
|
$whereClauses[] = "aav.adult_video_id IS NOT NULL";
|
|
} else {
|
|
$whereClauses[] = "aav.adult_video_id IS NULL";
|
|
}
|
|
}
|
|
|
|
// Combine WHERE clauses
|
|
if (!empty($whereClauses)) {
|
|
$sql .= " WHERE " . implode(' AND ', $whereClauses);
|
|
}
|
|
|
|
$stmt = $this->pdo->prepare($sql);
|
|
$stmt->execute($params);
|
|
$result = $stmt->fetch(\PDO::FETCH_ASSOC);
|
|
return (int)$result['total'];
|
|
}
|
|
|
|
/**
|
|
* Get all movies this actor is associated with
|
|
*/
|
|
public function movies()
|
|
{
|
|
$stmt = $this->pdo->prepare("
|
|
SELECT m.*, s.display_name as source_name
|
|
FROM movies m
|
|
JOIN sources s ON m.source_id = s.id
|
|
JOIN actor_movie am ON m.id = am.movie_id
|
|
WHERE am.actor_id = :actor_id
|
|
ORDER BY m.release_date DESC, m.title ASC
|
|
");
|
|
$stmt->execute(['actor_id' => $this->id]);
|
|
$movies = $stmt->fetchAll(\PDO::FETCH_ASSOC);
|
|
|
|
// Process poster URLs for each movie
|
|
foreach ($movies as &$movie) {
|
|
// Use poster_url field directly if available
|
|
if (!empty($movie['poster_url'])) {
|
|
// Keep the existing poster_url as-is since it's already in the correct format
|
|
}
|
|
|
|
// Also process metadata for additional fields
|
|
if (!empty($movie['metadata'])) {
|
|
$metadata = json_decode($movie['metadata'], true);
|
|
|
|
// Extract poster aspect ratio from metadata if available
|
|
if (!empty($metadata['poster_aspect_ratio'])) {
|
|
$movie['poster_aspect_ratio'] = $metadata['poster_aspect_ratio'];
|
|
}
|
|
|
|
// If no poster_url in main field, try to get it from metadata
|
|
if (empty($movie['poster_url'])) {
|
|
if (!empty($metadata['local_cover_path'])) {
|
|
$movie['poster_url'] = $metadata['local_cover_path'];
|
|
} elseif (!empty($metadata['cover_url'])) {
|
|
$movie['poster_url'] = $metadata['cover_url'];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return $movies;
|
|
}
|
|
|
|
/**
|
|
* Get all TV shows this actor is associated with (via episodes)
|
|
*/
|
|
public function tvShows()
|
|
{
|
|
$stmt = $this->pdo->prepare("
|
|
SELECT DISTINCT ts.*, s.display_name as source_name
|
|
FROM tv_shows ts
|
|
JOIN sources s ON ts.source_id = s.id
|
|
JOIN tv_episodes te ON ts.id = te.tv_show_id
|
|
JOIN actor_tv_episode ate ON te.id = ate.tv_episode_id
|
|
WHERE ate.actor_id = :actor_id
|
|
ORDER BY ts.first_air_date DESC, ts.title ASC
|
|
");
|
|
$stmt->execute(['actor_id' => $this->id]);
|
|
$tvShows = $stmt->fetchAll(\PDO::FETCH_ASSOC);
|
|
|
|
// Process poster URLs for each TV show
|
|
foreach ($tvShows as &$tvShow) {
|
|
// Use poster_url field directly if available
|
|
if (!empty($tvShow['poster_url'])) {
|
|
// Keep the existing poster_url as-is since it's already in the correct format
|
|
}
|
|
|
|
// Also process metadata for additional fields
|
|
if (!empty($tvShow['metadata'])) {
|
|
$metadata = json_decode($tvShow['metadata'], true);
|
|
|
|
// Extract poster aspect ratio from metadata if available
|
|
if (!empty($metadata['poster_aspect_ratio'])) {
|
|
$tvShow['poster_aspect_ratio'] = $metadata['poster_aspect_ratio'];
|
|
}
|
|
|
|
// If no poster_url in main field, try to get it from metadata
|
|
if (empty($tvShow['poster_url'])) {
|
|
if (!empty($metadata['local_cover_path'])) {
|
|
$tvShow['poster_url'] = $metadata['local_cover_path'];
|
|
} elseif (!empty($metadata['cover_url'])) {
|
|
$tvShow['poster_url'] = $metadata['cover_url'];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return $tvShows;
|
|
}
|
|
|
|
/**
|
|
* Get all adult videos this actor is associated with
|
|
*/
|
|
public function adultVideos()
|
|
{
|
|
$stmt = $this->pdo->prepare("
|
|
SELECT av.*, s.display_name as source_name
|
|
FROM adult_videos av
|
|
JOIN sources s ON av.source_id = s.id
|
|
JOIN actor_adult_video aav ON av.id = aav.adult_video_id
|
|
WHERE aav.actor_id = :actor_id
|
|
ORDER BY av.release_date DESC, av.title ASC
|
|
");
|
|
$stmt->execute(['actor_id' => $this->id]);
|
|
$adultVideos = $stmt->fetchAll(\PDO::FETCH_ASSOC);
|
|
|
|
// Process poster URLs for each adult video
|
|
foreach ($adultVideos as &$adultVideo) {
|
|
// Use poster_url field directly if available
|
|
if (!empty($adultVideo['poster_url'])) {
|
|
// Keep the existing poster_url as-is since it's already in the correct format
|
|
}
|
|
|
|
// Also process metadata for additional fields
|
|
if (!empty($adultVideo['metadata'])) {
|
|
$metadata = json_decode($adultVideo['metadata'], true);
|
|
|
|
// Extract poster aspect ratio from metadata if available
|
|
if (!empty($metadata['poster_aspect_ratio'])) {
|
|
$adultVideo['poster_aspect_ratio'] = $metadata['poster_aspect_ratio'];
|
|
}
|
|
|
|
// If no poster_url in main field, try to get it from metadata
|
|
if (empty($adultVideo['poster_url'])) {
|
|
if (!empty($metadata['local_cover_path'])) {
|
|
$adultVideo['poster_url'] = $metadata['local_cover_path'];
|
|
} elseif (!empty($metadata['cover_url'])) {
|
|
$adultVideo['poster_url'] = $metadata['cover_url'];
|
|
}
|
|
}
|
|
|
|
// Add screenshot URL if available
|
|
if (!empty($metadata['screenshot_url'])) {
|
|
$adultVideo['screenshot_url'] = $metadata['screenshot_url'];
|
|
}
|
|
}
|
|
}
|
|
|
|
return $adultVideos;
|
|
}
|
|
|
|
/**
|
|
* Get actor statistics
|
|
*/
|
|
public function getStats(): array
|
|
{
|
|
$stmt = $this->pdo->prepare("
|
|
SELECT
|
|
COUNT(DISTINCT am.movie_id) as movie_count,
|
|
COUNT(DISTINCT te.tv_show_id) as tv_show_count,
|
|
COUNT(DISTINCT aav.adult_video_id) as adult_video_count,
|
|
COUNT(DISTINCT am.movie_id) + COUNT(DISTINCT te.tv_show_id) + COUNT(DISTINCT aav.adult_video_id) as total_media_count
|
|
FROM actors a
|
|
LEFT JOIN actor_movie am ON a.id = am.actor_id
|
|
LEFT JOIN actor_tv_episode ate ON a.id = ate.actor_id
|
|
LEFT JOIN tv_episodes te ON ate.tv_episode_id = te.id
|
|
LEFT JOIN actor_adult_video aav ON a.id = aav.actor_id
|
|
WHERE a.id = :actor_id
|
|
");
|
|
$stmt->execute(['actor_id' => $this->id]);
|
|
return $stmt->fetch(\PDO::FETCH_ASSOC);
|
|
}
|
|
|
|
/**
|
|
* Link actor to a movie
|
|
*/
|
|
public function linkToMovie(int $movieId): bool
|
|
{
|
|
$stmt = $this->pdo->prepare("
|
|
INSERT IGNORE INTO actor_movie (actor_id, movie_id)
|
|
VALUES (:actor_id, :movie_id)
|
|
");
|
|
return $stmt->execute([
|
|
'actor_id' => $this->id,
|
|
'movie_id' => $movieId
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Link actor to a TV show
|
|
*/
|
|
public function linkToTvShow(int $tvShowId): bool
|
|
{
|
|
$stmt = $this->pdo->prepare("
|
|
INSERT IGNORE INTO actor_tv_show (actor_id, tv_show_id)
|
|
VALUES (:actor_id, :tv_show_id)
|
|
");
|
|
return $stmt->execute([
|
|
'actor_id' => $this->id,
|
|
'tv_show_id' => $tvShowId
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Link actor to an adult video
|
|
*/
|
|
public function linkToAdultVideo(int $adultVideoId): bool
|
|
{
|
|
$stmt = $this->pdo->prepare("
|
|
INSERT IGNORE INTO actor_adult_video (actor_id, adult_video_id)
|
|
VALUES (:actor_id, :adult_video_id)
|
|
");
|
|
return $stmt->execute([
|
|
'actor_id' => $this->id,
|
|
'adult_video_id' => $adultVideoId
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Unlink actor from a movie
|
|
*/
|
|
public function unlinkFromMovie(int $movieId): bool
|
|
{
|
|
$stmt = $this->pdo->prepare("
|
|
DELETE FROM actor_movie
|
|
WHERE actor_id = :actor_id AND movie_id = :movie_id
|
|
");
|
|
return $stmt->execute([
|
|
'actor_id' => $this->id,
|
|
'movie_id' => $movieId
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Unlink actor from a TV show
|
|
*/
|
|
public function unlinkFromTvShow(int $tvShowId): bool
|
|
{
|
|
$stmt = $this->pdo->prepare("
|
|
DELETE FROM actor_tv_show
|
|
WHERE actor_id = :actor_id AND tv_show_id = :tv_show_id
|
|
");
|
|
return $stmt->execute([
|
|
'actor_id' => $this->id,
|
|
'tv_show_id' => $tvShowId
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Unlink actor from an adult video
|
|
*/
|
|
public function unlinkFromAdultVideo(int $adultVideoId): bool
|
|
{
|
|
$stmt = $this->pdo->prepare("
|
|
DELETE FROM actor_adult_video
|
|
WHERE actor_id = :actor_id AND adult_video_id = :adult_video_id
|
|
");
|
|
return $stmt->execute([
|
|
'actor_id' => $this->id,
|
|
'adult_video_id' => $adultVideoId
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Get paginated actors with optional search and sorting
|
|
*/
|
|
public function getPaginated(\PDO $pdo, int $page = 1, int $perPage = 20, string $search = '', string $sort = 'name_asc'): array
|
|
{
|
|
$offset = ($page - 1) * $perPage;
|
|
|
|
// Build WHERE clause for search
|
|
$whereClause = '';
|
|
$params = [];
|
|
|
|
if (!empty($search)) {
|
|
$whereClause = 'WHERE name LIKE :search';
|
|
$params['search'] = '%' . $search . '%';
|
|
}
|
|
|
|
// Build ORDER BY clause
|
|
$orderBy = match ($sort) {
|
|
'name_desc' => 'name DESC',
|
|
'media_desc' => '(SELECT COUNT(*) FROM actor_movie WHERE actor_id = actors.id) + (SELECT COUNT(DISTINCT te.tv_show_id) FROM actor_tv_episode ate JOIN tv_episodes te ON ate.tv_episode_id = te.id WHERE ate.actor_id = actors.id) + (SELECT COUNT(*) FROM actor_adult_video WHERE actor_id = actors.id) DESC',
|
|
'media_asc' => '(SELECT COUNT(*) FROM actor_movie WHERE actor_id = actors.id) + (SELECT COUNT(DISTINCT te.tv_show_id) FROM actor_tv_episode ate JOIN tv_episodes te ON ate.tv_episode_id = te.id WHERE ate.actor_id = actors.id) + (SELECT COUNT(*) FROM actor_adult_video WHERE actor_id = actors.id) ASC',
|
|
default => 'name ASC'
|
|
};
|
|
|
|
// Get actors with their media counts
|
|
$stmt = $pdo->prepare("
|
|
SELECT
|
|
a.*,
|
|
(SELECT COUNT(*) FROM actor_movie WHERE actor_id = a.id) as movie_count,
|
|
(SELECT COUNT(DISTINCT te.tv_show_id) FROM actor_tv_episode ate JOIN tv_episodes te ON ate.tv_episode_id = te.id WHERE ate.actor_id = a.id) as tv_show_count,
|
|
(SELECT COUNT(*) FROM actor_adult_video WHERE actor_id = a.id) as adult_video_count
|
|
FROM actors a
|
|
{$whereClause}
|
|
ORDER BY {$orderBy}
|
|
LIMIT :limit OFFSET :offset
|
|
");
|
|
|
|
$stmt->bindValue(':limit', $perPage, \PDO::PARAM_INT);
|
|
$stmt->bindValue(':offset', $offset, \PDO::PARAM_INT);
|
|
|
|
foreach ($params as $key => $value) {
|
|
$stmt->bindValue(':' . $key, $value);
|
|
}
|
|
|
|
$stmt->execute();
|
|
$actors = $stmt->fetchAll(\PDO::FETCH_ASSOC);
|
|
|
|
// Add relationships data for each actor
|
|
foreach ($actors as &$actor) {
|
|
$actor['movies'] = $this->getActorMovies($actor['id']);
|
|
$actor['tvShows'] = $this->getActorTvShows($actor['id']);
|
|
$actor['adultVideos'] = $this->getActorAdultVideos($actor['id']);
|
|
}
|
|
|
|
return $actors;
|
|
}
|
|
|
|
/**
|
|
* Get total count of actors with optional search
|
|
*/
|
|
public function getTotalCount(\PDO $pdo, string $search = ''): int
|
|
{
|
|
$whereClause = '';
|
|
$params = [];
|
|
|
|
if (!empty($search)) {
|
|
$whereClause = 'WHERE name LIKE :search';
|
|
$params['search'] = '%' . $search . '%';
|
|
}
|
|
|
|
$stmt = $pdo->prepare("SELECT COUNT(*) as count FROM actors {$whereClause}");
|
|
|
|
foreach ($params as $key => $value) {
|
|
$stmt->bindValue(':' . $key, $value);
|
|
}
|
|
|
|
$stmt->execute();
|
|
return (int)$stmt->fetch(\PDO::FETCH_ASSOC)['count'];
|
|
}
|
|
|
|
/**
|
|
* Get movies for a specific actor (helper method)
|
|
*/
|
|
private function getActorMovies(int $actorId): array
|
|
{
|
|
$stmt = $this->pdo->prepare("
|
|
SELECT m.id, m.title
|
|
FROM movies m
|
|
JOIN actor_movie am ON m.id = am.movie_id
|
|
WHERE am.actor_id = ?
|
|
ORDER BY m.title
|
|
");
|
|
$stmt->execute([$actorId]);
|
|
return $stmt->fetchAll(\PDO::FETCH_ASSOC);
|
|
}
|
|
|
|
/**
|
|
* Get TV shows for a specific actor (helper method)
|
|
*/
|
|
private function getActorTvShows(int $actorId): array
|
|
{
|
|
$stmt = $this->pdo->prepare("
|
|
SELECT DISTINCT ts.id, ts.title
|
|
FROM tv_shows ts
|
|
JOIN tv_episodes te ON ts.id = te.tv_show_id
|
|
JOIN actor_tv_episode ate ON te.id = ate.tv_episode_id
|
|
WHERE ate.actor_id = ?
|
|
ORDER BY ts.title
|
|
");
|
|
$stmt->execute([$actorId]);
|
|
return $stmt->fetchAll(\PDO::FETCH_ASSOC);
|
|
}
|
|
|
|
/**
|
|
* Get adult videos for a specific actor (helper method)
|
|
*/
|
|
private function getActorAdultVideos(int $actorId): array
|
|
{
|
|
$stmt = $this->pdo->prepare("
|
|
SELECT av.id, av.title
|
|
FROM adult_videos av
|
|
JOIN actor_adult_video aav ON av.id = aav.adult_video_id
|
|
WHERE aav.actor_id = ?
|
|
ORDER BY av.title
|
|
");
|
|
$stmt->execute([$actorId]);
|
|
return $stmt->fetchAll(\PDO::FETCH_ASSOC);
|
|
}
|
|
}
|