Melo v2.10 - YouTube Proxy, Download-Screen, Logger, iOS
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
/// Umfassendes Log-System für die Melo App.
|
||||
/// Zeichnet ALLES auf: Aktionen, Fehler, Netzwerk, Performance.
|
||||
class MeloLogger {
|
||||
static final MeloLogger _instanz = MeloLogger._();
|
||||
factory MeloLogger() => _instanz;
|
||||
MeloLogger._();
|
||||
|
||||
bool _initialisiert = false;
|
||||
String _sessionId = '';
|
||||
String _version = '2.7';
|
||||
final List<Map<String, dynamic>> _eintraege = [];
|
||||
int _logId = 0;
|
||||
Timer? _flushTimer;
|
||||
|
||||
static const String _serverUrl = 'http://159.195.51.99:8991/api/log';
|
||||
|
||||
void init(String version) {
|
||||
if (_initialisiert) return;
|
||||
_initialisiert = true;
|
||||
_version = version;
|
||||
_sessionId = DateTime.now().millisecondsSinceEpoch.toString();
|
||||
|
||||
_flushTimer = Timer.periodic(const Duration(seconds: 30), (_) => _senden());
|
||||
_addEintrag('app', 'start', {'version': version, 'session': _sessionId});
|
||||
|
||||
FlutterError.onError = (details) {
|
||||
FlutterError.presentError(details);
|
||||
_addEintrag('crash', 'flutter_error', {
|
||||
'fehler': details.exceptionAsString(),
|
||||
'stack': details.stack.toString(),
|
||||
});
|
||||
};
|
||||
|
||||
PlatformDispatcher.instance.onError = (error, stack) {
|
||||
_addEintrag('crash', 'dart_error', {
|
||||
'fehler': error.toString(),
|
||||
'stack': stack.toString(),
|
||||
});
|
||||
return true;
|
||||
};
|
||||
|
||||
debugPrint('📋 MeloLogger ready (Session: $_sessionId)');
|
||||
}
|
||||
|
||||
void _addEintrag(String kategorie, String aktion, [Map<String, dynamic>? details]) {
|
||||
_eintraege.add({
|
||||
'id': _logId++,
|
||||
'zeit': DateTime.now().toIso8601String(),
|
||||
'kategorie': kategorie,
|
||||
'aktion': aktion,
|
||||
'details': details ?? {},
|
||||
});
|
||||
}
|
||||
|
||||
void aktion(String name, [Map<String, dynamic>? details]) {
|
||||
_addEintrag('aktion', name, details);
|
||||
}
|
||||
|
||||
void netzwerk(String methode, String url, {int? statusCode, String? fehler, int? dauerMs}) {
|
||||
_addEintrag('netzwerk', '$methode $url', {
|
||||
if (statusCode != null) 'status': statusCode,
|
||||
if (fehler != null) 'fehler': fehler,
|
||||
if (dauerMs != null) 'dauer_ms': dauerMs,
|
||||
});
|
||||
}
|
||||
|
||||
void fehler(String kontext, Object? error, [StackTrace? stack]) {
|
||||
_addEintrag('fehler', kontext, {
|
||||
'error': error.toString(),
|
||||
'stack': stack?.toString() ?? '',
|
||||
});
|
||||
_senden();
|
||||
}
|
||||
|
||||
void performance(String name, int dauerMs) {
|
||||
_addEintrag('performance', name, {'dauer_ms': dauerMs});
|
||||
}
|
||||
|
||||
void zustand(String name, Map<String, dynamic> daten) {
|
||||
_addEintrag('zustand', name, daten);
|
||||
}
|
||||
|
||||
void manuell(String nachricht) {
|
||||
_addEintrag('user', nachricht);
|
||||
_senden();
|
||||
}
|
||||
|
||||
Future<void> _senden() async {
|
||||
if (_eintraege.isEmpty) return;
|
||||
|
||||
final batch = _eintraege.toList();
|
||||
_eintraege.clear();
|
||||
|
||||
try {
|
||||
await http.post(
|
||||
Uri.parse(_serverUrl),
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: jsonEncode({
|
||||
'typ': 'log_batch',
|
||||
'session': _sessionId,
|
||||
'version': _version,
|
||||
'device': '${Platform.operatingSystem} ${Platform.operatingSystemVersion}',
|
||||
'eintraege': batch,
|
||||
}),
|
||||
).timeout(const Duration(seconds: 5));
|
||||
} catch (_) {
|
||||
_eintraege.insertAll(0, batch);
|
||||
}
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
_flushTimer?.cancel();
|
||||
_senden();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user