feat(library): add lyrics filtering and metadata actions

This commit is contained in:
zarzet
2026-08-27 01:44:06 +07:00
parent 04c42bd511
commit 1a7c20111a
60 changed files with 1454 additions and 276 deletions
+27
View File
@@ -0,0 +1,27 @@
final RegExp _canonicalIsrcPattern = RegExp(r'^[A-Z]{2}[A-Z0-9]{3}\d{7}$');
String normalizeIsrc(String? value) =>
(value ?? '').trim().toUpperCase().replaceAll(RegExp(r'[-\s]'), '');
/// Returns the compact canonical value only when [value] is a valid ISRC.
/// Invalid provider/tag values remain visible and copyable without mutation.
String canonicalIsrcForCopy(String? value) {
final original = value?.trim() ?? '';
final normalized = normalizeIsrc(original);
return _canonicalIsrcPattern.hasMatch(normalized) ? normalized : original;
}
/// Formats a valid canonical ISRC for display without changing stored tags.
///
/// Example: `AA9BQ2200061` becomes `AA-9BQ-22-00061`.
String formatIsrcForDisplay(String? value) {
final original = value?.trim() ?? '';
final canonical = canonicalIsrcForCopy(original);
if (!_canonicalIsrcPattern.hasMatch(canonical)) {
return original;
}
return '${canonical.substring(0, 2)}-'
'${canonical.substring(2, 5)}-'
'${canonical.substring(5, 7)}-'
'${canonical.substring(7)}';
}
+32
View File
@@ -0,0 +1,32 @@
/// Adds every visible item between [anchor] and [target] to [selected].
///
/// Dart's default [Set] preserves insertion order, so walking backwards also
/// preserves the direction chosen by the user when results are later copied,
/// shared, or batch processed.
T addOrderedSelectionRange<T>({
required Set<T> selected,
required List<T> visibleItems,
required T target,
T? anchor,
}) {
final anchorIndex = anchor == null ? -1 : visibleItems.indexOf(anchor);
final targetIndex = visibleItems.indexOf(target);
if (anchorIndex < 0 || targetIndex < 0) {
selected.add(target);
return target;
}
final step = targetIndex >= anchorIndex ? 1 : -1;
for (var index = anchorIndex; ; index += step) {
selected.add(visibleItems[index]);
if (index == targetIndex) break;
}
return target;
}
/// Toggles one item and returns the next range-selection anchor.
T? toggleOrderedSelection<T>({required Set<T> selected, required T target}) {
if (selected.add(target)) return target;
selected.remove(target);
return selected.isEmpty ? null : selected.last;
}