load trending images from tmdb at startup

- need TMDB_API_KEY as environment variable
- mount /cache for data persistence

Signed-off-by: Peter Siegmund <developer@mars3142.org>
This commit is contained in:
2025-11-17 23:21:01 +01:00
parent 4587901672
commit 4ce471599b
17 changed files with 305 additions and 10 deletions

View File

@@ -0,0 +1,18 @@
import 'package:built_value/built_value.dart';
import 'package:built_value/serializer.dart';
part 'movie.g.dart';
abstract class Movie implements Built<Movie, MovieBuilder> {
static Serializer<Movie> get serializer => _$movieSerializer;
int get id;
String get poster;
String get backdrop;
Movie._();
factory Movie([void Function(MovieBuilder) updates]) = _$Movie;
}

View File

@@ -0,0 +1,16 @@
enum PosterOrientation {
horizontal,
vertical,
}
enum PosterFormat {
png,
jpeg,
bmp,
}
enum PosterOutput {
image,
lvgl,
lvglBinary,
}

View File

@@ -0,0 +1,30 @@
import 'package:cinema/feature/poster/domain/poster.enums.dart';
import 'package:zard/zard.dart';
final _orientations = PosterOrientation.values.map((e) => e.name);
final _formats = PosterFormat.values.map((e) => e.name);
final _outputs = PosterOutput.values.map((e) => e.name);
final posterSchema = z.map({
'width': z.int().min(1, message: "'width' must be at least 1").max(4000, message: "'width' must be at most 4000"),
'height': z.int().min(1, message: "'height' must be at least 1").max(4000, message: "'height' must be a most 4000"),
'count': z.int().min(1, message: "'count' must be at least 1").max(20, message: "'count' must be at most 20"),
'orientation': z.string().refine(
(value) => _orientations.contains(value),
message: "'orientation' must be either ${_orientations.join(', ')}",
),
'shuffle': z.bool(message: "'shuffle' must be a boolean value"),
'language': z.string(message: "'language' must be a string").transform((value) => value.trim()),
'backgroundColor': z.string().regex(
RegExp(r'^#([a-fA-F0-9]{3}|[a-fA-F0-9]{6})$'),
message: "The 'backgroundColor' must be a valid hexadecimal color code (e.g., #000 or #FF0000)",
),
'format': z.string().refine(
(value) => _formats.contains(value),
message: "'format' must be either ${_formats.join(', ')}",
),
'output': z.string().refine(
(value) => _outputs.contains(value),
message: "'output' must be either ${_outputs.join(', ')}",
),
});

View File

@@ -0,0 +1,69 @@
import 'dart:convert';
import 'package:built_collection/built_collection.dart';
import 'package:built_value/built_value.dart';
import 'package:built_value/serializer.dart';
part 'tmdb_trending_response.g.dart';
abstract class TmdbTrendingResponse implements Built<TmdbTrendingResponse, TmdbTrendingResponseBuilder> {
static Serializer<TmdbTrendingResponse> get serializer => _$tmdbTrendingResponseSerializer;
int get page;
BuiltList<TmdbMovieResult> get results;
@BuiltValueField(wireName: 'total_pages')
int get totalPages;
@BuiltValueField(wireName: 'total_results')
int get totalResults;
TmdbTrendingResponse._();
factory TmdbTrendingResponse([void Function(TmdbTrendingResponseBuilder) updates]) = _$TmdbTrendingResponse;
}
abstract class TmdbMovieResult implements Built<TmdbMovieResult, TmdbMovieResultBuilder> {
static Serializer<TmdbMovieResult> get serializer => _$tmdbMovieResultSerializer;
bool get adult;
@BuiltValueField(wireName: 'backdrop_path')
String get backdropPath;
int get id;
String get title;
@BuiltValueField(wireName: 'original_title')
String get originalTitle;
String get overview;
@BuiltValueField(wireName: 'poster_path')
String get posterPath;
@BuiltValueField(wireName: 'media_type')
String get mediaType;
@BuiltValueField(wireName: 'genre_ids')
BuiltList<int> get genreIds;
double get popularity;
@BuiltValueField(wireName: 'release_date')
DateTime get releaseDate;
bool get video;
@BuiltValueField(wireName: 'vote_average')
double get voteAverage;
@BuiltValueField(wireName: 'vote_count')
int get voteCount;
TmdbMovieResult._();
factory TmdbMovieResult([void Function(TmdbMovieResultBuilder) updates]) = _$TmdbMovieResult;
}