You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

202 lines
6.7 KiB
Dart

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:dio/dio.dart';
import '../../core/api/backend_api.dart';
import '../../core/config.dart';
import '../../core/async_utils.dart';
import '../shared/providers.dart';
class ImportScreen extends ConsumerStatefulWidget {
const ImportScreen({super.key});
@override
ConsumerState<ImportScreen> createState() => _ImportScreenState();
}
class _ImportScreenState extends ConsumerState<ImportScreen> {
final _movieCtrl =
TextEditingController(text: '603, 27205'); // Matrix, Inception
final _showCtrl =
TextEditingController(text: '1396, 1399'); // Breaking Bad, GoT
String _log = '';
bool _busy = false;
bool _cancel = false;
void _append(String msg) => setState(() => _log += '$msg\n');
Future<void> _importMovies() async {
setState(() => _busy = true);
final tmdb = ref.read(tmdbApiProvider);
final backend = ref.read(backendApiProvider);
final ids = _movieCtrl.text
.split(RegExp(r'[,\s]+'))
.where((s) => s.isNotEmpty)
.map(int.parse);
for (final id in ids) {
try {
_append('Film $id: TMDB laden …');
final json = await tmdb.getMovie(id);
await backend.upsertMovie(json);
_append('Film $id: OK ✓');
} catch (e) {
_append('Film $id: Fehler → $e');
}
}
setState(() => _busy = false);
}
Future<void> _importShows() async {
setState(() { _busy = true; _cancel = false; });
final tmdb = ref.read(tmdbApiProvider);
final backend = ref.read(backendApiProvider);
final ids = _showCtrl.text
.split(RegExp(r'[,\s]+'))
.where((s) => s.isNotEmpty)
.map(int.parse);
for (final showId in ids) {
if (_cancel) break;
try {
_append('Serie $showId: TMDB laden …');
final showJson = await tmdb.getShow(showId);
// Debug-Ausgabe
await backend.upsertShow(showJson);
_append('Serie $showId: Show OK ✓');
final seasonNos = (showJson['seasons'] as List? ?? const [])
.where((s) => (s['season_number'] ?? 0) is int)
.map((s) => (s as Map<String, dynamic>)['season_number'] as int)
.where((n) => n >= 0)
.toList();
// Fetch all seasons (limited concurrency)
final seasonsJson = <int, Map<String, dynamic>>{};
await runChunked<int>(
seasonNos,
AppConfig.tmdbSeasonFetchConcurrency,
(sNo) async {
if (_cancel) return;
final sj = await tmdb.getSeason(showId, sNo);
seasonsJson[sNo] = sj;
},
isCancelled: () => _cancel,
);
final dbShowId = await _getDbShowIdByTmdb(backend, showId);
// Bulk upsert seasons and get IDs
Map<int,int> seasonIds = const {};
try {
seasonIds = await backend.upsertSeasonsBulk(dbShowId, seasonsJson.values.toList());
} catch (_) {
final m = <int,int>{};
for (final entry in seasonsJson.entries) { final id = await backend.upsertSeason(dbShowId, entry.value); m[entry.key] = id; }
seasonIds = m;
}
for (final entry in seasonsJson.entries) {
if (_cancel) break;
final sNo = entry.key; final seasonJson = entry.value; final dbSeasonId = seasonIds[sNo]; if (dbSeasonId == null) continue;
_append(' S$sNo: upserting episodes …');
final eps = (seasonJson['episodes'] as List? ?? const []).cast<Map<String, dynamic>>();
try { await backend.upsertEpisodesBulk(dbSeasonId, eps); }
catch (_) {
await runChunked<Map<String, dynamic>>(
eps,
AppConfig.dbEpisodeUpsertConcurrency,
(e) async { if (_cancel) return; await backend.upsertEpisode(dbSeasonId, e); },
isCancelled: () => _cancel,
);
}
_append(' S$sNo: ${eps.length} Episoden OK ✓');
}
} catch (e) {
// 👇 Hier kommt der erweiterte Catch hin!
if (e is DioException) {
}
_append('Serie $showId: Fehler → $e');
}
}
setState(() => _busy = false);
}
Future<int> _getDbShowIdByTmdb(BackendApi backend, int tmdbId) async {
final id = await backend.getShowDbIdByTmdbId(tmdbId);
if (id == null) {
throw Exception(
'Show mit tmdb_id=$tmdbId nicht gefunden zuerst upsert_show aufrufen.');
}
return id;
}
@override
Widget build(BuildContext context) {
final inputStyle = const TextStyle(fontSize: 13);
return Scaffold(
appBar: AppBar(title: const Text('Import (TMDB → DB)')),
body: Padding(
padding: const EdgeInsets.all(12),
child: Column(
children: [
Row(
children: [
const Text('Filme TMDB-IDs: '),
const SizedBox(width: 8),
Expanded(
child:
TextField(controller: _movieCtrl, style: inputStyle)),
const SizedBox(width: 8),
FilledButton(
onPressed: _busy ? null : _importMovies,
child: const Text('Import Filme'),
),
],
),
const SizedBox(height: 12),
Row(
children: [
const Text('Serien TMDB-IDs: '),
const SizedBox(width: 8),
Expanded(
child: TextField(controller: _showCtrl, style: inputStyle)),
const SizedBox(width: 8),
FilledButton(
onPressed: _busy ? null : _importShows,
child: const Text('Import Serien'),
),
const SizedBox(width: 8),
if (_busy)
TextButton(
onPressed: () => setState(() => _cancel = true),
child: const Text('Abbrechen'),
),
],
),
const SizedBox(height: 12),
if (_busy) const LinearProgressIndicator(),
const SizedBox(height: 12),
Expanded(
child: Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
border: Border.all(color: Colors.black12),
borderRadius: BorderRadius.circular(8),
),
alignment: Alignment.topLeft,
child: SingleChildScrollView(
child: SelectableText(_log,
style: const TextStyle(
fontFamily: 'monospace', fontSize: 12)),
),
),
),
],
),
),
);
}
}