Skip to content

Commit

Permalink
fix: get notified about cover store changes (#1011)
Browse files Browse the repository at this point in the history
  • Loading branch information
Feichtmeier authored Nov 13, 2024
1 parent 5b35fe3 commit 316adf5
Show file tree
Hide file tree
Showing 17 changed files with 324 additions and 181 deletions.
51 changes: 5 additions & 46 deletions lib/common/view/four_images_grid.dart
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
import 'dart:typed_data';

import 'package:flutter/material.dart';

class FourImagesGrid extends StatelessWidget {
const FourImagesGrid({
super.key,
required this.images,
this.fit = BoxFit.cover,
});

final Set<Uint8List> images;
final BoxFit fit;
final List<Widget> images;

@override
Widget build(BuildContext context) {
Expand All @@ -20,16 +16,10 @@ class FourImagesGrid extends StatelessWidget {
child: Row(
children: [
Expanded(
child: _Image(
bytes: images.elementAt(0),
fit: fit,
),
child: images.elementAt(0),
),
Expanded(
child: _Image(
bytes: images.elementAt(1),
fit: fit,
),
child: images.elementAt(1),
),
],
),
Expand All @@ -38,16 +28,10 @@ class FourImagesGrid extends StatelessWidget {
child: Row(
children: [
Expanded(
child: _Image(
bytes: images.elementAt(2),
fit: fit,
),
child: images.elementAt(2),
),
Expanded(
child: _Image(
bytes: images.elementAt(3),
fit: fit,
),
child: images.elementAt(3),
),
],
),
Expand All @@ -56,28 +40,3 @@ class FourImagesGrid extends StatelessWidget {
);
}
}

class _Image extends StatelessWidget {
final Uint8List bytes;

const _Image({
required this.bytes,
required this.fit,
});

final BoxFit fit;

@override
Widget build(BuildContext context) {
const quality = FilterQuality.medium;
const height = double.infinity;
const width = double.infinity;
return Image.memory(
bytes,
height: height,
width: width,
fit: fit,
filterQuality: quality,
);
}
}
53 changes: 15 additions & 38 deletions lib/common/view/round_image_container.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import 'dart:typed_data';

import 'package:flutter/material.dart';
import 'package:yaru/yaru.dart';

Expand All @@ -10,11 +8,11 @@ import 'theme.dart';
class RoundImageContainer extends StatelessWidget {
const RoundImageContainer({
super.key,
this.images,
required this.images,
required this.fallBackText,
});

final Set<Uint8List>? images;
final List<Widget> images;
final String fallBackText;

@override
Expand All @@ -27,52 +25,31 @@ class RoundImageContainer extends StatelessWidget {
color: theme.shadowColor.withOpacity(0.4),
);

if (images?.length == 1) {
return Container(
decoration: BoxDecoration(
image: DecorationImage(
image: MemoryImage(images!.first),
fit: BoxFit.fitHeight,
filterQuality: FilterQuality.medium,
),
boxShadow: [
boxShadow,
],
),
);
if (images.length == 1) {
return images.first;
}

if (images?.isNotEmpty == true) {
if (images!.length >= 4) {
if (images.isNotEmpty) {
if (images.length >= 4) {
return Container(
decoration: BoxDecoration(
boxShadow: [
boxShadow,
],
),
child: FourImagesGrid(
images: images!,
images: images,
),
);
} else if (images!.length >= 2) {
return Container(
decoration: BoxDecoration(
image: DecorationImage(
fit: BoxFit.fitHeight,
image: MemoryImage(images!.first),
} else if (images.length >= 2) {
return Stack(
children: [
images.first,
YaruClip.diagonal(
position: YaruDiagonalClip.bottomLeft,
child: images.elementAt(1),
),
boxShadow: [
boxShadow,
],
),
child: YaruClip.diagonal(
position: YaruDiagonalClip.bottomLeft,
child: Image.memory(
images!.elementAt(1),
fit: BoxFit.fitHeight,
filterQuality: FilterQuality.medium,
),
),
],
);
}
}
Expand Down
48 changes: 0 additions & 48 deletions lib/local_audio/cover_store.dart

This file was deleted.

3 changes: 3 additions & 0 deletions lib/local_audio/local_audio_model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ class LocalAudioModel extends SafeChangeNotifier {
}) =>
_service.findLocalCovers(audios: audios, limit: limit);

List<Audio> findUniqueAlbumAudios(List<Audio> audios) =>
_service.findUniqueAlbumAudios(audios);

List<String>? get failedImports => _service.failedImports;

List<String>? findAllAlbums({
Expand Down
29 changes: 19 additions & 10 deletions lib/local_audio/local_audio_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import '../common/logging.dart';
import '../common/view/audio_filter.dart';
import '../extensions/media_file_x.dart';
import '../settings/settings_service.dart';
import 'cover_store.dart';
import 'local_cover_service.dart';

typedef LocalSearchResult = ({
List<Audio>? titles,
Expand All @@ -22,9 +22,13 @@ typedef LocalSearchResult = ({

class LocalAudioService {
final SettingsService? _settingsService;
final LocalCoverService _localCoverService;

LocalAudioService({SettingsService? settingsService})
: _settingsService = settingsService;
LocalAudioService({
required LocalCoverService localCoverService,
SettingsService? settingsService,
}) : _settingsService = settingsService,
_localCoverService = localCoverService;

List<Audio>? _audios;
List<Audio>? get audios => _audios;
Expand Down Expand Up @@ -161,15 +165,10 @@ class LocalAudioService {
}) {
final images = <Uint8List>{};

final albumAudios = <Audio>[];
for (var audio in audios) {
if (albumAudios.none((a) => a.album == audio.album)) {
albumAudios.add(audio);
}
}
List<Audio> albumAudios = findUniqueAlbumAudios(audios);

for (var audio in albumAudios) {
var uint8list = CoverStore().get(audio.albumId);
var uint8list = _localCoverService.get(audio.albumId);
if (uint8list != null && images.length < limit) {
images.add(uint8list);
}
Expand All @@ -178,6 +177,16 @@ class LocalAudioService {
return images;
}

List<Audio> findUniqueAlbumAudios(List<Audio> audios) {
final albumAudios = <Audio>[];
for (var audio in audios) {
if (albumAudios.none((a) => a.album == audio.album)) {
albumAudios.add(audio);
}
}
return albumAudios;
}

LocalSearchResult? search(String? query) {
if (query == null) return null;
if (query.isEmpty) {
Expand Down
31 changes: 31 additions & 0 deletions lib/local_audio/local_cover_model.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import 'local_cover_service.dart';
import 'dart:async';
import 'dart:typed_data';
import 'package:safe_change_notifier/safe_change_notifier.dart';

class LocalCoverModel extends SafeChangeNotifier {
LocalCoverModel({
required LocalCoverService localCoverService,
}) : _localCoverService = localCoverService;

final LocalCoverService _localCoverService;
StreamSubscription<bool>? _propertiesChangedSub;

int get storeLength => _localCoverService.storeLength;
Uint8List? get(String? albumId) => _localCoverService.get(albumId);

Future<Uint8List?> getCover({
required String albumId,
required String path,
}) async =>
_localCoverService.getCover(albumId: albumId, path: path);

void init() => _propertiesChangedSub ??=
_localCoverService.propertiesChanged.listen((_) => notifyListeners());

@override
Future<void> dispose() async {
await _propertiesChangedSub?.cancel();
super.dispose();
}
}
55 changes: 55 additions & 0 deletions lib/local_audio/local_cover_service.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import 'dart:async';
import 'dart:io';
import 'dart:typed_data';

import 'package:audio_metadata_reader/audio_metadata_reader.dart';
import 'package:collection/collection.dart';

import '../compute_isolate.dart';
import '../constants.dart';
import '../persistence_utils.dart';

class LocalCoverService {
final _propertiesChangedController = StreamController<bool>.broadcast();
Stream<bool> get propertiesChanged => _propertiesChangedController.stream;

var _store = <String, Uint8List?>{};
int get storeLength => _store.length;

Future<Uint8List?> getCover({
required String albumId,
required String path,
}) async {
if (albumId.isNotEmpty == true) {
final metadata = await readMetadata(File(path), getImage: true);
final cover = _put(
albumId: albumId,
data: metadata.pictures
.firstWhereOrNull((e) => e.bytes.isNotEmpty)
?.bytes,
);
if (cover != null) {
_propertiesChangedController.add(true);
}
return cover;
}
return null;
}

Uint8List? _put({required String albumId, Uint8List? data}) {
return _store.containsKey(albumId)
? _store.update(albumId, (value) => data)
: _store.putIfAbsent(albumId, () => data);
}

Uint8List? get(String? albumId) => albumId == null ? null : _store[albumId];

Future<void> write() async => writeUint8ListMap(_store, kCoverStore);

// This does not make much sense except for small libs, where the store is filled
// fast anyways. Let's keep it for eventual use...
Future<void> read() async =>
_store = await computeIsolate(() => readUint8ListMap(kCoverStore)) ?? [];

Future<void> dispose() async => _propertiesChangedController.close();
}
Loading

0 comments on commit 316adf5

Please sign in to comment.