mirror of
https://gitlab.com/foxixus/neomovies_mobile.git
synced 2025-10-28 05:58:50 +05:00
Initial commit
This commit is contained in:
17
lib/data/models/auth_response.dart
Normal file
17
lib/data/models/auth_response.dart
Normal file
@@ -0,0 +1,17 @@
|
||||
import 'package:neomovies_mobile/data/models/user.dart';
|
||||
|
||||
class AuthResponse {
|
||||
final String token;
|
||||
final User user;
|
||||
final bool verified;
|
||||
|
||||
AuthResponse({required this.token, required this.user, required this.verified});
|
||||
|
||||
factory AuthResponse.fromJson(Map<String, dynamic> json) {
|
||||
return AuthResponse(
|
||||
token: json['token'] as String,
|
||||
user: User.fromJson(json['user'] as Map<String, dynamic>),
|
||||
verified: (json['verified'] as bool?) ?? (json['user']?['verified'] as bool? ?? true),
|
||||
);
|
||||
}
|
||||
}
|
||||
36
lib/data/models/favorite.dart
Normal file
36
lib/data/models/favorite.dart
Normal file
@@ -0,0 +1,36 @@
|
||||
import 'package:flutter_dotenv/flutter_dotenv.dart';
|
||||
|
||||
class Favorite {
|
||||
final int id;
|
||||
final String mediaId;
|
||||
final String mediaType;
|
||||
final String title;
|
||||
final String posterPath;
|
||||
|
||||
Favorite({
|
||||
required this.id,
|
||||
required this.mediaId,
|
||||
required this.mediaType,
|
||||
required this.title,
|
||||
required this.posterPath,
|
||||
});
|
||||
|
||||
factory Favorite.fromJson(Map<String, dynamic> json) {
|
||||
return Favorite(
|
||||
id: json['id'] as int? ?? 0,
|
||||
mediaId: json['mediaId'] as String? ?? '',
|
||||
mediaType: json['mediaType'] as String? ?? '',
|
||||
title: json['title'] as String? ?? '',
|
||||
posterPath: json['posterPath'] as String? ?? '',
|
||||
);
|
||||
}
|
||||
|
||||
String get fullPosterUrl {
|
||||
final baseUrl = dotenv.env['API_URL']!;
|
||||
if (posterPath.isEmpty) {
|
||||
return '$baseUrl/images/w500/placeholder.jpg';
|
||||
}
|
||||
final cleanPath = posterPath.startsWith('/') ? posterPath.substring(1) : posterPath;
|
||||
return '$baseUrl/images/w500/$cleanPath';
|
||||
}
|
||||
}
|
||||
57
lib/data/models/library_license.dart
Normal file
57
lib/data/models/library_license.dart
Normal file
@@ -0,0 +1,57 @@
|
||||
class LibraryLicense {
|
||||
final String name;
|
||||
final String version;
|
||||
final String license;
|
||||
final String url;
|
||||
final String description;
|
||||
final String? licenseText;
|
||||
|
||||
const LibraryLicense({
|
||||
required this.name,
|
||||
required this.version,
|
||||
required this.license,
|
||||
required this.url,
|
||||
required this.description,
|
||||
this.licenseText,
|
||||
});
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
return {
|
||||
'name': name,
|
||||
'version': version,
|
||||
'license': license,
|
||||
'url': url,
|
||||
'description': description,
|
||||
'licenseText': licenseText,
|
||||
};
|
||||
}
|
||||
|
||||
LibraryLicense copyWith({
|
||||
String? name,
|
||||
String? version,
|
||||
String? license,
|
||||
String? url,
|
||||
String? description,
|
||||
String? licenseText,
|
||||
}) {
|
||||
return LibraryLicense(
|
||||
name: name ?? this.name,
|
||||
version: version ?? this.version,
|
||||
license: license ?? this.license,
|
||||
url: url ?? this.url,
|
||||
description: description ?? this.description,
|
||||
licenseText: licenseText ?? this.licenseText,
|
||||
);
|
||||
}
|
||||
|
||||
factory LibraryLicense.fromMap(Map<String, dynamic> map) {
|
||||
return LibraryLicense(
|
||||
name: map['name'] as String? ?? '',
|
||||
version: map['version'] as String? ?? '',
|
||||
license: map['license'] as String? ?? '',
|
||||
url: map['url'] as String? ?? '',
|
||||
description: map['description'] as String? ?? '',
|
||||
licenseText: map['licenseText'] as String?,
|
||||
);
|
||||
}
|
||||
}
|
||||
100
lib/data/models/movie.dart
Normal file
100
lib/data/models/movie.dart
Normal file
@@ -0,0 +1,100 @@
|
||||
import 'package:flutter_dotenv/flutter_dotenv.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
|
||||
part 'movie.g.dart';
|
||||
|
||||
@HiveType(typeId: 0)
|
||||
class Movie extends HiveObject {
|
||||
@HiveField(0)
|
||||
final String id;
|
||||
|
||||
@HiveField(1)
|
||||
final String title;
|
||||
|
||||
@HiveField(2)
|
||||
final String? posterPath;
|
||||
|
||||
@HiveField(3)
|
||||
final String? overview;
|
||||
|
||||
@HiveField(4)
|
||||
final DateTime? releaseDate;
|
||||
|
||||
@HiveField(5)
|
||||
final List<String>? genres;
|
||||
|
||||
@HiveField(6)
|
||||
final double? voteAverage;
|
||||
|
||||
// Поле популярности из API (TMDB-style)
|
||||
@HiveField(9)
|
||||
final double popularity;
|
||||
|
||||
@HiveField(7)
|
||||
final int? runtime;
|
||||
|
||||
// TV specific
|
||||
@HiveField(10)
|
||||
final int? seasonsCount;
|
||||
@HiveField(11)
|
||||
final int? episodesCount;
|
||||
|
||||
@HiveField(8)
|
||||
final String? tagline;
|
||||
|
||||
// not stored in Hive, runtime-only field
|
||||
final String mediaType;
|
||||
|
||||
Movie({
|
||||
required this.id,
|
||||
required this.title,
|
||||
this.posterPath,
|
||||
this.overview,
|
||||
this.releaseDate,
|
||||
this.genres,
|
||||
this.voteAverage,
|
||||
this.popularity = 0.0,
|
||||
this.runtime,
|
||||
this.seasonsCount,
|
||||
this.episodesCount,
|
||||
this.tagline,
|
||||
this.mediaType = 'movie',
|
||||
});
|
||||
|
||||
factory Movie.fromJson(Map<String, dynamic> json) {
|
||||
return Movie(
|
||||
id: (json['id'] as num).toString(), // Ensure id is a string
|
||||
title: (json['title'] ?? json['name'] ?? '') as String,
|
||||
posterPath: json['poster_path'] as String?,
|
||||
overview: json['overview'] as String?,
|
||||
releaseDate: json['release_date'] != null && json['release_date'].isNotEmpty
|
||||
? DateTime.tryParse(json['release_date'] as String)
|
||||
: json['first_air_date'] != null && json['first_air_date'].isNotEmpty
|
||||
? DateTime.tryParse(json['first_air_date'] as String)
|
||||
: null,
|
||||
genres: List<String>.from(json['genres']?.map((g) => g['name']) ?? []),
|
||||
voteAverage: (json['vote_average'] as num?)?.toDouble() ?? 0.0,
|
||||
popularity: (json['popularity'] as num?)?.toDouble() ?? 0.0,
|
||||
runtime: json['runtime'] is num
|
||||
? (json['runtime'] as num).toInt()
|
||||
: (json['episode_run_time'] is List && (json['episode_run_time'] as List).isNotEmpty)
|
||||
? ((json['episode_run_time'] as List).first as num).toInt()
|
||||
: null,
|
||||
seasonsCount: json['number_of_seasons'] as int?,
|
||||
episodesCount: json['number_of_episodes'] as int?,
|
||||
tagline: json['tagline'] as String?,
|
||||
mediaType: (json['media_type'] ?? (json['title'] != null ? 'movie' : 'tv')) as String,
|
||||
);
|
||||
}
|
||||
|
||||
String get fullPosterUrl {
|
||||
final baseUrl = dotenv.env['API_URL']!;
|
||||
if (posterPath == null || posterPath!.isEmpty) {
|
||||
// Use the placeholder from our own backend
|
||||
return '$baseUrl/images/w500/placeholder.jpg';
|
||||
}
|
||||
// Null check is already performed above, so we can use `!`
|
||||
final cleanPath = posterPath!.startsWith('/') ? posterPath!.substring(1) : posterPath!;
|
||||
return '$baseUrl/images/w500/$cleanPath';
|
||||
}
|
||||
}
|
||||
59
lib/data/models/movie.g.dart
Normal file
59
lib/data/models/movie.g.dart
Normal file
@@ -0,0 +1,59 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'movie.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// TypeAdapterGenerator
|
||||
// **************************************************************************
|
||||
|
||||
class MovieAdapter extends TypeAdapter<Movie> {
|
||||
@override
|
||||
final int typeId = 0;
|
||||
|
||||
@override
|
||||
Movie read(BinaryReader reader) {
|
||||
final numOfFields = reader.readByte();
|
||||
final fields = <int, dynamic>{
|
||||
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
||||
};
|
||||
return Movie(
|
||||
id: fields[0] as String,
|
||||
title: fields[1] as String,
|
||||
posterPath: fields[2] as String?,
|
||||
overview: fields[3] as String?,
|
||||
releaseDate: fields[4] as DateTime?,
|
||||
genres: (fields[5] as List?)?.cast<String>(),
|
||||
voteAverage: fields[6] as double?,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void write(BinaryWriter writer, Movie obj) {
|
||||
writer
|
||||
..writeByte(7)
|
||||
..writeByte(0)
|
||||
..write(obj.id)
|
||||
..writeByte(1)
|
||||
..write(obj.title)
|
||||
..writeByte(2)
|
||||
..write(obj.posterPath)
|
||||
..writeByte(3)
|
||||
..write(obj.overview)
|
||||
..writeByte(4)
|
||||
..write(obj.releaseDate)
|
||||
..writeByte(5)
|
||||
..write(obj.genres)
|
||||
..writeByte(6)
|
||||
..write(obj.voteAverage);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => typeId.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is MovieAdapter &&
|
||||
runtimeType == other.runtimeType &&
|
||||
typeId == other.typeId;
|
||||
}
|
||||
21
lib/data/models/movie_preview.dart
Normal file
21
lib/data/models/movie_preview.dart
Normal file
@@ -0,0 +1,21 @@
|
||||
import 'package:hive/hive.dart';
|
||||
|
||||
part 'movie_preview.g.dart';
|
||||
|
||||
@HiveType(typeId: 1) // Use a new typeId to avoid conflicts with Movie
|
||||
class MoviePreview extends HiveObject {
|
||||
@HiveField(0)
|
||||
final String id;
|
||||
|
||||
@HiveField(1)
|
||||
final String title;
|
||||
|
||||
@HiveField(2)
|
||||
final String? posterPath;
|
||||
|
||||
MoviePreview({
|
||||
required this.id,
|
||||
required this.title,
|
||||
this.posterPath,
|
||||
});
|
||||
}
|
||||
47
lib/data/models/movie_preview.g.dart
Normal file
47
lib/data/models/movie_preview.g.dart
Normal file
@@ -0,0 +1,47 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'movie_preview.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// TypeAdapterGenerator
|
||||
// **************************************************************************
|
||||
|
||||
class MoviePreviewAdapter extends TypeAdapter<MoviePreview> {
|
||||
@override
|
||||
final int typeId = 1;
|
||||
|
||||
@override
|
||||
MoviePreview read(BinaryReader reader) {
|
||||
final numOfFields = reader.readByte();
|
||||
final fields = <int, dynamic>{
|
||||
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
||||
};
|
||||
return MoviePreview(
|
||||
id: fields[0] as String,
|
||||
title: fields[1] as String,
|
||||
posterPath: fields[2] as String?,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void write(BinaryWriter writer, MoviePreview obj) {
|
||||
writer
|
||||
..writeByte(3)
|
||||
..writeByte(0)
|
||||
..write(obj.id)
|
||||
..writeByte(1)
|
||||
..write(obj.title)
|
||||
..writeByte(2)
|
||||
..write(obj.posterPath);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => typeId.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is MoviePreviewAdapter &&
|
||||
runtimeType == other.runtimeType &&
|
||||
typeId == other.typeId;
|
||||
}
|
||||
80
lib/data/models/player/video_source.dart
Normal file
80
lib/data/models/player/video_source.dart
Normal file
@@ -0,0 +1,80 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
enum VideoSourceType {
|
||||
lumex,
|
||||
alloha,
|
||||
}
|
||||
|
||||
class VideoSource extends Equatable {
|
||||
final String id;
|
||||
final String name;
|
||||
final VideoSourceType type;
|
||||
final bool isActive;
|
||||
|
||||
const VideoSource({
|
||||
required this.id,
|
||||
required this.name,
|
||||
required this.type,
|
||||
this.isActive = true,
|
||||
});
|
||||
|
||||
// Default sources
|
||||
static final List<VideoSource> defaultSources = [
|
||||
const VideoSource(
|
||||
id: 'alloha',
|
||||
name: 'Alloha',
|
||||
type: VideoSourceType.alloha,
|
||||
isActive: true,
|
||||
),
|
||||
const VideoSource(
|
||||
id: 'lumex',
|
||||
name: 'Lumex',
|
||||
type: VideoSourceType.lumex,
|
||||
isActive: false,
|
||||
),
|
||||
];
|
||||
|
||||
@override
|
||||
List<Object?> get props => [id, name, type, isActive];
|
||||
|
||||
@override
|
||||
bool get stringify => true;
|
||||
|
||||
// Convert to map for serialization
|
||||
Map<String, dynamic> toMap() {
|
||||
return {
|
||||
'id': id,
|
||||
'name': name,
|
||||
'type': type.name,
|
||||
'isActive': isActive,
|
||||
};
|
||||
}
|
||||
|
||||
// Create from map for deserialization
|
||||
factory VideoSource.fromMap(Map<String, dynamic> map) {
|
||||
return VideoSource(
|
||||
id: map['id'] as String? ?? 'unknown',
|
||||
name: map['name'] as String? ?? 'Unknown',
|
||||
type: VideoSourceType.values.firstWhere(
|
||||
(e) => e.name == map['type'],
|
||||
orElse: () => VideoSourceType.lumex,
|
||||
),
|
||||
isActive: map['isActive'] as bool? ?? true,
|
||||
);
|
||||
}
|
||||
|
||||
// Copy with method for immutability
|
||||
VideoSource copyWith({
|
||||
String? id,
|
||||
String? name,
|
||||
VideoSourceType? type,
|
||||
bool? isActive,
|
||||
}) {
|
||||
return VideoSource(
|
||||
id: id ?? this.id,
|
||||
name: name ?? this.name,
|
||||
type: type ?? this.type,
|
||||
isActive: isActive ?? this.isActive,
|
||||
);
|
||||
}
|
||||
}
|
||||
25
lib/data/models/reaction.dart
Normal file
25
lib/data/models/reaction.dart
Normal file
@@ -0,0 +1,25 @@
|
||||
class Reaction {
|
||||
final String type;
|
||||
final int count;
|
||||
|
||||
Reaction({required this.type, required this.count});
|
||||
|
||||
factory Reaction.fromJson(Map<String, dynamic> json) {
|
||||
return Reaction(
|
||||
type: json['type'] as String? ?? '',
|
||||
count: json['count'] as int? ?? 0,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class UserReaction {
|
||||
final String? reactionType;
|
||||
|
||||
UserReaction({this.reactionType});
|
||||
|
||||
factory UserReaction.fromJson(Map<String, dynamic> json) {
|
||||
return UserReaction(
|
||||
reactionType: json['type'] as String?,
|
||||
);
|
||||
}
|
||||
}
|
||||
15
lib/data/models/user.dart
Normal file
15
lib/data/models/user.dart
Normal file
@@ -0,0 +1,15 @@
|
||||
class User {
|
||||
final String id;
|
||||
final String name;
|
||||
final String email;
|
||||
|
||||
User({required this.id, required this.name, required this.email});
|
||||
|
||||
factory User.fromJson(Map<String, dynamic> json) {
|
||||
return User(
|
||||
id: json['_id'] as String? ?? '',
|
||||
name: json['name'] as String? ?? '',
|
||||
email: json['email'] as String? ?? '',
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user