feat: Implement streaming handlers for live MPEG-TS proxy and VOD HLS transcoding via FFmpeg, and add a release build script.

This commit is contained in:
Michael committed 2025-12-16 07:39:33 +01:00
1 parent c7ba52b88d
commit 8128174ac3
26 files changed
+1529 -927

No files matched your search

+9 -10
View File
@@ -8,7 +8,7 @@ import '../models/playlist_config.dart';
class HiveService {
static const String _usersBoxName = 'users';
static const String _playlistsBoxName = 'playlists';
static bool _initialized = false;
static List<int>? _encryptionKey;
@@ -45,27 +45,27 @@ class HiveService {
// Generate new 256-bit key
_encryptionKey = Hive.generateSecureKey();
return _encryptionKey!;
}
/// Seed default admin user (admin/admin)
static Future<void> _seedDefaultAdmin() async {
final usersBox = Hive.box<AppUser>(_usersBoxName);
if (usersBox.isEmpty) {
final adminId = const Uuid().v4();
final passwordHash = _hashPassword('admin');
final admin = AppUser(
id: adminId,
username: 'admin',
passwordHash: passwordHash,
isAdmin: true,
assignedPlaylistIds: [],
assignedPlaylistIds: const [],
createdAt: DateTime.now(),
);
await usersBox.put(adminId, admin);
}
}
@@ -88,12 +88,12 @@ class HiveService {
final legacyHash = sha256.convert(utf8.encode(password)).toString();
return legacyHash == storedHash;
}
final salt = parts[0];
final expectedHash = parts[1];
final bytes = utf8.encode(password + salt);
final actualHash = sha256.convert(bytes).toString();
return actualHash == expectedHash;
} catch (e) {
return false;
@@ -103,12 +103,11 @@ class HiveService {
/// Public method to hash passwords (used by auth)
static String hashPassword(String password) => _hashPassword(password);
/// Get users box
static Box<AppUser> get usersBox => Hive.box<AppUser>(_usersBoxName);
/// Get playlists box
static Box<PlaylistConfig> get playlistsBox =>
static Box<PlaylistConfig> get playlistsBox =>
Hive.box<PlaylistConfig>(_playlistsBoxName);
/// Close all boxes (cleanup)
+27 -28
View File
@@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
/// XtremFlow Apple TV Style Palette
///
///
/// Ultra minimalist, focus-driven palette.
/// - Background: Pure Black (#000000)
/// - Focus/Active: Pure White (#FFFFFF)
@@ -13,20 +13,20 @@ class AppColors {
// ============ BACKGROUNDS ============
/// Pure black for that infinite depth look (OLED friendly)
static const Color background = Color(0xFF000000);
/// Very deep grey for surfaces that need to be distinct but subtle (Apple Dark Grey)
static const Color surface = Color(0xFF1C1C1E);
/// Slightly lighter grey for secondary surfaces or hover states
static const Color surfaceVariant = Color(0xFF3A3A3C);
/// Focused element background (often white in tvOS for text, or bright accent)
static const Color focusColor = Color(0xFFFFFFFF);
// ============ ACCENTS ============
/// Minimal white accent. In Apple TV, color is used sparingly.
/// We keep a subtle blue only for specific indicators if needed.
static const Color primary = Color(0xFFFFFFFF);
static const Color primary = Color(0xFFFFFFFF);
static const Color accent = Color(0xFFE5E5E5);
/// Primary Gradient (Subtle silver/white glow)
@@ -49,7 +49,7 @@ class AppColors {
// ============ BORDERS ============
// Borders are usually hidden until focused
static const Color border = Color(0xFF1F1F1F);
static const Color border = Color(0xFF1F1F1F);
static const Color focusBorder = Color(0xFFFFFFFF);
// ============ GLASSMORPHISM ============
@@ -59,28 +59,27 @@ class AppColors {
// ============ THEME SCHEMES ============
static ColorScheme get darkColorScheme => const ColorScheme.dark(
primary: primary,
secondary: accent,
surface: surface,
background: background,
error: error,
onPrimary: Colors.black, // White text on buttons -> Black text on White buttons
onSecondary: Colors.black,
onSurface: textPrimary,
onError: Colors.white,
brightness: Brightness.dark,
);
primary: primary,
secondary: accent,
surface: surface,
error: error,
onPrimary: Colors
.black, // White text on buttons -> Black text on White buttons
onSecondary: Colors.black,
onSurface: textPrimary,
onError: Colors.white,
brightness: Brightness.dark,
);
static ColorScheme get lightColorScheme => const ColorScheme.light(
primary: Colors.black,
secondary: Colors.grey,
surface: Color(0xFFF2F2F7),
background: Colors.white,
error: error,
onPrimary: Colors.white,
onSecondary: Colors.white,
onSurface: Colors.black,
onError: Colors.white,
brightness: Brightness.light,
);
primary: Colors.black,
secondary: Colors.grey,
surface: Color(0xFFF2F2F7),
error: error,
onPrimary: Colors.white,
onSecondary: Colors.white,
onSurface: Colors.black,
onError: Colors.white,
brightness: Brightness.light,
);
}
-1
View File
@@ -1,7 +1,6 @@
import 'dart:ui';
import 'package:flutter/material.dart';
import '../theme/app_colors.dart';
import '../theme/app_theme.dart';
class GlassContainer extends StatelessWidget {
final Widget child;
@@ -1,5 +1,4 @@
import 'package:flutter/material.dart';
import '../theme/app_colors.dart';
class ThemedLoading extends StatelessWidget {
const ThemedLoading({super.key});