feat(metadata): add embedded cover resize options

This commit is contained in:
zarzet
2026-08-01 18:24:27 +07:00
parent ed60cafb90
commit d4b617f49a
17 changed files with 441 additions and 34 deletions
+79 -1
View File
@@ -1,7 +1,7 @@
import 'dart:convert';
import 'dart:io';
import 'dart:math' as math;
import 'dart:typed_data';
import 'package:flutter/foundation.dart';
import 'package:ffmpeg_kit_flutter_new_full/ffmpeg_kit.dart';
import 'package:ffmpeg_kit_flutter_new_full/ffmpeg_kit_config.dart';
import 'package:ffmpeg_kit_flutter_new_full/ffmpeg_session.dart';
@@ -296,6 +296,84 @@ class FFmpegService {
}
}
@visibleForTesting
static List<String> buildCoverResizeArguments({
required String inputPath,
required String outputPath,
required int maxDimension,
}) {
if (maxDimension < 64 || maxDimension > 8192) {
throw ArgumentError.value(
maxDimension,
'maxDimension',
'Must be between 64 and 8192 pixels',
);
}
return [
'-y',
'-i',
inputPath,
'-map_metadata',
'-1',
'-an',
'-sn',
'-vf',
'scale=$maxDimension:$maxDimension:'
'force_original_aspect_ratio=decrease:flags=lanczos',
'-frames:v',
'1',
'-update',
'1',
'-q:v',
'2',
outputPath,
];
}
/// Resizes cover art so its longest edge is [maxDimension].
///
/// FFmpeg is allowed to upscale smaller sources because this is an explicit
/// user choice. Aspect ratio is preserved and the result is written as the
/// format implied by [outputPath].
static Future<bool> resizeCoverArt({
required String inputPath,
required String outputPath,
required int maxDimension,
}) async {
try {
final source = File(inputPath);
if (!await source.exists()) return false;
final result = await _executeWithArguments(
buildCoverResizeArguments(
inputPath: inputPath,
outputPath: outputPath,
maxDimension: maxDimension,
),
);
final output = File(outputPath);
final hasOutput = await output.exists() && await output.length() > 0;
if (!result.success || !hasOutput) {
_log.w(
'Cover resize failed (${result.returnCode}): '
'${_previewCommandForLog(result.output)}',
);
try {
if (await output.exists()) await output.delete();
} catch (_) {}
return false;
}
return true;
} catch (e) {
_log.w('Cover resize failed: $e');
try {
final output = File(outputPath);
if (await output.exists()) await output.delete();
} catch (_) {}
return false;
}
}
static Future<String?> probePrimaryAudioCodec(String filePath) async {
try {
final session = await FFprobeKit.getMediaInformation(filePath);