mirror of
https://github.com/R0m1k3/xtremflow.git
synced 2026-10-19 05:06:21 +02:00
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:
1 parent
3c0d472a31
commit
0ca2835cba
16 files changed
+1282
-688
No files matched your search
@@ -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,
|
||||
];
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
];
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user