dont know ?

This commit is contained in:
Lars Behrends
2025-11-03 23:34:36 +01:00
parent 7a7977d8b0
commit 1ec6016b10
27 changed files with 6854 additions and 3361 deletions

View File

@@ -21,16 +21,22 @@ class ActorController extends Controller
{
$actorId = $args['id'];
// Get actor details with counts from all media types
// Get actor details with counts from all media types (including episode actors)
$stmt = $this->pdo->prepare("
SELECT a.*,
COUNT(DISTINCT am.movie_id) as movie_count,
COUNT(DISTINCT ats.tv_show_id) as tv_show_count,
COUNT(DISTINCT CASE WHEN ats.tv_show_id IS NOT NULL THEN ats.tv_show_id
WHEN ate.tv_episode_id IS NOT NULL THEN te.tv_show_id END) 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
(COUNT(DISTINCT am.movie_id) +
COUNT(DISTINCT CASE WHEN ats.tv_show_id IS NOT NULL THEN ats.tv_show_id
WHEN ate.tv_episode_id IS NOT NULL THEN te.tv_show_id END) +
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_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
GROUP BY a.id
@@ -66,16 +72,18 @@ class ActorController extends Controller
$stmt->execute(['actor_id' => $actorId]);
$movies = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Get actor's TV shows
// Get actor's TV shows (from main cast and episodes)
$stmt = $this->pdo->prepare("
SELECT ts.*, s.display_name as source_name
SELECT DISTINCT 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
LEFT JOIN actor_tv_show ats ON ts.id = ats.tv_show_id AND ats.actor_id = :actor_id
LEFT JOIN tv_episodes te ON ts.id = te.tv_show_id
LEFT JOIN actor_tv_episode ate ON te.id = ate.tv_episode_id AND ate.actor_id = :actor_id2
WHERE ats.actor_id = :actor_id4 OR ate.actor_id = :actor_id3
ORDER BY ts.first_air_date DESC, ts.title ASC
");
$stmt->execute(['actor_id' => $actorId]);
$stmt->execute(['actor_id' => $actorId,'actor_id2' => $actorId,'actor_id3' => $actorId, 'actor_id4' => $actorId]);
$tvShows = $stmt->fetchAll(PDO::FETCH_ASSOC);
@@ -108,33 +116,314 @@ class ActorController extends Controller
'tv_shows' => $tvShows
]);
}
public function edit(Request $request, Response $response, $args)
{
$actorId = $args['id'];
// Get actor details
$stmt = $this->pdo->prepare("SELECT * FROM actors WHERE id = :id");
$stmt->execute(['id' => $actorId]);
$actor = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$actor) {
return $response->withStatus(404)->withHeader('Content-Type', 'text/html');
}
// Decode metadata for form population
$metadata = json_decode($actor['metadata'] ?? '{}', true);
// Handle POST request (form submission)
if ($request->getMethod() === 'POST') {
$data = $request->getParsedBody();
$uploadedFiles = $request->getUploadedFiles();
// Validate required fields
$name = trim($data['name'] ?? '');
if (empty($name)) {
return $this->view->render($response, 'actor/edit.twig', [
'title' => 'Edit Actor',
'actor' => $actor,
'metadata' => $metadata,
'error' => 'Name is required'
]);
}
// Handle image upload
$thumbnailPath = $actor['thumbnail_path']; // Keep existing by default
if (!empty($uploadedFiles['thumbnail']) && $uploadedFiles['thumbnail']->getError() === UPLOAD_ERR_OK) {
$uploadedFile = $uploadedFiles['thumbnail'];
// Validate file type
$allowedTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
if (!in_array($uploadedFile->getClientMediaType(), $allowedTypes)) {
return $this->view->render($response, 'actor/edit.twig', [
'title' => 'Edit Actor',
'actor' => $actor,
'metadata' => $metadata,
'error' => 'Invalid image type. Only JPEG, PNG, GIF, and WebP are allowed.'
]);
}
// Generate filename and move file
$extension = pathinfo($uploadedFile->getClientFilename(), PATHINFO_EXTENSION);
$filename = 'actor_' . $actorId . '_' . time() . '.' . $extension;
$uploadPath = __DIR__ . '/../../public/images/actors/' . $filename;
// Create directory if it doesn't exist
$uploadDir = dirname($uploadPath);
if (!is_dir($uploadDir)) {
mkdir($uploadDir, 0755, true);
}
$uploadedFile->moveTo($uploadPath);
$thumbnailPath = '/images/actors/' . $filename;
}
// Prepare metadata
$actorMetadata = [
'biography' => trim($data['biography'] ?? ''),
'birth_date' => trim($data['birth_date'] ?? ''),
'death_date' => trim($data['death_date'] ?? ''),
'birth_place' => trim($data['birth_place'] ?? ''),
'nationality' => trim($data['nationality'] ?? ''),
'gender' => trim($data['gender'] ?? ''),
'ethnicity' => trim($data['ethnicity'] ?? ''),
'country' => trim($data['nationality'] ?? ''), // Map nationality to country for Stash compatibility
'height' => trim($data['height'] ?? ''),
'measurements' => trim($data['measurements'] ?? ''),
'cup_size' => trim($data['cup_size'] ?? ''),
'piercings' => trim($data['piercings'] ?? ''),
'tattoos' => trim($data['tattoos'] ?? ''),
'hair_color' => trim($data['hair_color'] ?? ''),
'eye_color' => trim($data['eye_color'] ?? ''),
'weight' => trim($data['weight'] ?? ''),
'fake_tits' => trim($data['fake_tits'] ?? ''),
'penis_length' => trim($data['penis_length'] ?? ''),
'circumcised' => trim($data['circumcised'] ?? ''),
'career_length' => trim($data['career_length'] ?? ''),
'aliases' => array_filter(array_map('trim', explode(',', $data['aliases'] ?? ''))),
'favorite' => isset($data['favorite']) ? true : false,
'ignore_auto_tag' => isset($data['ignore_auto_tag']) ? true : false,
'scene_count' => (int)($data['scene_count'] ?? 0),
'details' => trim($data['details'] ?? ''),
'social_media' => [
'twitter' => trim($data['twitter'] ?? ''),
'instagram' => trim($data['instagram'] ?? ''),
'onlyfans' => trim($data['onlyfans'] ?? ''),
'website' => trim($data['website'] ?? '')
],
'adult_specific' => [
'debut_year' => trim($data['debut_year'] ?? ''),
'retirement_year' => trim($data['retirement_year'] ?? ''),
'active' => isset($data['active']) ? true : false,
'genres' => array_filter(array_map('trim', explode(',', $data['adult_genres'] ?? ''))),
'specialties' => array_filter(array_map('trim', explode(',', $data['specialties'] ?? '')))
]
];
// Update actor
$stmt = $this->pdo->prepare("
UPDATE actors
SET name = :name, thumbnail_path = :thumbnail_path, metadata = :metadata, updated_at = NOW()
WHERE id = :id
");
$stmt->execute([
'id' => $actorId,
'name' => $name,
'thumbnail_path' => $thumbnailPath,
'metadata' => json_encode($actorMetadata)
]);
// Redirect back to actor show page
return $response->withHeader('Location', '/media/actors/' . $actorId)->withStatus(302);
}
// GET request - show edit form
return $this->view->render($response, 'actor/edit.twig', [
'title' => 'Edit Actor',
'actor' => $actor,
'metadata' => $metadata
]);
}
public function index(Request $request, Response $response, $args)
{
// Get all actors with their media counts from all types
$stmt = $this->pdo->prepare("
SELECT a.*,
COUNT(DISTINCT aav.adult_video_id) as adult_video_count,
COUNT(DISTINCT am.movie_id) as movie_count,
COUNT(DISTINCT ats.tv_show_id) as tv_show_count,
(COUNT(DISTINCT aav.adult_video_id) + COUNT(DISTINCT am.movie_id) + COUNT(DISTINCT ats.tv_show_id)) as total_media_count,
MAX(COALESCE(av.release_date, m.release_date, ts.first_air_date)) as latest_media_date
$queryParams = $request->getQueryParams();
// Get pagination parameters
$page = max(1, (int)($queryParams['page'] ?? 1));
$perPage = max(12, min(100, (int)($queryParams['per_page'] ?? 24)));
// Get search parameters
$search = trim($queryParams['search'] ?? '');
// Get filter parameters
$hasMovies = $queryParams['has_movies'] ?? null;
$hasTvShows = $queryParams['has_tv_shows'] ?? null;
$hasAdultVideos = $queryParams['has_adult_videos'] ?? null;
// Get sort parameter
$sort = $queryParams['sort'] ?? 'total_media_desc'; // total_media_desc, total_media_asc, name_asc, name_desc
// Build the base query - simplified to ensure all actors are found
$sql = "
SELECT a.id, a.name, a.thumbnail_path,
COALESCE(adult_counts.adult_video_count, 0) as adult_video_count,
COALESCE(movie_counts.movie_count, 0) as movie_count,
COALESCE(tv_counts.tv_show_count, 0) as tv_show_count,
(COALESCE(adult_counts.adult_video_count, 0) + COALESCE(movie_counts.movie_count, 0) + COALESCE(tv_counts.tv_show_count, 0)) as total_media_count,
GREATEST(
COALESCE(adult_dates.latest_adult, '1900-01-01'),
COALESCE(movie_dates.latest_movie, '1900-01-01'),
COALESCE(tv_dates.latest_tv, '1900-01-01')
) as latest_media_date
FROM actors a
LEFT JOIN actor_adult_video aav ON a.id = aav.actor_id
LEFT JOIN adult_videos av ON aav.adult_video_id = av.id
LEFT JOIN actor_movie am ON a.id = am.actor_id
LEFT JOIN movies m ON am.movie_id = m.id
LEFT JOIN actor_tv_show ats ON a.id = ats.actor_id
LEFT JOIN tv_shows ts ON ats.tv_show_id = ts.id
GROUP BY a.id
ORDER BY total_media_count DESC, a.name ASC
LIMIT 50
");
LEFT JOIN (
SELECT actor_id, COUNT(DISTINCT adult_video_id) as adult_video_count
FROM actor_adult_video
GROUP BY actor_id
) adult_counts ON a.id = adult_counts.actor_id
LEFT JOIN (
SELECT actor_id, COUNT(DISTINCT movie_id) as movie_count
FROM actor_movie
GROUP BY actor_id
) movie_counts ON a.id = movie_counts.actor_id
LEFT JOIN (
SELECT actor_id, COUNT(DISTINCT tv_show_id) as tv_show_count
FROM (
SELECT actor_id, tv_show_id FROM actor_tv_show
UNION
SELECT ate.actor_id, te.tv_show_id
FROM actor_tv_episode ate
JOIN tv_episodes te ON ate.tv_episode_id = te.id
) combined_tv
GROUP BY actor_id
) tv_counts ON a.id = tv_counts.actor_id
LEFT JOIN (
SELECT aav.actor_id, MAX(av.release_date) as latest_adult
FROM actor_adult_video aav
JOIN adult_videos av ON aav.adult_video_id = av.id
GROUP BY aav.actor_id
) adult_dates ON a.id = adult_dates.actor_id
LEFT JOIN (
SELECT am.actor_id, MAX(m.release_date) as latest_movie
FROM actor_movie am
JOIN movies m ON am.movie_id = m.id
GROUP BY am.actor_id
) movie_dates ON a.id = movie_dates.actor_id
LEFT JOIN (
SELECT combined_tv.actor_id, MAX(ts.first_air_date) as latest_tv
FROM (
SELECT actor_id, tv_show_id FROM actor_tv_show
UNION
SELECT ate.actor_id, te.tv_show_id
FROM actor_tv_episode ate
JOIN tv_episodes te ON ate.tv_episode_id = te.id
) combined_tv
JOIN tv_shows ts ON combined_tv.tv_show_id = ts.id
GROUP BY combined_tv.actor_id
) tv_dates ON a.id = tv_dates.actor_id
";
$params = [];
$whereClauses = [];
// Add search filter
if (!empty($search)) {
$whereClauses[] = "a.name LIKE :search";
$params['search'] = "%{$search}%";
}
if (!empty($whereClauses)) {
$sql .= ' WHERE ' . implode(' AND ', $whereClauses);
}
$sql .= " GROUP BY a.id";
// Add HAVING clause for filters that require aggregation
$havingClauses = [];
if ($hasMovies === '1') {
$havingClauses[] = "movie_count > 0";
}
if ($hasTvShows === '1') {
$havingClauses[] = "tv_show_count > 0";
}
if ($hasAdultVideos === '1') {
$havingClauses[] = "adult_video_count > 0";
}
if (!empty($havingClauses)) {
$sql .= ' HAVING ' . implode(' AND ', $havingClauses);
}
// Add sorting
$sortMap = [
'total_media_desc' => 'total_media_count DESC, a.name ASC',
'total_media_asc' => 'total_media_count ASC, a.name ASC',
'name_asc' => 'a.name ASC',
'name_desc' => 'a.name DESC',
'latest_desc' => 'latest_media_date DESC NULLS LAST, a.name ASC',
];
$orderBy = $sortMap[$sort] ?? 'total_media_count DESC, a.name ASC';
$sql .= " ORDER BY {$orderBy}";
// Get total count for pagination
$countSql = str_replace('SELECT a.id, a.name, a.thumbnail_path,', 'SELECT COUNT(*) as count,', $sql);
$countSql = preg_replace('/ORDER BY.*$/', '', $countSql);
$countStmt = $this->pdo->prepare($countSql);
foreach ($params as $key => $value) {
$countStmt->bindValue($key, $value);
}
$countStmt->execute();
$countResult = $countStmt->fetch(PDO::FETCH_ASSOC);
$totalCount = (int) ($countResult['count'] ?? 0);
// Add pagination
$offset = ($page - 1) * $perPage;
$sql .= " LIMIT :limit OFFSET :offset";
// Execute main query
$stmt = $this->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();
$actors = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Calculate pagination info
$totalPages = ceil($totalCount / $perPage);
$hasNextPage = $page < $totalPages;
$hasPrevPage = $page > 1;
return $this->view->render($response, 'actor/index.twig', [
'title' => 'Actors & Performers',
'actors' => $actors
'actors' => $actors,
'pagination' => [
'current_page' => $page,
'per_page' => $perPage,
'total_pages' => $totalPages,
'total_items' => $totalCount,
'has_next' => $hasNextPage,
'has_prev' => $hasPrevPage,
'next_page' => $page + 1,
'prev_page' => $page - 1
],
'search' => $search,
'sort' => $sort,
'sort_options' => [
'total_media_desc' => 'Most Media',
'total_media_asc' => 'Least Media',
'name_asc' => 'Name A-Z',
'name_desc' => 'Name Z-A',
'latest_desc' => 'Recently Active'
],
'filters' => [
'has_movies' => $hasMovies,
'has_tv_shows' => $hasTvShows,
'has_adult_videos' => $hasAdultVideos
]
]);
}
}

View File

@@ -41,12 +41,18 @@ class AdultController extends Controller
}
$directors = array_filter($directors);
$sources = $queryParams['sources'] ?? [];
if (!is_array($sources)) {
$sources = [$sources];
}
$sources = array_filter($sources);
// Get view mode and sort
$viewMode = $queryParams['view'] ?? 'grid'; // grid, list, covers
$sort = $queryParams['sort'] ?? 'recent';
// Get adult videos with pagination, filters, and sorting
$adultVideos = AdultVideo::getAllWithPagination($this->pdo, $page, $perPage, $search, $genres, $directors, $sort);
$adultVideos = AdultVideo::getAllWithPagination($this->pdo, $page, $perPage, $search, $genres, $directors, $sources, $sort);
// Process metadata to extract local image paths for template compatibility
foreach ($adultVideos as &$video) {
@@ -68,11 +74,12 @@ class AdultController extends Controller
}
// Get total count for pagination
$totalCount = AdultVideo::getTotalCount($this->pdo, $search, $genres, $directors);
$totalCount = AdultVideo::getTotalCount($this->pdo, $search, $genres, $directors, $sources);
// Get available filter options
$availableGenres = AdultVideo::getAvailableGenres($this->pdo);
$availableDirectors = AdultVideo::getAvailableDirectors($this->pdo);
$availableSources = AdultVideo::getAvailableSources($this->pdo);
// Calculate pagination info
$totalPages = ceil($totalCount / $perPage);
@@ -112,11 +119,13 @@ class AdultController extends Controller
],
'filters' => [
'genres' => $genres,
'directors' => $directors
'directors' => $directors,
'sources' => $sources
],
'available_filters' => [
'genres' => $availableGenres,
'directors' => $availableDirectors
'directors' => $availableDirectors,
'sources' => $availableSources
]
]);
}
@@ -144,13 +153,13 @@ class AdultController extends Controller
// Add local image paths and other metadata to the video data for template compatibility
if (!empty($metadata['local_cover_path'])) {
$adultVideo['poster_url'] = '/images/'.$metadata['local_cover_path'];
$adultVideo['poster_url'] = $metadata['local_cover_path'];
} elseif (!empty($metadata['cover_url'])) {
$adultVideo['poster_url'] = $metadata['cover_url'];
}
if (!empty($metadata['local_screenshot_path'])) {
$adultVideo['screenshot_url'] = '/images/'.$metadata['local_screenshot_path'];
$adultVideo['screenshot_url'] = $metadata['local_screenshot_path'];
}
// Add actors data if available

