Files
kikootwo 50fb5a68af Add custom AI provider support and improve qBittorrent auth
Introduces support for custom OpenAI-compatible AI providers with configurable base URLs, including UI, backend validation, and connection testing. Enhances qBittorrent integration to support HTTP Basic Auth for reverse proxies, adds detailed debug logging, and updates documentation for both features. Also improves login page description logic and AI prompt generation for recommendations.
2026-01-28 11:41:58 -05:00

5.1 KiB

Login Page

Status: Implemented | Real floating book covers with professional animations

Stylized entry point with Plex/Audiobookshelf authentication, animated floating popular audiobook covers, and dynamic description based on backend configuration.

Design

  • Full-screen immersive experience with gradient background
  • Centered hero with login button
  • Animated floating real audiobook covers (popular releases)
  • 100 randomly positioned covers with varied sizes, animations, and depth
  • Multi-layer depth effect with z-index layering (0-20)
  • Dark theme optimized with glassmorphism card
  • Professional streaming service aesthetic
  • Dynamic description based on backend mode (Plex/Audiobookshelf) and automation status

Authentication Flow

  1. User visits protected route → redirected to /login
  2. Clicks "Login with Plex"
  3. POST /api/auth/plex/login → requests PIN
  4. Opens Plex OAuth in popup
  5. Polls /api/auth/plex/callback for authorization
  6. User authorizes in Plex popup
  7. Callback receives auth token
  8. Creates/updates user in DB
  9. Returns JWT tokens
  10. Client stores tokens in localStorage
  11. Redirects to originally requested page or homepage

Book Covers

Data Source: GET /api/audiobooks/covers

  • Returns up to 200 popular audiobook covers
  • Uses cached thumbnails from audible_cache table
  • Shuffled on each request for variety
  • Fallback to placeholder elements if API fails

Display:

  • 100 covers shown simultaneously for immersive experience
  • Varied sizes: 80-160px wide (1.5 aspect ratio)
  • Opacity range: 0.15-0.35 for subtle layering and depth
  • Staggered animation delays (0-10s) for natural movement
  • Z-index layering (0-20) for depth perception
  • Programmatic positioning using seeded random for consistency
  • Lazy loading (first 10 eager, rest lazy) for performance
  • Hover pauses animation and scales for interaction

Positioning Algorithm:

  • Seeded random function ensures consistent positions per cover index
  • Random distribution across full viewport (0-100% both axes)
  • Each cover gets unique: size, position, opacity, delay, z-index, animation type
  • Seed multipliers (7, 13, 17, 23, 29, 31) prevent pattern repetition
  • Math.sin() based pseudo-random for deterministic results

Dynamic Description

Description text adapts to backend configuration:

Plex + Automation Enabled: "Request audiobooks and they'll automatically download and appear in your Plex library" Plex + No Automation: "Request audiobooks for your Plex library" Audiobookshelf + Automation: "Request audiobooks and they'll automatically download and appear in your Audiobookshelf library" Audiobookshelf + No Automation: "Request audiobooks for your Audiobookshelf library" Loading State: "Your Personal Audiobook Library Manager"

Automation is detected by checking for configured indexer (Prowlarr) via /api/auth/providers endpoint.

State

interface LoginPageState {
  isLoggingIn: boolean;
  error: string | null;
  pinId: number | null;
  authWindow: Window | null;
  bookCovers: BookCover[];
  showAdminLogin: boolean;
  adminUsername: string;
  adminPassword: string;
  authProviders: {
    backendMode: string;
    providers: string[];
    registrationEnabled: boolean;
    hasLocalUsers: boolean;
    oidcProviderName: string | null;
    localLoginDisabled: boolean;
    automationEnabled: boolean;
  } | null;
}

interface BookCover {
  asin: string;
  title: string;
  author: string;
  coverUrl: string;
}

Error Handling

Popup Blocked: "Popup was blocked. Please allow popups." Login Timeout: 2 min polling timeout Plex Unavailable: "Plex services currently unavailable." Covers Fail: Silent fallback to placeholder gradient elements

Animations

Three animation speeds with realistic floating motion:

@keyframes float-slow {
  /* 22s cycle with 4 keyframes */
  0%, 100% { transform: translateY(0) translateX(0) rotate(0deg) scale(1); }
  25% { transform: translateY(-25px) translateX(15px) rotate(2deg) scale(1.03); }
  50% { transform: translateY(-35px) translateX(25px) rotate(4deg) scale(1.05); }
  75% { transform: translateY(-20px) translateX(-10px) rotate(-2deg) scale(1.02); }
}

@keyframes float-medium {
  /* 16s cycle with 3 keyframes */
  0%, 100% { transform: translateY(0) translateX(0) rotate(0deg) scale(1); }
  33% { transform: translateY(-30px) translateX(-20px) rotate(-3deg) scale(1.04); }
  66% { transform: translateY(-15px) translateX(10px) rotate(3deg) scale(1.02); }
}

@keyframes float-fast {
  /* 12s cycle with 2 keyframes */
  0%, 100% { transform: translateY(0) translateX(0) rotate(0deg) scale(1); }
  50% { transform: translateY(-28px) translateX(18px) rotate(5deg) scale(1.06); }
}

Features:

  • Scale transformations (1.02-1.06) for depth
  • Rotation (-5° to +5°) for natural movement
  • X/Y translation for floating effect
  • Hover pauses animation
  • Shadow-2xl for 3D depth

Security

  • Tokens in localStorage (access 1hr, refresh 7d)
  • Tokens cleared on logout
  • OAuth state parameter validation
  • SameSite cookie attributes

Tech Stack

  • Next.js 14+ Client Component
  • Tailwind CSS with custom animations
  • Plex OAuth via AuthContext