mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-13 07:55:18 +00:00
refactor: use replace http with dio and use it as the default
This commit is contained in:
parent
d2683c52d8
commit
b2d9e64758
@ -1,12 +1,11 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:catcher_2/catcher_2.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:http/http.dart';
|
||||
import 'package:spotube/models/skip_segment.dart';
|
||||
import 'package:spotube/provider/server/active_sourced_track.dart';
|
||||
import 'package:spotube/provider/user_preferences/user_preferences_provider.dart';
|
||||
import 'package:spotube/provider/user_preferences/user_preferences_state.dart';
|
||||
import 'package:spotube/services/dio/dio.dart';
|
||||
|
||||
class SourcedSegments {
|
||||
final String source;
|
||||
@ -30,7 +29,8 @@ Future<List<SkipSegment>> getAndCacheSkipSegments(String id) async {
|
||||
);
|
||||
}
|
||||
|
||||
final res = await get(Uri(
|
||||
final res = await globalDio.getUri(
|
||||
Uri(
|
||||
scheme: "https",
|
||||
host: "sponsor.ajay.app",
|
||||
path: "/api/skipSegments",
|
||||
@ -46,13 +46,18 @@ Future<List<SkipSegment>> getAndCacheSkipSegments(String id) async {
|
||||
],
|
||||
"actionType": 'skip'
|
||||
},
|
||||
));
|
||||
),
|
||||
options: Options(
|
||||
responseType: ResponseType.json,
|
||||
validateStatus: (status) => (status ?? 0) < 500,
|
||||
),
|
||||
);
|
||||
|
||||
if (res.body == "Not Found") {
|
||||
if (res.data == "Not Found") {
|
||||
return List.castFrom<dynamic, SkipSegment>([]);
|
||||
}
|
||||
|
||||
final data = jsonDecode(res.body) as List;
|
||||
final data = res.data as List;
|
||||
final segments = data.map((obj) {
|
||||
final start = obj["segment"].first.toInt();
|
||||
final end = obj["segment"].last.toInt();
|
||||
|
@ -9,29 +9,34 @@ class SyncedLyricsNotifier extends FamilyAsyncNotifier<SubtitleSimple, Track?>
|
||||
Track get _track => arg!;
|
||||
|
||||
Future<SubtitleSimple> getSpotifyLyrics(String? token) async {
|
||||
final res = await http.get(
|
||||
final res = await globalDio.getUri(
|
||||
Uri.parse(
|
||||
"https://spclient.wg.spotify.com/color-lyrics/v2/track/${_track.id}?format=json&market=from_token",
|
||||
),
|
||||
options: Options(
|
||||
headers: {
|
||||
"User-Agent":
|
||||
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.0.0 Safari/537.36",
|
||||
"App-platform": "WebPlayer",
|
||||
"authorization": "Bearer $token"
|
||||
});
|
||||
},
|
||||
responseType: ResponseType.json,
|
||||
validateStatus: (status) => true,
|
||||
),
|
||||
);
|
||||
|
||||
if (res.statusCode != 200) {
|
||||
return SubtitleSimple(
|
||||
lyrics: [],
|
||||
name: _track.name!,
|
||||
uri: res.request!.url,
|
||||
uri: res.realUri,
|
||||
rating: 0,
|
||||
provider: "Spotify",
|
||||
);
|
||||
}
|
||||
final linesRaw = Map.castFrom<dynamic, dynamic, String, dynamic>(
|
||||
jsonDecode(utf8.decode(res.bodyBytes)),
|
||||
)["lyrics"]?["lines"] as List?;
|
||||
final linesRaw =
|
||||
Map.castFrom<dynamic, dynamic, String, dynamic>(res.data)["lyrics"]
|
||||
?["lines"] as List?;
|
||||
|
||||
final lines = linesRaw?.map((line) {
|
||||
return LyricSlice(
|
||||
@ -44,7 +49,7 @@ class SyncedLyricsNotifier extends FamilyAsyncNotifier<SubtitleSimple, Track?>
|
||||
return SubtitleSimple(
|
||||
lyrics: lines,
|
||||
name: _track.name!,
|
||||
uri: res.request!.url,
|
||||
uri: res.realUri,
|
||||
rating: 100,
|
||||
provider: "Spotify",
|
||||
);
|
||||
@ -55,7 +60,7 @@ class SyncedLyricsNotifier extends FamilyAsyncNotifier<SubtitleSimple, Track?>
|
||||
Future<SubtitleSimple> getLRCLibLyrics() async {
|
||||
final packageInfo = await PackageInfo.fromPlatform();
|
||||
|
||||
final res = await http.get(
|
||||
final res = await globalDio.getUri(
|
||||
Uri(
|
||||
scheme: "https",
|
||||
host: "lrclib.net",
|
||||
@ -67,23 +72,26 @@ class SyncedLyricsNotifier extends FamilyAsyncNotifier<SubtitleSimple, Track?>
|
||||
"duration": _track.duration?.inSeconds.toString(),
|
||||
},
|
||||
),
|
||||
options: Options(
|
||||
headers: {
|
||||
"User-Agent":
|
||||
"Spotube v${packageInfo.version} (https://github.com/KRTirtho/spotube)"
|
||||
},
|
||||
responseType: ResponseType.json,
|
||||
),
|
||||
);
|
||||
|
||||
if (res.statusCode != 200) {
|
||||
return SubtitleSimple(
|
||||
lyrics: [],
|
||||
name: _track.name!,
|
||||
uri: res.request!.url,
|
||||
uri: res.realUri,
|
||||
rating: 0,
|
||||
provider: "LRCLib",
|
||||
);
|
||||
}
|
||||
|
||||
final json = jsonDecode(utf8.decode(res.bodyBytes)) as Map<String, dynamic>;
|
||||
final json = res.data as Map<String, dynamic>;
|
||||
|
||||
final syncedLyricsRaw = json["syncedLyrics"] as String?;
|
||||
final syncedLyrics = syncedLyricsRaw?.isNotEmpty == true
|
||||
@ -97,7 +105,7 @@ class SyncedLyricsNotifier extends FamilyAsyncNotifier<SubtitleSimple, Track?>
|
||||
return SubtitleSimple(
|
||||
lyrics: syncedLyrics!,
|
||||
name: _track.name!,
|
||||
uri: res.request!.url,
|
||||
uri: res.realUri,
|
||||
rating: 100,
|
||||
provider: "LRCLib",
|
||||
);
|
||||
@ -111,7 +119,7 @@ class SyncedLyricsNotifier extends FamilyAsyncNotifier<SubtitleSimple, Track?>
|
||||
return SubtitleSimple(
|
||||
lyrics: plainLyrics,
|
||||
name: _track.name!,
|
||||
uri: res.request!.url,
|
||||
uri: res.realUri,
|
||||
rating: 0,
|
||||
provider: "LRCLib",
|
||||
);
|
||||
|
@ -1,10 +1,10 @@
|
||||
library spotify;
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:catcher_2/catcher_2.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hive_flutter/hive_flutter.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
@ -23,9 +23,9 @@ import 'package:spotube/models/spotify_friends.dart';
|
||||
import 'package:spotube/provider/custom_spotify_endpoint_provider.dart';
|
||||
import 'package:spotube/provider/spotify_provider.dart';
|
||||
import 'package:spotube/provider/user_preferences/user_preferences_provider.dart';
|
||||
import 'package:spotube/services/dio/dio.dart';
|
||||
import 'package:spotube/services/wikipedia/wikipedia.dart';
|
||||
import 'package:spotube/utils/persisted_state_notifier.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
import 'package:wikipedia_api/wikipedia_api.dart';
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:spotify/spotify.dart';
|
||||
import 'package:spotube/models/spotify/home_feed.dart';
|
||||
import 'package:spotube/models/spotify_friends.dart';
|
||||
@ -9,9 +9,21 @@ import 'package:timezone/timezone.dart' as tz;
|
||||
class CustomSpotifyEndpoints {
|
||||
static const _baseUrl = 'https://api.spotify.com/v1';
|
||||
final String accessToken;
|
||||
final http.Client _client;
|
||||
final Dio _client;
|
||||
|
||||
CustomSpotifyEndpoints(this.accessToken) : _client = http.Client();
|
||||
CustomSpotifyEndpoints(this.accessToken)
|
||||
: _client = Dio(
|
||||
BaseOptions(
|
||||
baseUrl: _baseUrl,
|
||||
responseType: ResponseType.json,
|
||||
headers: {
|
||||
"content-type": "application/json",
|
||||
if (accessToken.isNotEmpty)
|
||||
"authorization": "Bearer $accessToken",
|
||||
"accept": "application/json",
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
// views API
|
||||
|
||||
@ -65,44 +77,34 @@ class CustomSpotifyEndpoints {
|
||||
if (country != null) 'country': country.name,
|
||||
}.entries.map((e) => '${e.key}=${e.value}').join('&');
|
||||
|
||||
final res = await _client.get(
|
||||
final res = await _client.getUri(
|
||||
Uri.parse('$_baseUrl/views/$view?$queryParams'),
|
||||
headers: {
|
||||
"content-type": "application/json",
|
||||
"authorization": "Bearer $accessToken",
|
||||
"accept": "application/json",
|
||||
},
|
||||
);
|
||||
|
||||
if (res.statusCode == 200) {
|
||||
return jsonDecode(utf8.decode(res.bodyBytes));
|
||||
return res.data;
|
||||
} else {
|
||||
throw Exception(
|
||||
'[CustomSpotifyEndpoints.getView]: Failed to get view'
|
||||
'\nStatus code: ${res.statusCode}'
|
||||
'\nBody: ${res.body}',
|
||||
'\nBody: ${res.data}',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<String>> listGenreSeeds() async {
|
||||
final res = await _client.get(
|
||||
final res = await _client.getUri(
|
||||
Uri.parse("$_baseUrl/recommendations/available-genre-seeds"),
|
||||
headers: {
|
||||
"content-type": "application/json",
|
||||
if (accessToken.isNotEmpty) "authorization": "Bearer $accessToken",
|
||||
"accept": "application/json",
|
||||
},
|
||||
);
|
||||
|
||||
if (res.statusCode == 200) {
|
||||
final body = jsonDecode(utf8.decode(res.bodyBytes));
|
||||
final body = res.data;
|
||||
return List<String>.from(body["genres"] ?? []);
|
||||
} else {
|
||||
throw Exception(
|
||||
'[CustomSpotifyEndpoints.listGenreSeeds]: Failed to get genre seeds'
|
||||
'\nStatus code: ${res.statusCode}'
|
||||
'\nBody: ${res.body}',
|
||||
'\nBody: ${res.data}',
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -152,30 +154,18 @@ class CustomSpotifyEndpoints {
|
||||
}
|
||||
final pathQuery =
|
||||
"$_baseUrl/recommendations?${parameters.entries.map((e) => '${e.key}=${e.value}').join('&')}";
|
||||
final res = await _client.get(
|
||||
Uri.parse(pathQuery),
|
||||
headers: {
|
||||
"content-type": "application/json",
|
||||
if (accessToken.isNotEmpty) "authorization": "Bearer $accessToken",
|
||||
"accept": "application/json",
|
||||
},
|
||||
);
|
||||
final result = jsonDecode(utf8.decode(res.bodyBytes));
|
||||
final res = await _client.getUri(Uri.parse(pathQuery));
|
||||
final result = res.data;
|
||||
return List.castFrom<dynamic, Track>(
|
||||
result["tracks"].map((track) => Track.fromJson(track)).toList(),
|
||||
);
|
||||
}
|
||||
|
||||
Future<SpotifyFriends> getFriendActivity() async {
|
||||
final res = await _client.get(
|
||||
final res = await _client.getUri(
|
||||
Uri.parse("https://guc-spclient.spotify.com/presence-view/v1/buddylist"),
|
||||
headers: {
|
||||
"content-type": "application/json",
|
||||
"authorization": "Bearer $accessToken",
|
||||
"accept": "application/json",
|
||||
},
|
||||
);
|
||||
return SpotifyFriends.fromJson(jsonDecode(utf8.decode(res.bodyBytes)));
|
||||
return SpotifyFriends.fromJson(res.data);
|
||||
}
|
||||
|
||||
Future<SpotifyHomeFeed> getHomeFeed({
|
||||
@ -190,7 +180,7 @@ class CustomSpotifyEndpoints {
|
||||
'origin': 'https://open.spotify.com',
|
||||
'referer': 'https://open.spotify.com/'
|
||||
};
|
||||
final response = await http.get(
|
||||
final response = await _client.getUri(
|
||||
Uri(
|
||||
scheme: "https",
|
||||
host: "api-partner.spotify.com",
|
||||
@ -219,21 +209,10 @@ class CustomSpotifyEndpoints {
|
||||
),
|
||||
},
|
||||
),
|
||||
headers: headers,
|
||||
);
|
||||
|
||||
if (response.statusCode >= 400) {
|
||||
throw Exception(
|
||||
"[RequestException] "
|
||||
"Status: ${response.statusCode}\n"
|
||||
"Body: ${response.body}",
|
||||
);
|
||||
}
|
||||
options: Options(headers: headers));
|
||||
|
||||
final data = SpotifyHomeFeed.fromJson(
|
||||
transformHomeFeedJsonMap(
|
||||
jsonDecode(utf8.decode(response.bodyBytes)),
|
||||
),
|
||||
transformHomeFeedJsonMap(response.data),
|
||||
);
|
||||
|
||||
return data;
|
||||
@ -252,7 +231,7 @@ class CustomSpotifyEndpoints {
|
||||
'origin': 'https://open.spotify.com',
|
||||
'referer': 'https://open.spotify.com/'
|
||||
};
|
||||
final response = await http.get(
|
||||
final response = await _client.getUri(
|
||||
Uri(
|
||||
scheme: "https",
|
||||
host: "api-partner.spotify.com",
|
||||
@ -280,21 +259,12 @@ class CustomSpotifyEndpoints {
|
||||
),
|
||||
},
|
||||
),
|
||||
headers: headers,
|
||||
options: Options(headers: headers),
|
||||
);
|
||||
|
||||
if (response.statusCode >= 400) {
|
||||
throw Exception(
|
||||
"[RequestException] "
|
||||
"Status: ${response.statusCode}\n"
|
||||
"Body: ${response.body}",
|
||||
);
|
||||
}
|
||||
|
||||
final data = SpotifyHomeFeedSection.fromJson(
|
||||
transformSectionItemJsonMap(
|
||||
jsonDecode(utf8.decode(response.bodyBytes))["data"]["homeSections"]
|
||||
["sections"][0],
|
||||
response.data["data"]["homeSections"]["sections"][0],
|
||||
),
|
||||
);
|
||||
|
||||
|
3
lib/services/dio/dio.dart
Normal file
3
lib/services/dio/dio.dart
Normal file
@ -0,0 +1,3 @@
|
||||
import 'package:dio/dio.dart';
|
||||
|
||||
final globalDio = Dio();
|
@ -1,13 +1,12 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:html/dom.dart' hide Text;
|
||||
import 'package:spotify/spotify.dart';
|
||||
import 'package:spotube/components/library/user_local_tracks.dart';
|
||||
import 'package:spotube/components/root/update_dialog.dart';
|
||||
import 'package:spotube/models/logger.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:spotube/models/lyrics.dart';
|
||||
import 'package:spotube/services/dio/dio.dart';
|
||||
import 'package:spotube/services/sourced_track/sourced_track.dart';
|
||||
|
||||
import 'package:spotube/utils/primitive_utils.dart';
|
||||
@ -70,9 +69,12 @@ abstract class ServiceUtils {
|
||||
}
|
||||
|
||||
static Future<String?> extractLyrics(Uri url) async {
|
||||
final response = await http.get(url);
|
||||
final response = await globalDio.getUri(
|
||||
url,
|
||||
options: Options(responseType: ResponseType.plain),
|
||||
);
|
||||
|
||||
Document document = parser.parse(response.body);
|
||||
Document document = parser.parse(response.data);
|
||||
String? lyrics = document.querySelector('div.lyrics')?.text.trim();
|
||||
if (lyrics == null) {
|
||||
lyrics = "";
|
||||
@ -111,11 +113,14 @@ abstract class ServiceUtils {
|
||||
|
||||
String reqUrl = "$searchUrl${Uri.encodeComponent(song)}";
|
||||
Map<String, String> headers = {"Authorization": 'Bearer $apiKey'};
|
||||
final response = await http.get(
|
||||
final response = await globalDio.getUri(
|
||||
Uri.parse(authHeader ? reqUrl : "$reqUrl&access_token=$apiKey"),
|
||||
options: Options(
|
||||
headers: authHeader ? headers : null,
|
||||
responseType: ResponseType.json,
|
||||
),
|
||||
);
|
||||
Map data = jsonDecode(utf8.decode(response.bodyBytes))["response"];
|
||||
Map data = response.data["response"];
|
||||
if (data["hits"]?.length == 0) return null;
|
||||
List results = data["hits"]?.map((val) {
|
||||
return <String, dynamic>{
|
||||
@ -195,8 +200,11 @@ abstract class ServiceUtils {
|
||||
queryParameters: {"q": query},
|
||||
);
|
||||
|
||||
final res = await http.get(searchUri);
|
||||
final document = parser.parse(res.body);
|
||||
final res = await globalDio.getUri(
|
||||
searchUri,
|
||||
options: Options(responseType: ResponseType.plain),
|
||||
);
|
||||
final document = parser.parse(res.data);
|
||||
final results =
|
||||
document.querySelectorAll("#tablecontainer table tbody tr td a");
|
||||
|
||||
@ -229,7 +237,11 @@ abstract class ServiceUtils {
|
||||
|
||||
logger.v("[Selected subtitle] ${topResult.text} | $subtitleUri");
|
||||
|
||||
final lrcDocument = parser.parse((await http.get(subtitleUri)).body);
|
||||
final lrcDocument = parser.parse((await globalDio.getUri(
|
||||
subtitleUri,
|
||||
options: Options(responseType: ResponseType.plain),
|
||||
))
|
||||
.data);
|
||||
final lrcList = lrcDocument
|
||||
.querySelector("#ctl00_ContentPlaceHolder1_lbllyrics")
|
||||
?.innerHtml
|
||||
@ -384,14 +396,16 @@ abstract class ServiceUtils {
|
||||
final packageInfo = await PackageInfo.fromPlatform();
|
||||
|
||||
if (Env.releaseChannel == ReleaseChannel.nightly) {
|
||||
final value = await http.get(
|
||||
final value = await globalDio.getUri(
|
||||
Uri.parse(
|
||||
"https://api.github.com/repos/KRTirtho/spotube/actions/workflows/spotube-release-binary.yml/runs?status=success&per_page=1",
|
||||
),
|
||||
options: Options(
|
||||
responseType: ResponseType.json,
|
||||
),
|
||||
);
|
||||
|
||||
final buildNum =
|
||||
jsonDecode(value.body)["workflow_runs"][0]["run_number"] as int;
|
||||
final buildNum = value.data["workflow_runs"][0]["run_number"] as int;
|
||||
|
||||
if (buildNum <= int.parse(packageInfo.buildNumber) || !context.mounted) {
|
||||
return;
|
||||
@ -406,13 +420,12 @@ abstract class ServiceUtils {
|
||||
},
|
||||
);
|
||||
} else {
|
||||
final value = await http.get(
|
||||
final value = await globalDio.getUri(
|
||||
Uri.parse(
|
||||
"https://api.github.com/repos/KRTirtho/spotube/releases/latest",
|
||||
),
|
||||
);
|
||||
final tagName =
|
||||
(jsonDecode(value.body)["tag_name"] as String).replaceAll("v", "");
|
||||
final tagName = (value.data["tag_name"] as String).replaceAll("v", "");
|
||||
final currentVersion = packageInfo.version == "Unknown"
|
||||
? null
|
||||
: Version.parse(packageInfo.version);
|
||||
|
@ -55,7 +55,6 @@ dependencies:
|
||||
hive_flutter: ^1.1.0
|
||||
hooks_riverpod: ^2.5.1
|
||||
html: ^0.15.1
|
||||
http: ^1.2.0
|
||||
image_picker: ^1.1.0
|
||||
intl: ^0.18.0
|
||||
introduction_screen: ^3.1.14
|
||||
@ -131,6 +130,7 @@ dependencies:
|
||||
crypto: ^3.0.3
|
||||
local_notifier: ^0.1.6
|
||||
tray_manager: ^0.2.2
|
||||
http: ^1.2.1
|
||||
|
||||
dev_dependencies:
|
||||
build_runner: ^2.4.9
|
||||
|
Loading…
Reference in New Issue
Block a user