From 95b04adede3a92adc2cb4cb940048e4307eb7e02 Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Tue, 27 Aug 2024 14:48:31 +0800 Subject: [PATCH] :art: Prefer single quote --- analysis_options.yaml | 1 + lib/collections/assets.gen.dart | 192 --------- lib/collections/formatters.dart | 2 +- lib/collections/language_codes.dart | 166 ++++---- lib/collections/spotify_markets.dart | 366 +++++++++--------- lib/providers/audio_player.dart | 4 +- lib/providers/spotify.dart | 4 +- lib/router.dart | 18 +- lib/screens/player/view.dart | 15 + lib/screens/playlist/view.dart | 2 +- lib/services/artist.dart | 4 +- lib/services/audio_player/audio_player.dart | 14 +- lib/services/audio_player/custom_player.dart | 16 +- .../audio_services/audio_services.dart | 8 +- .../audio_services/windows_audio_service.dart | 6 +- lib/services/primitive.dart | 10 +- lib/services/rhythm_media.dart | 14 +- lib/services/server/routes/playback.dart | 12 +- lib/services/server/server.dart | 4 +- lib/services/song_link/song_link.dart | 20 +- lib/services/sourced_track/enums.dart | 2 +- lib/services/sourced_track/models/search.dart | 4 +- .../sourced_track/models/video_info.dart | 2 +- lib/services/sourced_track/sourced_track.dart | 14 +- lib/services/sourced_track/sources/piped.dart | 8 +- .../sourced_track/sources/youtube.dart | 18 +- lib/services/utils.dart | 40 +- lib/widgets/player/bottom_player.dart | 127 +++--- lib/widgets/player/track_details.dart | 4 +- pubspec.lock | 2 +- pubspec.yaml | 1 + 31 files changed, 470 insertions(+), 630 deletions(-) delete mode 100755 lib/collections/assets.gen.dart create mode 100644 lib/screens/player/view.dart diff --git a/analysis_options.yaml b/analysis_options.yaml index 0d29021..5599e01 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -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 diff --git a/lib/collections/assets.gen.dart b/lib/collections/assets.gen.dart deleted file mode 100755 index 1f36103..0000000 --- a/lib/collections/assets.gen.dart +++ /dev/null @@ -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 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 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 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? 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; -} diff --git a/lib/collections/formatters.dart b/lib/collections/formatters.dart index 0aed9e9..8a60768 100755 --- a/lib/collections/formatters.dart +++ b/lib/collections/formatters.dart @@ -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, ); diff --git a/lib/collections/language_codes.dart b/lib/collections/language_codes.dart index 44da6ee..5712af9 100755 --- a/lib/collections/language_codes.dart +++ b/lib/collections/language_codes.dart @@ -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'); } } } diff --git a/lib/collections/spotify_markets.dart b/lib/collections/spotify_markets.dart index 514b3f0..ab6163a 100755 --- a/lib/collections/spotify_markets.dart +++ b/lib/collections/spotify_markets.dart @@ -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)'), ]; diff --git a/lib/providers/audio_player.dart b/lib/providers/audio_player.dart index 57a88f9..e979b1c 100644 --- a/lib/providers/audio_player.dart +++ b/lib/providers/audio_player.dart @@ -81,7 +81,7 @@ class AudioPlayerProvider extends GetxController { } Future _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 _updateSavedState() async { final out = jsonEncode(state.value.toJson()); - await _prefs.setString("player_state", out); + await _prefs.setString('player_state', out); } Future addCollections(List collectionIds) async { diff --git a/lib/providers/spotify.dart b/lib/providers/spotify.dart index aac7d2a..6ad6f52 100644 --- a/lib/providers/spotify.dart +++ b/lib/providers/spotify.dart @@ -8,8 +8,8 @@ class SpotifyProvider extends GetxController { void onInit() { api = SpotifyApi( SpotifyApiCredentials( - "f73d4bff91d64d89be9930036f553534", - "5cbec0b928d247cd891d06195f07b8c9", + 'f73d4bff91d64d89be9930036f553534', + '5cbec0b928d247cd891d06195f07b8c9', ), ); super.onInit(); diff --git a/lib/router.dart b/lib/router.dart index 025e2bd..f94de03 100644 --- a/lib/router.dart +++ b/lib/router.dart @@ -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(), + ), ]); diff --git a/lib/screens/player/view.dart b/lib/screens/player/view.dart new file mode 100644 index 0000000..42433ef --- /dev/null +++ b/lib/screens/player/view.dart @@ -0,0 +1,15 @@ +import 'package:flutter/material.dart'; + +class PlayerScreen extends StatefulWidget { + const PlayerScreen({super.key}); + + @override + State createState() => _PlayerScreenState(); +} + +class _PlayerScreenState extends State { + @override + Widget build(BuildContext context) { + return const Placeholder(); + } +} diff --git a/lib/screens/playlist/view.dart b/lib/screens/playlist/view.dart index 405b83a..a61e1f6 100644 --- a/lib/screens/playlist/view.dart +++ b/lib/screens/playlist/view.dart @@ -104,7 +104,7 @@ class _PlaylistViewScreenState extends State { "${NumberFormat.compactCurrency(symbol: '', decimalDigits: 2).format(_playlist!.followers!.total!)} saves", ), Text( - "#${_playlist!.id}", + '#${_playlist!.id}', style: GoogleFonts.robotoMono(fontSize: 10), ), ], diff --git a/lib/services/artist.dart b/lib/services/artist.dart index 1f26989..71b8ec0 100644 --- a/lib/services/artist.dart +++ b/lib/services/artist.dart @@ -2,12 +2,12 @@ import 'package:spotify/spotify.dart'; extension ArtistSimpleExtension on List { String asString() { - return map((e) => e.name?.replaceAll(",", " ")).join(", "); + return map((e) => e.name?.replaceAll(',', ' ')).join(', '); } } extension ArtistExtension on List { String asString() { - return map((e) => e.name?.replaceAll(",", " ")).join(", "); + return map((e) => e.name?.replaceAll(',', ' ')).join(', '); } } diff --git a/lib/services/audio_player/audio_player.dart b/lib/services/audio_player/audio_player.dart index cd41b55..80da8d7 100755 --- a/lib/services/audio_player/audio_player.dart +++ b/lib/services/audio_player/audio_player.dart @@ -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'); }); } diff --git a/lib/services/audio_player/custom_player.dart b/lib/services/audio_player/custom_player.dart index a3ed210..3f122ee 100755 --- a/lib/services/audio_player/custom_player.dart +++ b/lib/services/audio_player/custom_player.dart @@ -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 }, ), ); diff --git a/lib/services/audio_services/audio_services.dart b/lib/services/audio_services/audio_services.dart index bf5f279..0e43e84 100755 --- a/lib/services/audio_services/audio_services.dart +++ b/lib/services/audio_services/audio_services.dart @@ -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), diff --git a/lib/services/audio_services/windows_audio_service.dart b/lib/services/audio_services/windows_audio_service.dart index 40e13e5..d5795c2 100755 --- a/lib/services/audio_services/windows_audio_service.dart +++ b/lib/services/audio_services/windows_audio_service.dart @@ -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(), ), ); diff --git a/lib/services/primitive.dart b/lib/services/primitive.dart index 801c2e5..dd1eff1 100755 --- a/lib/services/primitive.dart +++ b/lib/services/primitive.dart @@ -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); } diff --git a/lib/services/rhythm_media.dart b/lib/services/rhythm_media.dart index 0037d1e..a9d8c5a 100644 --- a/lib/services/rhythm_media.dart +++ b/lib/services/rhythm_media.dart @@ -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'); }); } diff --git a/lib/services/server/routes/playback.dart b/lib/services/server/routes/playback.dart index e96460c..5cba05e 100755 --- a/lib/services/server/routes/playback.dart +++ b/lib/services/server/routes/playback.dart @@ -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, ); diff --git a/lib/services/server/server.dart b/lib/services/server/server.dart index 9a0684f..0b9cdfa 100755 --- a/lib/services/server/server.dart +++ b/lib/services/server/server.dart @@ -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/", + '/stream/', Get.find().getStreamTrackId, ); diff --git a/lib/services/song_link/song_link.dart b/lib/services/song_link/song_link.dart index a7e81f1..78c9ac8 100755 --- a/lib/services/song_link/song_link.dart +++ b/lib/services/song_link/song_link.dart @@ -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 []; } final pageProps = jsonDecode(script) as Map; - 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() ?? []; diff --git a/lib/services/sourced_track/enums.dart b/lib/services/sourced_track/enums.dart index e4803d0..9627dc4 100755 --- a/lib/services/sourced_track/enums.dart +++ b/lib/services/sourced_track/enums.dart @@ -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; diff --git a/lib/services/sourced_track/models/search.dart b/lib/services/sourced_track/models/search.dart index b62c975..976d583 100644 --- a/lib/services/sourced_track/models/search.dart +++ b/lib/services/sourced_track/models/search.dart @@ -1,6 +1,6 @@ enum SearchMode { - youtube._("YouTube"), - youtubeMusic._("YouTube Music"); + youtube._('YouTube'), + youtubeMusic._('YouTube Music'); final String label; diff --git a/lib/services/sourced_track/models/video_info.dart b/lib/services/sourced_track/models/video_info.dart index 38a9759..09394c0 100755 --- a/lib/services/sourced_track/models/video_info.dart +++ b/lib/services/sourced_track/models/video_info.dart @@ -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), diff --git a/lib/services/sourced_track/sourced_track.dart b/lib/services/sourced_track/sourced_track.dart index 9e2dfaa..ba980dd 100755 --- a/lib/services/sourced_track/sourced_track.dart +++ b/lib/services/sourced_track/sourced_track.dart @@ -41,18 +41,18 @@ abstract class SourcedTrack extends Track { static SourcedTrack fromJson(Map 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(); 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), }; } diff --git a/lib/services/sourced_track/sources/piped.dart b/lib/services/sourced_track/sources/piped.dart index 182c029..1814568 100755 --- a/lib/services/sourced_track/sources/piped.dart +++ b/lib/services/sourced_track/sources/piped.dart @@ -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 ?? []) diff --git a/lib/services/sourced_track/sources/youtube.dart b/lib/services/sourced_track/sources/youtube.dart index 938c71d..3d42b35 100755 --- a/lib/services/sourced_track/sources/youtube.dart +++ b/lib/services/sourced_track/sources/youtube.dart @@ -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 diff --git a/lib/services/utils.dart b/lib/services/utils.dart index 5f824db..6204157 100644 --- a/lib/services/utils.dart +++ b/lib/services/utils.dart @@ -20,7 +20,7 @@ abstract class ServiceUtils { static String clearArtistsOfTitle(String title, List artists) { return title - .replaceAll(RegExp(artists.join("|"), caseSensitive: false), "") + .replaceAll(RegExp(artists.join('|'), caseSensitive: false), '') .trim(); } @@ -29,13 +29,13 @@ abstract class ServiceUtils { List 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("
", "\n").replaceAll( - RegExp("<(?!\\s*br\\s*\\/?)[^>]+>", caseSensitive: false), - "", + final snippet = element.innerHtml.replaceAll('
', '\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; } diff --git a/lib/widgets/player/bottom_player.dart b/lib/widgets/player/bottom_player.dart index a9461d1..1e12e71 100644 --- a/lib/widgets/player/bottom_player.dart +++ b/lib/widgets/player/bottom_player.dart @@ -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 axis: Axis.vertical, axisAlignment: -1, child: Obx( - () => Column( - children: [ - if (_durationCurrent != Duration.zero) - TweenAnimationBuilder( - 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( + 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'); + }, ), ), ); diff --git a/lib/widgets/player/track_details.dart b/lib/widgets/player/track_details.dart index 79a004a..ea2fc22 100644 --- a/lib/widgets/player/track_details.dart +++ b/lib/widgets/player/track_details.dart @@ -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), ) diff --git a/pubspec.lock b/pubspec.lock index db964d1..99c073e 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -377,7 +377,7 @@ packages: source: hosted version: "0.6.7" json_annotation: - dependency: transitive + dependency: "direct main" description: name: json_annotation sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" diff --git a/pubspec.yaml b/pubspec.yaml index f7021cd..40f607a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -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: