diff --git a/app/Controllers/TvShowController.php b/app/Controllers/TvShowController.php index de4ea26..3fb9209 100644 --- a/app/Controllers/TvShowController.php +++ b/app/Controllers/TvShowController.php @@ -41,11 +41,23 @@ class TvShowController extends Controller } $years = array_filter($years); - // Get view mode + // Get view mode and sort $viewMode = $queryParams['view'] ?? 'grid'; // grid, list, covers + $sort = $queryParams['sort'] ?? 'title_asc'; - // Get TV shows with pagination and filters - $tvshows = TvShow::getAllWithPagination($this->pdo, $page, $perPage, $search, $genres, $years); + // Validate view mode + if (!in_array($viewMode, ['grid', 'list', 'covers'])) { + $viewMode = 'grid'; + } + + // Validate sort option + $validSorts = ['title_asc', 'title_desc', 'year_asc', 'year_desc', 'rating_asc', 'rating_desc']; + if (!in_array($sort, $validSorts)) { + $sort = 'title_asc'; + } + + // Get TV shows with pagination, filters, and sorting + $tvshows = TvShow::getAllWithPagination($this->pdo, $page, $perPage, $search, $genres, $years, $sort); // Get total count for pagination $totalCount = TvShow::getTotalCount($this->pdo, $search, $genres, $years); diff --git a/app/Models/TvShow.php b/app/Models/TvShow.php index 467c431..888fb18 100644 --- a/app/Models/TvShow.php +++ b/app/Models/TvShow.php @@ -130,7 +130,7 @@ class TvShow extends Model /** * Get all TV shows with pagination and optional search */ - public static function getAllWithPagination(\PDO $pdo, int $page, int $perPage, string $search = '', array $genres = [], array $years = []): array + public static function getAllWithPagination(\PDO $pdo, int $page, int $perPage, string $search = '', array $genres = [], array $years = [], string $sort = 'title_asc'): array { $offset = ($page - 1) * $perPage; @@ -166,7 +166,20 @@ class TvShow extends Model $sql .= $whereClause . " YEAR(first_air_date) IN (" . implode(',', $placeholders) . ")"; } - $sql .= " ORDER BY t.title ASC LIMIT :limit OFFSET :offset"; + // Add sorting + $sortMap = [ + 'title_asc' => 't.title ASC', + 'title_desc' => 't.title DESC', + 'year_desc' => 't.first_air_date DESC NULLS LAST', + 'year_asc' => 't.first_air_date ASC NULLS LAST', + 'rating_desc' => 't.rating DESC NULLS LAST', + 'rating_asc' => 't.rating ASC NULLS LAST', + 'recent' => 't.created_at DESC', + 'oldest' => 't.created_at ASC', + ]; + + $sortClause = $sortMap[$sort] ?? 't.title ASC'; + $sql .= " ORDER BY {$sortClause} LIMIT :limit OFFSET :offset"; $stmt = $pdo->prepare($sql); $stmt->bindValue(':limit', $perPage, \PDO::PARAM_INT); diff --git a/app/Services/StashSyncService.php b/app/Services/StashSyncService.php index ea3ecf8..3a9bddc 100644 --- a/app/Services/StashSyncService.php +++ b/app/Services/StashSyncService.php @@ -656,13 +656,32 @@ class StashSyncService extends BaseSyncService $name = $performer['name'] ?? ''; if (empty($name)) return null; - // Check if actor already exists + // Check if actor already exists by name or alias + // First check by exact name $stmt = $this->pdo->prepare(" - SELECT id, name, thumbnail_path, metadata FROM actors WHERE name = :name + SELECT id, name, thumbnail_path, metadata FROM actors + WHERE name = :name "); $stmt->execute(['name' => $name]); $existingActor = $stmt->fetch(PDO::FETCH_ASSOC); + // If not found by name, check aliases in PHP + if (!$existingActor) { + $stmt = $this->pdo->prepare("SELECT id, name, thumbnail_path, metadata FROM actors"); + $stmt->execute(); + $allActors = $stmt->fetchAll(PDO::FETCH_ASSOC); + + foreach ($allActors as $actor) { + $metadata = json_decode($actor['metadata'] ?? '{}', true); + $aliases = $metadata['aliases'] ?? []; + if (is_array($aliases) && in_array($name, $aliases)) { + $existingActor = $actor; + $this->logProgress("Found existing actor '{$actor['name']}' by alias '{$name}'"); + break; + } + } + } + // Prepare rich metadata from Stash performer data $actorMetadata = [ 'stash_id' => $performer['id'] ?? null, @@ -908,8 +927,9 @@ class StashSyncService extends BaseSyncService try { $this->logProgress('Starting existing performers sync with Stash...'); + echo "Starting existing performers sync with Stash..\n"; // Get all existing actors from database - $stmt = $this->pdo->prepare("SELECT id, name, metadata FROM actors ORDER BY name ASC"); + $stmt = $this->pdo->prepare("SELECT id, name, metadata FROM actors WHERE id IN (SELECT actor_id FROM actor_adult_video) ORDER BY name ASC"); $stmt->execute(); $existingActors = $stmt->fetchAll(\PDO::FETCH_ASSOC); diff --git a/app/Services/XbvrSyncService.php b/app/Services/XbvrSyncService.php index 39fbe52..256e30b 100644 --- a/app/Services/XbvrSyncService.php +++ b/app/Services/XbvrSyncService.php @@ -392,18 +392,65 @@ class XbvrSyncService extends BaseSyncService if (empty($actorName)) continue; - // Try to get detailed actor information from XBVR - $detailedActorData = $this->getActorDetails($actorName, $actorData); + // Handle XBVR "aka:" format (e.g., "aka:Bella Luna,Bella Luna") + $actorNames = $this->parseXbvrActorNames($actorName); - $actor = $this->getOrCreateActor($detailedActorData); - if ($actor) { - $actors[] = $actor; + $foundActor = null; + foreach ($actorNames as $name) { + $detailedActorData = $this->getActorDetails($name, $actorData); + $detailedActorData['name'] = $name; // Ensure the name is set correctly + + $actor = $this->getOrCreateActor($detailedActorData); + if ($actor) { + $foundActor = $actor; + break; // Use the first found actor + } + } + + // If no actor found with any of the names, create one with the first name + if (!$foundActor && !empty($actorNames)) { + $firstName = $actorNames[0]; + $detailedActorData = $this->getActorDetails($firstName, $actorData); + $detailedActorData['name'] = $firstName; + + $foundActor = $this->getOrCreateActor($detailedActorData); + } + + if ($foundActor) { + $actors[] = $foundActor; } } return $actors; } + /** + * Parse XBVR actor names that may contain "aka:" format + * Example: "aka:Bella Luna,Bella Luna" -> ["Bella Luna", "Bella Luna"] + */ + private function parseXbvrActorNames(string $actorName): array + { + // Check if the name starts with "aka:" + if (strpos($actorName, 'aka:') === 0) { + // Remove "aka:" prefix and split by comma + $namesPart = substr($actorName, 4); // Remove "aka:" + $names = array_map('trim', explode(',', $namesPart)); + + // Filter out empty names + $names = array_filter($names, function($name) { + return !empty(trim($name)); + }); + + if (!empty($names)) { + $this->logProgress("Parsed XBVR aka format '{$actorName}' into names: " . implode(', ', $names)); + return $names; + } + } + + // Return the original name if not in aka format + return [$actorName]; + } + private function getActorDetails(string $actorName, $actorData): array { // If we already have detailed actor data from the scene, use it @@ -587,13 +634,20 @@ class XbvrSyncService extends BaseSyncService $name = $actorData['name'] ?? ''; if (empty($name)) return null; - // Check if actor already exists + // Check if actor already exists by name or alias $stmt = $this->pdo->prepare(" - SELECT id, name, thumbnail_path, metadata FROM actors WHERE name = :name + SELECT id, name, thumbnail_path, metadata FROM actors + WHERE name = :name + OR JSON_CONTAINS(metadata->'$.aliases', :name) "); $stmt->execute(['name' => $name]); $existingActor = $stmt->fetch(\PDO::FETCH_ASSOC); + // If found by alias, log it for debugging + if ($existingActor && $existingActor['name'] !== $name) { + $this->logProgress("Found existing actor '{$existingActor['name']}' by alias '{$name}'"); + } + // Prepare metadata from XBVR actor data $actorMetadata = [ 'xbvr_id' => $actorData['xbvr_id'] ?? $actorData['id'] ?? null, diff --git a/resources/views/actor/index.twig b/resources/views/actor/index.twig index ca17527..5ec0b9e 100644 --- a/resources/views/actor/index.twig +++ b/resources/views/actor/index.twig @@ -11,7 +11,6 @@
{{ pagination.total_items }} performer{{ pagination.total_items != 1 ? 's' : '' }}
- {{dump(pagination)}} {% if pagination.total_pages > 1 %}