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
505 lines
16 KiB
PHP
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);
|
|
}
|
|
}
|