Files
MediaCollectorLibary/app/Models/AdultVideo.php
Lars Behrends eb1ec1153d Remove obsolete test scripts and add new API controllers for dashboard and game management
- 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
2026-01-18 01:42:03 +01:00

505 lines
16 KiB
PHP

<?php
namespace App\Models;
class AdultVideo extends Model
{
protected string $table = 'adult_videos';
protected array $fillable = [
'title',
'overview',
'poster_url',
'poster_aspect_ratio',
'backdrop_url',
'backdrop_aspect_ratio',
'rating',
'runtime_minutes',
'release_date',
'director',
'writer',
'cast',
'genre',
'metadata',
'watched',
'watch_count',
'is_favorite',
'source_id',
'external_id'
];
public static function getAllWithPagination(\PDO $pdo, int $page, int $perPage, string $search = '', array $genres = [], array $directors = [], array $sources = [], string $sort = 'recent'): array
{
$offset = ($page - 1) * $perPage;
$sql = "
SELECT av.*, s.display_name as source_name
FROM adult_videos av
JOIN sources s ON av.source_id = s.id
";
$params = [];
if (!empty($search)) {
$sql .= " WHERE (av.title LIKE :search OR av.overview LIKE :search)";
$params['search'] = "%{$search}%";
}
if (!empty($genres)) {
$placeholders = [];
foreach ($genres as $index => $genre) {
$placeholders[] = ":genre_{$index}";
$params["genre_{$index}"] = $genre;
}
$whereClause = !empty($search) ? " AND" : " WHERE";
$sql .= $whereClause . " av.genre IN (" . implode(',', $placeholders) . ")";
}
if (!empty($directors)) {
$placeholders = [];
foreach ($directors as $index => $director) {
$placeholders[] = ":director_{$index}";
$params["director_{$index}"] = $director;
}
$whereClause = (!empty($search) || !empty($genres)) ? " AND" : " WHERE";
$sql .= $whereClause . " av.director IN (" . implode(',', $placeholders) . ")";
}
if (!empty($sources)) {
$placeholders = [];
foreach ($sources as $index => $source) {
$placeholders[] = ":source_{$index}";
$params["source_{$index}"] = $source;
}
$whereClause = (!empty($search) || !empty($genres) || !empty($directors)) ? " AND" : " WHERE";
$sql .= $whereClause . " s.display_name IN (" . implode(',', $placeholders) . ")";
}
// Add sorting
$sortOptions = [
'recent' => 'av.created_at DESC',
'oldest' => 'av.created_at ASC',
'title_asc' => 'av.title ASC',
'title_desc' => 'av.title DESC',
'year_asc' => 'av.release_date ASC',
'year_desc' => 'av.release_date DESC',
'rating_asc' => 'av.rating ASC',
'rating_desc' => 'av.rating DESC',
'views_asc' => 'av.watch_count ASC',
'views_desc' => 'av.watch_count DESC',
'runtime_asc' => 'av.runtime_minutes ASC',
'runtime_desc' => 'av.runtime_minutes DESC',
];
$sortOrder = $sortOptions[$sort] ?? $sortOptions['recent'];
$sql .= " ORDER BY " . $sortOrder . " 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);
}
public static function getTotalCount(\PDO $pdo, string $search = '', array $genres = [], array $directors = [], array $sources = []): int
{
$sql = "SELECT COUNT(*) as count FROM adult_videos av JOIN sources s ON av.source_id = s.id";
$params = [];
if (!empty($search)) {
$sql .= " WHERE (av.title LIKE :search OR av.overview LIKE :search)";
$params['search'] = "%{$search}%";
}
if (!empty($genres)) {
$placeholders = [];
foreach ($genres as $index => $genre) {
$placeholders[] = ":genre_{$index}";
$params["genre_{$index}"] = $genre;
}
$whereClause = !empty($search) ? " AND" : " WHERE";
$sql .= $whereClause . " av.genre IN (" . implode(',', $placeholders) . ")";
}
if (!empty($directors)) {
$placeholders = [];
foreach ($directors as $index => $director) {
$placeholders[] = ":director_{$index}";
$params["director_{$index}"] = $director;
}
$whereClause = (!empty($search) || !empty($genres)) ? " AND" : " WHERE";
$sql .= $whereClause . " av.director IN (" . implode(',', $placeholders) . ")";
}
if (!empty($sources)) {
$placeholders = [];
foreach ($sources as $index => $source) {
$placeholders[] = ":source_{$index}";
$params["source_{$index}"] = $source;
}
$whereClause = (!empty($search) || !empty($genres) || !empty($directors)) ? " AND" : " WHERE";
$sql .= $whereClause . " s.display_name IN (" . implode(',', $placeholders) . ")";
}
$stmt = $pdo->prepare($sql);
foreach ($params as $key => $value) {
$stmt->bindValue(":{$key}", $value);
}
$stmt->execute();
return (int) $stmt->fetch()['count'];
}
public function markAsWatched(): bool
{
$stmt = $this->pdo->prepare("UPDATE adult_videos SET watched = 1, watch_count = watch_count + 1, updated_at = NOW() WHERE id = :id");
return $stmt->execute(['id' => $this->id]);
}
public function markAsUnwatched(): bool
{
$stmt = $this->pdo->prepare("UPDATE adult_videos SET watched = 0, updated_at = NOW() WHERE id = :id");
return $stmt->execute(['id' => $this->id]);
}
public function toggleFavorite(): bool
{
$stmt = $this->pdo->prepare("UPDATE adult_videos SET is_favorite = !is_favorite, updated_at = NOW() WHERE id = :id");
return $stmt->execute(['id' => $this->id]);
}
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;
}
/**
* Get all actors associated with this adult video
*/
public function actors($args)
{
$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' => $args]);
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($this->id);
$actorNames = array_column($actors, 'name');
$castString = implode(', ', $actorNames);
return $this->update($this->id, [
'cast' => $castString
]);
}
/**
* Find single adult video by ID with metadata processing
*/
public function find(int $id): ?array
{
$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
WHERE av.id = :id
");
$stmt->execute(['id' => $id]);
$video = $stmt->fetch(\PDO::FETCH_ASSOC);
if (!$video) {
return null;
}
// Process metadata to extract additional fields
if (!empty($video['metadata'])) {
$metadata = json_decode($video['metadata'], true);
// Extract poster aspect ratio from metadata if available
if (!empty($metadata['poster_aspect_ratio'])) {
$video['poster_aspect_ratio'] = $metadata['poster_aspect_ratio'];
}
// Use local cover path if available, otherwise fall back to original URL
if (!empty($metadata['local_cover_path'])) {
$video['poster_url'] = $metadata['local_cover_path'];
} elseif (!empty($metadata['cover_url'])) {
$video['poster_url'] = $metadata['cover_url'];
}
// Add screenshot URL if available
if (!empty($metadata['screenshot_url'])) {
$video['screenshot_url'] = $metadata['screenshot_url'];
}
// Add actors data if available
if (!empty($metadata['actors'])) {
$video['actors'] = $metadata['actors'];
}
}
return $video;
}
/**
* Get all adult videos with filtering and pagination
*/
public function findAll(array $filters = [], int $limit = null, int $offset = 0): array
{
$sql = "
SELECT av.*, s.display_name as source_name
FROM adult_videos av
JOIN sources s ON av.source_id = s.id
";
$params = [];
$whereClauses = [];
// Search filter
if (!empty($filters['search'])) {
$whereClauses[] = "(av.title LIKE :search OR av.overview LIKE :search)";
$params['search'] = "%{$filters['search']}%";
}
// Genre filter
if (!empty($filters['genre'])) {
$whereClauses[] = "av.genre = :genre";
$params['genre'] = $filters['genre'];
}
// Year filter
if (!empty($filters['year'])) {
$whereClauses[] = "YEAR(av.release_date) = :year";
$params['year'] = $filters['year'];
}
// Source filter
if (!empty($filters['sources'])) {
$sources = is_array($filters['sources']) ? $filters['sources'] : [$filters['sources']];
$placeholders = [];
foreach ($sources as $index => $source) {
$placeholders[] = ":source_{$index}";
$params["source_{$index}"] = $source;
}
$whereClauses[] = "s.display_name IN (" . implode(',', $placeholders) . ")";
}
// Combine WHERE clauses
if (!empty($whereClauses)) {
$sql .= " WHERE " . implode(' AND ', $whereClauses);
}
// Add ordering and pagination
$sql .= " ORDER BY av.release_date DESC, av.title ASC";
if ($limit) {
$sql .= " LIMIT :limit OFFSET :offset";
$params['limit'] = $limit;
$params['offset'] = $offset;
}
$stmt = $this->pdo->prepare($sql);
$stmt->execute($params);
$results = $stmt->fetchAll(\PDO::FETCH_ASSOC);
// Process metadata to extract additional fields
foreach ($results as &$video) {
if (!empty($video['metadata'])) {
$metadata = json_decode($video['metadata'], true);
// Extract poster aspect ratio from metadata if available
if (!empty($metadata['poster_aspect_ratio'])) {
$video['poster_aspect_ratio'] = $metadata['poster_aspect_ratio'];
}
// Use local cover path if available, otherwise fall back to original URL
if (!empty($metadata['local_cover_path'])) {
$video['poster_url'] = $metadata['local_cover_path'];
} elseif (!empty($metadata['cover_url'])) {
$video['poster_url'] = $metadata['cover_url'];
}
// Add actors data if available
if (!empty($metadata['actors'])) {
$video['actors'] = $metadata['actors'];
}
}
}
return $results;
}
/**
* Count adult videos with filters
*/
public function count(array $filters = []): int
{
$sql = "
SELECT COUNT(*) as total
FROM adult_videos av
JOIN sources s ON av.source_id = s.id
";
$params = [];
$whereClauses = [];
// Search filter
if (!empty($filters['search'])) {
$whereClauses[] = "(av.title LIKE :search OR av.overview LIKE :search)";
$params['search'] = "%{$filters['search']}%";
}
// Genre filter
if (!empty($filters['genre'])) {
$whereClauses[] = "av.genre = :genre";
$params['genre'] = $filters['genre'];
}
// Year filter
if (!empty($filters['year'])) {
$whereClauses[] = "YEAR(av.release_date) = :year";
$params['year'] = $filters['year'];
}
// Source filter
if (!empty($filters['sources'])) {
$sources = is_array($filters['sources']) ? $filters['sources'] : [$filters['sources']];
$placeholders = [];
foreach ($sources as $index => $source) {
$placeholders[] = ":source_{$index}";
$params["source_{$index}"] = $source;
}
$whereClauses[] = "s.display_name IN (" . implode(',', $placeholders) . ")";
}
// 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 available genres for filtering
*/
public static function getAvailableGenres(\PDO $pdo): array
{
$stmt = $pdo->query("
SELECT DISTINCT genre
FROM adult_videos
WHERE genre IS NOT NULL AND genre != ''
ORDER BY genre
");
return $stmt->fetchAll(\PDO::FETCH_COLUMN);
}
/**
* Get available directors for filtering
*/
public static function getAvailableDirectors(\PDO $pdo): array
{
$stmt = $pdo->query("
SELECT DISTINCT director
FROM adult_videos
WHERE director IS NOT NULL AND director != ''
ORDER BY director
");
return $stmt->fetchAll(\PDO::FETCH_COLUMN);
}
/**
* Get available sources for filtering
*/
public static function getAvailableSources(\PDO $pdo): array
{
$stmt = $pdo->query("
SELECT DISTINCT s.display_name
FROM sources s
JOIN adult_videos av ON s.id = av.source_id
WHERE s.display_name IS NOT NULL AND s.display_name != ''
ORDER BY s.display_name
");
return $stmt->fetchAll(\PDO::FETCH_COLUMN);
}
/**
* Get TV show statistics
*/
public static function getStats(\PDO $pdo): array
{
$stmt = $pdo->query("
SELECT
COUNT(*) as total_adult_videos,
COUNT(CASE WHEN is_favorite = 1 THEN 1 END) as favorite_adult_videos,
AVG(rating) as avg_rating
FROM adult_videos
");
return $stmt->fetch(\PDO::FETCH_ASSOC);
}
/**
* Search actors by name
*/
public static function searchActors(\PDO $pdo, string $query): array
{
$stmt = $pdo->prepare("
SELECT a.*
FROM actors a
WHERE a.name LIKE :query
ORDER BY a.name
LIMIT 10
");
$stmt->execute(['query' => "%{$query}%"]);
return $stmt->fetchAll(\PDO::FETCH_ASSOC);
}
}