Files
2025-04-18 10:16:03 +02:00

481 lines
14 KiB
Dart

import 'dart:convert';
import 'package:accordion/accordion.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:intl/intl.dart';
import 'package:logger/logger.dart';
import 'package:multimedia/data/movies.dart';
import 'package:multimedia/log_printer.dart';
import 'package:multimedia/widgets/checkbox_with_label.dart';
import 'package:multimedia/widgets/movies/add/movie_search_list.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:tmdb_api/tmdb_api.dart' as TMDB;
// ignore: must_be_immutable
class AddMovieScreen extends StatefulWidget {
final Function(Movie) addMovieToList;
final Function() addMovieFinished;
AddMovieScreen(
this.addMovieToList,
this.addMovieFinished, {
super.key,
});
@override
State<AddMovieScreen> createState() => _AddMovieScreenState();
}
class _AddMovieScreenState extends State<AddMovieScreen> {
Logger logger = getLogger();
String movieTitleSearch = '';
String yearSearch = '';
bool includeAdult = false;
bool searchGerman = true;
bool searchEnglish = false;
List<Movie> movieList = [];
late MovieSearchList movieSearchList;
late Function(Movie) addMovieToList;
late Function() addMovieFinished;
static const headerStyle = TextStyle(
color: Color(0xffffffff), fontSize: 18, fontWeight: FontWeight.bold);
bool advancedOptionsOpen = false;
@override
Widget build(BuildContext context) {
addMovieToList = widget.addMovieToList;
addMovieFinished = widget.addMovieFinished;
movieSearchList = MovieSearchList(movieList: movieList);
return Scaffold(
appBar: AppBar(
title: Text('Search Movie'),
actions: [
const VerticalDivider(
width: 10,
thickness: 3,
indent: 0,
endIndent: 0,
color: Colors.grey,
),
IconButton(
icon: const Icon(Icons.check_circle_outline),
tooltip: 'Add Movie(s)',
onPressed: () {
if (addMovies()) {
Navigator.pop(context, true);
}
},
),
IconButton(
icon: const Icon(Icons.highlight_off),
tooltip: 'Cancel Add',
onPressed: () {
Navigator.pop(context, null);
},
),
],
),
body: Column(
children: [
Row(
children: [
SizedBox(
width: 20,
),
SizedBox(
width: 250,
child: TextField(
style: TextStyle(color: Colors.white),
decoration: InputDecoration(
labelText: 'Movie Title',
),
onChanged: (text) {
movieTitleSearch = text;
},
),
),
const SizedBox(
width: 10,
),
SizedBox(
width: 50,
child: TextField(
style: TextStyle(color: Colors.white),
decoration: InputDecoration(
labelText: 'Year',
),
onChanged: (text) {
yearSearch = text;
},
),
),
const SizedBox(
width: 10,
),
ElevatedButton(
onPressed: () {
startSearch();
},
child: const Text('Search'),
),
],
),
Accordion(
headerBorderColor: Colors.blueGrey,
headerBorderColorOpened: Colors.transparent,
// headerBorderWidth: 1,
headerBackgroundColorOpened: Colors.grey,
contentBackgroundColor: Colors.black87,
contentBorderColor: Colors.grey,
contentBorderWidth: 3,
contentHorizontalPadding: 20,
scaleWhenAnimating: true,
openAndCloseAnimation: true,
headerPadding:
const EdgeInsets.symmetric(vertical: 7, horizontal: 15),
children: [
AccordionSection(
isOpen: advancedOptionsOpen,
contentVerticalPadding: 20,
leftIcon: const Icon(Icons.settings, color: Colors.white),
header: const Text('Advanced Options', style: headerStyle),
content: advancedOptions(),
onOpenSection: () {
advancedOptionsOpen = true;
},
onCloseSection: () {
advancedOptionsOpen = false;
},
),
],
),
const SizedBox(
height: 10,
),
const Divider(
height: 5,
thickness: 3,
),
Expanded(
child: MovieSearchList(
movieList: movieList,
),
),
],
),
);
}
void includeAdultCallback(bool state) {
includeAdult = state;
}
void searchGermanCallback(bool state) {
searchGerman = state;
}
void searchEnglishCallback(bool state) {
searchEnglish = state;
}
Widget advancedOptions() {
return Row(
children: [
CheckboxWithLabel(
label: 'Include Adult',
callback: includeAdultCallback,
state: includeAdult,
),
CheckboxWithLabel(
label: 'German',
callback: searchGermanCallback,
state: searchGerman,
),
CheckboxWithLabel(
label: 'English',
callback: searchEnglishCallback,
state: searchEnglish,
),
],
);
}
String parseString(dynamic data, String field) {
if (data[field].toString() == Null) {
return '';
}
return data[field].toString();
}
DateTime parseDate(dynamic data, String field) {
if (data[field].toString() == Null) {
return DateTime(1900, 1, 1);
}
if (data[field].toString().length < 1) {
return DateTime(1900, 1, 1);
}
return DateTime.parse(data[field].toString());
}
double parseDouble(dynamic data, String field) {
if (data[field] == Null) {
return 0.0;
}
return data[field];
}
int parseInt(dynamic data, String field) {
if (data[field] == Null) {
return 0;
}
return data[field];
}
bool parseBool(dynamic data, String field) {
if (data[field] == Null) {
return false;
}
return data[field];
}
Future<void> startSearch() async {
if (movieTitleSearch.isEmpty) {
return;
}
int year = int.tryParse(yearSearch) ?? 0;
final tmdb = TMDB.TMDB(TMDB.ApiKeys(
'a33271b9e54cdcb9a80680eaf5522f1b', 'apiReadAccessTokenv4'));
final response = await tmdb.v3.search.queryMovies(
movieTitleSearch,
year: year,
includeAdult: includeAdult,
language: searchGerman == true ? 'de' : 'en',
);
List<dynamic> movies = response['results'];
SharedPreferences prefs = await SharedPreferences.getInstance();
String? dbProtocol = prefs.getString('dbProtocol');
String? dbHost = prefs.getString('dbHost');
String? dbPath = prefs.getString('dbPath');
Uri url = Uri(
scheme: dbProtocol,
host: dbHost,
path: dbPath,
queryParameters: {
'module': 'movie',
'function': 'getIDs',
},
);
List<int> movieIDsTmp = [];
try {
final response = await http.get(url);
//LATER bool? error;
//LATER String? errorMessage;
Map<String, dynamic> movieIDs = json.decode(response.body);
//LATER error = movies['error'];
//LATER errorMessage = movies['errmsg'];
var list = movieIDs['data'];
for (int i = 0; i < list.length; i++) {
var entry = list[i];
movieIDsTmp.add(int.tryParse(entry['movieID'].toString()) ?? 0);
}
} catch (e) {
print(e.toString());
}
setState(
() {
movieList.clear();
for (int i = 0; i < movies.length; i++) {
dynamic movie = movies[i];
int movieID = parseInt(movie, 'id');
String movieTitle = parseString(movie, 'title');
String originalTitle = parseString(movie, 'original_title');
String overview = parseString(movie, 'overview');
DateTime releaseDate = parseDate(movie, 'release_date');
String backdropPath = parseString(movie, 'backdrop_path');
String posterPath = parseString(movie, 'poster_path');
String languages = ''; //MISSING
String productionCountries = ''; //MISSING
String productionCompanies = ''; //MISSING
double voteAverage = parseDouble(movie, 'vote_average');
int voteCount = parseInt(movie, 'vote_count');
String cast = ''; //MISSING
String crew = ''; //MISSING
String genre = ''; //DIFFERENT;
String localPath = '';
String imdbID = ''; //MISSING
String originalLanguage = parseString(movie, 'original_language');
double popularity = parseDouble(movie, 'popularity');
bool adult = parseBool(movie, 'adult');
String belongsToCollection = ''; //MISSING
double budget = 0; //MISSING
String homepage = ''; //MISSING
double revenue = 0; //MISSING
int runtime = 0; //MISSING
String tagline = ''; //MISSING
if (!movieIDsTmp.contains(movieID)) {
movieList.add(Movie(
movieID: movieID,
movieTitle: movieTitle,
originalTitle: originalTitle,
overview: overview,
releaseDate: releaseDate,
state: 0,
resolution: '',
backdropPath: backdropPath,
posterPath: posterPath,
languages: languages,
productionCountries: productionCountries,
productionCompanies: productionCompanies,
voteAverage: voteAverage,
voteCount: voteCount,
cast: cast,
crew: crew,
genre: genre,
localPath: localPath,
imdbID: imdbID,
originalLanguage: originalLanguage,
popularity: popularity,
adult: adult,
belongsToCollection: belongsToCollection,
budget: budget,
homepage: homepage,
revenue: revenue,
runtime: runtime,
tagline: tagline,
));
}
}
},
);
}
bool addMovies() {
bool somethingFound = false;
logger.i('addMovies');
for (int i = 0; i < movieList.length; i++) {
Movie movie = movieList[i];
if (movie.state == 1) {
somethingFound = true;
addMovie(movieList[i].movieID);
addMovieToList(movie);
}
}
addMovieFinished();
return somethingFound;
}
Future<bool> addMovie(int movieID) async {
String lang = 'de';
if (!searchGerman || searchEnglish) {
lang = 'en';
}
final tmdb = TMDB.TMDB(TMDB.ApiKeys(
'a33271b9e54cdcb9a80680eaf5522f1b', 'apiReadAccessTokenv4'));
final Map response = await tmdb.v3.movies.getDetails(
movieID,
language: lang,
appendToResponse: 'credits,external_ids',
);
Movie movie = Movie.fromJsonDynamic(response);
writeToDB(movie);
return true;
}
Future<bool> writeToDB(Movie movie) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String? dbProtocol = prefs.getString('dbProtocol');
String? dbHost = prefs.getString('dbHost');
String? dbPath = prefs.getString('dbPath');
Uri url = Uri(
scheme: dbProtocol,
host: dbHost,
path: dbPath,
);
try {
/*final r = */await http.post(
url,
headers: <String, String>{
'Content-Type': 'application/json; charset=UTF-8',
},
body: jsonEncode(
<String, String>{
'module': 'movie',
'function': 'add',
'movieID': movie.movieID.toString(),
'movieTitle': movie.movieTitle,
'originalTitle': movie.originalTitle,
'overview': movie.overview,
'releaseDate': DateFormat('yyyy-MM-dd').format(movie.releaseDate),
'resolution': movie.resolution,
'state': movie.state.toString(),
'backdropPath': movie.backdropPath,
'languages': movie.languages,
'productionCountries': movie.productionCountries,
'productionCompanies': movie.productionCompanies,
'voteAverage': movie.voteAverage.toString(),
'voteCount': movie.voteCount.toString(),
'cast': movie.cast,
'crew': movie.crew,
'genre': movie.genre,
'localPath': movie.localPath,
'posterPath': movie.posterPath,
'imdbid': movie.imdbID,
'originalLanguage': movie.originalLanguage,
'popularity': movie.popularity.toString(),
'adult': movie.adult.toString(),
'belongsToCollection': movie.belongsToCollection,
'budget': movie.budget.toString(),
'homepage': movie.homepage,
'revenue': movie.revenue.toString(),
'runtime': movie.runtime.toString(),
'status': '',
'tagline': movie.tagline,
'video': ''
},
),
);
} catch (e) {
logger.e(e.toString());
}
return true;
}
}