View File

@@ -122,6 +122,34 @@ class MovieController extends Controller
// Decode metadata for display
$metadata = json_decode($movie['metadata'], true);
// Extract additional fields from metadata if available
if ($metadata) {
// Production companies
if (isset($metadata['production_companies']) && is_array($metadata['production_companies'])) {
$companies = array_column($metadata['production_companies'], 'name');
$movie['production_companies'] = implode(', ', $companies);
}
// Production countries
if (isset($metadata['production_countries']) && is_array($metadata['production_countries'])) {
$countries = array_column($metadata['production_countries'], 'name');
$movie['production_countries'] = implode(', ', $countries);
}
// Collection info
if (isset($metadata['belongs_to_collection']) && is_array($metadata['belongs_to_collection'])) {
$movie['belongs_to_collection'] = $metadata['belongs_to_collection']['name'] ?? null;
}
// Additional metadata fields
//$movie['budget'] = $metadata['budget'] ?? $movie['budget'];
//$movie['revenue'] = $metadata['revenue'] ?? $movie['revenue'];
//$movie['original_language'] = $metadata['original_language'] ?? $movie['original_language'];
//$movie['tagline'] = $metadata['tagline'] ?? $movie['tagline'];
//$movie['status'] = $metadata['status'] ?? $movie['status'];
//$movie['vote_count'] = $metadata['vote_count'] ?? $movie['vote_count'];
}
// Get actors for this movie
$stmt = $this->pdo->prepare("
SELECT a.*

View File

@@ -109,7 +109,7 @@ class TvShowController extends Controller
$cast = json_decode($tvShow['cast'] ?? '[]', true);
$genre = json_decode($tvShow['genre'] ?? '[]', true);
// Get actors for this TV show
// Get actors for this TV show (from main cast)
$stmt = $this->pdo->prepare("
SELECT a.*
FROM actors a
@@ -118,10 +118,59 @@ class TvShowController extends Controller
ORDER BY a.name ASC
");
$stmt->execute(['tv_show_id' => $tvShowId]);
$actors = $stmt->fetchAll(\PDO::FETCH_ASSOC);
$mainActors = $stmt->fetchAll(\PDO::FETCH_ASSOC);
// Get all actors from episodes
$stmt = $this->pdo->prepare("
SELECT DISTINCT a.*
FROM actors a
JOIN actor_tv_episode ate ON a.id = ate.actor_id
JOIN tv_episodes e ON ate.tv_episode_id = e.id
WHERE e.tv_show_id = :tv_show_id
ORDER BY a.name ASC
");
$stmt->execute(['tv_show_id' => $tvShowId]);
$episodeActors = $stmt->fetchAll(\PDO::FETCH_ASSOC);
// Merge and deduplicate actors
$allActors = array_merge($mainActors, $episodeActors);
$actorsById = [];
foreach ($allActors as $actor) {
$actorsById[$actor['id']] = $actor;
}
$actors = array_values($actorsById);
// Sort by name
usort($actors, function($a, $b) {
return strcmp($a['name'], $b['name']);
});
// Get seasons and episodes for this TV show
$tvShowModel = new TvShow($this->pdo, $tvShow);
$seasons = $tvShowModel->getSeasonsWithEpisodes();
// Get recent episodes (last 5 aired episodes)
$stmt = $this->pdo->prepare("
SELECT e.*
FROM tv_episodes e
WHERE e.tv_show_id = :tv_show_id AND e.air_date IS NOT NULL
ORDER BY e.air_date DESC
LIMIT 5
");
$stmt->execute(['tv_show_id' => $tvShowId]);
$recent_episodes = $stmt->fetchAll(\PDO::FETCH_ASSOC);
// Add actors for recent episodes
foreach ($recent_episodes as &$episode) {
$episodeStmt = $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
");
$episodeStmt->execute(['tv_episode_id' => $episode['id']]);
$episode['actors'] = $episodeStmt->fetchAll(\PDO::FETCH_ASSOC);
}
return $this->view->render($response, 'tvshows/show.twig', [
'title' => $tvShow['title'],
'tvshow' => $tvShow,
@@ -129,7 +178,8 @@ class TvShowController extends Controller
'cast' => $cast,
'genre' => $genre,
'actors' => $actors,
'seasons' => $seasons
'seasons' => $seasons,
'recent_episodes' => $recent_episodes
]);
}