Compare commits

..

No commits in common. "c56e3dba0fbb38cdfbcd193ee8f2aa4b4af963a0" and "889be2ff2c91af1e6ffd2fb739e6bee4cf9dea9e" have entirely different histories.

9 changed files with 67 additions and 138 deletions

View File

@ -4,7 +4,6 @@ import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
import '../services/sync_service.dart'; import '../services/sync_service.dart';
import 'items.dart'; import 'items.dart';
import 'snackbars.dart';
class PagedListQueryView<T> extends HookConsumerWidget { class PagedListQueryView<T> extends HookConsumerWidget {
final PagingController<int, T> pagingController; final PagingController<int, T> pagingController;
@ -123,13 +122,7 @@ class SyncAllRefresh extends HookConsumerWidget {
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
return RefreshIndicator( return RefreshIndicator(
onRefresh: () async { onRefresh: () => ref.read(syncServiceProvider.notifier).syncAll(),
try {
await ref.read(syncServiceProvider.notifier).syncAll();
} catch (e) {
showErrorSnackbar(context, e.toString());
}
},
child: child, child: child,
); );
} }

View File

@ -12,7 +12,6 @@ import '../../log.dart';
import '../../models/settings.dart'; import '../../models/settings.dart';
import '../../services/settings_service.dart'; import '../../services/settings_service.dart';
import '../items.dart'; import '../items.dart';
import '../snackbars.dart';
class SourcePage extends HookConsumerWidget { class SourcePage extends HookConsumerWidget {
final int? id; final int? id;
@ -164,7 +163,7 @@ class SourcePage extends HookConsumerWidget {
); );
} }
} catch (e, st) { } catch (e, st) {
showErrorSnackbar(context, e.toString()); // TOOD: toast the error or whatever
log.severe('Saving source', e, st); log.severe('Saving source', e, st);
error = true; error = true;
} finally { } finally {

View File

@ -1,14 +0,0 @@
import 'package:flutter/material.dart';
void showErrorSnackbar(BuildContext context, String message) {
final colors = Theme.of(context).colorScheme;
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(message, style: TextStyle(color: colors.onErrorContainer)),
backgroundColor: colors.errorContainer,
showCloseIcon: true,
closeIconColor: colors.onErrorContainer,
behavior: SnackBarBehavior.floating,
duration: const Duration(seconds: 10),
));
}

View File

