mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-09-02 16:20:57 +02:00
feat(library): add lyrics filtering and metadata actions
This commit is contained in:
@@ -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)}';
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user