mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-13 07:55:18 +00:00
refactor: use envied instead of flutter_dotenv to ensure better security
This commit is contained in:
parent
3a6caeb35d
commit
10f1c675d0
@ -1,3 +1,7 @@
|
|||||||
POCKETBASE_URL=
|
POCKETBASE_URL=
|
||||||
USERNAME=
|
USERNAME=
|
||||||
PASSWORD=
|
PASSWORD=
|
||||||
|
|
||||||
|
# The format:
|
||||||
|
# SPOTIFY_SECRETS=clintId1:clientSecret1,clientId2:clientSecret2
|
||||||
|
SPOTIFY_SECRETS=
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -66,7 +66,7 @@ installer.exe
|
|||||||
|
|
||||||
# secrets
|
# secrets
|
||||||
*.env
|
*.env
|
||||||
lib/models/generated_secrets.dart
|
lib/collections/env.g.dart
|
||||||
help.txt
|
help.txt
|
||||||
secrets.json
|
secrets.json
|
||||||
|
|
||||||
|
@ -133,35 +133,15 @@ Do the following:
|
|||||||
```bash
|
```bash
|
||||||
dnf install libsecret libsecret-devel jsoncpp gstreamer1-devel gstreamer1-plugins-base-tools gstreamer1-doc gstreamer1-plugins-base-devel gstreamer1-plugins-good gstreamer1-plugins-good-extras
|
dnf install libsecret libsecret-devel jsoncpp gstreamer1-devel gstreamer1-plugins-base-tools gstreamer1-doc gstreamer1-plugins-base-devel gstreamer1-plugins-good gstreamer1-plugins-good-extras
|
||||||
```
|
```
|
||||||
- Clone the Repo & Run `flutter pub get` in the Terminal
|
- Clone the Repo
|
||||||
- Create a `secrets.json` in root of the project. The structure should be similar to the following example:
|
- Create a `.env` in root of the project following the `.env.example` template
|
||||||
|
- Now run the following to bootstrap the project
|
||||||
```jsoc name="secrets.json"
|
```bash
|
||||||
{
|
flutter pub get && flutter pub run build_runner build --delete-conflicting-outputs
|
||||||
"LYRICS_SECRET": [
|
```
|
||||||
"Bo3LQEMcL2xUAJ6yCfQowV6f8K78s9J9FLa67AsyWmvhkP9LWikkgcEyFrzvs7jsR",
|
|
||||||
"HiLHxLj8uv2VhBZfq9BQ9HVrWQk5Jc8aneMZX8RV4KjTmC387K692xrbNK35c8Qe4"
|
|
||||||
],
|
|
||||||
"SPOTIFY_SECRET": [
|
|
||||||
{
|
|
||||||
"clientId": "9ed19daf-c7a2-4c28-91ac-2c5283ad86cf",
|
|
||||||
"clientSecret": "236d5822-820e-457e-b18c-10e258c9386b"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"clientId": "b4769027-e048-4485-8f0b-b8a336f2cd97",
|
|
||||||
"clientSecret": "41df6ea4-eba2-4d42-b7be-6f727555fccc"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
> You can add more clientId/clientSecret/genius-access-token if you want. The credentials used in the example are dummy (fake). You've to use your own secrets
|
|
||||||
|
|
||||||
- Finally run these following commands in the root of the project to start the Spotube Locally
|
- Finally run these following commands in the root of the project to start the Spotube Locally
|
||||||
|
```bash
|
||||||
```bash
|
flutter run -d <window|macos|linux|(<android-device-id>)>
|
||||||
$ dart bin/create-secrets.dart --local
|
```
|
||||||
$ flutter run -d <window|macos|linux|(<android-device-id>)>
|
|
||||||
```
|
|
||||||
|
|
||||||
Do debugging/testing/build etc then submit to us with PR against the development branch (master) & we'll review your code
|
Do debugging/testing/build etc then submit to us with PR against the development branch (master) & we'll review your code
|
||||||
|
@ -1,177 +0,0 @@
|
|||||||
import 'dart:convert';
|
|
||||||
import 'dart:io';
|
|
||||||
|
|
||||||
import 'package:path/path.dart' as path;
|
|
||||||
|
|
||||||
// blob metadata for de-stringifying
|
|
||||||
const randHash = [
|
|
||||||
49,
|
|
||||||
111,
|
|
||||||
98,
|
|
||||||
72,
|
|
||||||
78,
|
|
||||||
122,
|
|
||||||
98,
|
|
||||||
48,
|
|
||||||
112,
|
|
||||||
73,
|
|
||||||
81,
|
|
||||||
50,
|
|
||||||
112,
|
|
||||||
89,
|
|
||||||
90,
|
|
||||||
50,
|
|
||||||
116,
|
|
||||||
83,
|
|
||||||
84,
|
|
||||||
110,
|
|
||||||
99,
|
|
||||||
105,
|
|
||||||
76,
|
|
||||||
67,
|
|
||||||
74,
|
|
||||||
67,
|
|
||||||
89,
|
|
||||||
121,
|
|
||||||
48,
|
|
||||||
119,
|
|
||||||
77,
|
|
||||||
106,
|
|
||||||
69,
|
|
||||||
50,
|
|
||||||
86,
|
|
||||||
69,
|
|
||||||
53,
|
|
||||||
107,
|
|
||||||
77,
|
|
||||||
69,
|
|
||||||
86,
|
|
||||||
71,
|
|
||||||
101,
|
|
||||||
68,
|
|
||||||
66,
|
|
||||||
113,
|
|
||||||
78,
|
|
||||||
110,
|
|
||||||
66,
|
|
||||||
119
|
|
||||||
];
|
|
||||||
const sugarCarbonator = [
|
|
||||||
81,
|
|
||||||
119,
|
|
||||||
79,
|
|
||||||
71,
|
|
||||||
85,
|
|
||||||
53,
|
|
||||||
78,
|
|
||||||
50,
|
|
||||||
69,
|
|
||||||
52,
|
|
||||||
90,
|
|
||||||
68,
|
|
||||||
107,
|
|
||||||
120,
|
|
||||||
77,
|
|
||||||
87,
|
|
||||||
89,
|
|
||||||
52,
|
|
||||||
89,
|
|
||||||
84,
|
|
||||||
73
|
|
||||||
];
|
|
||||||
const randomSalt = [
|
|
||||||
70,
|
|
||||||
117,
|
|
||||||
67,
|
|
||||||
75,
|
|
||||||
117,
|
|
||||||
116,
|
|
||||||
72,
|
|
||||||
101,
|
|
||||||
105,
|
|
||||||
102,
|
|
||||||
65,
|
|
||||||
110,
|
|
||||||
68,
|
|
||||||
87,
|
|
||||||
72,
|
|
||||||
97,
|
|
||||||
84,
|
|
||||||
85,
|
|
||||||
82,
|
|
||||||
100,
|
|
||||||
79,
|
|
||||||
73,
|
|
||||||
110,
|
|
||||||
103,
|
|
||||||
83,
|
|
||||||
117,
|
|
||||||
75,
|
|
||||||
115
|
|
||||||
];
|
|
||||||
const algorithmicSugar = [
|
|
||||||
70,
|
|
||||||
117,
|
|
||||||
67,
|
|
||||||
75,
|
|
||||||
117,
|
|
||||||
116,
|
|
||||||
72,
|
|
||||||
101,
|
|
||||||
105,
|
|
||||||
102,
|
|
||||||
65,
|
|
||||||
78,
|
|
||||||
100,
|
|
||||||
102,
|
|
||||||
68,
|
|
||||||
114,
|
|
||||||
79,
|
|
||||||
105,
|
|
||||||
100,
|
|
||||||
115,
|
|
||||||
85,
|
|
||||||
99,
|
|
||||||
107,
|
|
||||||
83
|
|
||||||
];
|
|
||||||
|
|
||||||
void main(List<String> args) async {
|
|
||||||
List<String> val;
|
|
||||||
List<Map> val2;
|
|
||||||
|
|
||||||
final cwd = Directory.current.path;
|
|
||||||
final binSafe = cwd.endsWith("/bin") ? ".." : "";
|
|
||||||
if (args.isEmpty) {
|
|
||||||
throw ArgumentError("Expected 1-3 arguments but passed none");
|
|
||||||
}
|
|
||||||
if (args.contains("--local")) {
|
|
||||||
final secretFilePath = path.join(cwd, binSafe, "secrets.json");
|
|
||||||
final file = File(secretFilePath);
|
|
||||||
if (!file.existsSync()) throw Exception("secrets.json file not found");
|
|
||||||
final data = jsonDecode(await file.readAsString());
|
|
||||||
val = List.castFrom<dynamic, String>(data["LYRICS_SECRET"]);
|
|
||||||
val2 = List.castFrom<dynamic, Map>(data["SPOTIFY_SECRET"]);
|
|
||||||
} else if (args.contains("--fdroid")) {
|
|
||||||
final decodedLyricSecret = utf8.decode(base64Decode(
|
|
||||||
args[1].replaceAll(
|
|
||||||
String.fromCharCodes(randomSalt), String.fromCharCodes(randHash)),
|
|
||||||
));
|
|
||||||
final decodedSpotifySecret = utf8.decode(base64Decode(
|
|
||||||
args.last.replaceAll(String.fromCharCodes(algorithmicSugar),
|
|
||||||
String.fromCharCodes(sugarCarbonator)),
|
|
||||||
));
|
|
||||||
val = List.castFrom<dynamic, String>(jsonDecode(decodedLyricSecret));
|
|
||||||
val2 = List.castFrom<dynamic, Map>(jsonDecode(decodedSpotifySecret));
|
|
||||||
} else {
|
|
||||||
final decodedLyricSecret = utf8.decode(base64Decode(args.first));
|
|
||||||
final decodedSpotifySecret = utf8.decode(base64Decode(args.last));
|
|
||||||
val = List.castFrom<dynamic, String>(jsonDecode(decodedLyricSecret));
|
|
||||||
val2 = List.castFrom<dynamic, Map>(jsonDecode(decodedSpotifySecret));
|
|
||||||
}
|
|
||||||
|
|
||||||
await File(path.join(cwd, binSafe, "lib/models/generated_secrets.dart"))
|
|
||||||
.writeAsString(
|
|
||||||
"final List<String> lyricsSecrets = ${jsonEncode(val)};\nfinal List<Map<String, dynamic>> spotifySecrets = ${jsonEncode(val2)};",
|
|
||||||
);
|
|
||||||
}
|
|
@ -5,11 +5,9 @@
|
|||||||
|
|
||||||
// coverage:ignore-file
|
// coverage:ignore-file
|
||||||
// ignore_for_file: type=lint
|
// ignore_for_file: type=lint
|
||||||
// ignore_for_file: directives_ordering,unnecessary_import,implicit_dynamic_list_literal
|
// ignore_for_file: directives_ordering,unnecessary_import,implicit_dynamic_list_literal,deprecated_member_use
|
||||||
|
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
|
|
||||||
class $AssetsTutorialGen {
|
class $AssetsTutorialGen {
|
||||||
const $AssetsTutorialGen();
|
const $AssetsTutorialGen();
|
||||||
@ -39,8 +37,7 @@ class Assets {
|
|||||||
AssetGenImage('assets/spotube-logo-foreground.jpg');
|
AssetGenImage('assets/spotube-logo-foreground.jpg');
|
||||||
static const AssetGenImage spotubeLogoPng =
|
static const AssetGenImage spotubeLogoPng =
|
||||||
AssetGenImage('assets/spotube-logo.png');
|
AssetGenImage('assets/spotube-logo.png');
|
||||||
static const SvgGenImage spotubeLogoSvg =
|
static const String spotubeLogoSvg = 'assets/spotube-logo.svg';
|
||||||
SvgGenImage('assets/spotube-logo.svg');
|
|
||||||
static const AssetGenImage spotubeScreenshot =
|
static const AssetGenImage spotubeScreenshot =
|
||||||
AssetGenImage('assets/spotube-screenshot.jpg');
|
AssetGenImage('assets/spotube-screenshot.jpg');
|
||||||
static const AssetGenImage spotubeBanner =
|
static const AssetGenImage spotubeBanner =
|
||||||
@ -129,54 +126,3 @@ class AssetGenImage {
|
|||||||
|
|
||||||
String get keyName => _assetName;
|
String get keyName => _assetName;
|
||||||
}
|
}
|
||||||
|
|
||||||
class SvgGenImage {
|
|
||||||
const SvgGenImage(this._assetName);
|
|
||||||
|
|
||||||
final String _assetName;
|
|
||||||
|
|
||||||
SvgPicture svg({
|
|
||||||
Key? key,
|
|
||||||
bool matchTextDirection = false,
|
|
||||||
AssetBundle? bundle,
|
|
||||||
String? package,
|
|
||||||
double? width,
|
|
||||||
double? height,
|
|
||||||
BoxFit fit = BoxFit.contain,
|
|
||||||
AlignmentGeometry alignment = Alignment.center,
|
|
||||||
bool allowDrawingOutsideViewBox = false,
|
|
||||||
WidgetBuilder? placeholderBuilder,
|
|
||||||
Color? color,
|
|
||||||
BlendMode colorBlendMode = BlendMode.srcIn,
|
|
||||||
String? semanticsLabel,
|
|
||||||
bool excludeFromSemantics = false,
|
|
||||||
Clip clipBehavior = Clip.hardEdge,
|
|
||||||
bool cacheColorFilter = false,
|
|
||||||
SvgTheme? theme,
|
|
||||||
}) {
|
|
||||||
return SvgPicture.asset(
|
|
||||||
_assetName,
|
|
||||||
key: key,
|
|
||||||
matchTextDirection: matchTextDirection,
|
|
||||||
bundle: bundle,
|
|
||||||
package: package,
|
|
||||||
width: width,
|
|
||||||
height: height,
|
|
||||||
fit: fit,
|
|
||||||
alignment: alignment,
|
|
||||||
allowDrawingOutsideViewBox: allowDrawingOutsideViewBox,
|
|
||||||
placeholderBuilder: placeholderBuilder,
|
|
||||||
color: color,
|
|
||||||
colorBlendMode: colorBlendMode,
|
|
||||||
semanticsLabel: semanticsLabel,
|
|
||||||
excludeFromSemantics: excludeFromSemantics,
|
|
||||||
clipBehavior: clipBehavior,
|
|
||||||
cacheColorFilter: cacheColorFilter,
|
|
||||||
theme: theme,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
String get path => _assetName;
|
|
||||||
|
|
||||||
String get keyName => _assetName;
|
|
||||||
}
|
|
||||||
|
@ -1,17 +1,24 @@
|
|||||||
import 'package:flutter/foundation.dart';
|
import 'package:envied/envied.dart';
|
||||||
import 'package:flutter_dotenv/flutter_dotenv.dart';
|
|
||||||
|
|
||||||
|
part 'env.g.dart';
|
||||||
|
|
||||||
|
@Envied(obfuscate: true, requireEnvFile: true, path: ".env")
|
||||||
abstract class Env {
|
abstract class Env {
|
||||||
static final String pocketbaseUrl =
|
@EnviedField(varName: 'POCKETBASE_URL', defaultValue: 'http://127.0.0.1:8090')
|
||||||
dotenv.get('POCKETBASE_URL', fallback: 'http://127.0.0.1:8090');
|
static final pocketbaseUrl = _Env.pocketbaseUrl;
|
||||||
static final String username = dotenv.get('USERNAME', fallback: 'root');
|
|
||||||
static final String password = dotenv.get('PASSWORD', fallback: '12345678');
|
|
||||||
|
|
||||||
static configure() async {
|
@EnviedField(varName: 'USERNAME', defaultValue: 'root')
|
||||||
if (kReleaseMode) {
|
static final username = _Env.username;
|
||||||
await dotenv.load(fileName: ".env");
|
|
||||||
} else {
|
@EnviedField(varName: 'PASSWORD', defaultValue: '12345678')
|
||||||
dotenv.testLoad();
|
static final password = _Env.password;
|
||||||
}
|
|
||||||
}
|
@EnviedField(varName: 'SPOTIFY_SECRETS')
|
||||||
|
static final spotifySecrets = _Env.spotifySecrets.split(',').map((e) {
|
||||||
|
final secrets = e.trim().split(":").map((e) => e.trim());
|
||||||
|
return {
|
||||||
|
"clientId": secrets.first,
|
||||||
|
"clientSecret": secrets.last,
|
||||||
|
};
|
||||||
|
}).toList();
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,6 @@ import 'package:package_info_plus/package_info_plus.dart';
|
|||||||
import 'package:platform_ui/platform_ui.dart';
|
import 'package:platform_ui/platform_ui.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
import 'package:spotube/collections/cache_keys.dart';
|
import 'package:spotube/collections/cache_keys.dart';
|
||||||
import 'package:spotube/collections/env.dart';
|
|
||||||
import 'package:spotube/components/shared/dialogs/replace_downloaded_dialog.dart';
|
import 'package:spotube/components/shared/dialogs/replace_downloaded_dialog.dart';
|
||||||
import 'package:spotube/entities/cache_track.dart';
|
import 'package:spotube/entities/cache_track.dart';
|
||||||
import 'package:spotube/collections/routes.dart';
|
import 'package:spotube/collections/routes.dart';
|
||||||
@ -80,7 +79,6 @@ void main(List<String> rawArgs) async {
|
|||||||
Hive.registerAdapter(CacheTrackAdapter());
|
Hive.registerAdapter(CacheTrackAdapter());
|
||||||
Hive.registerAdapter(CacheTrackEngagementAdapter());
|
Hive.registerAdapter(CacheTrackEngagementAdapter());
|
||||||
Hive.registerAdapter(CacheTrackSkipSegmentAdapter());
|
Hive.registerAdapter(CacheTrackSkipSegmentAdapter());
|
||||||
await Env.configure();
|
|
||||||
|
|
||||||
if (kIsDesktop) {
|
if (kIsDesktop) {
|
||||||
await windowManager.ensureInitialized();
|
await windowManager.ensureInitialized();
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:spotify/spotify.dart';
|
import 'package:spotify/spotify.dart';
|
||||||
import 'package:spotube/models/generated_secrets.dart';
|
import 'package:spotube/collections/env.dart';
|
||||||
|
|
||||||
import 'package:spotube/provider/authentication_provider.dart';
|
import 'package:spotube/provider/authentication_provider.dart';
|
||||||
import 'package:spotube/utils/primitive_utils.dart';
|
import 'package:spotube/utils/primitive_utils.dart';
|
||||||
|
|
||||||
final spotifyProvider = Provider<SpotifyApi>((ref) {
|
final spotifyProvider = Provider<SpotifyApi>((ref) {
|
||||||
final authState = ref.watch(AuthenticationNotifier.provider);
|
final authState = ref.watch(AuthenticationNotifier.provider);
|
||||||
final anonCred = PrimitiveUtils.getRandomElement(spotifySecrets);
|
final anonCred = PrimitiveUtils.getRandomElement(Env.spotifySecrets);
|
||||||
|
|
||||||
if (authState == null) {
|
if (authState == null) {
|
||||||
return SpotifyApi(
|
return SpotifyApi(
|
||||||
|
@ -4,11 +4,10 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:spotube/components/settings/color_scheme_picker_dialog.dart';
|
import 'package:spotube/components/settings/color_scheme_picker_dialog.dart';
|
||||||
import 'package:spotube/models/generated_secrets.dart';
|
|
||||||
import 'package:spotube/utils/persisted_change_notifier.dart';
|
import 'package:spotube/utils/persisted_change_notifier.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:spotube/utils/platform.dart';
|
import 'package:spotube/utils/platform.dart';
|
||||||
import 'package:spotube/utils/primitive_utils.dart';
|
|
||||||
import 'package:path/path.dart' as path;
|
import 'package:path/path.dart' as path;
|
||||||
|
|
||||||
enum LayoutMode {
|
enum LayoutMode {
|
||||||
@ -26,7 +25,6 @@ class UserPreferences extends PersistedChangeNotifier {
|
|||||||
ThemeMode themeMode;
|
ThemeMode themeMode;
|
||||||
String recommendationMarket;
|
String recommendationMarket;
|
||||||
bool saveTrackLyrics;
|
bool saveTrackLyrics;
|
||||||
String geniusAccessToken;
|
|
||||||
bool checkUpdate;
|
bool checkUpdate;
|
||||||
AudioQuality audioQuality;
|
AudioQuality audioQuality;
|
||||||
|
|
||||||
@ -41,7 +39,6 @@ class UserPreferences extends PersistedChangeNotifier {
|
|||||||
bool predownload;
|
bool predownload;
|
||||||
|
|
||||||
UserPreferences({
|
UserPreferences({
|
||||||
required this.geniusAccessToken,
|
|
||||||
required this.recommendationMarket,
|
required this.recommendationMarket,
|
||||||
required this.themeMode,
|
required this.themeMode,
|
||||||
required this.layoutMode,
|
required this.layoutMode,
|
||||||
@ -87,12 +84,6 @@ class UserPreferences extends PersistedChangeNotifier {
|
|||||||
updatePersistence();
|
updatePersistence();
|
||||||
}
|
}
|
||||||
|
|
||||||
void setGeniusAccessToken(String token) {
|
|
||||||
geniusAccessToken = token;
|
|
||||||
notifyListeners();
|
|
||||||
updatePersistence();
|
|
||||||
}
|
|
||||||
|
|
||||||
void setAccentColorScheme(MaterialColor color) {
|
void setAccentColorScheme(MaterialColor color) {
|
||||||
accentColorScheme = color;
|
accentColorScheme = color;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
@ -158,8 +149,6 @@ class UserPreferences extends PersistedChangeNotifier {
|
|||||||
saveTrackLyrics = map["saveTrackLyrics"] ?? false;
|
saveTrackLyrics = map["saveTrackLyrics"] ?? false;
|
||||||
recommendationMarket = map["recommendationMarket"] ?? recommendationMarket;
|
recommendationMarket = map["recommendationMarket"] ?? recommendationMarket;
|
||||||
checkUpdate = map["checkUpdate"] ?? checkUpdate;
|
checkUpdate = map["checkUpdate"] ?? checkUpdate;
|
||||||
geniusAccessToken = map["geniusAccessToken"] ??
|
|
||||||
PrimitiveUtils.getRandomElement(lyricsSecrets);
|
|
||||||
|
|
||||||
themeMode = ThemeMode.values[map["themeMode"] ?? 0];
|
themeMode = ThemeMode.values[map["themeMode"] ?? 0];
|
||||||
accentColorScheme = colorsMap.values
|
accentColorScheme = colorsMap.values
|
||||||
@ -185,7 +174,6 @@ class UserPreferences extends PersistedChangeNotifier {
|
|||||||
return {
|
return {
|
||||||
"saveTrackLyrics": saveTrackLyrics,
|
"saveTrackLyrics": saveTrackLyrics,
|
||||||
"recommendationMarket": recommendationMarket,
|
"recommendationMarket": recommendationMarket,
|
||||||
"geniusAccessToken": geniusAccessToken,
|
|
||||||
"themeMode": themeMode.index,
|
"themeMode": themeMode.index,
|
||||||
"accentColorScheme": accentColorScheme.value,
|
"accentColorScheme": accentColorScheme.value,
|
||||||
"checkUpdate": checkUpdate,
|
"checkUpdate": checkUpdate,
|
||||||
@ -201,7 +189,6 @@ class UserPreferences extends PersistedChangeNotifier {
|
|||||||
|
|
||||||
final userPreferencesProvider = ChangeNotifierProvider(
|
final userPreferencesProvider = ChangeNotifierProvider(
|
||||||
(_) => UserPreferences(
|
(_) => UserPreferences(
|
||||||
geniusAccessToken: "",
|
|
||||||
recommendationMarket: 'US',
|
recommendationMarket: 'US',
|
||||||
themeMode: ThemeMode.system,
|
themeMode: ThemeMode.system,
|
||||||
layoutMode: kIsMobile ? LayoutMode.compact : LayoutMode.adaptive,
|
layoutMode: kIsMobile ? LayoutMode.compact : LayoutMode.adaptive,
|
||||||
|
@ -9,7 +9,7 @@ import 'package:spotube/models/logger.dart';
|
|||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
import 'package:spotube/models/lyrics.dart';
|
import 'package:spotube/models/lyrics.dart';
|
||||||
import 'package:spotube/models/spotube_track.dart';
|
import 'package:spotube/models/spotube_track.dart';
|
||||||
import 'package:spotube/models/generated_secrets.dart';
|
|
||||||
import 'package:spotube/utils/primitive_utils.dart';
|
import 'package:spotube/utils/primitive_utils.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:html/parser.dart' as parser;
|
import 'package:html/parser.dart' as parser;
|
||||||
@ -23,6 +23,7 @@ abstract class ServiceUtils {
|
|||||||
.trim();
|
.trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated("In favor spotify lyrics api, this isn't needed anymore")
|
||||||
static String getTitle(
|
static String getTitle(
|
||||||
String title, {
|
String title, {
|
||||||
List<String> artists = const [],
|
List<String> artists = const [],
|
||||||
@ -77,6 +78,7 @@ abstract class ServiceUtils {
|
|||||||
return lyrics;
|
return lyrics;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated("In favor spotify lyrics api, this isn't needed anymore")
|
||||||
static Future<List?> searchSong(
|
static Future<List?> searchSong(
|
||||||
String title,
|
String title,
|
||||||
List<String> artist, {
|
List<String> artist, {
|
||||||
@ -85,7 +87,7 @@ abstract class ServiceUtils {
|
|||||||
bool authHeader = false,
|
bool authHeader = false,
|
||||||
}) async {
|
}) async {
|
||||||
if (apiKey == "" || apiKey == null) {
|
if (apiKey == "" || apiKey == null) {
|
||||||
apiKey = PrimitiveUtils.getRandomElement(lyricsSecrets);
|
apiKey = PrimitiveUtils.getRandomElement(/* lyricsSecrets */ []);
|
||||||
}
|
}
|
||||||
const searchUrl = 'https://api.genius.com/search?q=';
|
const searchUrl = 'https://api.genius.com/search?q=';
|
||||||
String song =
|
String song =
|
||||||
@ -111,6 +113,7 @@ abstract class ServiceUtils {
|
|||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated("In favor spotify lyrics api, this isn't needed anymore")
|
||||||
static Future<String?> getLyrics(
|
static Future<String?> getLyrics(
|
||||||
String title,
|
String title,
|
||||||
List<String> artists, {
|
List<String> artists, {
|
||||||
@ -158,8 +161,10 @@ abstract class ServiceUtils {
|
|||||||
return lyrics;
|
return lyrics;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated("In favor spotify lyrics api, this isn't needed anymore")
|
||||||
static const baseUri = "https://www.rentanadviser.com/subtitles";
|
static const baseUri = "https://www.rentanadviser.com/subtitles";
|
||||||
|
|
||||||
|
@Deprecated("In favor spotify lyrics api, this isn't needed anymore")
|
||||||
static Future<SubtitleSimple?> getTimedLyrics(SpotubeTrack track) async {
|
static Future<SubtitleSimple?> getTimedLyrics(SpotubeTrack track) async {
|
||||||
final artistNames =
|
final artistNames =
|
||||||
track.artists?.map((artist) => artist.name!).toList() ?? [];
|
track.artists?.map((artist) => artist.name!).toList() ?? [];
|
||||||
|
24
pubspec.lock
24
pubspec.lock
@ -482,6 +482,22 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.0"
|
version: "2.1.0"
|
||||||
|
envied:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: envied
|
||||||
|
sha256: d5d978fbd578b5c00123003609c39185e0b1ddf9d2ac460d710dd0eb2fc223d7
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.3.0"
|
||||||
|
envied_generator:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description:
|
||||||
|
name: envied_generator
|
||||||
|
sha256: "6c5a98c27c5eae925807692eb252ccac2b8e81f09bace1f07207c47dfb6a4eb0"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.3.0"
|
||||||
fading_edge_scrollview:
|
fading_edge_scrollview:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -607,14 +623,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.0.2"
|
version: "0.0.2"
|
||||||
flutter_dotenv:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: flutter_dotenv
|
|
||||||
sha256: d9283d92059a22e9834bc0a31336658ffba77089fb6f3cc36751f1fc7c6661a3
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "5.0.2"
|
|
||||||
flutter_feather_icons:
|
flutter_feather_icons:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -24,6 +24,7 @@ dependencies:
|
|||||||
collection: ^1.15.0
|
collection: ^1.15.0
|
||||||
cupertino_icons: ^1.0.5
|
cupertino_icons: ^1.0.5
|
||||||
dbus: ^0.7.8
|
dbus: ^0.7.8
|
||||||
|
envied: ^0.3.0
|
||||||
file_picker: ^5.2.2
|
file_picker: ^5.2.2
|
||||||
fl_query: ^1.0.0-alpha.2
|
fl_query: ^1.0.0-alpha.2
|
||||||
fl_query_hooks: ^1.0.0-alpha.2
|
fl_query_hooks: ^1.0.0-alpha.2
|
||||||
@ -32,7 +33,6 @@ dependencies:
|
|||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
flutter_cache_manager: ^3.3.0
|
flutter_cache_manager: ^3.3.0
|
||||||
flutter_dotenv: ^5.0.2
|
|
||||||
flutter_feather_icons: ^2.0.0+1
|
flutter_feather_icons: ^2.0.0+1
|
||||||
flutter_hooks: ^0.18.2+1
|
flutter_hooks: ^0.18.2+1
|
||||||
flutter_inappwebview: ^5.7.2+3
|
flutter_inappwebview: ^5.7.2+3
|
||||||
@ -89,6 +89,7 @@ dependencies:
|
|||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
build_runner: ^2.3.2
|
build_runner: ^2.3.2
|
||||||
|
envied_generator: ^0.3.0
|
||||||
flutter_distributor: ^0.0.2
|
flutter_distributor: ^0.0.2
|
||||||
flutter_gen_runner: ^5.1.0+1
|
flutter_gen_runner: ^5.1.0+1
|
||||||
flutter_launcher_icons: ^0.11.0
|
flutter_launcher_icons: ^0.11.0
|
||||||
@ -107,7 +108,6 @@ flutter:
|
|||||||
assets:
|
assets:
|
||||||
- assets/
|
- assets/
|
||||||
- assets/tutorial/
|
- assets/tutorial/
|
||||||
- .env
|
|
||||||
|
|
||||||
flutter_icons:
|
flutter_icons:
|
||||||
android: true
|
android: true
|
||||||
@ -124,5 +124,3 @@ flutter_icons:
|
|||||||
|
|
||||||
flutter_gen:
|
flutter_gen:
|
||||||
output: lib/collections
|
output: lib/collections
|
||||||
integrations:
|
|
||||||
flutter_svg: true
|
|
||||||
|
Loading…
Reference in New Issue
Block a user