@ -15,17 +15,26 @@ class SubtracksHttpClient extends BaseClient {
@override @override
Future<StreamedResponse> send(BaseRequest request) { Future<StreamedResponse> send(BaseRequest request) {
request.headers.addAll(subtracksHeaders); request.headers.addAll(subtracksHeaders);
log.info('${request.method} ${request.url}'); log.info('${request.method} ${_redactUri(request.url)}');
try {
return request.send(); return request.send();
} catch (e, st) {
log.severe('HTTP client: ${request.method} ${request.url}', e, st);
rethrow;
} }
} }
String _redactUri(Uri uri) {
var redacted = uri.toString();
redacted = _redactParam(redacted, 'u');
redacted = _redactParam(redacted, 'p');
redacted = _redactParam(redacted, 's');
redacted = _redactParam(redacted, 't');
return redacted.toString();
} }
RegExp _queryReplace(String key) => RegExp('$key=([^&|\\n|\\t\\s]+)');
String _redactParam(String url, String key) =>
url.replaceFirst(_queryReplace(key), '$key=REDACTED');
@Riverpod(keepAlive: true) @Riverpod(keepAlive: true)
BaseClient httpClient(HttpClientRef ref) { BaseClient httpClient(HttpClientRef ref) {
return SubtracksHttpClient(); return SubtracksHttpClient();

View File

@ -88,7 +88,6 @@ String _format(
bool color = false, bool color = false,
bool time = true, bool time = true,
bool level = true, bool level = true,
bool redact = true,
}) { }) {
var message = ''; var message = '';
if (time) message += '${event.time.toIso8601String()} '; if (time) message += '${event.time.toIso8601String()} ';
@ -108,11 +107,6 @@ String _format(
if (event.error != null) { if (event.error != null) {
message += '\n${event.error}'; message += '\n${event.error}';
} }
if (redact) {
message = _redactUrl(message);
}
if (event.stackTrace != null) { if (event.stackTrace != null) {
message += '\n${event.stackTrace}'; message += '\n${event.stackTrace}';
} }
@ -122,24 +116,17 @@ String _format(
: message; : message;
} }
String _redactUrl(String message) { Future<void> _printFile(String event, String dir) async {
if (!_queryReplace('u').hasMatch(message)) { final now = DateTime.now();
return message; final file = File(p.join(dir, '${now.year}-${now.month}-${now.day}.txt'));
if (!event.endsWith('\n')) {
event += '\n';
} }
message = _redactParam(message, 'u'); await file.writeAsString(event, mode: FileMode.writeOnlyAppend, flush: true);
message = _redactParam(message, 'p');
message = _redactParam(message, 's');
message = _redactParam(message, 't');
return message;
} }
RegExp _queryReplace(String key) => RegExp('$key=([^&|\\n|\\t\\s]+)');
String _redactParam(String url, String key) =>
url.replaceAll(_queryReplace(key), '$key=REDACTED');
Future<Directory> logDirectory() async { Future<Directory> logDirectory() async {
return Directory( return Directory(
p.join((await getApplicationDocumentsDirectory()).path, 'logs'), p.join((await getApplicationDocumentsDirectory()).path, 'logs'),
@ -154,43 +141,11 @@ Future<List<File>> logFiles() async {
); );
} }
File _currentLogFile(String logDir) {
final now = DateTime.now();
return File(p.join(logDir, '${now.year}-${now.month}-${now.day}.txt'));
}
Future<void> _printFile(String event, String logDir) async {
final file = _currentLogFile(logDir);
if (!event.endsWith('\n')) {
event += '\n';
}
await file.writeAsString(event, mode: FileMode.writeOnlyAppend, flush: true);
}
void _printDebug(LogRecord event) {
// ignore: avoid_print
print(_format(event, color: true, time: false, level: false, redact: false));
}
Future<void> _printRelease(LogRecord event, String logDir) async {
await _printFile(
_format(event, color: false, time: true, level: true, redact: true),
logDir,
);
}
final log = Logger('default'); final log = Logger('default');
Future<void> initLogging() async { Future<void> initLogging() async {
final dir = (await logDirectory())..create(); final dir = (await logDirectory())..create();
final file = _currentLogFile(dir.path);
if (!(await file.exists())) {
await file.create();
}
final files = await logFiles(); final files = await logFiles();
if (files.length > 7) { if (files.length > 7) {
for (var file in files.slice(7)) { for (var file in files.slice(7)) {
@ -201,9 +156,14 @@ Future<void> initLogging() async {
Logger.root.level = kDebugMode ? Level.ALL : Level.INFO; Logger.root.level = kDebugMode ? Level.ALL : Level.INFO;
Logger.root.onRecord.asyncMap((event) async { Logger.root.onRecord.asyncMap((event) async {
if (kDebugMode) { if (kDebugMode) {
_printDebug(event); print(_format(event, color: true, time: false, level: false));
} else { } else {
await _printRelease(event, dir.path); await _printFile(
_format(event, color: false, time: true, level: true),
dir.path,
);
} }
}).listen((_) {}, cancelOnError: false); }).listen((_) {}, cancelOnError: false);
log.info('start');
} }

View File

@ -95,7 +95,8 @@ class AudioControl extends BaseAudioHandler with QueueHandler, SeekHandler {
int get _sourceId => _ref.read(sourceIdProvider); int get _sourceId => _ref.read(sourceIdProvider);
AudioControl(this._player, this._ref) { AudioControl(this._player, this._ref) {
_player.playbackEventStream.listen((PlaybackEvent event) { _player.playbackEventStream.listen(
(PlaybackEvent event) {
final playing = _player.playing; final playing = _player.playing;
playbackState.add(playbackState.value.copyWith( playbackState.add(playbackState.value.copyWith(
controls: [ controls: [
@ -120,11 +121,10 @@ class AudioControl extends BaseAudioHandler with QueueHandler, SeekHandler {
bufferedPosition: _player.bufferedPosition, bufferedPosition: _player.bufferedPosition,
queueIndex: event.currentIndex, queueIndex: event.currentIndex,
)); ));
}); },
onError: (e, st) => log.warning('Audio playback error', e, st),
_player.playbackEventStream.doOnError((e, st) async { cancelOnError: false,
log.warning('playbackEventStream', e, st); );
});
shuffleIndicies.listen((value) { shuffleIndicies.listen((value) {
playbackState.add(playbackState.value.copyWith( playbackState.add(playbackState.value.copyWith(

View File

@ -53,8 +53,6 @@ class SettingsService extends _$SettingsService {
ref.read(httpClientProvider), ref.read(httpClientProvider),
); );
await client.test();
final features = IList([ final features = IList([
if (await client.testFeature(SubsonicFeature.emptyQuerySearch)) if (await client.testFeature(SubsonicFeature.emptyQuerySearch))
SubsonicFeature.emptyQuerySearch, SubsonicFeature.emptyQuerySearch,
@ -68,10 +66,6 @@ class SettingsService extends _$SettingsService {
} }
Future<void> updateSource(SubsonicSettings source) async { Future<void> updateSource(SubsonicSettings source) async {
final client = SubsonicClient(source, ref.read(httpClientProvider));
await client.test();
await _db.updateSource(source); await _db.updateSource(source);
await init(); await init();
} }

View File

@ -1,8 +1,6 @@
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:rxdart/rxdart.dart';
import '../database/database.dart'; import '../database/database.dart';
import '../log.dart';
import '../state/settings.dart'; import '../state/settings.dart';
abstract class BaseMusicSource { abstract class BaseMusicSource {
@ -42,33 +40,25 @@ class MusicSource implements BaseMusicSource {
@override @override
Stream<Iterable<AlbumsCompanion>> allAlbums() { Stream<Iterable<AlbumsCompanion>> allAlbums() {
_testOnline(); _testOnline();
return _source return _source.allAlbums();
.allAlbums()
.doOnError((e, st) => log.severe('allAlbums', e, st));
} }
@override @override
Stream<Iterable<ArtistsCompanion>> allArtists() { Stream<Iterable<ArtistsCompanion>> allArtists() {
_testOnline(); _testOnline();
return _source return _source.allArtists();
.allArtists()
.doOnError((e, st) => log.severe('allArtists', e, st));
} }
@override @override
Stream<Iterable<PlaylistWithSongsCompanion>> allPlaylists() { Stream<Iterable<PlaylistWithSongsCompanion>> allPlaylists() {
_testOnline(); _testOnline();
return _source return _source.allPlaylists();
.allPlaylists()
.doOnError((e, st) => log.severe('allPlaylists', e, st));
} }
@override @override
Stream<Iterable<SongsCompanion>> allSongs() { Stream<Iterable<SongsCompanion>> allSongs() {
_testOnline(); _testOnline();
return _source return _source.allSongs();
.allSongs()
.doOnError((e, st) => log.severe('allSongs', e, st));
} }
@override @override

View File

@ -98,8 +98,6 @@ class SubsonicClient {
return subsonicResponse; return subsonicResponse;
} }
Future<void> test() => get('ping');
Future<bool> testFeature(SubsonicFeature feature) async { Future<bool> testFeature(SubsonicFeature feature) async {
switch (feature) { switch (feature) {
case SubsonicFeature.emptyQuerySearch: case SubsonicFeature.emptyQuerySearch: