mirror of
https://github.com/ceratic/MediaCollectorLibary.git
synced 2026-05-13 23:56:46 +02:00
...
This commit is contained in:
179
app/Models/Actor.php
Normal file
179
app/Models/Actor.php
Normal file
@@ -0,0 +1,179 @@
|
||||
<?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 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]);
|
||||
return $stmt->fetchAll(\PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all TV shows this actor is associated with
|
||||
*/
|
||||
public function tvShows()
|
||||
{
|
||||
$stmt = $this->pdo->prepare("
|
||||
SELECT ts.*, s.display_name as source_name
|
||||
FROM tv_shows ts
|
||||
JOIN sources s ON ts.source_id = s.id
|
||||
JOIN actor_tv_show ats ON ts.id = ats.tv_show_id
|
||||
WHERE ats.actor_id = :actor_id
|
||||
ORDER BY ts.first_air_date DESC, ts.title ASC
|
||||
");
|
||||
$stmt->execute(['actor_id' => $this->id]);
|
||||
return $stmt->fetchAll(\PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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]);
|
||||
return $stmt->fetchAll(\PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get actor statistics
|
||||
*/
|
||||
public function getStats(): array
|
||||
{
|
||||
$stmt = $this->pdo->prepare("
|
||||
SELECT
|
||||
COUNT(DISTINCT am.movie_id) as movie_count,
|
||||
COUNT(DISTINCT ats.tv_show_id) as tv_show_count,
|
||||
COUNT(DISTINCT aav.adult_video_id) as adult_video_count,
|
||||
COUNT(DISTINCT am.movie_id) + COUNT(DISTINCT ats.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_show ats ON a.id = ats.actor_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
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -106,4 +106,64 @@ class AdultVideo extends Model
|
||||
|
||||
return $sourceData ? new Source($this->pdo, $sourceData) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all actors associated with this adult video
|
||||
*/
|
||||
public function actors()
|
||||
{
|
||||
$stmt = $this->pdo->prepare("
|
||||
SELECT a.*
|
||||
FROM actors a
|
||||
JOIN actor_adult_video aav ON a.id = aav.actor_id
|
||||
WHERE aav.adult_video_id = :adult_video_id
|
||||
ORDER BY a.name ASC
|
||||
");
|
||||
$stmt->execute(['adult_video_id' => $this->id]);
|
||||
return $stmt->fetchAll(\PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an actor to this adult video
|
||||
*/
|
||||
public function addActor(int $actorId): bool
|
||||
{
|
||||
$stmt = $this->pdo->prepare("
|
||||
INSERT IGNORE INTO actor_adult_video (adult_video_id, actor_id)
|
||||
VALUES (:adult_video_id, :actor_id)
|
||||
");
|
||||
return $stmt->execute([
|
||||
'adult_video_id' => $this->id,
|
||||
'actor_id' => $actorId
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an actor from this adult video
|
||||
*/
|
||||
public function removeActor(int $actorId): bool
|
||||
{
|
||||
$stmt = $this->pdo->prepare("
|
||||
DELETE FROM actor_adult_video
|
||||
WHERE adult_video_id = :adult_video_id AND actor_id = :actor_id
|
||||
");
|
||||
return $stmt->execute([
|
||||
'adult_video_id' => $this->id,
|
||||
'actor_id' => $actorId
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the cast field with actor names
|
||||
*/
|
||||
public function updateCastField(): bool
|
||||
{
|
||||
$actors = $this->actors();
|
||||
$actorNames = array_column($actors, 'name');
|
||||
$castString = implode(', ', $actorNames);
|
||||
|
||||
return $this->update($this->id, [
|
||||
'cast' => $castString
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,10 +11,37 @@ abstract class Model
|
||||
protected array $fillable = [];
|
||||
protected array $hidden = [];
|
||||
protected array $casts = [];
|
||||
protected array $attributes = [];
|
||||
|
||||
public function __construct(\PDO $pdo)
|
||||
public function __construct(\PDO $pdo, array $data = [])
|
||||
{
|
||||
$this->pdo = $pdo;
|
||||
$this->attributes = $data;
|
||||
}
|
||||
|
||||
public function __get(string $key)
|
||||
{
|
||||
return $this->attributes[$key] ?? null;
|
||||
}
|
||||
|
||||
public function __set(string $key, $value)
|
||||
{
|
||||
$this->attributes[$key] = $value;
|
||||
}
|
||||
|
||||
public function __isset(string $key): bool
|
||||
{
|
||||
return isset($this->attributes[$key]);
|
||||
}
|
||||
|
||||
public function getAttributes(): array
|
||||
{
|
||||
return $this->attributes;
|
||||
}
|
||||
|
||||
public function setAttributes(array $data)
|
||||
{
|
||||
$this->attributes = $data;
|
||||
}
|
||||
|
||||
public function find(int $id): ?array
|
||||
|
||||
@@ -39,7 +39,11 @@ class Movie extends Model
|
||||
|
||||
public function source()
|
||||
{
|
||||
return new Source($this->pdo);
|
||||
$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;
|
||||
}
|
||||
|
||||
public function markAsWatched(): bool
|
||||
@@ -88,7 +92,7 @@ class Movie extends Model
|
||||
SUM(runtime_minutes) as total_runtime
|
||||
FROM movies
|
||||
");
|
||||
return $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
return $stmt->fetch(\PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
public static function getRecent(\PDO $pdo, int $limit = 10): array
|
||||
@@ -102,7 +106,7 @@ class Movie extends Model
|
||||
LIMIT :limit
|
||||
");
|
||||
$stmt->execute(['limit' => $limit]);
|
||||
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
return $stmt->fetchAll(\PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
public static function getTotalCount(\PDO $pdo, string $search = ''): int
|
||||
@@ -162,4 +166,64 @@ class Movie extends Model
|
||||
$stmt->execute(['limit' => $limit]);
|
||||
return $stmt->fetchAll(\PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all actors associated with this movie
|
||||
*/
|
||||
public function actors()
|
||||
{
|
||||
$stmt = $this->pdo->prepare("
|
||||
SELECT a.*
|
||||
FROM actors a
|
||||
JOIN actor_movie am ON a.id = am.actor_id
|
||||
WHERE am.movie_id = :movie_id
|
||||
ORDER BY a.name ASC
|
||||
");
|
||||
$stmt->execute(['movie_id' => $this->id]);
|
||||
return $stmt->fetchAll(\PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an actor to this movie
|
||||
*/
|
||||
public function addActor(int $actorId): bool
|
||||
{
|
||||
$stmt = $this->pdo->prepare("
|
||||
INSERT IGNORE INTO actor_movie (movie_id, actor_id)
|
||||
VALUES (:movie_id, :actor_id)
|
||||
");
|
||||
return $stmt->execute([
|
||||
'movie_id' => $this->id,
|
||||
'actor_id' => $actorId
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an actor from this movie
|
||||
*/
|
||||
public function removeActor(int $actorId): bool
|
||||
{
|
||||
$stmt = $this->pdo->prepare("
|
||||
DELETE FROM actor_movie
|
||||
WHERE movie_id = :movie_id AND actor_id = :actor_id
|
||||
");
|
||||
return $stmt->execute([
|
||||
'movie_id' => $this->id,
|
||||
'actor_id' => $actorId
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the cast field with actor names
|
||||
*/
|
||||
public function updateCastField(): bool
|
||||
{
|
||||
$actors = $this->actors();
|
||||
$actorNames = array_column($actors, 'name');
|
||||
$castString = implode(', ', $actorNames);
|
||||
|
||||
return $this->update($this->id, [
|
||||
'cast' => $castString
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,39 +15,44 @@ class Source extends Model
|
||||
'last_sync_at'
|
||||
];
|
||||
|
||||
public function __construct(\PDO $pdo, array $data = [])
|
||||
{
|
||||
parent::__construct($pdo, $data);
|
||||
}
|
||||
|
||||
public function games(): array
|
||||
{
|
||||
$stmt = $this->pdo->prepare("SELECT * FROM games WHERE source_id = :source_id");
|
||||
$stmt->execute(['source_id' => $this->id]);
|
||||
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
return $stmt->fetchAll(\PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
public function movies(): array
|
||||
{
|
||||
$stmt = $this->pdo->prepare("SELECT * FROM movies WHERE source_id = :source_id");
|
||||
$stmt->execute(['source_id' => $this->id]);
|
||||
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
return $stmt->fetchAll(\PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
public function tvShows(): array
|
||||
{
|
||||
$stmt = $this->pdo->prepare("SELECT * FROM tv_shows WHERE source_id = :source_id");
|
||||
$stmt->execute(['source_id' => $this->id]);
|
||||
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
return $stmt->fetchAll(\PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
public function musicArtists(): array
|
||||
{
|
||||
$stmt = $this->pdo->prepare("SELECT * FROM music_artists WHERE source_id = :source_id");
|
||||
$stmt->execute(['source_id' => $this->id]);
|
||||
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
return $stmt->fetchAll(\PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
public function getSyncLogs(): array
|
||||
{
|
||||
$stmt = $this->pdo->prepare("SELECT * FROM sync_logs WHERE source_id = :source_id ORDER BY created_at DESC");
|
||||
$stmt->execute(['source_id' => $this->id]);
|
||||
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
return $stmt->fetchAll(\PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
public function createSyncLog(string $syncType, string $status): int
|
||||
|
||||
178
app/Models/TvEpisode.php
Normal file
178
app/Models/TvEpisode.php
Normal file
@@ -0,0 +1,178 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
class TvEpisode extends Model
|
||||
{
|
||||
protected string $table = 'tv_episodes';
|
||||
protected array $fillable = [
|
||||
'title',
|
||||
'overview',
|
||||
'season_number',
|
||||
'episode_number',
|
||||
'air_date',
|
||||
'runtime_minutes',
|
||||
'rating',
|
||||
'imdb_id',
|
||||
'tmdb_id',
|
||||
'tvdb_id',
|
||||
'poster_url',
|
||||
'backdrop_url',
|
||||
'is_watched',
|
||||
'is_favorite',
|
||||
'metadata',
|
||||
'tv_show_id',
|
||||
'source_id'
|
||||
];
|
||||
|
||||
protected array $casts = [
|
||||
'season_number' => 'int',
|
||||
'episode_number' => 'int',
|
||||
'runtime_minutes' => 'int',
|
||||
'rating' => 'float',
|
||||
'is_watched' => 'bool',
|
||||
'is_favorite' => 'bool',
|
||||
'air_date' => 'date',
|
||||
'metadata' => 'array'
|
||||
];
|
||||
|
||||
/**
|
||||
* Get all actors associated with this TV episode
|
||||
*/
|
||||
public function actors()
|
||||
{
|
||||
$stmt = $this->pdo->prepare("
|
||||
SELECT a.*
|
||||
FROM actors a
|
||||
JOIN actor_tv_episode ate ON a.id = ate.actor_id
|
||||
WHERE ate.tv_episode_id = :tv_episode_id
|
||||
ORDER BY a.name ASC
|
||||
");
|
||||
$stmt->execute(['tv_episode_id' => $this->id]);
|
||||
return $stmt->fetchAll(\PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the TV show this episode belongs to
|
||||
*/
|
||||
public function tvShow()
|
||||
{
|
||||
$stmt = $this->pdo->prepare("
|
||||
SELECT * FROM tv_shows WHERE id = :tv_show_id
|
||||
");
|
||||
$stmt->execute(['tv_show_id' => $this->tv_show_id]);
|
||||
$showData = $stmt->fetch(\PDO::FETCH_ASSOC);
|
||||
|
||||
return $showData ? new TvShow($this->pdo, $showData) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get TV episode statistics
|
||||
*/
|
||||
public static function getStats(\PDO $pdo): array
|
||||
{
|
||||
$stmt = $pdo->query("
|
||||
SELECT
|
||||
COUNT(*) as total_episodes,
|
||||
COUNT(CASE WHEN is_watched = 1 THEN 1 END) as watched_episodes,
|
||||
COUNT(CASE WHEN is_favorite = 1 THEN 1 END) as favorite_episodes,
|
||||
AVG(rating) as avg_rating
|
||||
FROM tv_episodes
|
||||
");
|
||||
return $stmt->fetch(\PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get total count with optional search
|
||||
*/
|
||||
public static function getTotalCount(\PDO $pdo, string $search = ''): int
|
||||
{
|
||||
$sql = "SELECT COUNT(*) as count FROM tv_episodes te JOIN tv_shows ts ON te.tv_show_id = ts.id JOIN sources s ON te.source_id = s.id";
|
||||
$params = [];
|
||||
|
||||
if (!empty($search)) {
|
||||
$sql .= " WHERE te.title LIKE :search OR ts.title LIKE :search";
|
||||
$params['search'] = "%{$search}%";
|
||||
}
|
||||
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$stmt->execute($params);
|
||||
return (int) $stmt->fetch()['count'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all TV episodes with pagination and optional search
|
||||
*/
|
||||
public static function getAllWithPagination(\PDO $pdo, int $page, int $perPage, string $search = ''): array
|
||||
{
|
||||
$offset = ($page - 1) * $perPage;
|
||||
|
||||
$sql = "
|
||||
SELECT te.*, ts.title as show_title, s.display_name as source_name
|
||||
FROM tv_episodes te
|
||||
JOIN tv_shows ts ON te.tv_show_id = ts.id
|
||||
JOIN sources s ON te.source_id = s.id
|
||||
";
|
||||
$params = [];
|
||||
|
||||
if (!empty($search)) {
|
||||
$sql .= " WHERE te.title LIKE :search OR ts.title LIKE :search";
|
||||
$params['search'] = "%{$search}%";
|
||||
}
|
||||
|
||||
$sql .= " ORDER BY ts.title ASC, te.season_number ASC, te.episode_number ASC LIMIT :limit OFFSET :offset";
|
||||
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$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();
|
||||
return $stmt->fetchAll(\PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle watched status
|
||||
*/
|
||||
public function toggleWatched(): bool
|
||||
{
|
||||
return $this->update($this->id, [
|
||||
'is_watched' => !$this->is_watched
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle favorite status
|
||||
*/
|
||||
public function toggleFavorite(): bool
|
||||
{
|
||||
return $this->update($this->id, [
|
||||
'is_favorite' => !$this->is_favorite
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update rating
|
||||
*/
|
||||
public function updateRating(float $rating): bool
|
||||
{
|
||||
return $this->update($this->id, [
|
||||
'rating' => min(10.0, max(0.0, $rating))
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the source relationship
|
||||
*/
|
||||
public function source(): ?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;
|
||||
}
|
||||
}
|
||||
256
app/Models/TvShow.php
Normal file
256
app/Models/TvShow.php
Normal file
@@ -0,0 +1,256 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
class TvShow extends Model
|
||||
{
|
||||
protected string $table = 'tv_shows';
|
||||
protected array $fillable = [
|
||||
'title',
|
||||
'overview',
|
||||
'creator',
|
||||
'genre',
|
||||
'cast',
|
||||
'first_air_date',
|
||||
'last_air_date',
|
||||
'number_of_seasons',
|
||||
'number_of_episodes',
|
||||
'rating',
|
||||
'imdb_id',
|
||||
'tmdb_id',
|
||||
'tvdb_id',
|
||||
'poster_url',
|
||||
'backdrop_url',
|
||||
'is_favorite',
|
||||
'metadata',
|
||||
'source_id'
|
||||
];
|
||||
|
||||
protected array $casts = [
|
||||
'number_of_seasons' => 'int',
|
||||
'number_of_episodes' => 'int',
|
||||
'rating' => 'float',
|
||||
'is_favorite' => 'bool',
|
||||
'first_air_date' => 'date',
|
||||
'last_air_date' => 'date',
|
||||
'metadata' => 'array'
|
||||
];
|
||||
|
||||
/**
|
||||
* Get all actors associated with this TV show
|
||||
*/
|
||||
public function actors()
|
||||
{
|
||||
$stmt = $this->pdo->prepare("
|
||||
SELECT a.*
|
||||
FROM actors a
|
||||
JOIN actor_tv_show ats ON a.id = ats.actor_id
|
||||
WHERE ats.tv_show_id = :tv_show_id
|
||||
ORDER BY a.name ASC
|
||||
");
|
||||
$stmt->execute(['tv_show_id' => $this->id]);
|
||||
return $stmt->fetchAll(\PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get TV show statistics
|
||||
*/
|
||||
public static function getStats(\PDO $pdo): array
|
||||
{
|
||||
$stmt = $pdo->query("
|
||||
SELECT
|
||||
COUNT(*) as total_tv_shows,
|
||||
COUNT(CASE WHEN is_favorite = 1 THEN 1 END) as favorite_tv_shows,
|
||||
AVG(rating) as avg_rating
|
||||
FROM tv_shows
|
||||
");
|
||||
return $stmt->fetch(\PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get total count with optional search
|
||||
*/
|
||||
public static function getTotalCount(\PDO $pdo, string $search = ''): int
|
||||
{
|
||||
$sql = "SELECT COUNT(*) as count FROM tv_shows t JOIN sources s ON t.source_id = s.id";
|
||||
$params = [];
|
||||
|
||||
if (!empty($search)) {
|
||||
$sql .= " WHERE t.title LIKE :search";
|
||||
$params['search'] = "%{$search}%";
|
||||
}
|
||||
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$stmt->execute($params);
|
||||
return (int) $stmt->fetch()['count'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all TV shows with pagination and optional search
|
||||
*/
|
||||
public static function getAllWithPagination(\PDO $pdo, int $page, int $perPage, string $search = ''): array
|
||||
{
|
||||
$offset = ($page - 1) * $perPage;
|
||||
|
||||
$sql = "
|
||||
SELECT t.*, s.display_name as source_name
|
||||
FROM tv_shows t
|
||||
JOIN sources s ON t.source_id = s.id
|
||||
";
|
||||
$params = [];
|
||||
|
||||
if (!empty($search)) {
|
||||
$sql .= " WHERE t.title LIKE :search";
|
||||
$params['search'] = "%{$search}%";
|
||||
}
|
||||
|
||||
$sql .= " ORDER BY t.title ASC LIMIT :limit OFFSET :offset";
|
||||
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$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();
|
||||
return $stmt->fetchAll(\PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle favorite status
|
||||
*/
|
||||
public function toggleFavorite(): bool
|
||||
{
|
||||
return $this->update($this->id, [
|
||||
'is_favorite' => !$this->is_favorite
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update rating
|
||||
*/
|
||||
public function updateRating(float $rating): bool
|
||||
{
|
||||
return $this->update($this->id, [
|
||||
'rating' => min(10.0, max(0.0, $rating))
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the source relationship
|
||||
*/
|
||||
public function source(): ?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;
|
||||
}
|
||||
|
||||
public function getSeasonsWithEpisodes(): array
|
||||
{
|
||||
$stmt = $this->pdo->prepare("
|
||||
SELECT s.*,
|
||||
COUNT(e.id) as episode_count,
|
||||
SUM(CASE WHEN e.watched = 1 THEN 1 ELSE 0 END) as watched_episodes
|
||||
FROM seasons s
|
||||
LEFT JOIN episodes e ON s.id = e.season_id
|
||||
WHERE s.tv_show_id = :tv_show_id
|
||||
GROUP BY s.id
|
||||
ORDER BY s.season_number ASC
|
||||
");
|
||||
$stmt->execute(['tv_show_id' => $this->id]);
|
||||
$seasons = $stmt->fetchAll(\PDO::FETCH_ASSOC);
|
||||
|
||||
// Get episodes for each season
|
||||
foreach ($seasons as &$season) {
|
||||
$stmt = $this->pdo->prepare("
|
||||
SELECT e.*,
|
||||
(SELECT COUNT(*) FROM user_episodes WHERE episode_id = e.id AND watched = 1) as watch_count
|
||||
FROM episodes e
|
||||
WHERE e.season_id = :season_id
|
||||
ORDER BY e.episode_number ASC
|
||||
");
|
||||
$stmt->execute(['season_id' => $season['id']]);
|
||||
$season['episodes'] = $stmt->fetchAll(\PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
return $seasons;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get similar TV shows based on genres
|
||||
*/
|
||||
public function getSimilarShows(int $limit = 6): array
|
||||
{
|
||||
$genres = $this->genre ? explode(',', $this->genre) : [];
|
||||
$placeholders = str_repeat('?,', count($genres) - 1) . '?';
|
||||
|
||||
$sql = "
|
||||
SELECT t.*,
|
||||
COUNT(DISTINCT g.genre) as matching_genres
|
||||
FROM tv_shows t
|
||||
CROSS JOIN (SELECT TRIM(value) as genre
|
||||
FROM json_each('[\"" . str_replace(',', '","', str_replace('"', '\\"', $this->genre)) . "\"]')
|
||||
WHERE value != '') g
|
||||
WHERE t.id != ?
|
||||
AND t.genre LIKE '%' || g.genre || '%'
|
||||
GROUP BY t.id
|
||||
HAVING matching_genres > 0
|
||||
ORDER BY matching_genres DESC, t.rating DESC
|
||||
LIMIT ?
|
||||
";
|
||||
|
||||
$params = array_merge([$this->id, $limit]);
|
||||
$stmt = $this->pdo->prepare($sql);
|
||||
$stmt->execute($params);
|
||||
|
||||
return $stmt->fetchAll(\PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
/**
|
||||
* Record that this TV show was viewed
|
||||
*/
|
||||
public function recordView(): bool
|
||||
{
|
||||
$stmt = $this->pdo->prepare("
|
||||
INSERT OR REPLACE INTO tv_show_views
|
||||
(tv_show_id, view_count, last_viewed_at)
|
||||
VALUES (?, COALESCE((SELECT view_count FROM tv_show_views WHERE tv_show_id = ?), 0) + 1, CURRENT_TIMESTAMP)
|
||||
");
|
||||
return $stmt->execute([$this->id, $this->id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all available genres from TV shows
|
||||
*/
|
||||
public static function getAvailableGenres(\PDO $pdo): array
|
||||
{
|
||||
$stmt = $pdo->query("
|
||||
SELECT DISTINCT TRIM(value) as genre
|
||||
FROM tv_shows,
|
||||
json_each('[\"' || REPLACE(genre, ',', '\",\"') || '\"]')
|
||||
WHERE genre IS NOT NULL AND genre != ''
|
||||
ORDER BY genre
|
||||
");
|
||||
return $stmt->fetchAll(\PDO::FETCH_COLUMN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all available years from TV shows' first_air_date
|
||||
*/
|
||||
public static function getAvailableYears(\PDO $pdo): array
|
||||
{
|
||||
$stmt = $pdo->query("
|
||||
SELECT DISTINCT strftime('%Y', first_air_date) as year
|
||||
FROM tv_shows
|
||||
WHERE first_air_date IS NOT NULL
|
||||
ORDER BY year DESC
|
||||
");
|
||||
return $stmt->fetchAll(\PDO::FETCH_COLUMN);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user