-
-
Notifications
You must be signed in to change notification settings - Fork 42
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
cce3e35
commit 7beb52d
Showing
12 changed files
with
291 additions
and
65 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import 'package:Bloomee/repository/Youtube/yt_charts_home.dart'; | ||
import 'package:bloc/bloc.dart'; | ||
|
||
part 'explore_state.dart'; | ||
|
||
class ExploreCubit extends Cubit<ExploreState> { | ||
ExploreCubit() : super(ExploreInitial()) { | ||
getTrendingVideos(); | ||
} | ||
|
||
void getTrendingVideos() async { | ||
final ytCharts = await fetchTrendingVideos(); | ||
|
||
emit(state.copyWith(ytCharts: ytCharts)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
// ignore_for_file: public_member_api_docs, sort_constructors_first | ||
part of 'explore_cubit.dart'; | ||
|
||
class ExploreState { | ||
List<List<Map<String, dynamic>>> ytCharts = List.empty(growable: true); | ||
ExploreState({ | ||
required this.ytCharts, | ||
}); | ||
|
||
ExploreState copyWith({ | ||
List<List<Map<String, dynamic>>>? ytCharts, | ||
}) { | ||
return ExploreState( | ||
ytCharts: ytCharts ?? this.ytCharts, | ||
); | ||
} | ||
} | ||
|
||
final class ExploreInitial extends ExploreState { | ||
ExploreInitial() : super(ytCharts: []); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
import 'package:http/http.dart' as http; | ||
import 'dart:convert'; | ||
|
||
Future<List<List<Map<String, dynamic>>>> fetchTrendingVideos() async { | ||
// Fetch the YouTube page to extract the INNERTUBE_API_KEY | ||
var response = await http | ||
.get(Uri.parse('https://charts.youtube.com/charts/TrendingVideos/gb')); | ||
final keyRegex = RegExp(r'"INNERTUBE_API_KEY"\s*:\s*"(.*?)"'); | ||
final apiKey = keyRegex.firstMatch(response.body)?.group(1); | ||
|
||
if (apiKey == null) { | ||
throw Exception('Failed to extract INNERTUBE_API_KEY'); | ||
} | ||
|
||
// Prepare the headers and data for the POST request | ||
final headers = { | ||
'referer': 'https://charts.youtube.com/charts/TrendingVideos/gb', | ||
}; | ||
|
||
final data = { | ||
"context": { | ||
"client": { | ||
"clientName": "WEB_MUSIC_ANALYTICS", | ||
"clientVersion": "2.0", | ||
"hl": "en", | ||
"gl": "AR", | ||
"experimentIds": [], | ||
"experimentsToken": "", | ||
"theme": "MUSIC" | ||
}, | ||
"capabilities": {}, | ||
"request": {"internalExperimentFlags": []} | ||
}, | ||
"browseId": "FEmusic_analytics_charts_home", | ||
"query": "perspective=CHART_HOME&chart_params_country_code=global" | ||
}; | ||
|
||
// Make the POST request | ||
response = await http.post( | ||
Uri.parse( | ||
'https://charts.youtube.com/youtubei/v1/browse?alt=json&key=$apiKey'), | ||
headers: headers, | ||
body: json.encode(data), | ||
); | ||
|
||
if (response.statusCode == 200) { | ||
// Parse the JSON response | ||
// return json.decode(response.body); | ||
List<dynamic> data = json.decode(response.body)["contents"] | ||
['sectionListRenderer']["contents"][0] | ||
['musicAnalyticsSectionRenderer']['content']['videos']; | ||
|
||
List<List<Map<String, dynamic>>> playlists = []; | ||
|
||
for (var types in data) { | ||
List<Map<String, dynamic>> playlist = []; | ||
for (var i in types['videoViews']) { | ||
String title = i['title']; | ||
String views = i['viewCount']; | ||
String id = i['id']; | ||
String img = i['thumbnail']['thumbnails'][0]['url']; | ||
List<String> artists = []; | ||
for (var artist in i['artists']) { | ||
artists.add(artist['name']); | ||
} | ||
playlist.add({ | ||
'title': title, | ||
'views': views, | ||
'id': id, | ||
'img': img, | ||
'artists': artists | ||
}); | ||
} | ||
playlists.add(playlist); | ||
} | ||
return playlists; | ||
} else { | ||
throw Exception('Failed to load data: ${response.statusCode}'); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
import 'dart:convert'; | ||
import 'dart:developer'; | ||
import 'package:http/http.dart' as http; | ||
|
||
String decodeEscapeSequences(String encodedString) { | ||
return encodedString.replaceAllMapped(RegExp(r'\\x([0-9a-fA-F]{2})'), | ||
(match) => String.fromCharCode(int.parse(match.group(1)!, radix: 16))); | ||
} | ||
|
||
Future<List<dynamic>> fetchMusicData() async { | ||
final client = http.Client(); | ||
final headers = { | ||
'User-Agent': | ||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36', | ||
'Sec-Ch-Ua': | ||
'"Chromium";v="94", "Google Chrome";v="94", ";Not A Brand";v="99"', | ||
'Sec-Ch-Ua-Mobile': '?0', | ||
'Sec-Ch-Ua-Platform': '"Windows"', | ||
}; | ||
|
||
final response = await client.get(Uri.parse('https://music.youtube.com/'), | ||
headers: headers); | ||
if (response.statusCode == 200) { | ||
final html = response.body; | ||
final pattern = RegExp(r"data:\s*'\\x7b(.*?)'"); | ||
final matches = pattern.allMatches(html); | ||
if (matches.isNotEmpty) { | ||
final encodedString = | ||
r'\x7b'.toString() + matches.first.group(1).toString(); | ||
final decodedBytes = | ||
utf8.decode(decodeEscapeSequences(encodedString).codeUnits); | ||
|
||
final Map<String, dynamic> data = jsonDecode(decodedBytes); | ||
// log(data.keys.toString(), name: "YT Music Home Data Keys"); | ||
final items = []; | ||
final contents = data['contents']['singleColumnBrowseResultsRenderer'] | ||
['tabs'][0]['tabRenderer']['content']['sectionListRenderer'] | ||
['contents'][0]['musicCarouselShelfRenderer']['contents']; | ||
|
||
for (var content in contents) { | ||
final img = content['musicResponsiveListItemRenderer']['thumbnail'] | ||
['musicThumbnailRenderer']["thumbnail"]["thumbnails"][0]["url"]; | ||
final title = content['musicResponsiveListItemRenderer']['flexColumns'] | ||
[0]['musicResponsiveListItemFlexColumnRenderer']["text"]["runs"] | ||
[0]["text"]; | ||
final watchid = content['musicResponsiveListItemRenderer'] | ||
['flexColumns'][0] | ||
['musicResponsiveListItemFlexColumnRenderer']["text"]["runs"][0] | ||
["navigationEndpoint"]["watchEndpoint"]["videoId"]; | ||
final playlistid = content['musicResponsiveListItemRenderer'] | ||
['flexColumns'][0] | ||
['musicResponsiveListItemFlexColumnRenderer']["text"]["runs"][0] | ||
["navigationEndpoint"]["watchEndpoint"]["playlistId"]; | ||
var artists = ''; | ||
for (var artist in content['musicResponsiveListItemRenderer'] | ||
['flexColumns'][1]['musicResponsiveListItemFlexColumnRenderer'] | ||
["text"]["runs"]) { | ||
artists += artist["text"]; | ||
} | ||
items.add({ | ||
"title": title, | ||
"img": img, | ||
"watchid": watchid, | ||
"playlistid": playlistid, | ||
"artists": artists | ||
}); | ||
} | ||
|
||
return items; | ||
} | ||
} | ||
|
||
throw Exception('Failed to load'); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.