mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-08-17 16:37:17 +02:00
feat: add persistent cover image cache
- Add CoverCacheManager service for persistent image caching - Cache stored in app_flutter/cover_cache/ (not cleared by system) - Maximum 1000 images cached for up to 365 days - Update all 11 screens to use persistent cache manager - Add flutter_cache_manager and path dependencies - Update CHANGELOG.md with all changes for v3.1.3
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
import 'dart:io';
|
||||
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
|
||||
/// Persistent cache manager for album/track cover images.
|
||||
///
|
||||
/// Unlike the default cache manager which stores in temp directory
|
||||
/// (can be cleared by system anytime), this stores in app support
|
||||
/// directory which persists across app restarts.
|
||||
class CoverCacheManager {
|
||||
static const String _cacheKey = 'coverImageCache';
|
||||
static const int _maxCacheObjects = 1000;
|
||||
static const Duration _maxCacheAge = Duration(days: 365);
|
||||
|
||||
static CacheManager? _instance;
|
||||
static bool _initialized = false;
|
||||
|
||||
/// Get the singleton cache manager instance.
|
||||
/// Must call [initialize] before using this.
|
||||
static CacheManager get instance {
|
||||
if (!_initialized || _instance == null) {
|
||||
throw StateError(
|
||||
'CoverCacheManager not initialized. Call CoverCacheManager.initialize() first.',
|
||||
);
|
||||
}
|
||||
return _instance!;
|
||||
}
|
||||
|
||||
/// Check if cache manager is initialized
|
||||
static bool get isInitialized => _initialized && _instance != null;
|
||||
|
||||
/// Initialize the cache manager with persistent storage path.
|
||||
/// Call this once during app startup (in main.dart).
|
||||
static Future<void> initialize() async {
|
||||
if (_initialized) return;
|
||||
|
||||
final appDir = await getApplicationSupportDirectory();
|
||||
final cachePath = p.join(appDir.path, 'cover_cache');
|
||||
|
||||
// Ensure cache directory exists
|
||||
await Directory(cachePath).create(recursive: true);
|
||||
|
||||
_instance = CacheManager(
|
||||
Config(
|
||||
_cacheKey,
|
||||
stalePeriod: _maxCacheAge,
|
||||
maxNrOfCacheObjects: _maxCacheObjects,
|
||||
repo: JsonCacheInfoRepository(databaseName: _cacheKey),
|
||||
fileSystem: IOFileSystem(cachePath),
|
||||
fileService: HttpFileService(),
|
||||
),
|
||||
);
|
||||
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
/// Clear all cached cover images.
|
||||
/// Returns the number of files deleted.
|
||||
static Future<void> clearCache() async {
|
||||
if (!_initialized || _instance == null) return;
|
||||
await _instance!.emptyCache();
|
||||
}
|
||||
|
||||
/// Get cache statistics
|
||||
static Future<CacheStats> getStats() async {
|
||||
if (!_initialized) {
|
||||
return const CacheStats(fileCount: 0, totalSizeBytes: 0);
|
||||
}
|
||||
|
||||
final appDir = await getApplicationSupportDirectory();
|
||||
final cacheDir = Directory(p.join(appDir.path, 'cover_cache'));
|
||||
|
||||
if (!await cacheDir.exists()) {
|
||||
return const CacheStats(fileCount: 0, totalSizeBytes: 0);
|
||||
}
|
||||
|
||||
int fileCount = 0;
|
||||
int totalSize = 0;
|
||||
|
||||
await for (final entity in cacheDir.list(recursive: true)) {
|
||||
if (entity is File) {
|
||||
fileCount++;
|
||||
totalSize += await entity.length();
|
||||
}
|
||||
}
|
||||
|
||||
return CacheStats(fileCount: fileCount, totalSizeBytes: totalSize);
|
||||
}
|
||||
}
|
||||
|
||||
/// Statistics about the cover image cache
|
||||
class CacheStats {
|
||||
final int fileCount;
|
||||
final int totalSizeBytes;
|
||||
|
||||
const CacheStats({
|
||||
required this.fileCount,
|
||||
required this.totalSizeBytes,
|
||||
});
|
||||
|
||||
/// Get human-readable size string
|
||||
String get formattedSize {
|
||||
if (totalSizeBytes < 1024) {
|
||||
return '$totalSizeBytes B';
|
||||
} else if (totalSizeBytes < 1024 * 1024) {
|
||||
return '${(totalSizeBytes / 1024).toStringAsFixed(1)} KB';
|
||||
} else if (totalSizeBytes < 1024 * 1024 * 1024) {
|
||||
return '${(totalSizeBytes / (1024 * 1024)).toStringAsFixed(1)} MB';
|
||||
} else {
|
||||
return '${(totalSizeBytes / (1024 * 1024 * 1024)).toStringAsFixed(2)} GB';
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user