🎨 Prefer single quote
This commit is contained in:
parent
e7ea852725
commit
95b04adede
@ -21,6 +21,7 @@ linter:
|
||||
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
|
||||
# producing the lint.
|
||||
rules:
|
||||
prefer_single_quotes: true
|
||||
# avoid_print: false # Uncomment to disable the `avoid_print` rule
|
||||
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
|
||||
|
||||
|
@ -1,192 +0,0 @@
|
||||
/// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
/// *****************************************************
|
||||
/// FlutterGen
|
||||
/// *****************************************************
|
||||
|
||||
// coverage:ignore-file
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: directives_ordering,unnecessary_import,implicit_dynamic_list_literal,deprecated_member_use
|
||||
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
class $AssetsLogosGen {
|
||||
const $AssetsLogosGen();
|
||||
|
||||
/// File path: assets/logos/songlink-transparent.png
|
||||
AssetGenImage get songlinkTransparent =>
|
||||
const AssetGenImage('assets/logos/songlink-transparent.png');
|
||||
|
||||
/// File path: assets/logos/songlink.png
|
||||
AssetGenImage get songlink =>
|
||||
const AssetGenImage('assets/logos/songlink.png');
|
||||
|
||||
/// List of all assets
|
||||
List<AssetGenImage> get values => [songlinkTransparent, songlink];
|
||||
}
|
||||
|
||||
class $AssetsTutorialGen {
|
||||
const $AssetsTutorialGen();
|
||||
|
||||
/// File path: assets/tutorial/step-1.png
|
||||
AssetGenImage get step1 => const AssetGenImage('assets/tutorial/step-1.png');
|
||||
|
||||
/// File path: assets/tutorial/step-2.png
|
||||
AssetGenImage get step2 => const AssetGenImage('assets/tutorial/step-2.png');
|
||||
|
||||
/// File path: assets/tutorial/step-3.png
|
||||
AssetGenImage get step3 => const AssetGenImage('assets/tutorial/step-3.png');
|
||||
|
||||
/// List of all assets
|
||||
List<AssetGenImage> get values => [step1, step2, step3];
|
||||
}
|
||||
|
||||
class Assets {
|
||||
Assets._();
|
||||
|
||||
static const AssetGenImage albumPlaceholder =
|
||||
AssetGenImage('assets/album-placeholder.png');
|
||||
static const AssetGenImage bengaliPatternsBg =
|
||||
AssetGenImage('assets/bengali-patterns-bg.jpg');
|
||||
static const AssetGenImage branding = AssetGenImage('assets/branding.png');
|
||||
static const AssetGenImage emptyBox = AssetGenImage('assets/empty_box.png');
|
||||
static const AssetGenImage jiosaavn = AssetGenImage('assets/jiosaavn.png');
|
||||
static const AssetGenImage likedTracks =
|
||||
AssetGenImage('assets/liked-tracks.jpg');
|
||||
static const $AssetsLogosGen logos = $AssetsLogosGen();
|
||||
static const AssetGenImage placeholder =
|
||||
AssetGenImage('assets/placeholder.png');
|
||||
static const AssetGenImage rhythm_boxHeroBanner =
|
||||
AssetGenImage('assets/rhythm_box-hero-banner.png');
|
||||
static const AssetGenImage rhythm_boxLogoForeground =
|
||||
AssetGenImage('assets/rhythm_box-logo-foreground.jpg');
|
||||
static const String rhythm_boxLogoIco = 'assets/rhythm_box-logo.ico';
|
||||
static const AssetGenImage rhythm_boxLogoPng =
|
||||
AssetGenImage('assets/rhythm_box-logo.png');
|
||||
static const String rhythm_boxLogoSvg = 'assets/rhythm_box-logo.svg';
|
||||
static const AssetGenImage rhythm_boxLogoAndroid12 =
|
||||
AssetGenImage('assets/rhythm_box-logo_android12.png');
|
||||
static const AssetGenImage rhythm_boxNightlyLogoForeground =
|
||||
AssetGenImage('assets/rhythm_box-nightly-logo-foreground.jpg');
|
||||
static const AssetGenImage rhythm_boxNightlyLogoPng =
|
||||
AssetGenImage('assets/rhythm_box-nightly-logo.png');
|
||||
static const String rhythm_boxNightlyLogoSvg =
|
||||
'assets/rhythm_box-nightly-logo.svg';
|
||||
static const AssetGenImage rhythm_boxNightlyLogoAndroid12 =
|
||||
AssetGenImage('assets/rhythm_box-nightly-logo_android12.png');
|
||||
static const AssetGenImage rhythm_boxScreenshot =
|
||||
AssetGenImage('assets/rhythm_box-screenshot.png');
|
||||
static const AssetGenImage rhythm_boxTallCapsule =
|
||||
AssetGenImage('assets/rhythm_box-tall-capsule.png');
|
||||
static const AssetGenImage rhythm_boxWideCapsuleLarge =
|
||||
AssetGenImage('assets/rhythm_box-wide-capsule-large.png');
|
||||
static const AssetGenImage rhythm_boxWideCapsuleSmall =
|
||||
AssetGenImage('assets/rhythm_box-wide-capsule-small.png');
|
||||
static const AssetGenImage rhythm_boxBanner =
|
||||
AssetGenImage('assets/rhythm_box_banner.png');
|
||||
static const AssetGenImage success = AssetGenImage('assets/success.png');
|
||||
static const $AssetsTutorialGen tutorial = $AssetsTutorialGen();
|
||||
static const AssetGenImage userPlaceholder =
|
||||
AssetGenImage('assets/user-placeholder.png');
|
||||
|
||||
/// List of all assets
|
||||
static List<dynamic> get values => [
|
||||
albumPlaceholder,
|
||||
bengaliPatternsBg,
|
||||
branding,
|
||||
emptyBox,
|
||||
jiosaavn,
|
||||
likedTracks,
|
||||
placeholder,
|
||||
rhythm_boxHeroBanner,
|
||||
rhythm_boxLogoForeground,
|
||||
rhythm_boxLogoIco,
|
||||
rhythm_boxLogoPng,
|
||||
rhythm_boxLogoSvg,
|
||||
rhythm_boxLogoAndroid12,
|
||||
rhythm_boxNightlyLogoForeground,
|
||||
rhythm_boxNightlyLogoPng,
|
||||
rhythm_boxNightlyLogoSvg,
|
||||
rhythm_boxNightlyLogoAndroid12,
|
||||
rhythm_boxScreenshot,
|
||||
rhythm_boxTallCapsule,
|
||||
rhythm_boxWideCapsuleLarge,
|
||||
rhythm_boxWideCapsuleSmall,
|
||||
rhythm_boxBanner,
|
||||
success,
|
||||
userPlaceholder
|
||||
];
|
||||
}
|
||||
|
||||
class AssetGenImage {
|
||||
const AssetGenImage(this._assetName);
|
||||
|
||||
final String _assetName;
|
||||
|
||||
Image image({
|
||||
Key? key,
|
||||
AssetBundle? bundle,
|
||||
ImageFrameBuilder? frameBuilder,
|
||||
ImageErrorWidgetBuilder? errorBuilder,
|
||||
String? semanticLabel,
|
||||
bool excludeFromSemantics = false,
|
||||
double? scale,
|
||||
double? width,
|
||||
double? height,
|
||||
Color? color,
|
||||
Animation<double>? opacity,
|
||||
BlendMode? colorBlendMode,
|
||||
BoxFit? fit,
|
||||
AlignmentGeometry alignment = Alignment.center,
|
||||
ImageRepeat repeat = ImageRepeat.noRepeat,
|
||||
Rect? centerSlice,
|
||||
bool matchTextDirection = false,
|
||||
bool gaplessPlayback = false,
|
||||
bool isAntiAlias = false,
|
||||
String? package,
|
||||
FilterQuality filterQuality = FilterQuality.low,
|
||||
int? cacheWidth,
|
||||
int? cacheHeight,
|
||||
}) {
|
||||
return Image.asset(
|
||||
_assetName,
|
||||
key: key,
|
||||
bundle: bundle,
|
||||
frameBuilder: frameBuilder,
|
||||
errorBuilder: errorBuilder,
|
||||
semanticLabel: semanticLabel,
|
||||
excludeFromSemantics: excludeFromSemantics,
|
||||
scale: scale,
|
||||
width: width,
|
||||
height: height,
|
||||
color: color,
|
||||
opacity: opacity,
|
||||
colorBlendMode: colorBlendMode,
|
||||
fit: fit,
|
||||
alignment: alignment,
|
||||
repeat: repeat,
|
||||
centerSlice: centerSlice,
|
||||
matchTextDirection: matchTextDirection,
|
||||
gaplessPlayback: gaplessPlayback,
|
||||
isAntiAlias: isAntiAlias,
|
||||
package: package,
|
||||
filterQuality: filterQuality,
|
||||
cacheWidth: cacheWidth,
|
||||
cacheHeight: cacheHeight,
|
||||
);
|
||||
}
|
||||
|
||||
ImageProvider provider({
|
||||
AssetBundle? bundle,
|
||||
String? package,
|
||||
}) {
|
||||
return AssetImage(
|
||||
_assetName,
|
||||
bundle: bundle,
|
||||
package: package,
|
||||
);
|
||||
}
|
||||
|
||||
String get path => _assetName;
|
||||
|
||||
String get keyName => _assetName;
|
||||
}
|
@ -3,6 +3,6 @@ import 'package:intl/intl.dart';
|
||||
final compactNumberFormatter = NumberFormat.compact();
|
||||
final usdFormatter = NumberFormat.compactCurrency(
|
||||
locale: 'en-US',
|
||||
symbol: r"$",
|
||||
symbol: r'$',
|
||||
decimalDigits: 2,
|
||||
);
|
||||
|
@ -9,7 +9,7 @@ class ISOLanguageName {
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return "$name ($nativeName)";
|
||||
return '$name ($nativeName)';
|
||||
}
|
||||
}
|
||||
|
||||
@ -41,9 +41,9 @@ abstract class LanguageLocals {
|
||||
// name: "Amharic",
|
||||
// nativeName: "አማርኛ",
|
||||
// ),
|
||||
"ar": const ISOLanguageName(
|
||||
name: "Arabic",
|
||||
nativeName: "العربية",
|
||||
'ar': const ISOLanguageName(
|
||||
name: 'Arabic',
|
||||
nativeName: 'العربية',
|
||||
),
|
||||
// "an": const ISOLanguageName(
|
||||
// name: "Aragonese",
|
||||
@ -81,17 +81,17 @@ abstract class LanguageLocals {
|
||||
// name: "Bashkir",
|
||||
// nativeName: "башҡорт теле",
|
||||
// ),
|
||||
"eu": const ISOLanguageName(
|
||||
name: "Basque",
|
||||
nativeName: "Euskara",
|
||||
'eu': const ISOLanguageName(
|
||||
name: 'Basque',
|
||||
nativeName: 'Euskara',
|
||||
),
|
||||
// "be": const ISOLanguageName(
|
||||
// name: "Belarusian",
|
||||
// nativeName: "Беларуская",
|
||||
// ),
|
||||
"bn": const ISOLanguageName(
|
||||
name: "Bengali",
|
||||
nativeName: "বাংলা",
|
||||
'bn': const ISOLanguageName(
|
||||
name: 'Bengali',
|
||||
nativeName: 'বাংলা',
|
||||
),
|
||||
// "bh": const ISOLanguageName(
|
||||
// name: "Bihari",
|
||||
@ -117,9 +117,9 @@ abstract class LanguageLocals {
|
||||
// name: "Burmese",
|
||||
// nativeName: "ဗမာစာ",
|
||||
// ),
|
||||
"ca": const ISOLanguageName(
|
||||
name: "Catalan",
|
||||
nativeName: "Català",
|
||||
'ca': const ISOLanguageName(
|
||||
name: 'Catalan',
|
||||
nativeName: 'Català',
|
||||
),
|
||||
// "ch": const ISOLanguageName(
|
||||
// name: "Chamorro",
|
||||
@ -133,9 +133,9 @@ abstract class LanguageLocals {
|
||||
// name: "Chichewa",
|
||||
// nativeName: "chiCheŵa",
|
||||
// ),
|
||||
"zh": const ISOLanguageName(
|
||||
name: "Simplified Chinese",
|
||||
nativeName: "简体中文",
|
||||
'zh': const ISOLanguageName(
|
||||
name: 'Simplified Chinese',
|
||||
nativeName: '简体中文',
|
||||
),
|
||||
// "cv": const ISOLanguageName(
|
||||
// name: "Chuvash",
|
||||
@ -157,9 +157,9 @@ abstract class LanguageLocals {
|
||||
// name: "Croatian",
|
||||
// nativeName: "hrvatski",
|
||||
// ),
|
||||
"cs": const ISOLanguageName(
|
||||
name: "Czech",
|
||||
nativeName: "česky, čeština",
|
||||
'cs': const ISOLanguageName(
|
||||
name: 'Czech',
|
||||
nativeName: 'česky, čeština',
|
||||
),
|
||||
// "da": const ISOLanguageName(
|
||||
// name: "Danish",
|
||||
@ -169,13 +169,13 @@ abstract class LanguageLocals {
|
||||
// name: "Maldivian;",
|
||||
// nativeName: "ދިވެހި",
|
||||
// ),
|
||||
"nl": const ISOLanguageName(
|
||||
name: "Dutch",
|
||||
nativeName: "Nederlands",
|
||||
'nl': const ISOLanguageName(
|
||||
name: 'Dutch',
|
||||
nativeName: 'Nederlands',
|
||||
),
|
||||
"en": const ISOLanguageName(
|
||||
name: "English",
|
||||
nativeName: "English",
|
||||
'en': const ISOLanguageName(
|
||||
name: 'English',
|
||||
nativeName: 'English',
|
||||
),
|
||||
// "eo": const ISOLanguageName(
|
||||
// name: "Esperanto",
|
||||
@ -197,13 +197,13 @@ abstract class LanguageLocals {
|
||||
// name: "Fijian",
|
||||
// nativeName: "vosa Vakaviti",
|
||||
// ),
|
||||
"fi": const ISOLanguageName(
|
||||
name: "Finnish",
|
||||
nativeName: "suomi",
|
||||
'fi': const ISOLanguageName(
|
||||
name: 'Finnish',
|
||||
nativeName: 'suomi',
|
||||
),
|
||||
"fr": const ISOLanguageName(
|
||||
name: "French",
|
||||
nativeName: "français",
|
||||
'fr': const ISOLanguageName(
|
||||
name: 'French',
|
||||
nativeName: 'français',
|
||||
),
|
||||
// "ff": const ISOLanguageName(
|
||||
// name: "Fula; Fulah; Pulaar; Pular",
|
||||
@ -213,13 +213,13 @@ abstract class LanguageLocals {
|
||||
// name: "Galician",
|
||||
// nativeName: "Galego",
|
||||
// ),
|
||||
"ka": const ISOLanguageName(
|
||||
name: "Georgian",
|
||||
nativeName: "ქართული",
|
||||
'ka': const ISOLanguageName(
|
||||
name: 'Georgian',
|
||||
nativeName: 'ქართული',
|
||||
),
|
||||
"de": const ISOLanguageName(
|
||||
name: "German",
|
||||
nativeName: "Deutsch",
|
||||
'de': const ISOLanguageName(
|
||||
name: 'German',
|
||||
nativeName: 'Deutsch',
|
||||
),
|
||||
// "el": const ISOLanguageName(
|
||||
// name: "Greek, Modern",
|
||||
@ -249,9 +249,9 @@ abstract class LanguageLocals {
|
||||
// name: "Herero",
|
||||
// nativeName: "Otjiherero",
|
||||
// ),
|
||||
"hi": const ISOLanguageName(
|
||||
name: "Hindi",
|
||||
nativeName: "हिन्दी, हिंदी",
|
||||
'hi': const ISOLanguageName(
|
||||
name: 'Hindi',
|
||||
nativeName: 'हिन्दी, हिंदी',
|
||||
),
|
||||
// "ho": const ISOLanguageName(
|
||||
// name: "Hiri Motu",
|
||||
@ -265,9 +265,9 @@ abstract class LanguageLocals {
|
||||
// name: "Interlingua",
|
||||
// nativeName: "Interlingua",
|
||||
// ),
|
||||
"id": const ISOLanguageName(
|
||||
name: "Indonesian",
|
||||
nativeName: "Bahasa Indonesia",
|
||||
'id': const ISOLanguageName(
|
||||
name: 'Indonesian',
|
||||
nativeName: 'Bahasa Indonesia',
|
||||
),
|
||||
// "ie": const ISOLanguageName(
|
||||
// name: "Interlingue",
|
||||
@ -293,17 +293,17 @@ abstract class LanguageLocals {
|
||||
// name: "Icelandic",
|
||||
// nativeName: "Íslenska",
|
||||
// ),
|
||||
"it": const ISOLanguageName(
|
||||
name: "Italian",
|
||||
nativeName: "Italiano",
|
||||
'it': const ISOLanguageName(
|
||||
name: 'Italian',
|
||||
nativeName: 'Italiano',
|
||||
),
|
||||
// "iu": const ISOLanguageName(
|
||||
// name: "Inuktitut",
|
||||
// nativeName: "ᐃᓄᒃᑎᑐᑦ",
|
||||
// ),
|
||||
"ja": const ISOLanguageName(
|
||||
name: "Japanese",
|
||||
nativeName: "日本語",
|
||||
'ja': const ISOLanguageName(
|
||||
name: 'Japanese',
|
||||
nativeName: '日本語',
|
||||
),
|
||||
// "jv": const ISOLanguageName(
|
||||
// name: "Javanese",
|
||||
@ -353,9 +353,9 @@ abstract class LanguageLocals {
|
||||
// name: "Kongo",
|
||||
// nativeName: "KiKongo",
|
||||
// ),
|
||||
"ko": const ISOLanguageName(
|
||||
name: "Korean",
|
||||
nativeName: "한국어 (韓國語), 조선말 (朝鮮語)",
|
||||
'ko': const ISOLanguageName(
|
||||
name: 'Korean',
|
||||
nativeName: '한국어 (韓國語), 조선말 (朝鮮語)',
|
||||
),
|
||||
// "ku": const ISOLanguageName(
|
||||
// name: "Kurdish",
|
||||
@ -457,9 +457,9 @@ abstract class LanguageLocals {
|
||||
// name: "North Ndebele",
|
||||
// nativeName: "isiNdebele",
|
||||
// ),
|
||||
"ne": const ISOLanguageName(
|
||||
name: "Nepali",
|
||||
nativeName: "नेपाली",
|
||||
'ne': const ISOLanguageName(
|
||||
name: 'Nepali',
|
||||
nativeName: 'नेपाली',
|
||||
),
|
||||
// "ng": const ISOLanguageName(
|
||||
// name: "Ndonga",
|
||||
@ -513,21 +513,21 @@ abstract class LanguageLocals {
|
||||
// name: "Pāli",
|
||||
// nativeName: "पाऴि",
|
||||
// ),
|
||||
"fa": const ISOLanguageName(
|
||||
name: "Persian",
|
||||
nativeName: "فارسی",
|
||||
'fa': const ISOLanguageName(
|
||||
name: 'Persian',
|
||||
nativeName: 'فارسی',
|
||||
),
|
||||
"pl": const ISOLanguageName(
|
||||
name: "Polish",
|
||||
nativeName: "polski",
|
||||
'pl': const ISOLanguageName(
|
||||
name: 'Polish',
|
||||
nativeName: 'polski',
|
||||
),
|
||||
// "ps": const ISOLanguageName(
|
||||
// name: "Pashto, Pushto",
|
||||
// nativeName: "پښتو",
|
||||
// ),
|
||||
"pt": const ISOLanguageName(
|
||||
name: "Portuguese",
|
||||
nativeName: "Português",
|
||||
'pt': const ISOLanguageName(
|
||||
name: 'Portuguese',
|
||||
nativeName: 'Português',
|
||||
),
|
||||
// "qu": const ISOLanguageName(
|
||||
// name: "Quechua",
|
||||
@ -545,9 +545,9 @@ abstract class LanguageLocals {
|
||||
// name: "Romanian, Moldavian, Moldovan",
|
||||
// nativeName: "română",
|
||||
// ),
|
||||
"ru": const ISOLanguageName(
|
||||
name: "Russian",
|
||||
nativeName: "русский язык",
|
||||
'ru': const ISOLanguageName(
|
||||
name: 'Russian',
|
||||
nativeName: 'русский язык',
|
||||
),
|
||||
// "sa": const ISOLanguageName(
|
||||
// name: "Sanskrit (Saṁskṛta)",
|
||||
@ -605,9 +605,9 @@ abstract class LanguageLocals {
|
||||
// name: "Southern Sotho",
|
||||
// nativeName: "Sesotho",
|
||||
// ),
|
||||
"es": const ISOLanguageName(
|
||||
name: "Spanish",
|
||||
nativeName: "español",
|
||||
'es': const ISOLanguageName(
|
||||
name: 'Spanish',
|
||||
nativeName: 'español',
|
||||
),
|
||||
// "su": const ISOLanguageName(
|
||||
// name: "Sundanese",
|
||||
@ -637,9 +637,9 @@ abstract class LanguageLocals {
|
||||
// name: "Tajik",
|
||||
// nativeName: "тоҷикӣ, toğikī, تاجیکی",
|
||||
// ),
|
||||
"th": const ISOLanguageName(
|
||||
name: "Thai",
|
||||
nativeName: "ไทย",
|
||||
'th': const ISOLanguageName(
|
||||
name: 'Thai',
|
||||
nativeName: 'ไทย',
|
||||
),
|
||||
// "ti": const ISOLanguageName(
|
||||
// name: "Tigrinya",
|
||||
@ -665,9 +665,9 @@ abstract class LanguageLocals {
|
||||
// name: "Tonga (Tonga Islands)",
|
||||
// nativeName: "faka Tonga",
|
||||
// ),
|
||||
"tr": const ISOLanguageName(
|
||||
name: "Turkish",
|
||||
nativeName: "Türkçe",
|
||||
'tr': const ISOLanguageName(
|
||||
name: 'Turkish',
|
||||
nativeName: 'Türkçe',
|
||||
),
|
||||
// "ts": const ISOLanguageName(
|
||||
// name: "Tsonga",
|
||||
@ -689,9 +689,9 @@ abstract class LanguageLocals {
|
||||
// name: "Uighur, Uyghur",
|
||||
// nativeName: "Uyƣurqə, ئۇيغۇرچە",
|
||||
// ),
|
||||
"uk": const ISOLanguageName(
|
||||
name: "Ukrainian",
|
||||
nativeName: "українська",
|
||||
'uk': const ISOLanguageName(
|
||||
name: 'Ukrainian',
|
||||
nativeName: 'українська',
|
||||
),
|
||||
// "ur": const ISOLanguageName(
|
||||
// name: "Urdu",
|
||||
@ -705,9 +705,9 @@ abstract class LanguageLocals {
|
||||
// name: "Venda",
|
||||
// nativeName: "Tshivenḓa",
|
||||
// ),
|
||||
"vi": const ISOLanguageName(
|
||||
name: "Vietnamese",
|
||||
nativeName: "Tiếng Việt",
|
||||
'vi': const ISOLanguageName(
|
||||
name: 'Vietnamese',
|
||||
nativeName: 'Tiếng Việt',
|
||||
),
|
||||
// "vo": const ISOLanguageName(
|
||||
// name: "Volapük",
|
||||
@ -751,7 +751,7 @@ abstract class LanguageLocals {
|
||||
if (isoLangs.containsKey(key)) {
|
||||
return isoLangs[key]!;
|
||||
} else {
|
||||
throw Exception("Language key incorrect");
|
||||
throw Exception('Language key incorrect');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,187 +3,187 @@
|
||||
import 'package:spotify/spotify.dart';
|
||||
|
||||
final spotifyMarkets = [
|
||||
(Market.AL, "Albania (AL)"),
|
||||
(Market.DZ, "Algeria (DZ)"),
|
||||
(Market.AD, "Andorra (AD)"),
|
||||
(Market.AO, "Angola (AO)"),
|
||||
(Market.AG, "Antigua and Barbuda (AG)"),
|
||||
(Market.AR, "Argentina (AR)"),
|
||||
(Market.AM, "Armenia (AM)"),
|
||||
(Market.AU, "Australia (AU)"),
|
||||
(Market.AT, "Austria (AT)"),
|
||||
(Market.AZ, "Azerbaijan (AZ)"),
|
||||
(Market.BH, "Bahrain (BH)"),
|
||||
(Market.BD, "Bangladesh (BD)"),
|
||||
(Market.BB, "Barbados (BB)"),
|
||||
(Market.BY, "Belarus (BY)"),
|
||||
(Market.BE, "Belgium (BE)"),
|
||||
(Market.BZ, "Belize (BZ)"),
|
||||
(Market.BJ, "Benin (BJ)"),
|
||||
(Market.BT, "Bhutan (BT)"),
|
||||
(Market.BO, "Bolivia (BO)"),
|
||||
(Market.BA, "Bosnia and Herzegovina (BA)"),
|
||||
(Market.BW, "Botswana (BW)"),
|
||||
(Market.BR, "Brazil (BR)"),
|
||||
(Market.BN, "Brunei Darussalam (BN)"),
|
||||
(Market.BG, "Bulgaria (BG)"),
|
||||
(Market.BF, "Burkina Faso (BF)"),
|
||||
(Market.BI, "Burundi (BI)"),
|
||||
(Market.CV, "Cabo Verde / Cape Verde (CV)"),
|
||||
(Market.KH, "Cambodia (KH)"),
|
||||
(Market.CM, "Cameroon (CM)"),
|
||||
(Market.CA, "Canada (CA)"),
|
||||
(Market.TD, "Chad (TD)"),
|
||||
(Market.CL, "Chile (CL)"),
|
||||
(Market.CO, "Colombia (CO)"),
|
||||
(Market.KM, "Comoros (KM)"),
|
||||
(Market.CR, "Costa Rica (CR)"),
|
||||
(Market.HR, "Croatia (HR)"),
|
||||
(Market.CW, "Curaçao (CW)"),
|
||||
(Market.CY, "Cyprus (CY)"),
|
||||
(Market.CZ, "Czech Republic (CZ)"),
|
||||
(Market.CI, "Ivory Coast (CI)"),
|
||||
(Market.CD, "Congo (CD)"),
|
||||
(Market.DK, "Denmark (DK)"),
|
||||
(Market.DJ, "Djibouti (DJ)"),
|
||||
(Market.DM, "Dominica (DM)"),
|
||||
(Market.DO, "Dominican Republic (DO)"),
|
||||
(Market.EC, "Ecuador (EC)"),
|
||||
(Market.EG, "Egypt (EG)"),
|
||||
(Market.SV, "El Salvador (SV)"),
|
||||
(Market.GQ, "Equatorial Guinea (GQ)"),
|
||||
(Market.EE, "Estonia (EE)"),
|
||||
(Market.SZ, "Eswatini (SZ)"),
|
||||
(Market.FJ, "Fiji (FJ)"),
|
||||
(Market.FI, "Finland (FI)"),
|
||||
(Market.FR, "France (FR)"),
|
||||
(Market.GA, "Gabon (GA)"),
|
||||
(Market.GE, "Georgia (GE)"),
|
||||
(Market.DE, "Germany (DE)"),
|
||||
(Market.GH, "Ghana (GH)"),
|
||||
(Market.GR, "Greece (GR)"),
|
||||
(Market.GD, "Grenada (GD)"),
|
||||
(Market.GT, "Guatemala (GT)"),
|
||||
(Market.GN, "Guinea (GN)"),
|
||||
(Market.GW, "Guinea-Bissau (GW)"),
|
||||
(Market.GY, "Guyana (GY)"),
|
||||
(Market.HT, "Haiti (HT)"),
|
||||
(Market.HN, "Honduras (HN)"),
|
||||
(Market.HK, "Hong Kong (HK)"),
|
||||
(Market.HU, "Hungary (HU)"),
|
||||
(Market.IS, "Iceland (IS)"),
|
||||
(Market.IN, "India (IN)"),
|
||||
(Market.ID, "Indonesia (ID)"),
|
||||
(Market.IQ, "Iraq (IQ)"),
|
||||
(Market.IE, "Ireland (IE)"),
|
||||
(Market.IL, "Israel (IL)"),
|
||||
(Market.IT, "Italy (IT)"),
|
||||
(Market.JM, "Jamaica (JM)"),
|
||||
(Market.JP, "Japan (JP)"),
|
||||
(Market.JO, "Jordan (JO)"),
|
||||
(Market.KZ, "Kazakhstan (KZ)"),
|
||||
(Market.KE, "Kenya (KE)"),
|
||||
(Market.KI, "Kiribati (KI)"),
|
||||
(Market.XK, "Kosovo (XK)"),
|
||||
(Market.KW, "Kuwait (KW)"),
|
||||
(Market.KG, "Kyrgyzstan (KG)"),
|
||||
(Market.LA, "Laos (LA)"),
|
||||
(Market.LV, "Latvia (LV)"),
|
||||
(Market.LB, "Lebanon (LB)"),
|
||||
(Market.LS, "Lesotho (LS)"),
|
||||
(Market.LR, "Liberia (LR)"),
|
||||
(Market.LY, "Libya (LY)"),
|
||||
(Market.LI, "Liechtenstein (LI)"),
|
||||
(Market.LT, "Lithuania (LT)"),
|
||||
(Market.LU, "Luxembourg (LU)"),
|
||||
(Market.MO, "Macao / Macau (MO)"),
|
||||
(Market.MG, "Madagascar (MG)"),
|
||||
(Market.MW, "Malawi (MW)"),
|
||||
(Market.MY, "Malaysia (MY)"),
|
||||
(Market.MV, "Maldives (MV)"),
|
||||
(Market.ML, "Mali (ML)"),
|
||||
(Market.MT, "Malta (MT)"),
|
||||
(Market.MH, "Marshall Islands (MH)"),
|
||||
(Market.MR, "Mauritania (MR)"),
|
||||
(Market.MU, "Mauritius (MU)"),
|
||||
(Market.MX, "Mexico (MX)"),
|
||||
(Market.FM, "Micronesia (FM)"),
|
||||
(Market.MD, "Moldova (MD)"),
|
||||
(Market.MC, "Monaco (MC)"),
|
||||
(Market.MN, "Mongolia (MN)"),
|
||||
(Market.ME, "Montenegro (ME)"),
|
||||
(Market.MA, "Morocco (MA)"),
|
||||
(Market.MZ, "Mozambique (MZ)"),
|
||||
(Market.NA, "Namibia (NA)"),
|
||||
(Market.NR, "Nauru (NR)"),
|
||||
(Market.NP, "Nepal (NP)"),
|
||||
(Market.NL, "Netherlands (NL)"),
|
||||
(Market.NZ, "New Zealand (NZ)"),
|
||||
(Market.NI, "Nicaragua (NI)"),
|
||||
(Market.NE, "Niger (NE)"),
|
||||
(Market.NG, "Nigeria (NG)"),
|
||||
(Market.MK, "North Macedonia (MK)"),
|
||||
(Market.NO, "Norway (NO)"),
|
||||
(Market.OM, "Oman (OM)"),
|
||||
(Market.PK, "Pakistan (PK)"),
|
||||
(Market.PW, "Palau (PW)"),
|
||||
(Market.PS, "Palestine (PS)"),
|
||||
(Market.PA, "Panama (PA)"),
|
||||
(Market.PG, "Papua New Guinea (PG)"),
|
||||
(Market.PY, "Paraguay (PY)"),
|
||||
(Market.PE, "Peru (PE)"),
|
||||
(Market.PH, "Philippines (PH)"),
|
||||
(Market.PL, "Poland (PL)"),
|
||||
(Market.PT, "Portugal (PT)"),
|
||||
(Market.QA, "Qatar (QA)"),
|
||||
(Market.CG, "Congo (CG)"),
|
||||
(Market.RO, "Romania (RO)"),
|
||||
(Market.RU, "Russia (RU)"),
|
||||
(Market.RW, "Rwanda (RW)"),
|
||||
(Market.WS, "Samoa (WS)"),
|
||||
(Market.SM, "San Marino (SM)"),
|
||||
(Market.SA, "Saudi Arabia (SA)"),
|
||||
(Market.SN, "Senegal (SN)"),
|
||||
(Market.RS, "Serbia (RS)"),
|
||||
(Market.SC, "Seychelles (SC)"),
|
||||
(Market.SL, "Sierra Leone (SL)"),
|
||||
(Market.SG, "Singapore (SG)"),
|
||||
(Market.SK, "Slovakia (SK)"),
|
||||
(Market.SI, "Slovenia (SI)"),
|
||||
(Market.SB, "Solomon Islands (SB)"),
|
||||
(Market.ZA, "South Africa (ZA)"),
|
||||
(Market.KR, "South Korea (KR)"),
|
||||
(Market.ES, "Spain (ES)"),
|
||||
(Market.LK, "Sri Lanka (LK)"),
|
||||
(Market.KN, "St. Kitts and Nevis (KN)"),
|
||||
(Market.LC, "St. Lucia (LC)"),
|
||||
(Market.SR, "Suriname (SR)"),
|
||||
(Market.SE, "Sweden (SE)"),
|
||||
(Market.CH, "Switzerland (CH)"),
|
||||
(Market.ST, "São Tomé and Príncipe (ST)"),
|
||||
(Market.TW, "Taiwan (TW)"),
|
||||
(Market.TJ, "Tajikistan (TJ)"),
|
||||
(Market.TZ, "Tanzania (TZ)"),
|
||||
(Market.TH, "Thailand (TH)"),
|
||||
(Market.BS, "The Bahamas (BS)"),
|
||||
(Market.GM, "The Gambia (GM)"),
|
||||
(Market.TL, "East Timor (TL)"),
|
||||
(Market.TG, "Togo (TG)"),
|
||||
(Market.TO, "Tonga (TO)"),
|
||||
(Market.TT, "Trinidad and Tobago (TT)"),
|
||||
(Market.TN, "Tunisia (TN)"),
|
||||
(Market.TR, "Turkey (TR)"),
|
||||
(Market.TV, "Tuvalu (TV)"),
|
||||
(Market.UG, "Uganda (UG)"),
|
||||
(Market.UA, "Ukraine (UA)"),
|
||||
(Market.AE, "United Arab Emirates (AE)"),
|
||||
(Market.GB, "United Kingdom (GB)"),
|
||||
(Market.US, "United States (US)"),
|
||||
(Market.UY, "Uruguay (UY)"),
|
||||
(Market.UZ, "Uzbekistan (UZ)"),
|
||||
(Market.VU, "Vanuatu (VU)"),
|
||||
(Market.VE, "Venezuela (VE)"),
|
||||
(Market.VN, "Vietnam (VN)"),
|
||||
(Market.ZM, "Zambia (ZM)"),
|
||||
(Market.ZW, "Zimbabwe (ZW)"),
|
||||
(Market.AL, 'Albania (AL)'),
|
||||
(Market.DZ, 'Algeria (DZ)'),
|
||||
(Market.AD, 'Andorra (AD)'),
|
||||
(Market.AO, 'Angola (AO)'),
|
||||
(Market.AG, 'Antigua and Barbuda (AG)'),
|
||||
(Market.AR, 'Argentina (AR)'),
|
||||
(Market.AM, 'Armenia (AM)'),
|
||||
(Market.AU, 'Australia (AU)'),
|
||||
(Market.AT, 'Austria (AT)'),
|
||||
(Market.AZ, 'Azerbaijan (AZ)'),
|
||||
(Market.BH, 'Bahrain (BH)'),
|
||||
(Market.BD, 'Bangladesh (BD)'),
|
||||
(Market.BB, 'Barbados (BB)'),
|
||||
(Market.BY, 'Belarus (BY)'),
|
||||
(Market.BE, 'Belgium (BE)'),
|
||||
(Market.BZ, 'Belize (BZ)'),
|
||||
(Market.BJ, 'Benin (BJ)'),
|
||||
(Market.BT, 'Bhutan (BT)'),
|
||||
(Market.BO, 'Bolivia (BO)'),
|
||||
(Market.BA, 'Bosnia and Herzegovina (BA)'),
|
||||
(Market.BW, 'Botswana (BW)'),
|
||||
(Market.BR, 'Brazil (BR)'),
|
||||
(Market.BN, 'Brunei Darussalam (BN)'),
|
||||
(Market.BG, 'Bulgaria (BG)'),
|
||||
(Market.BF, 'Burkina Faso (BF)'),
|
||||
(Market.BI, 'Burundi (BI)'),
|
||||
(Market.CV, 'Cabo Verde / Cape Verde (CV)'),
|
||||
(Market.KH, 'Cambodia (KH)'),
|
||||
(Market.CM, 'Cameroon (CM)'),
|
||||
(Market.CA, 'Canada (CA)'),
|
||||
(Market.TD, 'Chad (TD)'),
|
||||
(Market.CL, 'Chile (CL)'),
|
||||
(Market.CO, 'Colombia (CO)'),
|
||||
(Market.KM, 'Comoros (KM)'),
|
||||
(Market.CR, 'Costa Rica (CR)'),
|
||||
(Market.HR, 'Croatia (HR)'),
|
||||
(Market.CW, 'Curaçao (CW)'),
|
||||
(Market.CY, 'Cyprus (CY)'),
|
||||
(Market.CZ, 'Czech Republic (CZ)'),
|
||||
(Market.CI, 'Ivory Coast (CI)'),
|
||||
(Market.CD, 'Congo (CD)'),
|
||||
(Market.DK, 'Denmark (DK)'),
|
||||
(Market.DJ, 'Djibouti (DJ)'),
|
||||
(Market.DM, 'Dominica (DM)'),
|
||||
(Market.DO, 'Dominican Republic (DO)'),
|
||||
(Market.EC, 'Ecuador (EC)'),
|
||||
(Market.EG, 'Egypt (EG)'),
|
||||
(Market.SV, 'El Salvador (SV)'),
|
||||
(Market.GQ, 'Equatorial Guinea (GQ)'),
|
||||
(Market.EE, 'Estonia (EE)'),
|
||||
(Market.SZ, 'Eswatini (SZ)'),
|
||||
(Market.FJ, 'Fiji (FJ)'),
|
||||
(Market.FI, 'Finland (FI)'),
|
||||
(Market.FR, 'France (FR)'),
|
||||
(Market.GA, 'Gabon (GA)'),
|
||||
(Market.GE, 'Georgia (GE)'),
|
||||
(Market.DE, 'Germany (DE)'),
|
||||
(Market.GH, 'Ghana (GH)'),
|
||||
(Market.GR, 'Greece (GR)'),
|
||||
(Market.GD, 'Grenada (GD)'),
|
||||
(Market.GT, 'Guatemala (GT)'),
|
||||
(Market.GN, 'Guinea (GN)'),
|
||||
(Market.GW, 'Guinea-Bissau (GW)'),
|
||||
(Market.GY, 'Guyana (GY)'),
|
||||
(Market.HT, 'Haiti (HT)'),
|
||||
(Market.HN, 'Honduras (HN)'),
|
||||
(Market.HK, 'Hong Kong (HK)'),
|
||||
(Market.HU, 'Hungary (HU)'),
|
||||
(Market.IS, 'Iceland (IS)'),
|
||||
(Market.IN, 'India (IN)'),
|
||||
(Market.ID, 'Indonesia (ID)'),
|
||||
(Market.IQ, 'Iraq (IQ)'),
|
||||
(Market.IE, 'Ireland (IE)'),
|
||||
(Market.IL, 'Israel (IL)'),
|
||||
(Market.IT, 'Italy (IT)'),
|
||||
(Market.JM, 'Jamaica (JM)'),
|
||||
(Market.JP, 'Japan (JP)'),
|
||||
(Market.JO, 'Jordan (JO)'),
|
||||
(Market.KZ, 'Kazakhstan (KZ)'),
|
||||
(Market.KE, 'Kenya (KE)'),
|
||||
(Market.KI, 'Kiribati (KI)'),
|
||||
(Market.XK, 'Kosovo (XK)'),
|
||||
(Market.KW, 'Kuwait (KW)'),
|
||||
(Market.KG, 'Kyrgyzstan (KG)'),
|
||||
(Market.LA, 'Laos (LA)'),
|
||||
(Market.LV, 'Latvia (LV)'),
|
||||
(Market.LB, 'Lebanon (LB)'),
|
||||
(Market.LS, 'Lesotho (LS)'),
|
||||
(Market.LR, 'Liberia (LR)'),
|
||||
(Market.LY, 'Libya (LY)'),
|
||||
(Market.LI, 'Liechtenstein (LI)'),
|
||||
(Market.LT, 'Lithuania (LT)'),
|
||||
(Market.LU, 'Luxembourg (LU)'),
|
||||
(Market.MO, 'Macao / Macau (MO)'),
|
||||
(Market.MG, 'Madagascar (MG)'),
|
||||
(Market.MW, 'Malawi (MW)'),
|
||||
(Market.MY, 'Malaysia (MY)'),
|
||||
(Market.MV, 'Maldives (MV)'),
|
||||
(Market.ML, 'Mali (ML)'),
|
||||
(Market.MT, 'Malta (MT)'),
|
||||
(Market.MH, 'Marshall Islands (MH)'),
|
||||
(Market.MR, 'Mauritania (MR)'),
|
||||
(Market.MU, 'Mauritius (MU)'),
|
||||
(Market.MX, 'Mexico (MX)'),
|
||||
(Market.FM, 'Micronesia (FM)'),
|
||||
(Market.MD, 'Moldova (MD)'),
|
||||
(Market.MC, 'Monaco (MC)'),
|
||||
(Market.MN, 'Mongolia (MN)'),
|
||||
(Market.ME, 'Montenegro (ME)'),
|
||||
(Market.MA, 'Morocco (MA)'),
|
||||
(Market.MZ, 'Mozambique (MZ)'),
|
||||
(Market.NA, 'Namibia (NA)'),
|
||||
(Market.NR, 'Nauru (NR)'),
|
||||
(Market.NP, 'Nepal (NP)'),
|
||||
(Market.NL, 'Netherlands (NL)'),
|
||||
(Market.NZ, 'New Zealand (NZ)'),
|
||||
(Market.NI, 'Nicaragua (NI)'),
|
||||
(Market.NE, 'Niger (NE)'),
|
||||
(Market.NG, 'Nigeria (NG)'),
|
||||
(Market.MK, 'North Macedonia (MK)'),
|
||||
(Market.NO, 'Norway (NO)'),
|
||||
(Market.OM, 'Oman (OM)'),
|
||||
(Market.PK, 'Pakistan (PK)'),
|
||||
(Market.PW, 'Palau (PW)'),
|
||||
(Market.PS, 'Palestine (PS)'),
|
||||
(Market.PA, 'Panama (PA)'),
|
||||
(Market.PG, 'Papua New Guinea (PG)'),
|
||||
(Market.PY, 'Paraguay (PY)'),
|
||||
(Market.PE, 'Peru (PE)'),
|
||||
(Market.PH, 'Philippines (PH)'),
|
||||
(Market.PL, 'Poland (PL)'),
|
||||
(Market.PT, 'Portugal (PT)'),
|
||||
(Market.QA, 'Qatar (QA)'),
|
||||
(Market.CG, 'Congo (CG)'),
|
||||
(Market.RO, 'Romania (RO)'),
|
||||
(Market.RU, 'Russia (RU)'),
|
||||
(Market.RW, 'Rwanda (RW)'),
|
||||
(Market.WS, 'Samoa (WS)'),
|
||||
(Market.SM, 'San Marino (SM)'),
|
||||
(Market.SA, 'Saudi Arabia (SA)'),
|
||||
(Market.SN, 'Senegal (SN)'),
|
||||
(Market.RS, 'Serbia (RS)'),
|
||||
(Market.SC, 'Seychelles (SC)'),
|
||||
(Market.SL, 'Sierra Leone (SL)'),
|
||||
(Market.SG, 'Singapore (SG)'),
|
||||
(Market.SK, 'Slovakia (SK)'),
|
||||
(Market.SI, 'Slovenia (SI)'),
|
||||
(Market.SB, 'Solomon Islands (SB)'),
|
||||
(Market.ZA, 'South Africa (ZA)'),
|
||||
(Market.KR, 'South Korea (KR)'),
|
||||
(Market.ES, 'Spain (ES)'),
|
||||
(Market.LK, 'Sri Lanka (LK)'),
|
||||
(Market.KN, 'St. Kitts and Nevis (KN)'),
|
||||
(Market.LC, 'St. Lucia (LC)'),
|
||||
(Market.SR, 'Suriname (SR)'),
|
||||
(Market.SE, 'Sweden (SE)'),
|
||||
(Market.CH, 'Switzerland (CH)'),
|
||||
(Market.ST, 'São Tomé and Príncipe (ST)'),
|
||||
(Market.TW, 'Taiwan (TW)'),
|
||||
(Market.TJ, 'Tajikistan (TJ)'),
|
||||
(Market.TZ, 'Tanzania (TZ)'),
|
||||
(Market.TH, 'Thailand (TH)'),
|
||||
(Market.BS, 'The Bahamas (BS)'),
|
||||
(Market.GM, 'The Gambia (GM)'),
|
||||
(Market.TL, 'East Timor (TL)'),
|
||||
(Market.TG, 'Togo (TG)'),
|
||||
(Market.TO, 'Tonga (TO)'),
|
||||
(Market.TT, 'Trinidad and Tobago (TT)'),
|
||||
(Market.TN, 'Tunisia (TN)'),
|
||||
(Market.TR, 'Turkey (TR)'),
|
||||
(Market.TV, 'Tuvalu (TV)'),
|
||||
(Market.UG, 'Uganda (UG)'),
|
||||
(Market.UA, 'Ukraine (UA)'),
|
||||
(Market.AE, 'United Arab Emirates (AE)'),
|
||||
(Market.GB, 'United Kingdom (GB)'),
|
||||
(Market.US, 'United States (US)'),
|
||||
(Market.UY, 'Uruguay (UY)'),
|
||||
(Market.UZ, 'Uzbekistan (UZ)'),
|
||||
(Market.VU, 'Vanuatu (VU)'),
|
||||
(Market.VE, 'Venezuela (VE)'),
|
||||
(Market.VN, 'Vietnam (VN)'),
|
||||
(Market.ZM, 'Zambia (ZM)'),
|
||||
(Market.ZW, 'Zimbabwe (ZW)'),
|
||||
];
|
||||
|
@ -81,7 +81,7 @@ class AudioPlayerProvider extends GetxController {
|
||||
}
|
||||
|
||||
Future<AudioPlayerState?> _readSavedState() async {
|
||||
final data = _prefs.getString("player_state");
|
||||
final data = _prefs.getString('player_state');
|
||||
if (data == null) return null;
|
||||
|
||||
return AudioPlayerState.fromJson(jsonDecode(data));
|
||||
@ -89,7 +89,7 @@ class AudioPlayerProvider extends GetxController {
|
||||
|
||||
Future<void> _updateSavedState() async {
|
||||
final out = jsonEncode(state.value.toJson());
|
||||
await _prefs.setString("player_state", out);
|
||||
await _prefs.setString('player_state', out);
|
||||
}
|
||||
|
||||
Future<void> addCollections(List<String> collectionIds) async {
|
||||
|
@ -8,8 +8,8 @@ class SpotifyProvider extends GetxController {
|
||||
void onInit() {
|
||||
api = SpotifyApi(
|
||||
SpotifyApiCredentials(
|
||||
"f73d4bff91d64d89be9930036f553534",
|
||||
"5cbec0b928d247cd891d06195f07b8c9",
|
||||
'f73d4bff91d64d89be9930036f553534',
|
||||
'5cbec0b928d247cd891d06195f07b8c9',
|
||||
),
|
||||
);
|
||||
super.onInit();
|
||||
|
@ -1,5 +1,6 @@
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:rhythm_box/screens/explore.dart';
|
||||
import 'package:rhythm_box/screens/player/view.dart';
|
||||
import 'package:rhythm_box/screens/playlist/view.dart';
|
||||
import 'package:rhythm_box/screens/settings.dart';
|
||||
import 'package:rhythm_box/shells/nav_shell.dart';
|
||||
@ -9,22 +10,27 @@ final router = GoRouter(routes: [
|
||||
builder: (context, state, child) => NavShell(child: child),
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: "/",
|
||||
name: "explore",
|
||||
path: '/',
|
||||
name: 'explore',
|
||||
builder: (context, state) => const ExploreScreen(),
|
||||
),
|
||||
GoRoute(
|
||||
path: "/playlist/:id",
|
||||
name: "playlistView",
|
||||
path: '/playlist/:id',
|
||||
name: 'playlistView',
|
||||
builder: (context, state) => PlaylistViewScreen(
|
||||
playlistId: state.pathParameters['id']!,
|
||||
),
|
||||
),
|
||||
GoRoute(
|
||||
path: "/settings",
|
||||
name: "settings",
|
||||
path: '/settings',
|
||||
name: 'settings',
|
||||
builder: (context, state) => const SettingsScreen(),
|
||||
),
|
||||
],
|
||||
),
|
||||
GoRoute(
|
||||
path: '/player',
|
||||
name: 'player',
|
||||
builder: (context, state) => const PlayerScreen(),
|
||||
),
|
||||
]);
|
||||
|
15
lib/screens/player/view.dart
Normal file
15
lib/screens/player/view.dart
Normal file
@ -0,0 +1,15 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class PlayerScreen extends StatefulWidget {
|
||||
const PlayerScreen({super.key});
|
||||
|
||||
@override
|
||||
State<PlayerScreen> createState() => _PlayerScreenState();
|
||||
}
|
||||
|
||||
class _PlayerScreenState extends State<PlayerScreen> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const Placeholder();
|
||||
}
|
||||
}
|
@ -104,7 +104,7 @@ class _PlaylistViewScreenState extends State<PlaylistViewScreen> {
|
||||
"${NumberFormat.compactCurrency(symbol: '', decimalDigits: 2).format(_playlist!.followers!.total!)} saves",
|
||||
),
|
||||
Text(
|
||||
"#${_playlist!.id}",
|
||||
'#${_playlist!.id}',
|
||||
style: GoogleFonts.robotoMono(fontSize: 10),
|
||||
),
|
||||
],
|
||||
|
@ -2,12 +2,12 @@ import 'package:spotify/spotify.dart';
|
||||
|
||||
extension ArtistSimpleExtension on List<ArtistSimple> {
|
||||
String asString() {
|
||||
return map((e) => e.name?.replaceAll(",", " ")).join(", ");
|
||||
return map((e) => e.name?.replaceAll(',', ' ')).join(', ');
|
||||
}
|
||||
}
|
||||
|
||||
extension ArtistExtension on List<Artist> {
|
||||
String asString() {
|
||||
return map((e) => e.name?.replaceAll(",", " ")).join(", ");
|
||||
return map((e) => e.name?.replaceAll(',', ' ')).join(', ');
|
||||
}
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ class RhythmMedia extends mk.Media {
|
||||
: "http://${PlatformInfo.isWindows ? "localhost" : InternetAddress.anyIPv4.address}:$serverPort/stream/${track.id}",
|
||||
extras: {
|
||||
...?extras,
|
||||
"track": switch (track) {
|
||||
'track': switch (track) {
|
||||
LocalTrack() => track.toJson(),
|
||||
SourcedTrack() => track.toJson(),
|
||||
_ => track.toJson(),
|
||||
@ -50,14 +50,14 @@ class RhythmMedia extends mk.Media {
|
||||
LocalTrack() => super.uri,
|
||||
_ =>
|
||||
"http://${PlatformInfo.isWindows ? "localhost" : InternetAddress.anyIPv4.address}:"
|
||||
"$serverPort/stream/${track.id}",
|
||||
'$serverPort/stream/${track.id}',
|
||||
};
|
||||
}
|
||||
|
||||
factory RhythmMedia.fromMedia(mk.Media media) {
|
||||
final track = media.uri.startsWith("http")
|
||||
? Track.fromJson(media.extras?["track"])
|
||||
: LocalTrack.fromJson(media.extras?["track"]);
|
||||
final track = media.uri.startsWith('http')
|
||||
? Track.fromJson(media.extras?['track'])
|
||||
: LocalTrack.fromJson(media.extras?['track']);
|
||||
return RhythmMedia(
|
||||
track,
|
||||
extras: media.extras,
|
||||
@ -87,12 +87,12 @@ abstract class AudioPlayerInterface {
|
||||
AudioPlayerInterface()
|
||||
: _mkPlayer = CustomPlayer(
|
||||
configuration: const mk.PlayerConfiguration(
|
||||
title: "Rhythm",
|
||||
title: 'Rhythm',
|
||||
logLevel: kDebugMode ? mk.MPVLogLevel.info : mk.MPVLogLevel.error,
|
||||
),
|
||||
) {
|
||||
_mkPlayer.stream.error.listen((event) {
|
||||
log("[Playback] Error: $event");
|
||||
log('[Playback] Error: $event');
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -19,14 +19,14 @@ class CustomPlayer extends Player {
|
||||
|
||||
bool _shuffled;
|
||||
int _androidAudioSessionId = 0;
|
||||
String _packageName = "";
|
||||
String _packageName = '';
|
||||
AndroidAudioManager? _androidAudioManager;
|
||||
|
||||
CustomPlayer({super.configuration})
|
||||
: _playerStateStream = StreamController.broadcast(),
|
||||
_shuffleStream = StreamController.broadcast(),
|
||||
_shuffled = false {
|
||||
nativePlayer.setProperty("network-timeout", "120");
|
||||
nativePlayer.setProperty('network-timeout', '120');
|
||||
|
||||
_subscriptions = [
|
||||
stream.buffering.listen((event) {
|
||||
@ -63,10 +63,10 @@ class CustomPlayer extends Player {
|
||||
notifyAudioSessionUpdate(true);
|
||||
|
||||
await nativePlayer.setProperty(
|
||||
"audiotrack-session-id",
|
||||
'audiotrack-session-id',
|
||||
_androidAudioSessionId.toString(),
|
||||
);
|
||||
await nativePlayer.setProperty("ao", "audiotrack,opensles,");
|
||||
await nativePlayer.setProperty('ao', 'audiotrack,opensles,');
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -76,11 +76,11 @@ class CustomPlayer extends Player {
|
||||
sendBroadcast(
|
||||
BroadcastMessage(
|
||||
name: active
|
||||
? "android.media.action.OPEN_AUDIO_EFFECT_CONTROL_SESSION"
|
||||
: "android.media.action.CLOSE_AUDIO_EFFECT_CONTROL_SESSION",
|
||||
? 'android.media.action.OPEN_AUDIO_EFFECT_CONTROL_SESSION'
|
||||
: 'android.media.action.CLOSE_AUDIO_EFFECT_CONTROL_SESSION',
|
||||
data: {
|
||||
"android.media.extra.AUDIO_SESSION": _androidAudioSessionId,
|
||||
"android.media.extra.PACKAGE_NAME": _packageName
|
||||
'android.media.extra.AUDIO_SESSION': _androidAudioSessionId,
|
||||
'android.media.extra.PACKAGE_NAME': _packageName
|
||||
},
|
||||
),
|
||||
);
|
||||
|
@ -27,9 +27,9 @@ class AudioServices with WidgetsBindingObserver {
|
||||
: 'dev.solsynth.rhythmBox',
|
||||
androidNotificationChannelName: 'RhythmBox',
|
||||
androidNotificationOngoing: false,
|
||||
androidNotificationIcon: "drawable/ic_launcher_monochrome",
|
||||
androidNotificationIcon: 'drawable/ic_launcher_monochrome',
|
||||
androidStopForegroundOnPause: false,
|
||||
androidNotificationChannelDescription: "RhythmBox Music",
|
||||
androidNotificationChannelDescription: 'RhythmBox Music',
|
||||
),
|
||||
)
|
||||
: null;
|
||||
@ -42,9 +42,9 @@ class AudioServices with WidgetsBindingObserver {
|
||||
await smtc?.addTrack(track);
|
||||
mobile?.addItem(MediaItem(
|
||||
id: track.id!,
|
||||
album: track.album?.name ?? "",
|
||||
album: track.album?.name ?? '',
|
||||
title: track.name!,
|
||||
artist: (track.artists)?.asString() ?? "",
|
||||
artist: (track.artists)?.asString() ?? '',
|
||||
duration: track is SourcedTrack
|
||||
? track.sourceInfo.duration
|
||||
: Duration(milliseconds: track.durationMs ?? 0),
|
||||
|
@ -81,9 +81,9 @@ class WindowsAudioService {
|
||||
await smtc.updateMetadata(
|
||||
MusicMetadata(
|
||||
title: track.name!,
|
||||
albumArtist: track.artists?.firstOrNull?.name ?? "Unknown",
|
||||
artist: track.artists?.asString() ?? "Unknown",
|
||||
album: track.album?.name ?? "Unknown",
|
||||
albumArtist: track.artists?.firstOrNull?.name ?? 'Unknown',
|
||||
artist: track.artists?.asString() ?? 'Unknown',
|
||||
album: track.album?.name ?? 'Unknown',
|
||||
thumbnail: (track.album?.images).asUrlString(),
|
||||
),
|
||||
);
|
||||
|
@ -3,7 +3,7 @@ import 'package:uuid/uuid.dart';
|
||||
|
||||
abstract class PrimitiveUtils {
|
||||
static bool containsTextInBracket(String matcher, String text) {
|
||||
final allMatches = RegExp(r"(?<=\().+?(?=\))").allMatches(matcher);
|
||||
final allMatches = RegExp(r'(?<=\().+?(?=\))').allMatches(matcher);
|
||||
if (allMatches.isEmpty) return false;
|
||||
return allMatches
|
||||
.map((e) => e.group(0))
|
||||
@ -19,13 +19,13 @@ abstract class PrimitiveUtils {
|
||||
|
||||
static String toReadableNumber(double num) {
|
||||
if (num > 999 && num < 99999) {
|
||||
return "${(num / 1000).toStringAsFixed(0)}K";
|
||||
return '${(num / 1000).toStringAsFixed(0)}K';
|
||||
} else if (num > 99999 && num < 999999) {
|
||||
return "${(num / 1000).toStringAsFixed(0)}K";
|
||||
return '${(num / 1000).toStringAsFixed(0)}K';
|
||||
} else if (num > 999999 && num < 999999999) {
|
||||
return "${(num / 1000000).toStringAsFixed(0)}M";
|
||||
return '${(num / 1000000).toStringAsFixed(0)}M';
|
||||
} else if (num > 999999999) {
|
||||
return "${(num / 1000000000).toStringAsFixed(0)}B";
|
||||
return '${(num / 1000000000).toStringAsFixed(0)}B';
|
||||
} else {
|
||||
return num.toStringAsFixed(0);
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ class RhythmMedia extends mk.Media {
|
||||
: "http://${PlatformInfo.isWindows ? "localhost" : InternetAddress.anyIPv4.address}:$serverPort/stream/${track.id}",
|
||||
extras: {
|
||||
...?extras,
|
||||
"track": switch (track) {
|
||||
'track': switch (track) {
|
||||
LocalTrack() => track.toJson(),
|
||||
SourcedTrack() => track.toJson(),
|
||||
_ => track.toJson(),
|
||||
@ -43,14 +43,14 @@ class RhythmMedia extends mk.Media {
|
||||
LocalTrack() => super.uri,
|
||||
_ =>
|
||||
"http://${PlatformInfo.isWindows ? "localhost" : InternetAddress.anyIPv4.address}:"
|
||||
"$serverPort/stream/${track.id}",
|
||||
'$serverPort/stream/${track.id}',
|
||||
};
|
||||
}
|
||||
|
||||
factory RhythmMedia.fromMedia(mk.Media media) {
|
||||
final track = media.uri.startsWith("http")
|
||||
? Track.fromJson(media.extras?["track"])
|
||||
: LocalTrack.fromJson(media.extras?["track"]);
|
||||
final track = media.uri.startsWith('http')
|
||||
? Track.fromJson(media.extras?['track'])
|
||||
: LocalTrack.fromJson(media.extras?['track']);
|
||||
return RhythmMedia(
|
||||
track,
|
||||
extras: media.extras,
|
||||
@ -80,12 +80,12 @@ abstract class AudioPlayerInterface {
|
||||
AudioPlayerInterface()
|
||||
: _mkPlayer = CustomPlayer(
|
||||
configuration: const mk.PlayerConfiguration(
|
||||
title: "Rhythm",
|
||||
title: 'Rhythm',
|
||||
logLevel: kDebugMode ? mk.MPVLogLevel.info : mk.MPVLogLevel.error,
|
||||
),
|
||||
) {
|
||||
_mkPlayer.stream.error.listen((event) {
|
||||
log("[Playback] Error: $event");
|
||||
log('[Playback] Error: $event');
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -31,11 +31,11 @@ class ServerPlaybackRoutesProvider {
|
||||
options: Options(
|
||||
headers: {
|
||||
...request.headers,
|
||||
"User-Agent":
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36",
|
||||
"host": Uri.parse(sourcedTrack.url).host,
|
||||
"Cache-Control": "max-age=0",
|
||||
"Connection": "keep-alive",
|
||||
'User-Agent':
|
||||
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36',
|
||||
'host': Uri.parse(sourcedTrack.url).host,
|
||||
'Cache-Control': 'max-age=0',
|
||||
'Connection': 'keep-alive',
|
||||
},
|
||||
responseType: ResponseType.stream,
|
||||
validateStatus: (status) => status! < 500,
|
||||
@ -54,7 +54,7 @@ class ServerPlaybackRoutesProvider {
|
||||
res.statusCode!,
|
||||
body: audioStream,
|
||||
context: {
|
||||
"shelf.io.buffer_output": false,
|
||||
'shelf.io.buffer_output': false,
|
||||
},
|
||||
headers: res.headers.map,
|
||||
);
|
||||
|
@ -31,9 +31,9 @@ class PlaybackServerProvider extends GetxController {
|
||||
RhythmMedia.serverPort = port;
|
||||
|
||||
_router = Router();
|
||||
_router!.get("/ping", (Request request) => Response.ok("pong"));
|
||||
_router!.get('/ping', (Request request) => Response.ok('pong'));
|
||||
_router!.get(
|
||||
"/stream/<trackId>",
|
||||
'/stream/<trackId>',
|
||||
Get.find<ServerPlaybackRoutesProvider>().getStreamTrackId,
|
||||
);
|
||||
|
||||
|
@ -17,29 +17,29 @@ abstract class SongLinkService {
|
||||
try {
|
||||
final client = GetConnect();
|
||||
final res = await client.get(
|
||||
"https://song.link/s/$spotifyId",
|
||||
'https://song.link/s/$spotifyId',
|
||||
headers: {
|
||||
"Accept":
|
||||
"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
|
||||
"User-Agent":
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36"
|
||||
'Accept':
|
||||
'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
|
||||
'User-Agent':
|
||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36'
|
||||
},
|
||||
);
|
||||
|
||||
final document = parse(res.body);
|
||||
|
||||
final script = document.getElementById("__NEXT_DATA__")?.text;
|
||||
final script = document.getElementById('__NEXT_DATA__')?.text;
|
||||
|
||||
if (script == null) {
|
||||
return <SongLink>[];
|
||||
}
|
||||
|
||||
final pageProps = jsonDecode(script) as Map<String, dynamic>;
|
||||
final songLinks = pageProps["props"]?["pageProps"]?["pageData"]
|
||||
?["sections"]
|
||||
final songLinks = pageProps['props']?['pageProps']?['pageData']
|
||||
?['sections']
|
||||
?.firstWhere(
|
||||
(section) => section?["sectionId"] == "section|auto|links|listen",
|
||||
)?["links"] as List?;
|
||||
(section) => section?['sectionId'] == 'section|auto|links|listen',
|
||||
)?['links'] as List?;
|
||||
|
||||
return songLinks?.map((link) => SongLink.fromJson(link)).toList() ??
|
||||
<SongLink>[];
|
||||
|
@ -2,7 +2,7 @@ import 'package:rhythm_box/services/sourced_track/models/source_info.dart';
|
||||
import 'package:rhythm_box/services/sourced_track/models/source_map.dart';
|
||||
|
||||
enum SourceCodecs {
|
||||
m4a._("M4a (Best for downloaded music)"),
|
||||
m4a._('M4a (Best for downloaded music)'),
|
||||
weba._("WebA (Best for streamed music)\nDoesn't support audio metadata");
|
||||
|
||||
final String label;
|
||||
|
@ -1,6 +1,6 @@
|
||||
enum SearchMode {
|
||||
youtube._("YouTube"),
|
||||
youtubeMusic._("YouTube Music");
|
||||
youtube._('YouTube'),
|
||||
youtubeMusic._('YouTube Music');
|
||||
|
||||
final String label;
|
||||
|
||||
|
@ -87,7 +87,7 @@ class YoutubeVideoInfo {
|
||||
dislikes: 0,
|
||||
views: searchItem.views,
|
||||
channelName: searchItem.uploaderName,
|
||||
channelId: searchItem.uploaderUrl ?? "",
|
||||
channelId: searchItem.uploaderUrl ?? '',
|
||||
publishedAt: searchItem.uploadedDate != null
|
||||
? DateTime.tryParse(searchItem.uploadedDate!) ?? DateTime(2003, 9, 9)
|
||||
: DateTime(2003, 9, 9),
|
||||
|
@ -41,18 +41,18 @@ abstract class SourcedTrack extends Track {
|
||||
|
||||
static SourcedTrack fromJson(Map<String, dynamic> json) {
|
||||
// TODO Follow user preferences
|
||||
const audioSource = "youtube";
|
||||
const audioSource = 'youtube';
|
||||
|
||||
final sourceInfo = SourceInfo.fromJson(json);
|
||||
final source = SourceMap.fromJson(json);
|
||||
final track = Track.fromJson(json);
|
||||
final siblings = (json["siblings"] as List)
|
||||
final siblings = (json['siblings'] as List)
|
||||
.map((sibling) => SourceInfo.fromJson(sibling))
|
||||
.toList()
|
||||
.cast<SourceInfo>();
|
||||
|
||||
return switch (audioSource) {
|
||||
"piped" => PipedSourcedTrack(
|
||||
'piped' => PipedSourcedTrack(
|
||||
source: source,
|
||||
siblings: siblings,
|
||||
sourceInfo: sourceInfo,
|
||||
@ -87,11 +87,11 @@ abstract class SourcedTrack extends Track {
|
||||
required Track track,
|
||||
}) async {
|
||||
// TODO Follow user preferences
|
||||
const audioSource = "youtube";
|
||||
const audioSource = 'youtube';
|
||||
|
||||
try {
|
||||
return switch (audioSource) {
|
||||
"piped" => await PipedSourcedTrack.fetchFromTrack(track: track),
|
||||
'piped' => await PipedSourcedTrack.fetchFromTrack(track: track),
|
||||
_ => await YoutubeSourcedTrack.fetchFromTrack(track: track),
|
||||
};
|
||||
} on TrackNotFoundError catch (_) {
|
||||
@ -111,10 +111,10 @@ abstract class SourcedTrack extends Track {
|
||||
required Track track,
|
||||
}) {
|
||||
// TODO Follow user preferences
|
||||
const audioSource = "youtube";
|
||||
const audioSource = 'youtube';
|
||||
|
||||
return switch (audioSource) {
|
||||
"piped" => PipedSourcedTrack.fetchSiblings(track: track),
|
||||
'piped' => PipedSourcedTrack.fetchSiblings(track: track),
|
||||
_ => YoutubeSourcedTrack.fetchSiblings(track: track),
|
||||
};
|
||||
}
|
||||
|
@ -97,8 +97,8 @@ class PipedSourcedTrack extends SourcedTrack {
|
||||
info: PipedSourceInfo(
|
||||
id: item.id,
|
||||
artist: item.channelName,
|
||||
artistUrl: "https://www.youtube.com/${item.channelId}",
|
||||
pageUrl: "https://www.youtube.com/watch?v=${item.id}",
|
||||
artistUrl: 'https://www.youtube.com/${item.channelId}',
|
||||
pageUrl: 'https://www.youtube.com/watch?v=${item.id}',
|
||||
thumbnail: item.thumbnailUrl,
|
||||
title: item.title,
|
||||
duration: item.duration,
|
||||
@ -118,7 +118,7 @@ class PipedSourcedTrack extends SourcedTrack {
|
||||
// TODO Allow user search with normal youtube video (`youtube`)
|
||||
const searchMode = SearchMode.youtubeMusic;
|
||||
// TODO Follow user preferences
|
||||
const audioSource = "youtube";
|
||||
const audioSource = 'youtube';
|
||||
|
||||
final query = SourcedTrack.getSearchTerm(track);
|
||||
|
||||
@ -131,7 +131,7 @@ class PipedSourcedTrack extends SourcedTrack {
|
||||
|
||||
// when falling back to piped API make sure to use the YouTube mode
|
||||
const isYouTubeMusic =
|
||||
audioSource != "piped" ? false : searchMode == SearchMode.youtubeMusic;
|
||||
audioSource != 'piped' ? false : searchMode == SearchMode.youtubeMusic;
|
||||
|
||||
if (isYouTubeMusic) {
|
||||
final artists = (track.artists ?? [])
|
||||
|
@ -15,7 +15,7 @@ import 'package:youtube_explode_dart/youtube_explode_dart.dart';
|
||||
|
||||
final youtubeClient = YoutubeExplode();
|
||||
final officialMusicRegex = RegExp(
|
||||
r"official\s(video|audio|music\svideo|lyric\svideo|visualizer)",
|
||||
r'official\s(video|audio|music\svideo|lyric\svideo|visualizer)',
|
||||
caseSensitive: false,
|
||||
);
|
||||
|
||||
@ -62,11 +62,11 @@ class YoutubeSourcedTrack extends SourcedTrack {
|
||||
|
||||
static SourceMap toSourceMap(StreamManifest manifest) {
|
||||
var m4a = manifest.audioOnly
|
||||
.where((audio) => audio.codec.mimeType == "audio/mp4")
|
||||
.where((audio) => audio.codec.mimeType == 'audio/mp4')
|
||||
.sortByBitrate();
|
||||
|
||||
var weba = manifest.audioOnly
|
||||
.where((audio) => audio.codec.mimeType == "audio/webm")
|
||||
.where((audio) => audio.codec.mimeType == 'audio/webm')
|
||||
.sortByBitrate();
|
||||
|
||||
m4a = m4a.isEmpty ? weba.toList() : m4a;
|
||||
@ -96,7 +96,7 @@ class YoutubeSourcedTrack extends SourcedTrack {
|
||||
final manifest =
|
||||
await youtubeClient.videos.streamsClient.getManifest(item.id).timeout(
|
||||
const Duration(seconds: 5),
|
||||
onTimeout: () => throw ClientException("Timeout"),
|
||||
onTimeout: () => throw ClientException('Timeout'),
|
||||
);
|
||||
sourceMap = toSourceMap(manifest);
|
||||
}
|
||||
@ -105,8 +105,8 @@ class YoutubeSourcedTrack extends SourcedTrack {
|
||||
info: YoutubeSourceInfo(
|
||||
id: item.id,
|
||||
artist: item.channelName,
|
||||
artistUrl: "https://www.youtube.com/channel/${item.channelId}",
|
||||
pageUrl: "https://www.youtube.com/watch?v=${item.id}",
|
||||
artistUrl: 'https://www.youtube.com/channel/${item.channelId}',
|
||||
pageUrl: 'https://www.youtube.com/watch?v=${item.id}',
|
||||
thumbnail: item.thumbnailUrl,
|
||||
title: item.title,
|
||||
duration: item.duration,
|
||||
@ -179,7 +179,7 @@ class YoutubeSourcedTrack extends SourcedTrack {
|
||||
required Track track,
|
||||
}) async {
|
||||
final links = await SongLinkService.links(track.id!);
|
||||
final ytLink = links.firstWhereOrNull((link) => link.platform == "youtube");
|
||||
final ytLink = links.firstWhereOrNull((link) => link.platform == 'youtube');
|
||||
|
||||
if (ytLink?.url != null
|
||||
// allows to fetch siblings more results for already sourced track
|
||||
@ -203,7 +203,7 @@ class YoutubeSourcedTrack extends SourcedTrack {
|
||||
final query = SourcedTrack.getSearchTerm(track);
|
||||
|
||||
final searchResults = await youtubeClient.search.search(
|
||||
"$query - Topic",
|
||||
'$query - Topic',
|
||||
filter: TypeFilters.video,
|
||||
);
|
||||
|
||||
@ -240,7 +240,7 @@ class YoutubeSourcedTrack extends SourcedTrack {
|
||||
.getManifest(newSourceInfo.id)
|
||||
.timeout(
|
||||
const Duration(seconds: 5),
|
||||
onTimeout: () => throw ClientException("Timeout"),
|
||||
onTimeout: () => throw ClientException('Timeout'),
|
||||
);
|
||||
|
||||
// TODO Save to cache here
|
||||
|
@ -20,7 +20,7 @@ abstract class ServiceUtils {
|
||||
|
||||
static String clearArtistsOfTitle(String title, List<String> artists) {
|
||||
return title
|
||||
.replaceAll(RegExp(artists.join("|"), caseSensitive: false), "")
|
||||
.replaceAll(RegExp(artists.join('|'), caseSensitive: false), '')
|
||||
.trim();
|
||||
}
|
||||
|
||||
@ -29,13 +29,13 @@ abstract class ServiceUtils {
|
||||
List<String> artists = const [],
|
||||
bool onlyCleanArtist = false,
|
||||
}) {
|
||||
final match = RegExp(r"(?<=\().+?(?=\))").firstMatch(title)?.group(0);
|
||||
final match = RegExp(r'(?<=\().+?(?=\))').firstMatch(title)?.group(0);
|
||||
final artistInBracket =
|
||||
artists.any((artist) => match?.contains(artist) ?? false);
|
||||
|
||||
if (artistInBracket) {
|
||||
title = title.replaceAll(
|
||||
RegExp(" *\\([^)]*\\) *"),
|
||||
RegExp(' *\\([^)]*\\) *'),
|
||||
'',
|
||||
);
|
||||
}
|
||||
@ -47,9 +47,9 @@ abstract class ServiceUtils {
|
||||
|
||||
return "$title ${artists.map((e) => e.replaceAll(",", " ")).join(", ")}"
|
||||
.toLowerCase()
|
||||
.replaceAll(RegExp(r"\s*\[[^\]]*]"), ' ')
|
||||
.replaceAll(RegExp(r"\sfeat\.|\sft\."), ' ')
|
||||
.replaceAll(RegExp(r"\s+"), ' ')
|
||||
.replaceAll(RegExp(r'\s*\[[^\]]*]'), ' ')
|
||||
.replaceAll(RegExp(r'\sfeat\.|\sft\.'), ' ')
|
||||
.replaceAll(RegExp(r'\s+'), ' ')
|
||||
.trim();
|
||||
}
|
||||
|
||||
@ -60,18 +60,18 @@ abstract class ServiceUtils {
|
||||
Document document = parser.parse(response.body);
|
||||
String? lyrics = document.querySelector('div.lyrics')?.text.trim();
|
||||
if (lyrics == null) {
|
||||
lyrics = "";
|
||||
lyrics = '';
|
||||
document
|
||||
.querySelectorAll("div[class^=\"Lyrics__Container\"]")
|
||||
.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 snippet = element.innerHtml.replaceAll('<br>', '\n').replaceAll(
|
||||
RegExp('<(?!\\s*br\\s*\\/?)[^>]+>', caseSensitive: false),
|
||||
'',
|
||||
);
|
||||
final el = document.createElement("textarea");
|
||||
final el = document.createElement('textarea');
|
||||
el.innerHtml = snippet;
|
||||
lyrics = "$lyrics${el.text.trim()}\n\n";
|
||||
lyrics = '$lyrics${el.text.trim()}\n\n';
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -143,16 +143,16 @@ abstract class ServiceUtils {
|
||||
|
||||
static DateTime parseSpotifyAlbumDate(AlbumSimple? album) {
|
||||
if (album == null || album.releaseDate == null) {
|
||||
return DateTime.parse("1975-01-01");
|
||||
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");
|
||||
return DateTime.parse('${album.releaseDate}-01');
|
||||
case DatePrecision.year:
|
||||
return DateTime.parse("${album.releaseDate}-01-01");
|
||||
return DateTime.parse('${album.releaseDate}-01-01');
|
||||
}
|
||||
}
|
||||
|
||||
@ -162,9 +162,9 @@ abstract class ServiceUtils {
|
||||
..sort((a, b) {
|
||||
switch (sortBy) {
|
||||
case SortBy.ascending:
|
||||
return a.name?.compareTo(b.name ?? "") ?? 0;
|
||||
return a.name?.compareTo(b.name ?? '') ?? 0;
|
||||
case SortBy.descending:
|
||||
return b.name?.compareTo(a.name ?? "") ?? 0;
|
||||
return b.name?.compareTo(a.name ?? '') ?? 0;
|
||||
case SortBy.newest:
|
||||
final aDate = parseSpotifyAlbumDate(a.album);
|
||||
final bDate = parseSpotifyAlbumDate(b.album);
|
||||
@ -177,10 +177,10 @@ abstract class ServiceUtils {
|
||||
return a.durationMs?.compareTo(b.durationMs ?? 0) ?? 0;
|
||||
case SortBy.artist:
|
||||
return a.artists?.first.name
|
||||
?.compareTo(b.artists?.first.name ?? "") ??
|
||||
?.compareTo(b.artists?.first.name ?? '') ??
|
||||
0;
|
||||
case SortBy.album:
|
||||
return a.album?.name?.compareTo(b.album?.name ?? "") ?? 0;
|
||||
return a.album?.name?.compareTo(b.album?.name ?? '') ?? 0;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import 'dart:math';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:gap/gap.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:rhythm_box/providers/audio_player.dart';
|
||||
import 'package:rhythm_box/services/audio_player/audio_player.dart';
|
||||
import 'package:rhythm_box/services/audio_services/image.dart';
|
||||
@ -102,69 +103,77 @@ class _BottomPlayerState extends State<BottomPlayer>
|
||||
axis: Axis.vertical,
|
||||
axisAlignment: -1,
|
||||
child: Obx(
|
||||
() => Column(
|
||||
children: [
|
||||
if (_durationCurrent != Duration.zero)
|
||||
TweenAnimationBuilder<double>(
|
||||
tween: Tween(
|
||||
begin: 0,
|
||||
end: _durationCurrent.inMilliseconds /
|
||||
max(_durationTotal.inMilliseconds, 1),
|
||||
),
|
||||
duration: const Duration(milliseconds: 100),
|
||||
builder: (context, value, _) => LinearProgressIndicator(
|
||||
minHeight: 3,
|
||||
value: value,
|
||||
),
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
ClipRRect(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||
child: _albumArt != null
|
||||
? AutoCacheImage(_albumArt!, width: 64, height: 64)
|
||||
: Container(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.surfaceContainerHigh,
|
||||
width: 64,
|
||||
height: 64,
|
||||
child: const Center(child: Icon(Icons.image)),
|
||||
),
|
||||
),
|
||||
const Gap(12),
|
||||
Expanded(
|
||||
child: PlayerTrackDetails(
|
||||
track: _playback.state.value.activeTrack,
|
||||
() => GestureDetector(
|
||||
child: Column(
|
||||
children: [
|
||||
if (_durationCurrent != Duration.zero)
|
||||
TweenAnimationBuilder<double>(
|
||||
tween: Tween(
|
||||
begin: 0,
|
||||
end: _durationCurrent.inMilliseconds /
|
||||
max(_durationTotal.inMilliseconds, 1),
|
||||
),
|
||||
duration: const Duration(milliseconds: 100),
|
||||
builder: (context, value, _) => LinearProgressIndicator(
|
||||
minHeight: 3,
|
||||
value: value,
|
||||
),
|
||||
),
|
||||
const Gap(12),
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
IconButton(
|
||||
icon: _isFetchingActiveTrack
|
||||
? const SizedBox(
|
||||
height: 20,
|
||||
width: 20,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 3,
|
||||
),
|
||||
)
|
||||
: Icon(
|
||||
!_isPlaying ? Icons.play_arrow : Icons.pause,
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
ClipRRect(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||
child: Hero(
|
||||
tag: const Key('current-active-track-album-art'),
|
||||
child: _albumArt != null
|
||||
? AutoCacheImage(_albumArt!, width: 64, height: 64)
|
||||
: Container(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.surfaceContainerHigh,
|
||||
width: 64,
|
||||
height: 64,
|
||||
child: const Center(child: Icon(Icons.image)),
|
||||
),
|
||||
onPressed:
|
||||
_isFetchingActiveTrack ? null : _togglePlayState,
|
||||
),
|
||||
],
|
||||
),
|
||||
const Gap(12),
|
||||
],
|
||||
).paddingSymmetric(horizontal: 12, vertical: 8),
|
||||
],
|
||||
),
|
||||
const Gap(12),
|
||||
Expanded(
|
||||
child: PlayerTrackDetails(
|
||||
track: _playback.state.value.activeTrack,
|
||||
),
|
||||
),
|
||||
const Gap(12),
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
IconButton(
|
||||
icon: _isFetchingActiveTrack
|
||||
? const SizedBox(
|
||||
height: 20,
|
||||
width: 20,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 3,
|
||||
),
|
||||
)
|
||||
: Icon(
|
||||
!_isPlaying ? Icons.play_arrow : Icons.pause,
|
||||
),
|
||||
onPressed:
|
||||
_isFetchingActiveTrack ? null : _togglePlayState,
|
||||
),
|
||||
],
|
||||
),
|
||||
const Gap(12),
|
||||
],
|
||||
).paddingSymmetric(horizontal: 12, vertical: 8),
|
||||
],
|
||||
),
|
||||
onTap: () {
|
||||
GoRouter.of(context).pushNamed('player');
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -23,7 +23,7 @@ class PlayerTrackDetails extends StatelessWidget {
|
||||
const SizedBox(height: 4),
|
||||
InkWell(
|
||||
child: Text(
|
||||
playback.state.value.activeTrack?.name ?? "Not playing",
|
||||
playback.state.value.activeTrack?.name ?? 'Not playing',
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: theme.textTheme.bodyMedium!.copyWith(
|
||||
@ -36,7 +36,7 @@ class PlayerTrackDetails extends StatelessWidget {
|
||||
),
|
||||
Text(
|
||||
playback.state.value.activeTrack?.artists?.asString() ??
|
||||
"No author",
|
||||
'No author',
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: theme.textTheme.bodySmall!.copyWith(color: color),
|
||||
)
|
||||
|
@ -377,7 +377,7 @@ packages:
|
||||
source: hosted
|
||||
version: "0.6.7"
|
||||
json_annotation:
|
||||
dependency: transitive
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: json_annotation
|
||||
sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1"
|
||||
|
@ -65,6 +65,7 @@ dependencies:
|
||||
shelf: ^1.4.1
|
||||
shelf_router: ^1.1.4
|
||||
dio: ^5.6.0
|
||||
json_annotation: ^4.9.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
Loading…
Reference in New Issue
Block a user