chore: Update dependencies, switch Flutter web renderer to HTML, replace media_kit with video_player/chewie, and remove Hive volume from Docker setup.

This commit is contained in:
Michael committed 2025-12-05 09:52:40 +01:00
1 parent 3c0d472a31
commit 0ca2835cba
16 files changed
+1282 -688

No files matched your search

+21 -4
View File
@@ -1,21 +1,27 @@
import 'package:hive/hive.dart';
import 'package:equatable/equatable.dart';
part 'app_user.g.dart';
/// Hive model for local application users
///
/// Stores user credentials with hashed passwords and admin privileges
@HiveType(typeId: 0)
class AppUser {
class AppUser extends Equatable {
@HiveField(0)
final String id;
@HiveField(1)
final String username;
/// Password hash in format "salt:hash"
@HiveField(2)
final String passwordHash; // SHA-256 hash
final String passwordHash;
@HiveField(3)
final bool isAdmin;
/// List of playlist IDs assigned to this user
@HiveField(4)
final List<String> assignedPlaylistIds;
@@ -26,11 +32,12 @@ class AppUser {
required this.id,
required this.username,
required this.passwordHash,
required this.isAdmin,
required this.assignedPlaylistIds,
this.isAdmin = false,
this.assignedPlaylistIds = const [],
required this.createdAt,
});
/// Creates a copy of this user with updated fields
AppUser copyWith({
String? id,
String? username,
@@ -48,4 +55,14 @@ class AppUser {
createdAt: createdAt ?? this.createdAt,
);
}
@override
List<Object?> get props => [
id,
username,
passwordHash,
isAdmin,
assignedPlaylistIds,
createdAt,
];
}
+189
View File
@@ -0,0 +1,189 @@
/// Channel model for Live TV
class Channel {
final String streamId;
final String num;
final String name;
final String streamType;
final String streamIcon;
final String epgChannelId;
final String categoryId;
final String categoryName;
const Channel({
required this.streamId,
required this.num,
required this.name,
required this.streamType,
this.streamIcon = '',
this.epgChannelId = '',
required this.categoryId,
required this.categoryName,
});
factory Channel.fromJson(Map<String, dynamic> json) {
return Channel(
streamId: json['stream_id']?.toString() ?? '',
num: json['num']?.toString() ?? '',
name: json['name']?.toString() ?? '',
streamType: json['stream_type']?.toString() ?? '',
streamIcon: json['stream_icon']?.toString() ?? '',
epgChannelId: json['epg_channel_id']?.toString() ?? '',
categoryId: json['category_id']?.toString() ?? '',
categoryName: json['category_name']?.toString() ?? '',
);
}
Map<String, dynamic> toJson() {
return {
'stream_id': streamId,
'num': num,
'name': name,
'stream_type': streamType,
'stream_icon': streamIcon,
'epg_channel_id': epgChannelId,
'category_id': categoryId,
'category_name': categoryName,
};
}
}
/// VOD (Video on Demand) model for Movies
class VodItem {
final String streamId;
final String name;
final String streamIcon;
final String rating;
final String categoryId;
final String categoryName;
final String containerExtension;
const VodItem({
required this.streamId,
required this.name,
this.streamIcon = '',
this.rating = '',
required this.categoryId,
required this.categoryName,
this.containerExtension = 'mp4',
});
factory VodItem.fromJson(Map<String, dynamic> json) {
return VodItem(
streamId: json['stream_id']?.toString() ?? '',
name: json['name']?.toString() ?? '',
streamIcon: json['stream_icon']?.toString() ?? '',
rating: json['rating']?.toString() ?? '',
categoryId: json['category_id']?.toString() ?? '',
categoryName: json['category_name']?.toString() ?? '',
containerExtension: json['container_extension']?.toString() ?? 'mp4',
);
}
Map<String, dynamic> toJson() {
return {
'stream_id': streamId,
'name': name,
'stream_icon': streamIcon,
'rating': rating,
'category_id': categoryId,
'category_name': categoryName,
'container_extension': containerExtension,
};
}
}
/// Series model
class Series {
final String seriesId;
final String name;
final String cover;
final String plot;
final String categoryId;
final String categoryName;
const Series({
required this.seriesId,
required this.name,
this.cover = '',
this.plot = '',
required this.categoryId,
required this.categoryName,
});
factory Series.fromJson(Map<String, dynamic> json) {
return Series(
seriesId: json['series_id']?.toString() ?? '',
name: json['name']?.toString() ?? '',
cover: json['cover']?.toString() ?? '',
plot: json['plot']?.toString() ?? '',
categoryId: json['category_id']?.toString() ?? '',
categoryName: json['category_name']?.toString() ?? '',
);
}
Map<String, dynamic> toJson() {
return {
'series_id': seriesId,
'name': name,
'cover': cover,
'plot': plot,
'category_id': categoryId,
'category_name': categoryName,
};
}
}
/// EPG (Electronic Program Guide) entry
class EpgEntry {
final String id;
final String epgId;
final String title;
final String lang;
final String start;
final String end;
final String description;
final String channelId;
const EpgEntry({
required this.id,
required this.epgId,
required this.title,
this.lang = '',
required this.start,
required this.end,
this.description = '',
required this.channelId,
});
factory EpgEntry.fromJson(Map<String, dynamic> json) {
return EpgEntry(
id: json['id']?.toString() ?? '',
epgId: json['epg_id']?.toString() ?? '',
title: json['title']?.toString() ?? '',
lang: json['lang']?.toString() ?? '',
start: json['start']?.toString() ?? '',
end: json['end']?.toString() ?? '',
description: json['description']?.toString() ?? '',
channelId: json['channel_id']?.toString() ?? '',
);
}
/// Calculate progress percentage for current program
double getProgress() {
try {
final startTime = DateTime.parse(start);
final endTime = DateTime.parse(end);
final now = DateTime.now();
if (now.isBefore(startTime)) return 0.0;
if (now.isAfter(endTime)) return 1.0;
final totalDuration = endTime.difference(startTime).inSeconds;
final elapsed = now.difference(startTime).inSeconds;
return elapsed / totalDuration;
} catch (e) {
return 0.0;
}
}
}
+22 -3
View File
@@ -1,23 +1,29 @@
import 'package:hive/hive.dart';
import 'package:equatable/equatable.dart';
part 'playlist_config.g.dart';
/// Hive model for Xtream Codes playlist configuration
///
/// Stores credentials for connecting to an Xtream server
@HiveType(typeId: 1)
class PlaylistConfig {
class PlaylistConfig extends Equatable {
@HiveField(0)
final String id;
@HiveField(1)
final String name;
/// Xtream server URL (without trailing slash)
@HiveField(2)
final String dns; // Xtream server URL (without trailing slash)
final String dns;
@HiveField(3)
final String username;
/// Xtream password (stored encrypted by Hive)
@HiveField(4)
final String password; // Will be encrypted by Hive
final String password;
@HiveField(5)
final DateTime createdAt;
@@ -35,6 +41,7 @@ class PlaylistConfig {
this.isActive = true,
});
/// Creates a copy of this playlist with updated fields
PlaylistConfig copyWith({
String? id,
String? name,
@@ -57,4 +64,16 @@ class PlaylistConfig {
/// Generate Xtream API base URL
String get apiBaseUrl => '$dns/player_api.php';
@override
List<Object?> get props => [
id,
name,
dns,
username,
password,
createdAt,
isActive,
];
}