feat: introduce streaming handlers for live TV proxy and VOD HLS transcoding using FFmpeg.

This commit is contained in:
Michael committed 2026-01-01 18:12:45 +01:00
1 parent d22350fb89
commit a1a8810ddc
10 files changed
+968 -612

No files matched your search

+64 -28
View File
@@ -3,7 +3,7 @@ import 'package:google_fonts/google_fonts.dart';
import 'app_colors.dart';
/// XtremFlow Apple TV Theme
///
///
/// Focus-driven, immersive, minimal.
class AppTheme {
AppTheme._();
@@ -27,27 +27,57 @@ class AppTheme {
// ============ ANIMATION ============
static const Duration durationFast = Duration(milliseconds: 200);
static const Duration durationNormal = Duration(milliseconds: 300);
static const Curve curveDefault = Curves.fastOutSlowIn; // Apple-like snappy curve
static const Curve curveDefault =
Curves.fastOutSlowIn; // Apple-like snappy curve
// ============ DARK THEME (TV Main) ============
static ThemeData get darkTheme {
final baseTheme = ThemeData.dark(useMaterial3: true);
return baseTheme.copyWith(
colorScheme: AppColors.darkColorScheme,
scaffoldBackgroundColor: AppColors.background,
// Typography: San Francisco style (using Inter as proxy)
// Typography: "Cinematic Modern" (Outfit for Headings, Inter for UI)
textTheme: TextTheme(
displayLarge: GoogleFonts.inter(fontSize: 56, fontWeight: FontWeight.w700, letterSpacing: -1.0, color: AppColors.textPrimary),
displayMedium: GoogleFonts.inter(fontSize: 48, fontWeight: FontWeight.w700, letterSpacing: -0.5, color: AppColors.textPrimary),
displaySmall: GoogleFonts.inter(fontSize: 32, fontWeight: FontWeight.w600, color: AppColors.textPrimary),
headlineMedium: GoogleFonts.inter(fontSize: 24, fontWeight: FontWeight.w600, color: AppColors.textPrimary),
titleLarge: GoogleFonts.inter(fontSize: 20, fontWeight: FontWeight.w600, color: AppColors.textPrimary),
titleMedium: GoogleFonts.inter(fontSize: 16, fontWeight: FontWeight.w600, color: AppColors.textPrimary),
bodyLarge: GoogleFonts.inter(fontSize: 16, fontWeight: FontWeight.w400, color: AppColors.textSecondary),
bodyMedium: GoogleFonts.inter(fontSize: 14, fontWeight: FontWeight.w400, color: AppColors.textSecondary),
labelLarge: GoogleFonts.inter(fontSize: 14, fontWeight: FontWeight.w600, color: AppColors.textPrimary),
displayLarge: GoogleFonts.outfit(
fontSize: 56,
fontWeight: FontWeight.w700,
letterSpacing: -1.0,
color: AppColors.textPrimary),
displayMedium: GoogleFonts.outfit(
fontSize: 48,
fontWeight: FontWeight.w700,
letterSpacing: -0.5,
color: AppColors.textPrimary),
displaySmall: GoogleFonts.outfit(
fontSize: 32,
fontWeight: FontWeight.w600,
color: AppColors.textPrimary),
headlineMedium: GoogleFonts.outfit(
fontSize: 24,
fontWeight: FontWeight.w600,
color: AppColors.textPrimary),
titleLarge: GoogleFonts.outfit(
fontSize: 20,
fontWeight: FontWeight.w600,
color: AppColors.textPrimary),
titleMedium: GoogleFonts.inter(
fontSize: 16,
fontWeight: FontWeight.w600,
color: AppColors.textPrimary),
bodyLarge: GoogleFonts.inter(
fontSize: 16,
fontWeight: FontWeight.w400,
color: AppColors.textSecondary),
bodyMedium: GoogleFonts.inter(
fontSize: 14,
fontWeight: FontWeight.w400,
color: AppColors.textSecondary),
labelLarge: GoogleFonts.inter(
fontSize: 14,
fontWeight: FontWeight.w600,
color: AppColors.textPrimary),
),
// AppBar: Transparent / Glass
@@ -57,18 +87,19 @@ class AppTheme {
elevation: 0,
centerTitle: true,
scrolledUnderElevation: 0,
titleTextStyle: GoogleFonts.inter(
fontSize: 17,
titleTextStyle: GoogleFonts.outfit(
fontSize: 20,
fontWeight: FontWeight.w600,
color: AppColors.textPrimary,
),
),
// Inputs: Dark, minimal, no borders unless focused
inputDecorationTheme: InputDecorationTheme(
filled: true,
fillColor: AppColors.surface,
contentPadding: const EdgeInsets.symmetric(horizontal: spacing24, vertical: spacing16),
contentPadding: const EdgeInsets.symmetric(
horizontal: spacing24, vertical: spacing16),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(radiusMd),
borderSide: BorderSide.none,
@@ -83,7 +114,7 @@ class AppTheme {
),
hintStyle: GoogleFonts.inter(color: AppColors.textTertiary),
),
// Buttons: White pill or minimal text
filledButtonTheme: FilledButtonThemeData(
style: FilledButton.styleFrom(
@@ -91,17 +122,21 @@ class AppTheme {
foregroundColor: Colors.black, // Black text
elevation: 0,
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(radiusMd)),
textStyle: GoogleFonts.inter(fontWeight: FontWeight.w600, fontSize: 16),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(radiusMd)),
textStyle:
GoogleFonts.inter(fontWeight: FontWeight.w600, fontSize: 16),
).copyWith(
overlayColor: WidgetStateProperty.resolveWith((states) {
if (states.contains(WidgetState.hovered)) return Colors.black.withOpacity(0.1);
if (states.contains(WidgetState.pressed)) return Colors.black.withOpacity(0.2);
if (states.contains(WidgetState.hovered))
return Colors.black.withOpacity(0.1);
if (states.contains(WidgetState.pressed))
return Colors.black.withOpacity(0.2);
return null;
}),
),
),
// Cards: Transparent by default (content defines look)
// cardTheme: const CardTheme(
// color: AppColors.surface,
@@ -124,8 +159,9 @@ class AppTheme {
static const double radiusFull = 999.0;
// Light theme stub
static ThemeData get lightTheme => ThemeData.light(useMaterial3: true).copyWith(
colorScheme: AppColors.lightColorScheme,
textTheme: GoogleFonts.interTextTheme(ThemeData.light().textTheme),
);
static ThemeData get lightTheme =>
ThemeData.light(useMaterial3: true).copyWith(
colorScheme: AppColors.lightColorScheme,
textTheme: GoogleFonts.interTextTheme(ThemeData.light().textTheme),
);
}