✨ Playback server
This commit is contained in:
parent
84d66fbc4b
commit
031cab75e0
@ -1,34 +1,77 @@
|
|||||||
PODS:
|
PODS:
|
||||||
|
- audio_service (0.0.1):
|
||||||
|
- Flutter
|
||||||
|
- audio_session (0.0.1):
|
||||||
|
- Flutter
|
||||||
|
- device_info_plus (0.0.1):
|
||||||
|
- Flutter
|
||||||
- Flutter (1.0.0)
|
- Flutter (1.0.0)
|
||||||
|
- flutter_broadcasts (0.0.1):
|
||||||
|
- Flutter
|
||||||
|
- media_kit_libs_ios_audio (1.0.4):
|
||||||
|
- Flutter
|
||||||
|
- media_kit_native_event_loop (1.0.0):
|
||||||
|
- Flutter
|
||||||
- package_info_plus (0.4.5):
|
- package_info_plus (0.4.5):
|
||||||
- Flutter
|
- Flutter
|
||||||
- path_provider_foundation (0.0.1):
|
- path_provider_foundation (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
|
- shared_preferences_foundation (0.0.1):
|
||||||
|
- Flutter
|
||||||
|
- FlutterMacOS
|
||||||
- sqflite (0.0.3):
|
- sqflite (0.0.3):
|
||||||
- Flutter
|
- Flutter
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
|
|
||||||
DEPENDENCIES:
|
DEPENDENCIES:
|
||||||
|
- audio_service (from `.symlinks/plugins/audio_service/ios`)
|
||||||
|
- audio_session (from `.symlinks/plugins/audio_session/ios`)
|
||||||
|
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
|
||||||
- Flutter (from `Flutter`)
|
- Flutter (from `Flutter`)
|
||||||
|
- flutter_broadcasts (from `.symlinks/plugins/flutter_broadcasts/ios`)
|
||||||
|
- media_kit_libs_ios_audio (from `.symlinks/plugins/media_kit_libs_ios_audio/ios`)
|
||||||
|
- media_kit_native_event_loop (from `.symlinks/plugins/media_kit_native_event_loop/ios`)
|
||||||
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
|
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
|
||||||
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
|
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
|
||||||
|
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
|
||||||
- sqflite (from `.symlinks/plugins/sqflite/darwin`)
|
- sqflite (from `.symlinks/plugins/sqflite/darwin`)
|
||||||
|
|
||||||
EXTERNAL SOURCES:
|
EXTERNAL SOURCES:
|
||||||
|
audio_service:
|
||||||
|
:path: ".symlinks/plugins/audio_service/ios"
|
||||||
|
audio_session:
|
||||||
|
:path: ".symlinks/plugins/audio_session/ios"
|
||||||
|
device_info_plus:
|
||||||
|
:path: ".symlinks/plugins/device_info_plus/ios"
|
||||||
Flutter:
|
Flutter:
|
||||||
:path: Flutter
|
:path: Flutter
|
||||||
|
flutter_broadcasts:
|
||||||
|
:path: ".symlinks/plugins/flutter_broadcasts/ios"
|
||||||
|
media_kit_libs_ios_audio:
|
||||||
|
:path: ".symlinks/plugins/media_kit_libs_ios_audio/ios"
|
||||||
|
media_kit_native_event_loop:
|
||||||
|
:path: ".symlinks/plugins/media_kit_native_event_loop/ios"
|
||||||
package_info_plus:
|
package_info_plus:
|
||||||
:path: ".symlinks/plugins/package_info_plus/ios"
|
:path: ".symlinks/plugins/package_info_plus/ios"
|
||||||
path_provider_foundation:
|
path_provider_foundation:
|
||||||
:path: ".symlinks/plugins/path_provider_foundation/darwin"
|
:path: ".symlinks/plugins/path_provider_foundation/darwin"
|
||||||
|
shared_preferences_foundation:
|
||||||
|
:path: ".symlinks/plugins/shared_preferences_foundation/darwin"
|
||||||
sqflite:
|
sqflite:
|
||||||
:path: ".symlinks/plugins/sqflite/darwin"
|
:path: ".symlinks/plugins/sqflite/darwin"
|
||||||
|
|
||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
|
audio_service: f509d65da41b9521a61f1c404dd58651f265a567
|
||||||
|
audio_session: 088d2483ebd1dc43f51d253d4a1c517d9a2e7207
|
||||||
|
device_info_plus: 97af1d7e84681a90d0693e63169a5d50e0839a0d
|
||||||
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
|
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
|
||||||
|
flutter_broadcasts: 3ece15b27d8ccbe2132c3df303e7c3401feab882
|
||||||
|
media_kit_libs_ios_audio: 8f39d96a9c630685dfb844c289bd1d114c486fb3
|
||||||
|
media_kit_native_event_loop: 99111eded5acbdc9c2738021ea6550dd36ca8837
|
||||||
package_info_plus: 58f0028419748fad15bf008b270aaa8e54380b1c
|
package_info_plus: 58f0028419748fad15bf008b270aaa8e54380b1c
|
||||||
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
|
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
|
||||||
|
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
|
||||||
sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec
|
sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec
|
||||||
|
|
||||||
PODFILE CHECKSUM: 819463e6a0290f5a72f145ba7cde16e8b6ef0796
|
PODFILE CHECKSUM: 819463e6a0290f5a72f145ba7cde16e8b6ef0796
|
||||||
|
192
lib/collections/assets.gen.dart
Executable file
192
lib/collections/assets.gen.dart
Executable file
@ -0,0 +1,192 @@
|
|||||||
|
/// 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;
|
||||||
|
}
|
8
lib/collections/formatters.dart
Executable file
8
lib/collections/formatters.dart
Executable file
@ -0,0 +1,8 @@
|
|||||||
|
import 'package:intl/intl.dart';
|
||||||
|
|
||||||
|
final compactNumberFormatter = NumberFormat.compact();
|
||||||
|
final usdFormatter = NumberFormat.compactCurrency(
|
||||||
|
locale: 'en-US',
|
||||||
|
symbol: r"$",
|
||||||
|
decimalDigits: 2,
|
||||||
|
);
|
232
lib/collections/gradients.dart
Executable file
232
lib/collections/gradients.dart
Executable file
@ -0,0 +1,232 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
const gradients = [
|
||||||
|
LinearGradient(colors: [
|
||||||
|
Color.fromRGBO(123, 102, 255, 1),
|
||||||
|
Color.fromRGBO(95, 189, 255, 1),
|
||||||
|
Color.fromRGBO(150, 239, 255, 1),
|
||||||
|
Color.fromRGBO(197, 255, 248, 1)
|
||||||
|
]),
|
||||||
|
LinearGradient(colors: [
|
||||||
|
Color.fromRGBO(245, 204, 160, 1),
|
||||||
|
Color.fromRGBO(228, 143, 69, 1),
|
||||||
|
Color.fromRGBO(153, 77, 28, 1),
|
||||||
|
Color.fromRGBO(107, 36, 12, 1)
|
||||||
|
]),
|
||||||
|
LinearGradient(colors: [
|
||||||
|
Color.fromRGBO(243, 243, 243, 1),
|
||||||
|
Color.fromRGBO(197, 232, 152, 1),
|
||||||
|
Color.fromRGBO(41, 173, 178, 1),
|
||||||
|
Color.fromRGBO(7, 102, 173, 1)
|
||||||
|
]),
|
||||||
|
LinearGradient(colors: [
|
||||||
|
Color.fromRGBO(240, 89, 65, 1),
|
||||||
|
Color.fromRGBO(190, 49, 68, 1),
|
||||||
|
Color.fromRGBO(135, 35, 65, 1),
|
||||||
|
Color.fromRGBO(34, 9, 44, 1)
|
||||||
|
]),
|
||||||
|
LinearGradient(colors: [
|
||||||
|
Color.fromRGBO(119, 107, 93, 1),
|
||||||
|
Color.fromRGBO(176, 166, 149, 1),
|
||||||
|
Color.fromRGBO(235, 227, 213, 1),
|
||||||
|
Color.fromRGBO(243, 238, 234, 1)
|
||||||
|
]),
|
||||||
|
LinearGradient(colors: [
|
||||||
|
Color.fromRGBO(208, 162, 247, 1),
|
||||||
|
Color.fromRGBO(220, 191, 255, 1),
|
||||||
|
Color.fromRGBO(229, 212, 255, 1),
|
||||||
|
Color.fromRGBO(241, 234, 255, 1)
|
||||||
|
]),
|
||||||
|
LinearGradient(colors: [
|
||||||
|
Color.fromRGBO(221, 242, 253, 1),
|
||||||
|
Color.fromRGBO(155, 190, 200, 1),
|
||||||
|
Color.fromRGBO(66, 125, 157, 1),
|
||||||
|
Color.fromRGBO(22, 72, 99, 1)
|
||||||
|
]),
|
||||||
|
LinearGradient(colors: [
|
||||||
|
Color.fromRGBO(119, 67, 219, 1),
|
||||||
|
Color.fromRGBO(195, 172, 208, 1),
|
||||||
|
Color.fromRGBO(247, 239, 229, 1),
|
||||||
|
Color.fromRGBO(255, 251, 245, 1)
|
||||||
|
]),
|
||||||
|
LinearGradient(colors: [
|
||||||
|
Color.fromRGBO(194, 217, 255, 1),
|
||||||
|
Color.fromRGBO(142, 143, 250, 1),
|
||||||
|
Color.fromRGBO(119, 82, 254, 1),
|
||||||
|
Color.fromRGBO(25, 4, 130, 1)
|
||||||
|
]),
|
||||||
|
LinearGradient(colors: [
|
||||||
|
Color.fromRGBO(104, 126, 255, 1),
|
||||||
|
Color.fromRGBO(128, 179, 255, 1),
|
||||||
|
Color.fromRGBO(152, 228, 255, 1),
|
||||||
|
Color.fromRGBO(182, 255, 250, 1)
|
||||||
|
]),
|
||||||
|
LinearGradient(colors: [
|
||||||
|
Color.fromRGBO(176, 87, 141, 1),
|
||||||
|
Color.fromRGBO(217, 136, 185, 1),
|
||||||
|
Color.fromRGBO(250, 203, 234, 1),
|
||||||
|
Color.fromRGBO(255, 228, 214, 1)
|
||||||
|
]),
|
||||||
|
LinearGradient(colors: [
|
||||||
|
Color.fromRGBO(190, 255, 247, 1),
|
||||||
|
Color.fromRGBO(166, 246, 255, 1),
|
||||||
|
Color.fromRGBO(158, 221, 255, 1),
|
||||||
|
Color.fromRGBO(100, 153, 233, 1)
|
||||||
|
]),
|
||||||
|
LinearGradient(colors: [
|
||||||
|
Color.fromRGBO(245, 252, 205, 1),
|
||||||
|
Color.fromRGBO(120, 214, 198, 1),
|
||||||
|
Color.fromRGBO(65, 145, 151, 1),
|
||||||
|
Color.fromRGBO(18, 72, 107, 1)
|
||||||
|
]),
|
||||||
|
LinearGradient(colors: [
|
||||||
|
Color.fromRGBO(229, 207, 247, 1),
|
||||||
|
Color.fromRGBO(157, 118, 193, 1),
|
||||||
|
Color.fromRGBO(113, 58, 190, 1),
|
||||||
|
Color.fromRGBO(91, 8, 136, 1)
|
||||||
|
]),
|
||||||
|
LinearGradient(colors: [
|
||||||
|
Color.fromRGBO(249, 222, 201, 1),
|
||||||
|
Color.fromRGBO(247, 140, 162, 1),
|
||||||
|
Color.fromRGBO(216, 0, 50, 1),
|
||||||
|
Color.fromRGBO(61, 12, 17, 1)
|
||||||
|
]),
|
||||||
|
LinearGradient(colors: [
|
||||||
|
Color.fromRGBO(242, 247, 161, 1),
|
||||||
|
Color.fromRGBO(53, 162, 159, 1),
|
||||||
|
Color.fromRGBO(8, 131, 149, 1),
|
||||||
|
Color.fromRGBO(7, 25, 82, 1)
|
||||||
|
]),
|
||||||
|
LinearGradient(colors: [
|
||||||
|
Color.fromRGBO(243, 159, 90, 1),
|
||||||
|
Color.fromRGBO(174, 68, 90, 1),
|
||||||
|
Color.fromRGBO(102, 37, 73, 1),
|
||||||
|
Color.fromRGBO(69, 25, 82, 1)
|
||||||
|
]),
|
||||||
|
LinearGradient(colors: [
|
||||||
|
Color.fromRGBO(255, 200, 200, 1),
|
||||||
|
Color.fromRGBO(255, 155, 130, 1),
|
||||||
|
Color.fromRGBO(255, 63, 164, 1),
|
||||||
|
Color.fromRGBO(87, 55, 93, 1)
|
||||||
|
]),
|
||||||
|
LinearGradient(colors: [
|
||||||
|
Color.fromRGBO(238, 238, 238, 1),
|
||||||
|
Color.fromRGBO(100, 204, 197, 1),
|
||||||
|
Color.fromRGBO(23, 107, 135, 1),
|
||||||
|
Color.fromRGBO(5, 59, 80, 1)
|
||||||
|
]),
|
||||||
|
LinearGradient(colors: [
|
||||||
|
Color.fromRGBO(198, 61, 47, 1),
|
||||||
|
Color.fromRGBO(226, 94, 62, 1),
|
||||||
|
Color.fromRGBO(255, 155, 80, 1),
|
||||||
|
Color.fromRGBO(255, 187, 92, 1)
|
||||||
|
]),
|
||||||
|
LinearGradient(colors: [
|
||||||
|
Color.fromRGBO(236, 83, 176, 1),
|
||||||
|
Color.fromRGBO(157, 68, 192, 1),
|
||||||
|
Color.fromRGBO(77, 45, 183, 1),
|
||||||
|
Color.fromRGBO(14, 33, 160, 1)
|
||||||
|
]),
|
||||||
|
LinearGradient(colors: [
|
||||||
|
Color.fromRGBO(242, 236, 190, 1),
|
||||||
|
Color.fromRGBO(226, 199, 153, 1),
|
||||||
|
Color.fromRGBO(192, 130, 97, 1),
|
||||||
|
Color.fromRGBO(154, 59, 59, 1)
|
||||||
|
]),
|
||||||
|
LinearGradient(colors: [
|
||||||
|
Color.fromRGBO(255, 253, 140, 1),
|
||||||
|
Color.fromRGBO(151, 255, 244, 1),
|
||||||
|
Color.fromRGBO(112, 145, 245, 1),
|
||||||
|
Color.fromRGBO(121, 63, 223, 1)
|
||||||
|
]),
|
||||||
|
LinearGradient(colors: [
|
||||||
|
Color.fromRGBO(67, 83, 52, 1),
|
||||||
|
Color.fromRGBO(158, 179, 132, 1),
|
||||||
|
Color.fromRGBO(206, 222, 189, 1),
|
||||||
|
Color.fromRGBO(250, 241, 228, 1)
|
||||||
|
]),
|
||||||
|
LinearGradient(colors: [
|
||||||
|
Color.fromRGBO(250, 240, 230, 1),
|
||||||
|
Color.fromRGBO(185, 180, 199, 1),
|
||||||
|
Color.fromRGBO(92, 84, 112, 1),
|
||||||
|
Color.fromRGBO(53, 47, 68, 1)
|
||||||
|
]),
|
||||||
|
LinearGradient(colors: [
|
||||||
|
Color.fromRGBO(255, 186, 134, 1),
|
||||||
|
Color.fromRGBO(246, 99, 92, 1),
|
||||||
|
Color.fromRGBO(194, 51, 115, 1),
|
||||||
|
Color.fromRGBO(121, 21, 91, 1)
|
||||||
|
]),
|
||||||
|
LinearGradient(colors: [
|
||||||
|
Color.fromRGBO(213, 255, 208, 1),
|
||||||
|
Color.fromRGBO(64, 248, 255, 1),
|
||||||
|
Color.fromRGBO(39, 158, 255, 1),
|
||||||
|
Color.fromRGBO(12, 53, 106, 1)
|
||||||
|
]),
|
||||||
|
LinearGradient(colors: [
|
||||||
|
Color.fromRGBO(131, 96, 150, 1),
|
||||||
|
Color.fromRGBO(237, 123, 123, 1),
|
||||||
|
Color.fromRGBO(240, 184, 110, 1),
|
||||||
|
Color.fromRGBO(235, 231, 108, 1)
|
||||||
|
]),
|
||||||
|
LinearGradient(colors: [
|
||||||
|
Color.fromRGBO(63, 29, 56, 1),
|
||||||
|
Color.fromRGBO(77, 60, 119, 1),
|
||||||
|
Color.fromRGBO(162, 103, 138, 1),
|
||||||
|
Color.fromRGBO(225, 152, 152, 1)
|
||||||
|
]),
|
||||||
|
LinearGradient(colors: [
|
||||||
|
Color.fromRGBO(254, 123, 229, 1),
|
||||||
|
Color.fromRGBO(151, 78, 195, 1),
|
||||||
|
Color.fromRGBO(80, 64, 153, 1),
|
||||||
|
Color.fromRGBO(49, 56, 102, 1)
|
||||||
|
]),
|
||||||
|
LinearGradient(colors: [
|
||||||
|
Color.fromRGBO(248, 222, 34, 1),
|
||||||
|
Color.fromRGBO(249, 76, 16, 1),
|
||||||
|
Color.fromRGBO(199, 0, 57, 1),
|
||||||
|
Color.fromRGBO(144, 12, 63, 1)
|
||||||
|
]),
|
||||||
|
LinearGradient(colors: [
|
||||||
|
Color.fromRGBO(101, 69, 31, 1),
|
||||||
|
Color.fromRGBO(118, 88, 39, 1),
|
||||||
|
Color.fromRGBO(200, 174, 125, 1),
|
||||||
|
Color.fromRGBO(234, 198, 150, 1)
|
||||||
|
]),
|
||||||
|
LinearGradient(colors: [
|
||||||
|
Color.fromRGBO(255, 246, 224, 1),
|
||||||
|
Color.fromRGBO(216, 217, 218, 1),
|
||||||
|
Color.fromRGBO(97, 103, 122, 1),
|
||||||
|
Color.fromRGBO(39, 40, 41, 1)
|
||||||
|
]),
|
||||||
|
LinearGradient(colors: [
|
||||||
|
Color.fromRGBO(145, 109, 179, 1),
|
||||||
|
Color.fromRGBO(228, 133, 134, 1),
|
||||||
|
Color.fromRGBO(252, 186, 173, 1),
|
||||||
|
Color.fromRGBO(253, 229, 236, 1)
|
||||||
|
]),
|
||||||
|
LinearGradient(colors: [
|
||||||
|
Color.fromRGBO(124, 115, 192, 1),
|
||||||
|
Color.fromRGBO(148, 173, 215, 1),
|
||||||
|
Color.fromRGBO(172, 250, 223, 1),
|
||||||
|
Color.fromRGBO(232, 255, 206, 1)
|
||||||
|
]),
|
||||||
|
LinearGradient(colors: [
|
||||||
|
Color.fromRGBO(174, 216, 204, 1),
|
||||||
|
Color.fromRGBO(205, 102, 136, 1),
|
||||||
|
Color.fromRGBO(122, 49, 111, 1),
|
||||||
|
Color.fromRGBO(70, 25, 89, 1)
|
||||||
|
]),
|
||||||
|
LinearGradient(colors: [
|
||||||
|
Color.fromRGBO(237, 228, 255, 1),
|
||||||
|
Color.fromRGBO(215, 187, 245, 1),
|
||||||
|
Color.fromRGBO(160, 118, 249, 1),
|
||||||
|
Color.fromRGBO(101, 40, 247, 1)
|
||||||
|
]),
|
||||||
|
LinearGradient(colors: [
|
||||||
|
Color.fromRGBO(255, 236, 175, 1),
|
||||||
|
Color.fromRGBO(255, 176, 127, 1),
|
||||||
|
Color.fromRGBO(255, 82, 162, 1),
|
||||||
|
Color.fromRGBO(243, 21, 89, 1)
|
||||||
|
]),
|
||||||
|
];
|
26
lib/collections/initializers.dart
Executable file
26
lib/collections/initializers.dart
Executable file
@ -0,0 +1,26 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:rhythm_box/platform.dart';
|
||||||
|
import 'package:win32_registry/win32_registry.dart';
|
||||||
|
|
||||||
|
Future<void> registerWindowsScheme(String scheme) async {
|
||||||
|
if (!PlatformInfo.isWindows) return;
|
||||||
|
String appPath = Platform.resolvedExecutable;
|
||||||
|
|
||||||
|
String protocolRegKey = 'Software\\Classes\\$scheme';
|
||||||
|
RegistryValue protocolRegValue = const RegistryValue(
|
||||||
|
'URL Protocol',
|
||||||
|
RegistryValueType.string,
|
||||||
|
'',
|
||||||
|
);
|
||||||
|
String protocolCmdRegKey = 'shell\\open\\command';
|
||||||
|
RegistryValue protocolCmdRegValue = RegistryValue(
|
||||||
|
'',
|
||||||
|
RegistryValueType.string,
|
||||||
|
'"$appPath" "%1"',
|
||||||
|
);
|
||||||
|
|
||||||
|
final regKey = Registry.currentUser.createKey(protocolRegKey);
|
||||||
|
regKey.createValue(protocolRegValue);
|
||||||
|
regKey.createKey(protocolCmdRegKey).createValue(protocolCmdRegValue);
|
||||||
|
}
|
88
lib/collections/intents.dart
Executable file
88
lib/collections/intents.dart
Executable file
@ -0,0 +1,88 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
import 'package:rhythm_box/platform.dart';
|
||||||
|
import 'package:rhythm_box/services/audio_player/audio_player.dart';
|
||||||
|
|
||||||
|
class PlayPauseIntent extends Intent {
|
||||||
|
const PlayPauseIntent();
|
||||||
|
}
|
||||||
|
|
||||||
|
class PlayPauseAction extends Action<PlayPauseIntent> {
|
||||||
|
@override
|
||||||
|
invoke(intent) async {
|
||||||
|
if (!audioPlayer.isPlaying) {
|
||||||
|
await audioPlayer.resume();
|
||||||
|
} else {
|
||||||
|
await audioPlayer.pause();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class NavigationIntent extends Intent {
|
||||||
|
final GoRouter router;
|
||||||
|
final String path;
|
||||||
|
const NavigationIntent(this.router, this.path);
|
||||||
|
}
|
||||||
|
|
||||||
|
class NavigationAction extends Action<NavigationIntent> {
|
||||||
|
@override
|
||||||
|
invoke(intent) {
|
||||||
|
intent.router.go(intent.path);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum HomeTabs {
|
||||||
|
browse,
|
||||||
|
search,
|
||||||
|
library,
|
||||||
|
lyrics,
|
||||||
|
}
|
||||||
|
|
||||||
|
class HomeTabIntent extends Intent {
|
||||||
|
final HomeTabs tab;
|
||||||
|
const HomeTabIntent({required this.tab});
|
||||||
|
}
|
||||||
|
|
||||||
|
class HomeTabAction extends Action<HomeTabIntent> {
|
||||||
|
@override
|
||||||
|
invoke(intent) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SeekIntent extends Intent {
|
||||||
|
final bool forward;
|
||||||
|
const SeekIntent(this.forward);
|
||||||
|
}
|
||||||
|
|
||||||
|
class SeekAction extends Action<SeekIntent> {
|
||||||
|
@override
|
||||||
|
invoke(intent) async {
|
||||||
|
final position = audioPlayer.position.inSeconds;
|
||||||
|
await audioPlayer.seek(
|
||||||
|
Duration(
|
||||||
|
seconds: intent.forward ? position + 5 : position - 5,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CloseAppIntent extends Intent {}
|
||||||
|
|
||||||
|
class CloseAppAction extends Action<CloseAppIntent> {
|
||||||
|
@override
|
||||||
|
invoke(intent) {
|
||||||
|
if (PlatformInfo.isDesktop) {
|
||||||
|
exit(0);
|
||||||
|
} else {
|
||||||
|
SystemNavigator.pop();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
757
lib/collections/language_codes.dart
Executable file
757
lib/collections/language_codes.dart
Executable file
@ -0,0 +1,757 @@
|
|||||||
|
class ISOLanguageName {
|
||||||
|
final String name;
|
||||||
|
final String nativeName;
|
||||||
|
|
||||||
|
const ISOLanguageName({
|
||||||
|
required this.name,
|
||||||
|
required this.nativeName,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return "$name ($nativeName)";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uncomment the languages as we add support for them
|
||||||
|
// Currently supported: bn,en,fr,hi,zh
|
||||||
|
abstract class LanguageLocals {
|
||||||
|
static final Map isoLangs = {
|
||||||
|
// "ab": const ISOLanguageName(
|
||||||
|
// name: "Abkhaz",
|
||||||
|
// nativeName: "аҧсуа",
|
||||||
|
// ),
|
||||||
|
// "aa": const ISOLanguageName(
|
||||||
|
// name: "Afar",
|
||||||
|
// nativeName: "Afaraf",
|
||||||
|
// ),
|
||||||
|
// "af": const ISOLanguageName(
|
||||||
|
// name: "Afrikaans",
|
||||||
|
// nativeName: "Afrikaans",
|
||||||
|
// ),
|
||||||
|
// "ak": const ISOLanguageName(
|
||||||
|
// name: "Akan",
|
||||||
|
// nativeName: "Akan",
|
||||||
|
// ),
|
||||||
|
// "sq": const ISOLanguageName(
|
||||||
|
// name: "Albanian",
|
||||||
|
// nativeName: "Shqip",
|
||||||
|
// ),
|
||||||
|
// "am": const ISOLanguageName(
|
||||||
|
// name: "Amharic",
|
||||||
|
// nativeName: "አማርኛ",
|
||||||
|
// ),
|
||||||
|
"ar": const ISOLanguageName(
|
||||||
|
name: "Arabic",
|
||||||
|
nativeName: "العربية",
|
||||||
|
),
|
||||||
|
// "an": const ISOLanguageName(
|
||||||
|
// name: "Aragonese",
|
||||||
|
// nativeName: "Aragonés",
|
||||||
|
// ),
|
||||||
|
// "hy": const ISOLanguageName(
|
||||||
|
// name: "Armenian",
|
||||||
|
// nativeName: "Հայերեն",
|
||||||
|
// ),
|
||||||
|
// "as": const ISOLanguageName(
|
||||||
|
// name: "Assamese",
|
||||||
|
// nativeName: "অসমীয়া",
|
||||||
|
// ),
|
||||||
|
// "av": const ISOLanguageName(
|
||||||
|
// name: "Avaric",
|
||||||
|
// nativeName: "авар мацӀ, магӀарул мацӀ",
|
||||||
|
// ),
|
||||||
|
// "ae": const ISOLanguageName(
|
||||||
|
// name: "Avestan",
|
||||||
|
// nativeName: "avesta",
|
||||||
|
// ),
|
||||||
|
// "ay": const ISOLanguageName(
|
||||||
|
// name: "Aymara",
|
||||||
|
// nativeName: "aymar aru",
|
||||||
|
// ),
|
||||||
|
// "az": const ISOLanguageName(
|
||||||
|
// name: "Azerbaijani",
|
||||||
|
// nativeName: "azərbaycan dili",
|
||||||
|
// ),
|
||||||
|
// "bm": const ISOLanguageName(
|
||||||
|
// name: "Bambara",
|
||||||
|
// nativeName: "bamanankan",
|
||||||
|
// ),
|
||||||
|
// "ba": const ISOLanguageName(
|
||||||
|
// name: "Bashkir",
|
||||||
|
// nativeName: "башҡорт теле",
|
||||||
|
// ),
|
||||||
|
"eu": const ISOLanguageName(
|
||||||
|
name: "Basque",
|
||||||
|
nativeName: "Euskara",
|
||||||
|
),
|
||||||
|
// "be": const ISOLanguageName(
|
||||||
|
// name: "Belarusian",
|
||||||
|
// nativeName: "Беларуская",
|
||||||
|
// ),
|
||||||
|
"bn": const ISOLanguageName(
|
||||||
|
name: "Bengali",
|
||||||
|
nativeName: "বাংলা",
|
||||||
|
),
|
||||||
|
// "bh": const ISOLanguageName(
|
||||||
|
// name: "Bihari",
|
||||||
|
// nativeName: "भोजपुरी",
|
||||||
|
// ),
|
||||||
|
// "bi": const ISOLanguageName(
|
||||||
|
// name: "Bislama",
|
||||||
|
// nativeName: "Bislama",
|
||||||
|
// ),
|
||||||
|
// "bs": const ISOLanguageName(
|
||||||
|
// name: "Bosnian",
|
||||||
|
// nativeName: "bosanski jezik",
|
||||||
|
// ),
|
||||||
|
// "br": const ISOLanguageName(
|
||||||
|
// name: "Breton",
|
||||||
|
// nativeName: "brezhoneg",
|
||||||
|
// ),
|
||||||
|
// "bg": const ISOLanguageName(
|
||||||
|
// name: "Bulgarian",
|
||||||
|
// nativeName: "български език",
|
||||||
|
// ),
|
||||||
|
// "my": const ISOLanguageName(
|
||||||
|
// name: "Burmese",
|
||||||
|
// nativeName: "ဗမာစာ",
|
||||||
|
// ),
|
||||||
|
"ca": const ISOLanguageName(
|
||||||
|
name: "Catalan",
|
||||||
|
nativeName: "Català",
|
||||||
|
),
|
||||||
|
// "ch": const ISOLanguageName(
|
||||||
|
// name: "Chamorro",
|
||||||
|
// nativeName: "Chamoru",
|
||||||
|
// ),
|
||||||
|
// "ce": const ISOLanguageName(
|
||||||
|
// name: "Chechen",
|
||||||
|
// nativeName: "нохчийн мотт",
|
||||||
|
// ),
|
||||||
|
// "ny": const ISOLanguageName(
|
||||||
|
// name: "Chichewa",
|
||||||
|
// nativeName: "chiCheŵa",
|
||||||
|
// ),
|
||||||
|
"zh": const ISOLanguageName(
|
||||||
|
name: "Simplified Chinese",
|
||||||
|
nativeName: "简体中文",
|
||||||
|
),
|
||||||
|
// "cv": const ISOLanguageName(
|
||||||
|
// name: "Chuvash",
|
||||||
|
// nativeName: "чӑваш чӗлхи",
|
||||||
|
// ),
|
||||||
|
// "kw": const ISOLanguageName(
|
||||||
|
// name: "Cornish",
|
||||||
|
// nativeName: "Kernewek",
|
||||||
|
// ),
|
||||||
|
// "co": const ISOLanguageName(
|
||||||
|
// name: "Corsican",
|
||||||
|
// nativeName: "lingua corsa",
|
||||||
|
// ),
|
||||||
|
// "cr": const ISOLanguageName(
|
||||||
|
// name: "Cree",
|
||||||
|
// nativeName: "ᓀᐦᐃᔭᐍᐏᐣ",
|
||||||
|
// ),
|
||||||
|
// "hr": const ISOLanguageName(
|
||||||
|
// name: "Croatian",
|
||||||
|
// nativeName: "hrvatski",
|
||||||
|
// ),
|
||||||
|
"cs": const ISOLanguageName(
|
||||||
|
name: "Czech",
|
||||||
|
nativeName: "česky, čeština",
|
||||||
|
),
|
||||||
|
// "da": const ISOLanguageName(
|
||||||
|
// name: "Danish",
|
||||||
|
// nativeName: "dansk",
|
||||||
|
// ),
|
||||||
|
// "dv": const ISOLanguageName(
|
||||||
|
// name: "Maldivian;",
|
||||||
|
// nativeName: "ދިވެހި",
|
||||||
|
// ),
|
||||||
|
"nl": const ISOLanguageName(
|
||||||
|
name: "Dutch",
|
||||||
|
nativeName: "Nederlands",
|
||||||
|
),
|
||||||
|
"en": const ISOLanguageName(
|
||||||
|
name: "English",
|
||||||
|
nativeName: "English",
|
||||||
|
),
|
||||||
|
// "eo": const ISOLanguageName(
|
||||||
|
// name: "Esperanto",
|
||||||
|
// nativeName: "Esperanto",
|
||||||
|
// ),
|
||||||
|
// "et": const ISOLanguageName(
|
||||||
|
// name: "Estonian",
|
||||||
|
// nativeName: "eesti",
|
||||||
|
// ),
|
||||||
|
// "ee": const ISOLanguageName(
|
||||||
|
// name: "Ewe",
|
||||||
|
// nativeName: "Eʋegbe",
|
||||||
|
// ),
|
||||||
|
// "fo": const ISOLanguageName(
|
||||||
|
// name: "Faroese",
|
||||||
|
// nativeName: "føroyskt",
|
||||||
|
// ),
|
||||||
|
// "fj": const ISOLanguageName(
|
||||||
|
// name: "Fijian",
|
||||||
|
// nativeName: "vosa Vakaviti",
|
||||||
|
// ),
|
||||||
|
"fi": const ISOLanguageName(
|
||||||
|
name: "Finnish",
|
||||||
|
nativeName: "suomi",
|
||||||
|
),
|
||||||
|
"fr": const ISOLanguageName(
|
||||||
|
name: "French",
|
||||||
|
nativeName: "français",
|
||||||
|
),
|
||||||
|
// "ff": const ISOLanguageName(
|
||||||
|
// name: "Fula; Fulah; Pulaar; Pular",
|
||||||
|
// nativeName: "Fulfulde, Pulaar, Pular",
|
||||||
|
// ),
|
||||||
|
// "gl": const ISOLanguageName(
|
||||||
|
// name: "Galician",
|
||||||
|
// nativeName: "Galego",
|
||||||
|
// ),
|
||||||
|
"ka": const ISOLanguageName(
|
||||||
|
name: "Georgian",
|
||||||
|
nativeName: "ქართული",
|
||||||
|
),
|
||||||
|
"de": const ISOLanguageName(
|
||||||
|
name: "German",
|
||||||
|
nativeName: "Deutsch",
|
||||||
|
),
|
||||||
|
// "el": const ISOLanguageName(
|
||||||
|
// name: "Greek, Modern",
|
||||||
|
// nativeName: "Ελληνικά",
|
||||||
|
// ),
|
||||||
|
// "gn": const ISOLanguageName(
|
||||||
|
// name: "Guaraní",
|
||||||
|
// nativeName: "Avañeẽ",
|
||||||
|
// ),
|
||||||
|
// "gu": const ISOLanguageName(
|
||||||
|
// name: "Gujarati",
|
||||||
|
// nativeName: "ગુજરાતી",
|
||||||
|
// ),
|
||||||
|
// "ht": const ISOLanguageName(
|
||||||
|
// name: "Haitian; Haitian Creole",
|
||||||
|
// nativeName: "Kreyòl ayisyen",
|
||||||
|
// ),
|
||||||
|
// "ha": const ISOLanguageName(
|
||||||
|
// name: "Hausa",
|
||||||
|
// nativeName: "Hausa, هَوُسَ",
|
||||||
|
// ),
|
||||||
|
// "he": const ISOLanguageName(
|
||||||
|
// name: "Hebrew (modern)",
|
||||||
|
// nativeName: "עברית",
|
||||||
|
// ),
|
||||||
|
// "hz": const ISOLanguageName(
|
||||||
|
// name: "Herero",
|
||||||
|
// nativeName: "Otjiherero",
|
||||||
|
// ),
|
||||||
|
"hi": const ISOLanguageName(
|
||||||
|
name: "Hindi",
|
||||||
|
nativeName: "हिन्दी, हिंदी",
|
||||||
|
),
|
||||||
|
// "ho": const ISOLanguageName(
|
||||||
|
// name: "Hiri Motu",
|
||||||
|
// nativeName: "Hiri Motu",
|
||||||
|
// ),
|
||||||
|
// "hu": const ISOLanguageName(
|
||||||
|
// name: "Hungarian",
|
||||||
|
// nativeName: "Magyar",
|
||||||
|
// ),
|
||||||
|
// "ia": const ISOLanguageName(
|
||||||
|
// name: "Interlingua",
|
||||||
|
// nativeName: "Interlingua",
|
||||||
|
// ),
|
||||||
|
"id": const ISOLanguageName(
|
||||||
|
name: "Indonesian",
|
||||||
|
nativeName: "Bahasa Indonesia",
|
||||||
|
),
|
||||||
|
// "ie": const ISOLanguageName(
|
||||||
|
// name: "Interlingue",
|
||||||
|
// nativeName: "Occidental",
|
||||||
|
// ),
|
||||||
|
// "ga": const ISOLanguageName(
|
||||||
|
// name: "Irish",
|
||||||
|
// nativeName: "Gaeilge",
|
||||||
|
// ),
|
||||||
|
// "ig": const ISOLanguageName(
|
||||||
|
// name: "Igbo",
|
||||||
|
// nativeName: "Asụsụ Igbo",
|
||||||
|
// ),
|
||||||
|
// "ik": const ISOLanguageName(
|
||||||
|
// name: "Inupiaq",
|
||||||
|
// nativeName: "Iñupiaq, Iñupiatun",
|
||||||
|
// ),
|
||||||
|
// "io": const ISOLanguageName(
|
||||||
|
// name: "Ido",
|
||||||
|
// nativeName: "Ido",
|
||||||
|
// ),
|
||||||
|
// "is": const ISOLanguageName(
|
||||||
|
// name: "Icelandic",
|
||||||
|
// nativeName: "Íslenska",
|
||||||
|
// ),
|
||||||
|
"it": const ISOLanguageName(
|
||||||
|
name: "Italian",
|
||||||
|
nativeName: "Italiano",
|
||||||
|
),
|
||||||
|
// "iu": const ISOLanguageName(
|
||||||
|
// name: "Inuktitut",
|
||||||
|
// nativeName: "ᐃᓄᒃᑎᑐᑦ",
|
||||||
|
// ),
|
||||||
|
"ja": const ISOLanguageName(
|
||||||
|
name: "Japanese",
|
||||||
|
nativeName: "日本語",
|
||||||
|
),
|
||||||
|
// "jv": const ISOLanguageName(
|
||||||
|
// name: "Javanese",
|
||||||
|
// nativeName: "basa Jawa",
|
||||||
|
// ),
|
||||||
|
// "kl": const ISOLanguageName(
|
||||||
|
// name: "Kalaallisut, Greenlandic",
|
||||||
|
// nativeName: "kalaallisut, kalaallit oqaasii",
|
||||||
|
// ),
|
||||||
|
// "kn": const ISOLanguageName(
|
||||||
|
// name: "Kannada",
|
||||||
|
// nativeName: "ಕನ್ನಡ",
|
||||||
|
// ),
|
||||||
|
// "kr": const ISOLanguageName(
|
||||||
|
// name: "Kanuri",
|
||||||
|
// nativeName: "Kanuri",
|
||||||
|
// ),
|
||||||
|
// "ks": const ISOLanguageName(
|
||||||
|
// name: "Kashmiri",
|
||||||
|
// nativeName: "कश्मीरी, كشميري",
|
||||||
|
// ),
|
||||||
|
// "kk": const ISOLanguageName(
|
||||||
|
// name: "Kazakh",
|
||||||
|
// nativeName: "Қазақ тілі",
|
||||||
|
// ),
|
||||||
|
// "km": const ISOLanguageName(
|
||||||
|
// name: "Khmer",
|
||||||
|
// nativeName: "ភាសាខ្មែរ",
|
||||||
|
// ),
|
||||||
|
// "ki": const ISOLanguageName(
|
||||||
|
// name: "Kikuyu, Gikuyu",
|
||||||
|
// nativeName: "Gĩkũyũ",
|
||||||
|
// ),
|
||||||
|
// "rw": const ISOLanguageName(
|
||||||
|
// name: "Kinyarwanda",
|
||||||
|
// nativeName: "Ikinyarwanda",
|
||||||
|
// ),
|
||||||
|
// "ky": const ISOLanguageName(
|
||||||
|
// name: "Kirghiz, Kyrgyz",
|
||||||
|
// nativeName: "кыргыз тили",
|
||||||
|
// ),
|
||||||
|
// "kv": const ISOLanguageName(
|
||||||
|
// name: "Komi",
|
||||||
|
// nativeName: "коми кыв",
|
||||||
|
// ),
|
||||||
|
// "kg": const ISOLanguageName(
|
||||||
|
// name: "Kongo",
|
||||||
|
// nativeName: "KiKongo",
|
||||||
|
// ),
|
||||||
|
"ko": const ISOLanguageName(
|
||||||
|
name: "Korean",
|
||||||
|
nativeName: "한국어 (韓國語), 조선말 (朝鮮語)",
|
||||||
|
),
|
||||||
|
// "ku": const ISOLanguageName(
|
||||||
|
// name: "Kurdish",
|
||||||
|
// nativeName: "Kurdî, كوردی",
|
||||||
|
// ),
|
||||||
|
// "kj": const ISOLanguageName(
|
||||||
|
// name: "Kwanyama, Kuanyama",
|
||||||
|
// nativeName: "Kuanyama",
|
||||||
|
// ),
|
||||||
|
// "la": const ISOLanguageName(
|
||||||
|
// name: "Latin",
|
||||||
|
// nativeName: "latine, lingua latina",
|
||||||
|
// ),
|
||||||
|
// "lb": const ISOLanguageName(
|
||||||
|
// name: "Luxembourgish, Letzeburgesch",
|
||||||
|
// nativeName: "Lëtzebuergesch",
|
||||||
|
// ),
|
||||||
|
// "lg": const ISOLanguageName(
|
||||||
|
// name: "Luganda",
|
||||||
|
// nativeName: "Luganda",
|
||||||
|
// ),
|
||||||
|
// "li": const ISOLanguageName(
|
||||||
|
// name: "Limburgish, Limburgan, Limburger",
|
||||||
|
// nativeName: "Limburgs",
|
||||||
|
// ),
|
||||||
|
// "ln": const ISOLanguageName(
|
||||||
|
// name: "Lingala",
|
||||||
|
// nativeName: "Lingála",
|
||||||
|
// ),
|
||||||
|
// "lo": const ISOLanguageName(
|
||||||
|
// name: "Lao",
|
||||||
|
// nativeName: "ພາສາລາວ",
|
||||||
|
// ),
|
||||||
|
// "lt": const ISOLanguageName(
|
||||||
|
// name: "Lithuanian",
|
||||||
|
// nativeName: "lietuvių kalba",
|
||||||
|
// ),
|
||||||
|
// "lu": const ISOLanguageName(
|
||||||
|
// name: "Luba-Katanga",
|
||||||
|
// nativeName: "",
|
||||||
|
// ),
|
||||||
|
// "lv": const ISOLanguageName(
|
||||||
|
// name: "Latvian",
|
||||||
|
// nativeName: "latviešu valoda",
|
||||||
|
// ),
|
||||||
|
// "gv": const ISOLanguageName(
|
||||||
|
// name: "Manx",
|
||||||
|
// nativeName: "Gaelg, Gailck",
|
||||||
|
// ),
|
||||||
|
// "mk": const ISOLanguageName(
|
||||||
|
// name: "Macedonian",
|
||||||
|
// nativeName: "македонски јазик",
|
||||||
|
// ),
|
||||||
|
// "mg": const ISOLanguageName(
|
||||||
|
// name: "Malagasy",
|
||||||
|
// nativeName: "Malagasy fiteny",
|
||||||
|
// ),
|
||||||
|
// "ms": const ISOLanguageName(
|
||||||
|
// name: "Malay",
|
||||||
|
// nativeName: "bahasa Melayu, بهاس ملايو",
|
||||||
|
// ),
|
||||||
|
// "ml": const ISOLanguageName(
|
||||||
|
// name: "Malayalam",
|
||||||
|
// nativeName: "മലയാളം",
|
||||||
|
// ),
|
||||||
|
// "mt": const ISOLanguageName(
|
||||||
|
// name: "Maltese",
|
||||||
|
// nativeName: "Malti",
|
||||||
|
// ),
|
||||||
|
// "mi": const ISOLanguageName(
|
||||||
|
// name: "Māori",
|
||||||
|
// nativeName: "te reo Māori",
|
||||||
|
// ),
|
||||||
|
// "mr": const ISOLanguageName(
|
||||||
|
// name: "Marathi (Marāṭhī)",
|
||||||
|
// nativeName: "मराठी",
|
||||||
|
// ),
|
||||||
|
// "mh": const ISOLanguageName(
|
||||||
|
// name: "Marshallese",
|
||||||
|
// nativeName: "Kajin M̧ajeļ",
|
||||||
|
// ),
|
||||||
|
// "mn": const ISOLanguageName(
|
||||||
|
// name: "Mongolian",
|
||||||
|
// nativeName: "монгол",
|
||||||
|
// ),
|
||||||
|
// "na": const ISOLanguageName(
|
||||||
|
// name: "Nauru",
|
||||||
|
// nativeName: "Ekakairũ Naoero",
|
||||||
|
// ),
|
||||||
|
// "nv": const ISOLanguageName(
|
||||||
|
// name: "Navajo, Navaho",
|
||||||
|
// nativeName: "Diné bizaad, Dinékʼehǰí",
|
||||||
|
// ),
|
||||||
|
// "nb": const ISOLanguageName(
|
||||||
|
// name: "Norwegian Bokmål",
|
||||||
|
// nativeName: "Norsk bokmål",
|
||||||
|
// ),
|
||||||
|
// "nd": const ISOLanguageName(
|
||||||
|
// name: "North Ndebele",
|
||||||
|
// nativeName: "isiNdebele",
|
||||||
|
// ),
|
||||||
|
"ne": const ISOLanguageName(
|
||||||
|
name: "Nepali",
|
||||||
|
nativeName: "नेपाली",
|
||||||
|
),
|
||||||
|
// "ng": const ISOLanguageName(
|
||||||
|
// name: "Ndonga",
|
||||||
|
// nativeName: "Owambo",
|
||||||
|
// ),
|
||||||
|
// "nn": const ISOLanguageName(
|
||||||
|
// name: "Norwegian Nynorsk",
|
||||||
|
// nativeName: "Norsk nynorsk",
|
||||||
|
// ),
|
||||||
|
// "no": const ISOLanguageName(
|
||||||
|
// name: "Norwegian",
|
||||||
|
// nativeName: "Norsk",
|
||||||
|
// ),
|
||||||
|
// "ii": const ISOLanguageName(
|
||||||
|
// name: "Nuosu",
|
||||||
|
// nativeName: "ꆈꌠ꒿ Nuosuhxop",
|
||||||
|
// ),
|
||||||
|
// "nr": const ISOLanguageName(
|
||||||
|
// name: "South Ndebele",
|
||||||
|
// nativeName: "isiNdebele",
|
||||||
|
// ),
|
||||||
|
// "oc": const ISOLanguageName(
|
||||||
|
// name: "Occitan",
|
||||||
|
// nativeName: "Occitan",
|
||||||
|
// ),
|
||||||
|
// "oj": const ISOLanguageName(
|
||||||
|
// name: "Ojibwe, Ojibwa",
|
||||||
|
// nativeName: "ᐊᓂᔑᓈᐯᒧᐎᓐ",
|
||||||
|
// ),
|
||||||
|
// "cu": const ISOLanguageName(
|
||||||
|
// name: "Old Church Slavonic",
|
||||||
|
// nativeName: "ѩзыкъ словѣньскъ",
|
||||||
|
// ),
|
||||||
|
// "om": const ISOLanguageName(
|
||||||
|
// name: "Oromo",
|
||||||
|
// nativeName: "Afaan Oromoo",
|
||||||
|
// ),
|
||||||
|
// "or": const ISOLanguageName(
|
||||||
|
// name: "Oriya",
|
||||||
|
// nativeName: "ଓଡ଼ିଆ",
|
||||||
|
// ),
|
||||||
|
// "os": const ISOLanguageName(
|
||||||
|
// name: "Ossetian, Ossetic",
|
||||||
|
// nativeName: "ирон æвзаг",
|
||||||
|
// ),
|
||||||
|
// "pa": const ISOLanguageName(
|
||||||
|
// name: "Panjabi, Punjabi",
|
||||||
|
// nativeName: "ਪੰਜਾਬੀ, پنجابی",
|
||||||
|
// ),
|
||||||
|
// "pi": const ISOLanguageName(
|
||||||
|
// name: "Pāli",
|
||||||
|
// nativeName: "पाऴि",
|
||||||
|
// ),
|
||||||
|
"fa": const ISOLanguageName(
|
||||||
|
name: "Persian",
|
||||||
|
nativeName: "فارسی",
|
||||||
|
),
|
||||||
|
"pl": const ISOLanguageName(
|
||||||
|
name: "Polish",
|
||||||
|
nativeName: "polski",
|
||||||
|
),
|
||||||
|
// "ps": const ISOLanguageName(
|
||||||
|
// name: "Pashto, Pushto",
|
||||||
|
// nativeName: "پښتو",
|
||||||
|
// ),
|
||||||
|
"pt": const ISOLanguageName(
|
||||||
|
name: "Portuguese",
|
||||||
|
nativeName: "Português",
|
||||||
|
),
|
||||||
|
// "qu": const ISOLanguageName(
|
||||||
|
// name: "Quechua",
|
||||||
|
// nativeName: "Runa Simi, Kichwa",
|
||||||
|
// ),
|
||||||
|
// "rm": const ISOLanguageName(
|
||||||
|
// name: "Romansh",
|
||||||
|
// nativeName: "rumantsch grischun",
|
||||||
|
// ),
|
||||||
|
// "rn": const ISOLanguageName(
|
||||||
|
// name: "Kirundi",
|
||||||
|
// nativeName: "kiRundi",
|
||||||
|
// ),
|
||||||
|
// "ro": const ISOLanguageName(
|
||||||
|
// name: "Romanian, Moldavian, Moldovan",
|
||||||
|
// nativeName: "română",
|
||||||
|
// ),
|
||||||
|
"ru": const ISOLanguageName(
|
||||||
|
name: "Russian",
|
||||||
|
nativeName: "русский язык",
|
||||||
|
),
|
||||||
|
// "sa": const ISOLanguageName(
|
||||||
|
// name: "Sanskrit (Saṁskṛta)",
|
||||||
|
// nativeName: "संस्कृतम्",
|
||||||
|
// ),
|
||||||
|
// "sc": const ISOLanguageName(
|
||||||
|
// name: "Sardinian",
|
||||||
|
// nativeName: "sardu",
|
||||||
|
// ),
|
||||||
|
// "sd": const ISOLanguageName(
|
||||||
|
// name: "Sindhi",
|
||||||
|
// nativeName: "सिन्धी, سنڌي، سندھی",
|
||||||
|
// ),
|
||||||
|
// "se": const ISOLanguageName(
|
||||||
|
// name: "Northern Sami",
|
||||||
|
// nativeName: "Davvisámegiella",
|
||||||
|
// ),
|
||||||
|
// "sm": const ISOLanguageName(
|
||||||
|
// name: "Samoan",
|
||||||
|
// nativeName: "gagana faa Samoa",
|
||||||
|
// ),
|
||||||
|
// "sg": const ISOLanguageName(
|
||||||
|
// name: "Sango",
|
||||||
|
// nativeName: "yângâ tî sängö",
|
||||||
|
// ),
|
||||||
|
// "sr": const ISOLanguageName(
|
||||||
|
// name: "Serbian",
|
||||||
|
// nativeName: "српски језик",
|
||||||
|
// ),
|
||||||
|
// "gd": const ISOLanguageName(
|
||||||
|
// name: "Scottish Gaelic; Gaelic",
|
||||||
|
// nativeName: "Gàidhlig",
|
||||||
|
// ),
|
||||||
|
// "sn": const ISOLanguageName(
|
||||||
|
// name: "Shona",
|
||||||
|
// nativeName: "chiShona",
|
||||||
|
// ),
|
||||||
|
// "si": const ISOLanguageName(
|
||||||
|
// name: "Sinhala, Sinhalese",
|
||||||
|
// nativeName: "සිංහල",
|
||||||
|
// ),
|
||||||
|
// "sk": const ISOLanguageName(
|
||||||
|
// name: "Slovak",
|
||||||
|
// nativeName: "slovenčina",
|
||||||
|
// ),
|
||||||
|
// "sl": const ISOLanguageName(
|
||||||
|
// name: "Slovene",
|
||||||
|
// nativeName: "slovenščina",
|
||||||
|
// ),
|
||||||
|
// "so": const ISOLanguageName(
|
||||||
|
// name: "Somali",
|
||||||
|
// nativeName: "Soomaaliga, af Soomaali",
|
||||||
|
// ),
|
||||||
|
// "st": const ISOLanguageName(
|
||||||
|
// name: "Southern Sotho",
|
||||||
|
// nativeName: "Sesotho",
|
||||||
|
// ),
|
||||||
|
"es": const ISOLanguageName(
|
||||||
|
name: "Spanish",
|
||||||
|
nativeName: "español",
|
||||||
|
),
|
||||||
|
// "su": const ISOLanguageName(
|
||||||
|
// name: "Sundanese",
|
||||||
|
// nativeName: "Basa Sunda",
|
||||||
|
// ),
|
||||||
|
// "sw": const ISOLanguageName(
|
||||||
|
// name: "Swahili",
|
||||||
|
// nativeName: "Kiswahili",
|
||||||
|
// ),
|
||||||
|
// "ss": const ISOLanguageName(
|
||||||
|
// name: "Swati",
|
||||||
|
// nativeName: "SiSwati",
|
||||||
|
// ),
|
||||||
|
// "sv": const ISOLanguageName(
|
||||||
|
// name: "Swedish",
|
||||||
|
// nativeName: "svenska",
|
||||||
|
// ),
|
||||||
|
// "ta": const ISOLanguageName(
|
||||||
|
// name: "Tamil",
|
||||||
|
// nativeName: "தமிழ்",
|
||||||
|
// ),
|
||||||
|
// "te": const ISOLanguageName(
|
||||||
|
// name: "Telugu",
|
||||||
|
// nativeName: "తెలుగు",
|
||||||
|
// ),
|
||||||
|
// "tg": const ISOLanguageName(
|
||||||
|
// name: "Tajik",
|
||||||
|
// nativeName: "тоҷикӣ, toğikī, تاجیکی",
|
||||||
|
// ),
|
||||||
|
"th": const ISOLanguageName(
|
||||||
|
name: "Thai",
|
||||||
|
nativeName: "ไทย",
|
||||||
|
),
|
||||||
|
// "ti": const ISOLanguageName(
|
||||||
|
// name: "Tigrinya",
|
||||||
|
// nativeName: "ትግርኛ",
|
||||||
|
// ),
|
||||||
|
// "bo": const ISOLanguageName(
|
||||||
|
// name: "Tibetan Standard, Tibetan, Central",
|
||||||
|
// nativeName: "བོད་ཡིག",
|
||||||
|
// ),
|
||||||
|
// "tk": const ISOLanguageName(
|
||||||
|
// name: "Turkmen",
|
||||||
|
// nativeName: "Türkmen, Түркмен",
|
||||||
|
// ),
|
||||||
|
// "tl": const ISOLanguageName(
|
||||||
|
// name: "Tagalog",
|
||||||
|
// nativeName: "Wikang Tagalog, ᜏᜒᜃᜅ᜔ ᜆᜄᜎᜓᜄ᜔",
|
||||||
|
// ),
|
||||||
|
// "tn": const ISOLanguageName(
|
||||||
|
// name: "Tswana",
|
||||||
|
// nativeName: "Setswana",
|
||||||
|
// ),
|
||||||
|
// "to": const ISOLanguageName(
|
||||||
|
// name: "Tonga (Tonga Islands)",
|
||||||
|
// nativeName: "faka Tonga",
|
||||||
|
// ),
|
||||||
|
"tr": const ISOLanguageName(
|
||||||
|
name: "Turkish",
|
||||||
|
nativeName: "Türkçe",
|
||||||
|
),
|
||||||
|
// "ts": const ISOLanguageName(
|
||||||
|
// name: "Tsonga",
|
||||||
|
// nativeName: "Xitsonga",
|
||||||
|
// ),
|
||||||
|
// "tt": const ISOLanguageName(
|
||||||
|
// name: "Tatar",
|
||||||
|
// nativeName: "татарча, tatarça, تاتارچا",
|
||||||
|
// ),
|
||||||
|
// "tw": const ISOLanguageName(
|
||||||
|
// name: "Twi",
|
||||||
|
// nativeName: "Twi",
|
||||||
|
// ),
|
||||||
|
// "ty": const ISOLanguageName(
|
||||||
|
// name: "Tahitian",
|
||||||
|
// nativeName: "Reo Tahiti",
|
||||||
|
// ),
|
||||||
|
// "ug": const ISOLanguageName(
|
||||||
|
// name: "Uighur, Uyghur",
|
||||||
|
// nativeName: "Uyƣurqə, ئۇيغۇرچە",
|
||||||
|
// ),
|
||||||
|
"uk": const ISOLanguageName(
|
||||||
|
name: "Ukrainian",
|
||||||
|
nativeName: "українська",
|
||||||
|
),
|
||||||
|
// "ur": const ISOLanguageName(
|
||||||
|
// name: "Urdu",
|
||||||
|
// nativeName: "اردو",
|
||||||
|
// ),
|
||||||
|
// "uz": const ISOLanguageName(
|
||||||
|
// name: "Uzbek",
|
||||||
|
// nativeName: "zbek, Ўзбек, أۇزبېك",
|
||||||
|
// ),
|
||||||
|
// "ve": const ISOLanguageName(
|
||||||
|
// name: "Venda",
|
||||||
|
// nativeName: "Tshivenḓa",
|
||||||
|
// ),
|
||||||
|
"vi": const ISOLanguageName(
|
||||||
|
name: "Vietnamese",
|
||||||
|
nativeName: "Tiếng Việt",
|
||||||
|
),
|
||||||
|
// "vo": const ISOLanguageName(
|
||||||
|
// name: "Volapük",
|
||||||
|
// nativeName: "Volapük",
|
||||||
|
// ),
|
||||||
|
// "wa": const ISOLanguageName(
|
||||||
|
// name: "Walloon",
|
||||||
|
// nativeName: "Walon",
|
||||||
|
// ),
|
||||||
|
// "cy": const ISOLanguageName(
|
||||||
|
// name: "Welsh",
|
||||||
|
// nativeName: "Cymraeg",
|
||||||
|
// ),
|
||||||
|
// "wo": const ISOLanguageName(
|
||||||
|
// name: "Wolof",
|
||||||
|
// nativeName: "Wollof",
|
||||||
|
// ),
|
||||||
|
// "fy": const ISOLanguageName(
|
||||||
|
// name: "Western Frisian",
|
||||||
|
// nativeName: "Frysk",
|
||||||
|
// ),
|
||||||
|
// "xh": const ISOLanguageName(
|
||||||
|
// name: "Xhosa",
|
||||||
|
// nativeName: "isiXhosa",
|
||||||
|
// ),
|
||||||
|
// "yi": const ISOLanguageName(
|
||||||
|
// name: "Yiddish",
|
||||||
|
// nativeName: "ייִדיש",
|
||||||
|
// ),
|
||||||
|
// "yo": const ISOLanguageName(
|
||||||
|
// name: "Yoruba",
|
||||||
|
// nativeName: "Yorùbá",
|
||||||
|
// ),
|
||||||
|
// "za": const ISOLanguageName(
|
||||||
|
// name: "Zhuang, Chuang",
|
||||||
|
// nativeName: "Saɯ cueŋƅ, Saw cuengh",
|
||||||
|
// )
|
||||||
|
};
|
||||||
|
|
||||||
|
static ISOLanguageName getDisplayLanguage(key) {
|
||||||
|
if (isoLangs.containsKey(key)) {
|
||||||
|
return isoLangs[key]!;
|
||||||
|
} else {
|
||||||
|
throw Exception("Language key incorrect");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
189
lib/collections/spotify_markets.dart
Executable file
189
lib/collections/spotify_markets.dart
Executable file
@ -0,0 +1,189 @@
|
|||||||
|
// Country Codes contributed by momobobe <https://github.com/momobobe>
|
||||||
|
|
||||||
|
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)"),
|
||||||
|
];
|
@ -1,10 +1,18 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
import 'package:media_kit/media_kit.dart';
|
||||||
|
import 'package:rhythm_box/providers/audio_player.dart';
|
||||||
import 'package:rhythm_box/providers/spotify.dart';
|
import 'package:rhythm_box/providers/spotify.dart';
|
||||||
import 'package:rhythm_box/router.dart';
|
import 'package:rhythm_box/router.dart';
|
||||||
|
import 'package:rhythm_box/services/server/active_sourced_track.dart';
|
||||||
|
import 'package:rhythm_box/services/server/routes/playback.dart';
|
||||||
|
import 'package:rhythm_box/services/server/server.dart';
|
||||||
|
import 'package:rhythm_box/services/server/sourced_track.dart';
|
||||||
import 'package:rhythm_box/translations.dart';
|
import 'package:rhythm_box/translations.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
|
MediaKit.ensureInitialized();
|
||||||
|
|
||||||
runApp(const MyApp());
|
runApp(const MyApp());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,5 +50,10 @@ class MyApp extends StatelessWidget {
|
|||||||
|
|
||||||
void _initializeProviders(BuildContext context) async {
|
void _initializeProviders(BuildContext context) async {
|
||||||
Get.lazyPut(() => SpotifyProvider());
|
Get.lazyPut(() => SpotifyProvider());
|
||||||
|
Get.lazyPut(() => AudioPlayerProvider());
|
||||||
|
Get.lazyPut(() => ActiveSourcedTrackProvider());
|
||||||
|
Get.lazyPut(() => SourcedTrackProvider());
|
||||||
|
Get.lazyPut(() => ServerPlaybackRoutesProvider());
|
||||||
|
Get.lazyPut(() => PlaybackServerProvider());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
122
lib/providers/audio_player.dart
Normal file
122
lib/providers/audio_player.dart
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:media_kit/media_kit.dart' hide Track;
|
||||||
|
import 'package:rhythm_box/services/audio_player/state.dart';
|
||||||
|
import 'package:rhythm_box/services/local_track.dart';
|
||||||
|
import 'package:rhythm_box/services/sourced_track/sourced_track.dart';
|
||||||
|
import 'package:spotify/spotify.dart' hide Playlist;
|
||||||
|
import 'package:rhythm_box/services/audio_player/audio_player.dart';
|
||||||
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
|
class AudioPlayerProvider extends GetxController {
|
||||||
|
late final SharedPreferences _prefs;
|
||||||
|
|
||||||
|
Rx<AudioPlayerState> state = Rx(AudioPlayerState(
|
||||||
|
playing: false,
|
||||||
|
shuffled: false,
|
||||||
|
loopMode: PlaylistMode.none,
|
||||||
|
playlist: const Playlist([]),
|
||||||
|
collections: [],
|
||||||
|
));
|
||||||
|
|
||||||
|
AudioPlayerProvider() {
|
||||||
|
SharedPreferences.getInstance().then((ins) {
|
||||||
|
_prefs = ins;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _syncSavedState() async {
|
||||||
|
final data = _prefs.getBool("player_state");
|
||||||
|
if (data == null) return;
|
||||||
|
|
||||||
|
// TODO Serilize and deserilize this state
|
||||||
|
|
||||||
|
// TODO Sync saved playlist
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> load(
|
||||||
|
List<Track> tracks, {
|
||||||
|
int initialIndex = 0,
|
||||||
|
bool autoPlay = false,
|
||||||
|
}) async {
|
||||||
|
final medias = tracks.map((x) => RhythmMedia(x)).toList();
|
||||||
|
|
||||||
|
// Giving the initial track a boost so MediaKit won't skip
|
||||||
|
// because of timeout
|
||||||
|
final intendedActiveTrack = medias.elementAt(initialIndex);
|
||||||
|
if (intendedActiveTrack.track is! LocalTrack) {
|
||||||
|
await SourcedTrack.fetchFromTrack(track: intendedActiveTrack.track);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (medias.isEmpty) return;
|
||||||
|
|
||||||
|
await audioPlayer.openPlaylist(
|
||||||
|
medias.map((s) => s as Media).toList(),
|
||||||
|
initialIndex: initialIndex,
|
||||||
|
autoPlay: autoPlay,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> addTracksAtFirst(Iterable<Track> tracks) async {
|
||||||
|
if (state.value.tracks.length == 1) {
|
||||||
|
return addTracks(tracks);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < tracks.length; i++) {
|
||||||
|
final track = tracks.elementAt(i);
|
||||||
|
|
||||||
|
await audioPlayer.addTrackAt(
|
||||||
|
RhythmMedia(track),
|
||||||
|
max(state.value.playlist.index, 0) + i + 1,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> addTrack(Track track) async {
|
||||||
|
await audioPlayer.addTrack(RhythmMedia(track));
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> addTracks(Iterable<Track> tracks) async {
|
||||||
|
for (final track in tracks) {
|
||||||
|
await audioPlayer.addTrack(RhythmMedia(track));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> removeTrack(String trackId) async {
|
||||||
|
final index =
|
||||||
|
state.value.tracks.indexWhere((element) => element.id == trackId);
|
||||||
|
|
||||||
|
if (index == -1) return;
|
||||||
|
|
||||||
|
await audioPlayer.removeTrack(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> removeTracks(Iterable<String> trackIds) async {
|
||||||
|
for (final trackId in trackIds) {
|
||||||
|
await removeTrack(trackId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> jumpToTrack(Track track) async {
|
||||||
|
final index = state.value.tracks
|
||||||
|
.toList()
|
||||||
|
.indexWhere((element) => element.id == track.id);
|
||||||
|
if (index == -1) return;
|
||||||
|
await audioPlayer.jumpTo(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> moveTrack(int oldIndex, int newIndex) async {
|
||||||
|
if (oldIndex == newIndex ||
|
||||||
|
newIndex < 0 ||
|
||||||
|
oldIndex < 0 ||
|
||||||
|
newIndex > state.value.tracks.length - 1 ||
|
||||||
|
oldIndex > state.value.tracks.length - 1) return;
|
||||||
|
|
||||||
|
await audioPlayer.moveTrack(oldIndex, newIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> stop() async {
|
||||||
|
await audioPlayer.stop();
|
||||||
|
}
|
||||||
|
}
|
@ -1,3 +0,0 @@
|
|||||||
import 'package:get/get.dart';
|
|
||||||
|
|
||||||
class PipedProvider extends GetxController {}
|
|
@ -13,13 +13,6 @@ final router = GoRouter(routes: [
|
|||||||
name: "explore",
|
name: "explore",
|
||||||
builder: (context, state) => const ExploreScreen(),
|
builder: (context, state) => const ExploreScreen(),
|
||||||
),
|
),
|
||||||
GoRoute(
|
|
||||||
path: "/playlist/:id",
|
|
||||||
name: "playlistView",
|
|
||||||
builder: (context, state) => PlaylistViewScreen(
|
|
||||||
playlistId: state.pathParameters['id']!,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: "/settings",
|
path: "/settings",
|
||||||
name: "settings",
|
name: "settings",
|
||||||
@ -27,4 +20,11 @@ final router = GoRouter(routes: [
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
GoRoute(
|
||||||
|
path: "/playlist/:id",
|
||||||
|
name: "playlistView",
|
||||||
|
builder: (context, state) => PlaylistViewScreen(
|
||||||
|
playlistId: state.pathParameters['id']!,
|
||||||
|
),
|
||||||
|
),
|
||||||
]);
|
]);
|
||||||
|
7
lib/services/artist.dart
Normal file
7
lib/services/artist.dart
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import 'package:spotify/spotify.dart';
|
||||||
|
|
||||||
|
extension ArtistExtension on List<ArtistSimple> {
|
||||||
|
String asString() {
|
||||||
|
return map((e) => e.name?.replaceAll(",", " ")).join(", ");
|
||||||
|
}
|
||||||
|
}
|
108
lib/services/audio_player/state.dart
Normal file
108
lib/services/audio_player/state.dart
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
import 'package:media_kit/media_kit.dart' hide Track;
|
||||||
|
import 'package:spotify/spotify.dart' hide Playlist;
|
||||||
|
import 'package:rhythm_box/services/audio_player/audio_player.dart';
|
||||||
|
|
||||||
|
class AudioPlayerState {
|
||||||
|
final bool playing;
|
||||||
|
final PlaylistMode loopMode;
|
||||||
|
final bool shuffled;
|
||||||
|
final Playlist playlist;
|
||||||
|
|
||||||
|
final List<Track> tracks;
|
||||||
|
final List<String> collections;
|
||||||
|
|
||||||
|
AudioPlayerState({
|
||||||
|
required this.playing,
|
||||||
|
required this.loopMode,
|
||||||
|
required this.shuffled,
|
||||||
|
required this.playlist,
|
||||||
|
required this.collections,
|
||||||
|
List<Track>? tracks,
|
||||||
|
}) : tracks = tracks ??
|
||||||
|
playlist.medias
|
||||||
|
.map((media) => RhythmMedia.fromMedia(media).track)
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
factory AudioPlayerState.fromJson(Map<String, dynamic> json) {
|
||||||
|
return AudioPlayerState(
|
||||||
|
playing: json['playing'],
|
||||||
|
loopMode: PlaylistMode.values.firstWhere(
|
||||||
|
(e) => e.name == json['loopMode'],
|
||||||
|
orElse: () => audioPlayer.loopMode,
|
||||||
|
),
|
||||||
|
shuffled: json['shuffled'],
|
||||||
|
playlist: Playlist(
|
||||||
|
json['playlist']['medias']
|
||||||
|
.map(
|
||||||
|
(media) => RhythmMedia.fromMedia(Media(
|
||||||
|
media['uri'],
|
||||||
|
extras: media['extras'],
|
||||||
|
httpHeaders: media['httpHeaders'],
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
.cast<Media>()
|
||||||
|
.toList(),
|
||||||
|
index: json['playlist']['index'],
|
||||||
|
),
|
||||||
|
collections: List<String>.from(json['collections']),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return {
|
||||||
|
'playing': playing,
|
||||||
|
'loopMode': loopMode.name,
|
||||||
|
'shuffled': shuffled,
|
||||||
|
'playlist': {
|
||||||
|
'medias': playlist.medias
|
||||||
|
.map((media) => {
|
||||||
|
'uri': media.uri,
|
||||||
|
'extras': media.extras,
|
||||||
|
'httpHeaders': media.httpHeaders,
|
||||||
|
})
|
||||||
|
.toList(),
|
||||||
|
'index': playlist.index,
|
||||||
|
},
|
||||||
|
'collections': collections,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioPlayerState copyWith({
|
||||||
|
bool? playing,
|
||||||
|
PlaylistMode? loopMode,
|
||||||
|
bool? shuffled,
|
||||||
|
Playlist? playlist,
|
||||||
|
List<String>? collections,
|
||||||
|
}) {
|
||||||
|
return AudioPlayerState(
|
||||||
|
playing: playing ?? this.playing,
|
||||||
|
loopMode: loopMode ?? this.loopMode,
|
||||||
|
shuffled: shuffled ?? this.shuffled,
|
||||||
|
playlist: playlist ?? this.playlist,
|
||||||
|
collections: collections ?? this.collections,
|
||||||
|
tracks: playlist == null ? tracks : null,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Track? get activeTrack {
|
||||||
|
if (playlist.index == -1) return null;
|
||||||
|
return tracks.elementAtOrNull(playlist.index);
|
||||||
|
}
|
||||||
|
|
||||||
|
Media? get activeMedia {
|
||||||
|
if (playlist.index == -1 || playlist.medias.isEmpty) return null;
|
||||||
|
return playlist.medias.elementAt(playlist.index);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool containsTrack(Track track) {
|
||||||
|
return tracks.any((t) => t.id == track.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool containsTracks(List<Track> tracks) {
|
||||||
|
return tracks.every(containsTrack);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool containsCollection(String collectionId) {
|
||||||
|
return collections.contains(collectionId);
|
||||||
|
}
|
||||||
|
}
|
84
lib/services/audio_services/audio_services.dart
Executable file
84
lib/services/audio_services/audio_services.dart
Executable file
@ -0,0 +1,84 @@
|
|||||||
|
import 'package:audio_service/audio_service.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:rhythm_box/platform.dart';
|
||||||
|
import 'package:rhythm_box/services/audio_services/image.dart';
|
||||||
|
import 'package:spotify/spotify.dart';
|
||||||
|
import 'package:rhythm_box/services/audio_services/mobile_audio_service.dart';
|
||||||
|
import 'package:rhythm_box/services/audio_services/windows_audio_service.dart';
|
||||||
|
import 'package:rhythm_box/services/sourced_track/sourced_track.dart';
|
||||||
|
import 'package:rhythm_box/services/artist.dart';
|
||||||
|
|
||||||
|
class AudioServices with WidgetsBindingObserver {
|
||||||
|
final MobileAudioService? mobile;
|
||||||
|
final WindowsAudioService? smtc;
|
||||||
|
|
||||||
|
AudioServices(this.mobile, this.smtc) {
|
||||||
|
WidgetsBinding.instance.addObserver(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<AudioServices> create() async {
|
||||||
|
final mobile =
|
||||||
|
PlatformInfo.isMobile || PlatformInfo.isMacOS || PlatformInfo.isLinux
|
||||||
|
? await AudioService.init(
|
||||||
|
builder: () => MobileAudioService(),
|
||||||
|
config: AudioServiceConfig(
|
||||||
|
androidNotificationChannelId: PlatformInfo.isLinux
|
||||||
|
? 'RhythmBox'
|
||||||
|
: 'dev.solsynth.rhythmBox',
|
||||||
|
androidNotificationChannelName: 'RhythmBox',
|
||||||
|
androidNotificationOngoing: false,
|
||||||
|
androidNotificationIcon: "drawable/ic_launcher_monochrome",
|
||||||
|
androidStopForegroundOnPause: false,
|
||||||
|
androidNotificationChannelDescription: "RhythmBox Music",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: null;
|
||||||
|
final smtc = PlatformInfo.isWindows ? WindowsAudioService() : null;
|
||||||
|
|
||||||
|
return AudioServices(mobile, smtc);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> addTrack(Track track) async {
|
||||||
|
await smtc?.addTrack(track);
|
||||||
|
mobile?.addItem(MediaItem(
|
||||||
|
id: track.id!,
|
||||||
|
album: track.album?.name ?? "",
|
||||||
|
title: track.name!,
|
||||||
|
artist: (track.artists)?.asString() ?? "",
|
||||||
|
duration: track is SourcedTrack
|
||||||
|
? track.sourceInfo.duration
|
||||||
|
: Duration(milliseconds: track.durationMs ?? 0),
|
||||||
|
artUri: Uri.parse(
|
||||||
|
(track.album?.images).asUrlString(
|
||||||
|
placeholder: ImagePlaceholder.albumArt,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
playable: true,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
void activateSession() {
|
||||||
|
mobile?.session?.setActive(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void deactivateSession() {
|
||||||
|
mobile?.session?.setActive(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didChangeAppLifecycleState(AppLifecycleState state) {
|
||||||
|
switch (state) {
|
||||||
|
case AppLifecycleState.detached:
|
||||||
|
deactivateSession();
|
||||||
|
mobile?.stop();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void dispose() {
|
||||||
|
smtc?.dispose();
|
||||||
|
WidgetsBinding.instance.removeObserver(this);
|
||||||
|
}
|
||||||
|
}
|
34
lib/services/audio_services/image.dart
Normal file
34
lib/services/audio_services/image.dart
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import 'package:rhythm_box/services/primitive.dart';
|
||||||
|
import 'package:spotify/spotify.dart';
|
||||||
|
import 'package:rhythm_box/collections/assets.gen.dart';
|
||||||
|
import 'package:collection/collection.dart';
|
||||||
|
|
||||||
|
enum ImagePlaceholder {
|
||||||
|
albumArt,
|
||||||
|
artist,
|
||||||
|
collection,
|
||||||
|
online,
|
||||||
|
}
|
||||||
|
|
||||||
|
extension SpotifyImageExtensions on List<Image>? {
|
||||||
|
String asUrlString({
|
||||||
|
int index = 1,
|
||||||
|
required ImagePlaceholder placeholder,
|
||||||
|
}) {
|
||||||
|
final String placeholderUrl = {
|
||||||
|
ImagePlaceholder.albumArt: Assets.albumPlaceholder.path,
|
||||||
|
ImagePlaceholder.artist: Assets.userPlaceholder.path,
|
||||||
|
ImagePlaceholder.collection: Assets.placeholder.path,
|
||||||
|
ImagePlaceholder.online:
|
||||||
|
"https://avatars.dicebear.com/api/bottts/${PrimitiveUtils.uuid.v4()}.png",
|
||||||
|
}[placeholder]!;
|
||||||
|
|
||||||
|
final sortedImage = this?.sorted((a, b) => a.width!.compareTo(b.width!));
|
||||||
|
|
||||||
|
return sortedImage != null && sortedImage.isNotEmpty
|
||||||
|
? sortedImage[
|
||||||
|
index > sortedImage.length - 1 ? sortedImage.length - 1 : index]
|
||||||
|
.url!
|
||||||
|
: placeholderUrl;
|
||||||
|
}
|
||||||
|
}
|
153
lib/services/audio_services/mobile_audio_service.dart
Executable file
153
lib/services/audio_services/mobile_audio_service.dart
Executable file
@ -0,0 +1,153 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:audio_service/audio_service.dart';
|
||||||
|
import 'package:audio_session/audio_session.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:rhythm_box/providers/audio_player.dart';
|
||||||
|
import 'package:rhythm_box/services/audio_player/audio_player.dart';
|
||||||
|
import 'package:media_kit/media_kit.dart' hide Track;
|
||||||
|
import 'package:rhythm_box/services/audio_player/state.dart';
|
||||||
|
|
||||||
|
class MobileAudioService extends BaseAudioHandler {
|
||||||
|
AudioSession? session;
|
||||||
|
// ignore: invalid_use_of_protected_member, invalid_use_of_visible_for_testing_member
|
||||||
|
AudioPlayerState get playlist => Get.find<AudioPlayerProvider>().state.value;
|
||||||
|
|
||||||
|
MobileAudioService() {
|
||||||
|
AudioSession.instance.then((s) {
|
||||||
|
session = s;
|
||||||
|
session?.configure(const AudioSessionConfiguration.music());
|
||||||
|
|
||||||
|
bool wasPausedByBeginEvent = false;
|
||||||
|
|
||||||
|
s.interruptionEventStream.listen((event) async {
|
||||||
|
if (event.begin) {
|
||||||
|
switch (event.type) {
|
||||||
|
case AudioInterruptionType.duck:
|
||||||
|
await audioPlayer.setVolume(0.5);
|
||||||
|
break;
|
||||||
|
case AudioInterruptionType.pause:
|
||||||
|
case AudioInterruptionType.unknown:
|
||||||
|
{
|
||||||
|
wasPausedByBeginEvent = audioPlayer.isPlaying;
|
||||||
|
await audioPlayer.pause();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch (event.type) {
|
||||||
|
case AudioInterruptionType.duck:
|
||||||
|
await audioPlayer.setVolume(1.0);
|
||||||
|
break;
|
||||||
|
case AudioInterruptionType.pause when wasPausedByBeginEvent:
|
||||||
|
case AudioInterruptionType.unknown when wasPausedByBeginEvent:
|
||||||
|
await audioPlayer.resume();
|
||||||
|
wasPausedByBeginEvent = false;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
s.becomingNoisyEventStream.listen((_) {
|
||||||
|
audioPlayer.pause();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
audioPlayer.playerStateStream.listen((state) async {
|
||||||
|
playbackState.add(await _transformEvent());
|
||||||
|
});
|
||||||
|
|
||||||
|
audioPlayer.positionStream.listen((pos) async {
|
||||||
|
playbackState.add(await _transformEvent());
|
||||||
|
});
|
||||||
|
audioPlayer.bufferedPositionStream.listen((pos) async {
|
||||||
|
playbackState.add(await _transformEvent());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void addItem(MediaItem item) {
|
||||||
|
session?.setActive(true);
|
||||||
|
mediaItem.add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> play() => audioPlayer.resume();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> pause() => audioPlayer.pause();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> seek(Duration position) => audioPlayer.seek(position);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> setShuffleMode(AudioServiceShuffleMode shuffleMode) async {
|
||||||
|
await super.setShuffleMode(shuffleMode);
|
||||||
|
|
||||||
|
audioPlayer.setShuffle(shuffleMode == AudioServiceShuffleMode.all);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> setRepeatMode(AudioServiceRepeatMode repeatMode) async {
|
||||||
|
super.setRepeatMode(repeatMode);
|
||||||
|
audioPlayer.setLoopMode(switch (repeatMode) {
|
||||||
|
AudioServiceRepeatMode.all ||
|
||||||
|
AudioServiceRepeatMode.group =>
|
||||||
|
PlaylistMode.loop,
|
||||||
|
AudioServiceRepeatMode.one => PlaylistMode.single,
|
||||||
|
_ => PlaylistMode.none,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> stop() async {
|
||||||
|
await Get.find<AudioPlayerProvider>().stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> skipToNext() async {
|
||||||
|
await audioPlayer.skipToNext();
|
||||||
|
await super.skipToNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> skipToPrevious() async {
|
||||||
|
await audioPlayer.skipToPrevious();
|
||||||
|
await super.skipToPrevious();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> onTaskRemoved() async {
|
||||||
|
await Get.find<AudioPlayerProvider>().stop();
|
||||||
|
return super.onTaskRemoved();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<PlaybackState> _transformEvent() async {
|
||||||
|
return PlaybackState(
|
||||||
|
controls: [
|
||||||
|
MediaControl.skipToPrevious,
|
||||||
|
audioPlayer.isPlaying ? MediaControl.pause : MediaControl.play,
|
||||||
|
MediaControl.skipToNext,
|
||||||
|
MediaControl.stop,
|
||||||
|
],
|
||||||
|
systemActions: {
|
||||||
|
MediaAction.seek,
|
||||||
|
},
|
||||||
|
androidCompactActionIndices: const [0, 1, 2],
|
||||||
|
playing: audioPlayer.isPlaying,
|
||||||
|
updatePosition: audioPlayer.position,
|
||||||
|
bufferedPosition: audioPlayer.bufferedPosition,
|
||||||
|
shuffleMode: audioPlayer.isShuffled == true
|
||||||
|
? AudioServiceShuffleMode.all
|
||||||
|
: AudioServiceShuffleMode.none,
|
||||||
|
repeatMode: switch (audioPlayer.loopMode) {
|
||||||
|
PlaylistMode.loop => AudioServiceRepeatMode.all,
|
||||||
|
PlaylistMode.single => AudioServiceRepeatMode.one,
|
||||||
|
_ => AudioServiceRepeatMode.none,
|
||||||
|
},
|
||||||
|
processingState: audioPlayer.isBuffering
|
||||||
|
? AudioProcessingState.loading
|
||||||
|
: AudioProcessingState.ready,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
101
lib/services/audio_services/windows_audio_service.dart
Executable file
101
lib/services/audio_services/windows_audio_service.dart
Executable file
@ -0,0 +1,101 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:rhythm_box/providers/audio_player.dart';
|
||||||
|
import 'package:rhythm_box/services/audio_services/image.dart';
|
||||||
|
import 'package:smtc_windows/smtc_windows.dart';
|
||||||
|
import 'package:spotify/spotify.dart';
|
||||||
|
import 'package:rhythm_box/services/audio_player/audio_player.dart';
|
||||||
|
import 'package:rhythm_box/services/audio_player/playback_state.dart';
|
||||||
|
import 'package:rhythm_box/services/artist.dart';
|
||||||
|
|
||||||
|
class WindowsAudioService {
|
||||||
|
final SMTCWindows smtc;
|
||||||
|
|
||||||
|
final subscriptions = <StreamSubscription>[];
|
||||||
|
|
||||||
|
WindowsAudioService() : smtc = SMTCWindows(enabled: false) {
|
||||||
|
smtc.setPlaybackStatus(PlaybackStatus.Stopped);
|
||||||
|
final buttonStream = smtc.buttonPressStream.listen((event) {
|
||||||
|
switch (event) {
|
||||||
|
case PressedButton.play:
|
||||||
|
audioPlayer.resume();
|
||||||
|
break;
|
||||||
|
case PressedButton.pause:
|
||||||
|
audioPlayer.pause();
|
||||||
|
break;
|
||||||
|
case PressedButton.next:
|
||||||
|
audioPlayer.skipToNext();
|
||||||
|
break;
|
||||||
|
case PressedButton.previous:
|
||||||
|
audioPlayer.skipToPrevious();
|
||||||
|
break;
|
||||||
|
case PressedButton.stop:
|
||||||
|
Get.find<AudioPlayerProvider>().stop();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
final playerStateStream =
|
||||||
|
audioPlayer.playerStateStream.listen((state) async {
|
||||||
|
switch (state) {
|
||||||
|
case AudioPlaybackState.playing:
|
||||||
|
await smtc.setPlaybackStatus(PlaybackStatus.Playing);
|
||||||
|
break;
|
||||||
|
case AudioPlaybackState.paused:
|
||||||
|
await smtc.setPlaybackStatus(PlaybackStatus.Paused);
|
||||||
|
break;
|
||||||
|
case AudioPlaybackState.stopped:
|
||||||
|
await smtc.setPlaybackStatus(PlaybackStatus.Stopped);
|
||||||
|
break;
|
||||||
|
case AudioPlaybackState.completed:
|
||||||
|
await smtc.setPlaybackStatus(PlaybackStatus.Changing);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
final positionStream = audioPlayer.positionStream.listen((pos) async {
|
||||||
|
await smtc.setPosition(pos);
|
||||||
|
});
|
||||||
|
|
||||||
|
final durationStream = audioPlayer.durationStream.listen((duration) async {
|
||||||
|
await smtc.setEndTime(duration);
|
||||||
|
});
|
||||||
|
|
||||||
|
subscriptions.addAll([
|
||||||
|
buttonStream,
|
||||||
|
playerStateStream,
|
||||||
|
positionStream,
|
||||||
|
durationStream,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> addTrack(Track track) async {
|
||||||
|
if (!smtc.enabled) {
|
||||||
|
await smtc.enableSmtc();
|
||||||
|
}
|
||||||
|
await smtc.updateMetadata(
|
||||||
|
MusicMetadata(
|
||||||
|
title: track.name!,
|
||||||
|
albumArtist: track.artists?.firstOrNull?.name ?? "Unknown",
|
||||||
|
artist: track.artists?.asString() ?? "Unknown",
|
||||||
|
album: track.album?.name ?? "Unknown",
|
||||||
|
thumbnail: (track.album?.images).asUrlString(
|
||||||
|
placeholder: ImagePlaceholder.albumArt,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void dispose() {
|
||||||
|
smtc.disableSmtc();
|
||||||
|
smtc.dispose();
|
||||||
|
for (var element in subscriptions) {
|
||||||
|
element.cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
53
lib/services/primitive.dart
Executable file
53
lib/services/primitive.dart
Executable file
@ -0,0 +1,53 @@
|
|||||||
|
import 'dart:math';
|
||||||
|
import 'package:uuid/uuid.dart';
|
||||||
|
|
||||||
|
abstract class PrimitiveUtils {
|
||||||
|
static bool containsTextInBracket(String matcher, String text) {
|
||||||
|
final allMatches = RegExp(r"(?<=\().+?(?=\))").allMatches(matcher);
|
||||||
|
if (allMatches.isEmpty) return false;
|
||||||
|
return allMatches
|
||||||
|
.map((e) => e.group(0))
|
||||||
|
.every((match) => match?.contains(text) ?? false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static final Random _random = Random();
|
||||||
|
static T getRandomElement<T>(List<T> list) {
|
||||||
|
return list[_random.nextInt(list.length)];
|
||||||
|
}
|
||||||
|
|
||||||
|
static const uuid = Uuid();
|
||||||
|
|
||||||
|
static String toReadableNumber(double num) {
|
||||||
|
if (num > 999 && num < 99999) {
|
||||||
|
return "${(num / 1000).toStringAsFixed(0)}K";
|
||||||
|
} else if (num > 99999 && num < 999999) {
|
||||||
|
return "${(num / 1000).toStringAsFixed(0)}K";
|
||||||
|
} else if (num > 999999 && num < 999999999) {
|
||||||
|
return "${(num / 1000000).toStringAsFixed(0)}M";
|
||||||
|
} else if (num > 999999999) {
|
||||||
|
return "${(num / 1000000000).toStringAsFixed(0)}B";
|
||||||
|
} else {
|
||||||
|
return num.toStringAsFixed(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<T> raceMultiple<T>(
|
||||||
|
Future<T> Function() inner, {
|
||||||
|
Duration timeout = const Duration(milliseconds: 2500),
|
||||||
|
int retryCount = 4,
|
||||||
|
}) async {
|
||||||
|
return Future.any(
|
||||||
|
List.generate(retryCount, (i) {
|
||||||
|
if (i == 0) return inner();
|
||||||
|
return Future.delayed(
|
||||||
|
Duration(milliseconds: timeout.inMilliseconds * i),
|
||||||
|
inner,
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static String toSafeFileName(String str) {
|
||||||
|
return str.replaceAll(RegExp(r'[/\?%*:|"<>]'), ' ');
|
||||||
|
}
|
||||||
|
}
|
39
lib/services/server/active_sourced_track.dart
Executable file
39
lib/services/server/active_sourced_track.dart
Executable file
@ -0,0 +1,39 @@
|
|||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:rhythm_box/providers/audio_player.dart';
|
||||||
|
import 'package:rhythm_box/services/audio_player/audio_player.dart';
|
||||||
|
import 'package:rhythm_box/services/sourced_track/models/source_info.dart';
|
||||||
|
import 'package:rhythm_box/services/sourced_track/sourced_track.dart';
|
||||||
|
|
||||||
|
class ActiveSourcedTrackProvider extends GetxController {
|
||||||
|
Rx<SourcedTrack?> state = Rx(null);
|
||||||
|
|
||||||
|
void updateTrack(SourcedTrack? sourcedTrack) {
|
||||||
|
state.value = sourcedTrack;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> populateSibling() async {
|
||||||
|
if (state.value == null) return;
|
||||||
|
state.value = await state.value!.copyWithSibling();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> swapSibling(SourceInfo sibling) async {
|
||||||
|
if (state.value == null) return;
|
||||||
|
await populateSibling();
|
||||||
|
final newTrack = await state.value!.swapWithSibling(sibling);
|
||||||
|
if (newTrack == null) return;
|
||||||
|
|
||||||
|
state.value = newTrack;
|
||||||
|
await audioPlayer.pause();
|
||||||
|
|
||||||
|
final playback = Get.find<AudioPlayerProvider>();
|
||||||
|
final oldActiveIndex = audioPlayer.currentIndex;
|
||||||
|
|
||||||
|
await playback.addTracksAtFirst([newTrack]);
|
||||||
|
await Future.delayed(const Duration(milliseconds: 50));
|
||||||
|
await playback.jumpToTrack(newTrack);
|
||||||
|
|
||||||
|
await audioPlayer.removeTrack(oldActiveIndex);
|
||||||
|
|
||||||
|
await audioPlayer.resume();
|
||||||
|
}
|
||||||
|
}
|
66
lib/services/server/routes/playback.dart
Executable file
66
lib/services/server/routes/playback.dart
Executable file
@ -0,0 +1,66 @@
|
|||||||
|
import 'dart:developer';
|
||||||
|
|
||||||
|
import 'package:dio/dio.dart' hide Response;
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:get/get.dart' hide Response;
|
||||||
|
import 'package:rhythm_box/providers/audio_player.dart';
|
||||||
|
import 'package:rhythm_box/services/audio_player/audio_player.dart';
|
||||||
|
import 'package:rhythm_box/services/server/active_sourced_track.dart';
|
||||||
|
import 'package:rhythm_box/services/server/sourced_track.dart';
|
||||||
|
import 'package:rhythm_box/services/sourced_track/sourced_track.dart';
|
||||||
|
import 'package:shelf/shelf.dart';
|
||||||
|
|
||||||
|
class ServerPlaybackRoutesProvider {
|
||||||
|
/// @get('/stream/<trackId>')
|
||||||
|
Future<Response> getStreamTrackId(Request request, String trackId) async {
|
||||||
|
final AudioPlayerProvider playback = Get.find();
|
||||||
|
|
||||||
|
try {
|
||||||
|
final track = playback.state.value.tracks
|
||||||
|
.firstWhere((element) => element.id == trackId);
|
||||||
|
|
||||||
|
final ActiveSourcedTrackProvider activeSourcedTrack = Get.find();
|
||||||
|
final sourcedTrack = activeSourcedTrack.state.value?.id == track.id
|
||||||
|
? activeSourcedTrack
|
||||||
|
: await Get.find<SourcedTrackProvider>().fetch(RhythmMedia(track));
|
||||||
|
|
||||||
|
activeSourcedTrack.updateTrack(sourcedTrack as SourcedTrack?);
|
||||||
|
|
||||||
|
final res = await Dio().get(
|
||||||
|
sourcedTrack!.url,
|
||||||
|
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",
|
||||||
|
},
|
||||||
|
responseType: ResponseType.stream,
|
||||||
|
validateStatus: (status) => status! < 500,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final audioStream =
|
||||||
|
(res.data?.stream as Stream<Uint8List>?)?.asBroadcastStream();
|
||||||
|
|
||||||
|
audioStream!.listen(
|
||||||
|
(event) {},
|
||||||
|
cancelOnError: true,
|
||||||
|
);
|
||||||
|
|
||||||
|
return Response(
|
||||||
|
res.statusCode!,
|
||||||
|
body: audioStream,
|
||||||
|
context: {
|
||||||
|
"shelf.io.buffer_output": false,
|
||||||
|
},
|
||||||
|
headers: res.headers.map,
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
log('[PlaybackSever] Error: $e');
|
||||||
|
return Response.internalServerError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
48
lib/services/server/server.dart
Executable file
48
lib/services/server/server.dart
Executable file
@ -0,0 +1,48 @@
|
|||||||
|
import 'dart:developer';
|
||||||
|
import 'dart:io';
|
||||||
|
import 'dart:math' hide log;
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:get/get.dart' hide Response;
|
||||||
|
import 'package:rhythm_box/services/rhythm_media.dart';
|
||||||
|
import 'package:rhythm_box/services/server/routes/playback.dart';
|
||||||
|
import 'package:shelf/shelf.dart';
|
||||||
|
import 'package:shelf/shelf_io.dart';
|
||||||
|
import 'package:shelf_router/shelf_router.dart';
|
||||||
|
|
||||||
|
class PlaybackServerProvider extends GetxController {
|
||||||
|
HttpServer? _server;
|
||||||
|
Router? _router;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onInit() {
|
||||||
|
_initServer();
|
||||||
|
super.onInit();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _initServer() async {
|
||||||
|
const pipeline = Pipeline();
|
||||||
|
if (kDebugMode) {
|
||||||
|
pipeline.addMiddleware(logRequests());
|
||||||
|
}
|
||||||
|
|
||||||
|
final port = Random().nextInt(17500) + 5000;
|
||||||
|
|
||||||
|
RhythmMedia.serverPort = port;
|
||||||
|
|
||||||
|
_router = Router();
|
||||||
|
_router!.get("/ping", (Request request) => Response.ok("pong"));
|
||||||
|
_router!.get(
|
||||||
|
"/stream/<trackId>",
|
||||||
|
Get.find<ServerPlaybackRoutesProvider>().getStreamTrackId,
|
||||||
|
);
|
||||||
|
|
||||||
|
_server = await serve(
|
||||||
|
pipeline.addHandler(_router!.call),
|
||||||
|
InternetAddress.anyIPv4,
|
||||||
|
port,
|
||||||
|
);
|
||||||
|
|
||||||
|
log('[Playback] Playback server at http://${_server!.address.host}:${_server!.port}');
|
||||||
|
}
|
||||||
|
}
|
17
lib/services/server/sourced_track.dart
Executable file
17
lib/services/server/sourced_track.dart
Executable file
@ -0,0 +1,17 @@
|
|||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:rhythm_box/services/audio_player/audio_player.dart';
|
||||||
|
import 'package:rhythm_box/services/local_track.dart';
|
||||||
|
import 'package:rhythm_box/services/sourced_track/sourced_track.dart';
|
||||||
|
|
||||||
|
class SourcedTrackProvider extends GetxController {
|
||||||
|
Future<SourcedTrack?> fetch(RhythmMedia? media) async {
|
||||||
|
final track = media?.track;
|
||||||
|
if (track == null || track is LocalTrack) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final sourcedTrack = await SourcedTrack.fetchFromTrack(track: track);
|
||||||
|
|
||||||
|
return sourcedTrack;
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
import 'package:rhythm_box/providers/audio_player.dart';
|
||||||
import 'package:rhythm_box/providers/spotify.dart';
|
import 'package:rhythm_box/providers/spotify.dart';
|
||||||
import 'package:rhythm_box/widgets/auto_cache_image.dart';
|
import 'package:rhythm_box/widgets/auto_cache_image.dart';
|
||||||
import 'package:skeletonizer/skeletonizer.dart';
|
import 'package:skeletonizer/skeletonizer.dart';
|
||||||
@ -65,6 +66,10 @@ class _PlaylistTrackListState extends State<PlaylistTrackList> {
|
|||||||
item?.artists!.map((x) => x.name!).join(', ') ??
|
item?.artists!.map((x) => x.name!).join(', ') ??
|
||||||
'Please stand by...',
|
'Please stand by...',
|
||||||
),
|
),
|
||||||
|
onTap: () {
|
||||||
|
if (item == null) return;
|
||||||
|
Get.find<AudioPlayerProvider>().load([item], autoPlay: true);
|
||||||
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -5,16 +5,22 @@
|
|||||||
import FlutterMacOS
|
import FlutterMacOS
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
import audio_service
|
||||||
import audio_session
|
import audio_session
|
||||||
|
import device_info_plus
|
||||||
import media_kit_libs_macos_audio
|
import media_kit_libs_macos_audio
|
||||||
import package_info_plus
|
import package_info_plus
|
||||||
import path_provider_foundation
|
import path_provider_foundation
|
||||||
|
import shared_preferences_foundation
|
||||||
import sqflite
|
import sqflite
|
||||||
|
|
||||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||||
|
AudioServicePlugin.register(with: registry.registrar(forPlugin: "AudioServicePlugin"))
|
||||||
AudioSessionPlugin.register(with: registry.registrar(forPlugin: "AudioSessionPlugin"))
|
AudioSessionPlugin.register(with: registry.registrar(forPlugin: "AudioSessionPlugin"))
|
||||||
|
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
|
||||||
MediaKitLibsMacosAudioPlugin.register(with: registry.registrar(forPlugin: "MediaKitLibsMacosAudioPlugin"))
|
MediaKitLibsMacosAudioPlugin.register(with: registry.registrar(forPlugin: "MediaKitLibsMacosAudioPlugin"))
|
||||||
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
|
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
|
||||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||||
|
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||||
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
|
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
|
||||||
}
|
}
|
||||||
|
228
pubspec.lock
228
pubspec.lock
@ -9,6 +9,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.6.1"
|
version: "3.6.1"
|
||||||
|
args:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: args
|
||||||
|
sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.5.0"
|
||||||
async:
|
async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -17,6 +25,30 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.11.0"
|
version: "2.11.0"
|
||||||
|
audio_service:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: audio_service
|
||||||
|
sha256: "9dd5ba7e77567b290c35908b1950d61485b4dfdd3a0ac398e98cfeec04651b75"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.18.15"
|
||||||
|
audio_service_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: audio_service_platform_interface
|
||||||
|
sha256: "8431a455dac9916cc9ee6f7da5620a666436345c906ad2ebb7fa41d18b3c1bf4"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.1.1"
|
||||||
|
audio_service_web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: audio_service_web
|
||||||
|
sha256: "4cdc2127cd4562b957fb49227dc58e3303fafb09bde2573bc8241b938cf759d9"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.1.3"
|
||||||
audio_session:
|
audio_session:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -33,6 +65,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.1"
|
version: "2.1.1"
|
||||||
|
build_cli_annotations:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: build_cli_annotations
|
||||||
|
sha256: b59d2769769efd6c9ff6d4c4cede0be115a566afc591705c2040b707534b1172
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.0"
|
||||||
cached_network_image:
|
cached_network_image:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -113,8 +153,24 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.8"
|
version: "1.0.8"
|
||||||
dio:
|
device_info_plus:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: device_info_plus
|
||||||
|
sha256: a7fd703482b391a87d60b6061d04dfdeab07826b96f9abd8f5ed98068acc0074
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "10.1.2"
|
||||||
|
device_info_plus_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: device_info_plus_platform_interface
|
||||||
|
sha256: "282d3cf731045a2feb66abfe61bbc40870ae50a3ed10a4d3d217556c35c8c2ba"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "7.0.1"
|
||||||
|
dio:
|
||||||
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: dio
|
name: dio
|
||||||
sha256: "0dfb6b6a1979dac1c1245e17cef824d7b452ea29bd33d3467269f9bef3715fb0"
|
sha256: "0dfb6b6a1979dac1c1245e17cef824d7b452ea29bd33d3467269f9bef3715fb0"
|
||||||
@ -198,6 +254,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.0.0"
|
version: "4.0.0"
|
||||||
|
flutter_rust_bridge:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_rust_bridge
|
||||||
|
sha256: "02720226035257ad0b571c1256f43df3e1556a499f6bcb004849a0faaa0e87f0"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.82.6"
|
||||||
flutter_test:
|
flutter_test:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description: flutter
|
description: flutter
|
||||||
@ -272,6 +336,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.2"
|
version: "1.2.2"
|
||||||
|
http_methods:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: http_methods
|
||||||
|
sha256: "6bccce8f1ec7b5d701e7921dca35e202d425b57e317ba1a37f2638590e29e566"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.1"
|
||||||
http_parser:
|
http_parser:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -440,6 +512,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.15.0"
|
version: "1.15.0"
|
||||||
|
mime:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: mime
|
||||||
|
sha256: "801fd0b26f14a4a58ccb09d5892c3fbdeff209594300a542492cf13fba9d247a"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.6"
|
||||||
oauth2:
|
oauth2:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -560,6 +640,22 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.8"
|
version: "2.1.8"
|
||||||
|
pool:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: pool
|
||||||
|
sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.5.1"
|
||||||
|
puppeteer:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: puppeteer
|
||||||
|
sha256: a6752d4f09b510ae41911bfd0997f957e723d38facf320dd9ee0e5661108744a
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.13.0"
|
||||||
rxdart:
|
rxdart:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -576,6 +672,94 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.2"
|
version: "1.0.2"
|
||||||
|
shared_preferences:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: shared_preferences
|
||||||
|
sha256: "746e5369a43170c25816cc472ee016d3a66bc13fcf430c0bc41ad7b4b2922051"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.2"
|
||||||
|
shared_preferences_android:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences_android
|
||||||
|
sha256: "480ba4345773f56acda9abf5f50bd966f581dac5d514e5fc4a18c62976bbba7e"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.2"
|
||||||
|
shared_preferences_foundation:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences_foundation
|
||||||
|
sha256: c4b35f6cb8f63c147312c054ce7c2254c8066745125264f0c88739c417fc9d9f
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.5.2"
|
||||||
|
shared_preferences_linux:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences_linux
|
||||||
|
sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.4.1"
|
||||||
|
shared_preferences_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences_platform_interface
|
||||||
|
sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.4.1"
|
||||||
|
shared_preferences_web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences_web
|
||||||
|
sha256: d2ca4132d3946fec2184261726b355836a82c33d7d5b67af32692aff18a4684e
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.4.2"
|
||||||
|
shared_preferences_windows:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences_windows
|
||||||
|
sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.4.1"
|
||||||
|
shelf:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: shelf
|
||||||
|
sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.4.1"
|
||||||
|
shelf_router:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: shelf_router
|
||||||
|
sha256: f5e5d492440a7fb165fe1e2e1a623f31f734d3370900070b2b1e0d0428d59864
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.4"
|
||||||
|
shelf_static:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shelf_static
|
||||||
|
sha256: a41d3f53c4adf0f57480578c1d61d90342cd617de7fc8077b1304643c2d85c1e
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.2"
|
||||||
|
shelf_web_socket:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shelf_web_socket
|
||||||
|
sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.4"
|
||||||
skeletonizer:
|
skeletonizer:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -589,6 +773,14 @@ packages:
|
|||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.99"
|
version: "0.0.99"
|
||||||
|
smtc_windows:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: smtc_windows
|
||||||
|
sha256: "0fd64d0c6a0c8ea4ea7908d31195eadc8f6d45d5245159fc67259e9e8704100f"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.1.3"
|
||||||
source_span:
|
source_span:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -685,6 +877,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.7.2"
|
version: "0.7.2"
|
||||||
|
tuple:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: tuple
|
||||||
|
sha256: a97ce2013f240b2f3807bcbaf218765b6f301c3eff91092bcfa23a039e7dd151
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.2"
|
||||||
typed_data:
|
typed_data:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -710,7 +910,7 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.2"
|
version: "2.0.2"
|
||||||
uuid:
|
uuid:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: uuid
|
name: uuid
|
||||||
sha256: "83d37c7ad7aaf9aa8e275490669535c8080377cfa7a7004c24dfac53afffaa90"
|
sha256: "83d37c7ad7aaf9aa8e275490669535c8080377cfa7a7004c24dfac53afffaa90"
|
||||||
@ -741,6 +941,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.0"
|
version: "1.0.0"
|
||||||
|
web_socket_channel:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: web_socket_channel
|
||||||
|
sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.4.0"
|
||||||
win32:
|
win32:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -749,6 +957,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.5.4"
|
version: "5.5.4"
|
||||||
|
win32_registry:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: win32_registry
|
||||||
|
sha256: "723b7f851e5724c55409bb3d5a32b203b3afe8587eaf5dafb93a5fed8ecda0d6"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.4"
|
||||||
xdg_directories:
|
xdg_directories:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -765,6 +981,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.5.0"
|
version: "6.5.0"
|
||||||
|
yaml:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: yaml
|
||||||
|
sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.2"
|
||||||
youtube_explode_dart:
|
youtube_explode_dart:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -56,6 +56,15 @@ dependencies:
|
|||||||
piped_client: ^0.1.1
|
piped_client: ^0.1.1
|
||||||
flutter_broadcasts: ^0.4.0
|
flutter_broadcasts: ^0.4.0
|
||||||
audio_session: ^0.1.21
|
audio_session: ^0.1.21
|
||||||
|
shared_preferences: ^2.3.2
|
||||||
|
audio_service: ^0.18.15
|
||||||
|
smtc_windows: ^0.1.3
|
||||||
|
win32_registry: ^1.1.4
|
||||||
|
uuid: ^4.4.2
|
||||||
|
device_info_plus: ^10.1.2
|
||||||
|
shelf: ^1.4.1
|
||||||
|
shelf_router: ^1.1.4
|
||||||
|
dio: ^5.6.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
@ -8,6 +8,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
|
|||||||
|
|
||||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||||
media_kit_native_event_loop
|
media_kit_native_event_loop
|
||||||
|
smtc_windows
|
||||||
)
|
)
|
||||||
|
|
||||||
set(PLUGIN_BUNDLED_LIBRARIES)
|
set(PLUGIN_BUNDLED_LIBRARIES)
|
||||||
|
Loading…
Reference in New Issue
Block a user