190 lines
5.8 KiB
Dart
190 lines
5.8 KiB
Dart
import 'package:get/get.dart';
|
|
import 'package:go_router/go_router.dart';
|
|
import 'package:html/dom.dart' hide Text;
|
|
import 'package:rhythm_box/services/sort.dart';
|
|
import 'package:spotify/spotify.dart';
|
|
|
|
import 'package:html/parser.dart' as parser;
|
|
|
|
import 'dart:async';
|
|
|
|
import 'package:flutter/material.dart' hide Element;
|
|
|
|
abstract class ServiceUtils {
|
|
static final _englishMatcherRegex = RegExp(
|
|
"^[a-zA-Z0-9\\s!\"#\$%&\\'()*+,-.\\/:;<=>?@\\[\\]^_`{|}~]*\$",
|
|
);
|
|
static bool onlyContainsEnglish(String text) {
|
|
return _englishMatcherRegex.hasMatch(text);
|
|
}
|
|
|
|
static String clearArtistsOfTitle(String title, List<String> artists) {
|
|
return title
|
|
.replaceAll(RegExp(artists.join('|'), caseSensitive: false), '')
|
|
.trim();
|
|
}
|
|
|
|
static String getTitle(
|
|
String title, {
|
|
List<String> artists = const [],
|
|
bool onlyCleanArtist = false,
|
|
}) {
|
|
final match = RegExp(r'(?<=\().+?(?=\))').firstMatch(title)?.group(0);
|
|
final artistInBracket =
|
|
artists.any((artist) => match?.contains(artist) ?? false);
|
|
|
|
if (artistInBracket) {
|
|
title = title.replaceAll(
|
|
RegExp(' *\\([^)]*\\) *'),
|
|
'',
|
|
);
|
|
}
|
|
|
|
title = clearArtistsOfTitle(title, artists);
|
|
if (onlyCleanArtist) {
|
|
artists = [];
|
|
}
|
|
|
|
return "$title ${artists.map((e) => e.replaceAll(",", " ")).join(", ")}"
|
|
.toLowerCase()
|
|
.replaceAll(RegExp(r'\s*\[[^\]]*]'), ' ')
|
|
.replaceAll(RegExp(r'\sfeat\.|\sft\.'), ' ')
|
|
.replaceAll(RegExp(r'\s+'), ' ')
|
|
.trim();
|
|
}
|
|
|
|
static Future<String?> extractLyrics(Uri url) async {
|
|
final client = GetConnect();
|
|
final response = await client.get(url.toString());
|
|
|
|
Document document = parser.parse(response.body);
|
|
String? lyrics = document.querySelector('div.lyrics')?.text.trim();
|
|
if (lyrics == null) {
|
|
lyrics = '';
|
|
document
|
|
.querySelectorAll('div[class^="Lyrics__Container"]')
|
|
.forEach((element) {
|
|
if (element.text.trim().isNotEmpty) {
|
|
final snippet = element.innerHtml.replaceAll('<br>', '\n').replaceAll(
|
|
RegExp('<(?!\\s*br\\s*\\/?)[^>]+>', caseSensitive: false),
|
|
'',
|
|
);
|
|
final el = document.createElement('textarea');
|
|
el.innerHtml = snippet;
|
|
lyrics = '$lyrics${el.text.trim()}\n\n';
|
|
}
|
|
});
|
|
}
|
|
|
|
return lyrics;
|
|
}
|
|
|
|
static void navigate(BuildContext context, String location, {Object? extra}) {
|
|
if (GoRouterState.of(context).matchedLocation == location) return;
|
|
GoRouter.of(context).go(location, extra: extra);
|
|
}
|
|
|
|
static void navigateNamed(
|
|
BuildContext context,
|
|
String name, {
|
|
Object? extra,
|
|
Map<String, String>? pathParameters,
|
|
Map<String, dynamic>? queryParameters,
|
|
}) {
|
|
if (GoRouterState.of(context).matchedLocation == name) return;
|
|
GoRouter.of(context).goNamed(
|
|
name,
|
|
pathParameters: pathParameters ?? const {},
|
|
queryParameters: queryParameters ?? const {},
|
|
extra: extra,
|
|
);
|
|
}
|
|
|
|
static void push(BuildContext context, String location, {Object? extra}) {
|
|
final router = GoRouter.of(context);
|
|
final routerState = GoRouterState.of(context);
|
|
final routerStack = router.routerDelegate.currentConfiguration.matches
|
|
.map((e) => e.matchedLocation);
|
|
|
|
if (routerState.matchedLocation == location ||
|
|
routerStack.contains(location)) return;
|
|
router.push(location, extra: extra);
|
|
}
|
|
|
|
static void pushNamed(
|
|
BuildContext context,
|
|
String name, {
|
|
Object? extra,
|
|
Map<String, String> pathParameters = const {},
|
|
Map<String, String> queryParameters = const {},
|
|
}) {
|
|
final router = GoRouter.of(context);
|
|
final routerState = GoRouterState.of(context);
|
|
final routerStack = router.routerDelegate.currentConfiguration.matches
|
|
.map((e) => e.matchedLocation);
|
|
|
|
final nameLocation = routerState.namedLocation(
|
|
name,
|
|
pathParameters: pathParameters,
|
|
queryParameters: queryParameters,
|
|
);
|
|
|
|
if (routerState.matchedLocation == nameLocation ||
|
|
routerStack.contains(nameLocation)) {
|
|
return;
|
|
}
|
|
router.pushNamed(
|
|
name,
|
|
pathParameters: pathParameters,
|
|
queryParameters: queryParameters,
|
|
extra: extra,
|
|
);
|
|
}
|
|
|
|
static DateTime parseSpotifyAlbumDate(AlbumSimple? album) {
|
|
if (album == null || album.releaseDate == null) {
|
|
return DateTime.parse('1975-01-01');
|
|
}
|
|
|
|
switch (album.releaseDatePrecision ?? DatePrecision.year) {
|
|
case DatePrecision.day:
|
|
return DateTime.parse(album.releaseDate!);
|
|
case DatePrecision.month:
|
|
return DateTime.parse('${album.releaseDate}-01');
|
|
case DatePrecision.year:
|
|
return DateTime.parse('${album.releaseDate}-01-01');
|
|
}
|
|
}
|
|
|
|
static List<T> sortTracks<T extends Track>(List<T> tracks, SortBy sortBy) {
|
|
if (sortBy == SortBy.none) return tracks;
|
|
return List<T>.from(tracks)
|
|
..sort((a, b) {
|
|
switch (sortBy) {
|
|
case SortBy.ascending:
|
|
return a.name?.compareTo(b.name ?? '') ?? 0;
|
|
case SortBy.descending:
|
|
return b.name?.compareTo(a.name ?? '') ?? 0;
|
|
case SortBy.newest:
|
|
final aDate = parseSpotifyAlbumDate(a.album);
|
|
final bDate = parseSpotifyAlbumDate(b.album);
|
|
return bDate.compareTo(aDate);
|
|
case SortBy.oldest:
|
|
final aDate = parseSpotifyAlbumDate(a.album);
|
|
final bDate = parseSpotifyAlbumDate(b.album);
|
|
return aDate.compareTo(bDate);
|
|
case SortBy.duration:
|
|
return a.durationMs?.compareTo(b.durationMs ?? 0) ?? 0;
|
|
case SortBy.artist:
|
|
return a.artists?.first.name
|
|
?.compareTo(b.artists?.first.name ?? '') ??
|
|
0;
|
|
case SortBy.album:
|
|
return a.album?.name?.compareTo(b.album?.name ?? '') ?? 0;
|
|
default:
|
|
return 0;
|
|
}
|
|
});
|
|
}
|
|
}
|