Last active
July 2, 2020 10:58
-
-
Save jaceshim/eaf78b8b6e3a94ee1d79c3f56ac839af to your computer and use it in GitHub Desktop.
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
import 'dart:async'; | |
import 'dart:convert'; | |
import 'package:html/parser.dart'; | |
import 'package:http/http.dart' as http; | |
class VideoService extends BaseService { | |
final KeyStorageService _keyStorageService = locator<KeyStorageService>(); | |
Future<List<Channel>> getChannels(String searchQuery) async { | |
final List<Channel> result = []; | |
try { | |
final Map<String, dynamic> data = | |
await getYoutubeContents("/results?sp=EgIQAg%253D%253D&search_query=${Uri.encodeComponent(searchQuery)}"); | |
if (data != null) { | |
List<dynamic> channelDataList = | |
data["contents"]["sectionListRenderer"]["contents"][0]["itemSectionRenderer"]["contents"]; | |
channelDataList.forEach((channelData) { | |
var channelRenderer = channelData["compactChannelRenderer"]; | |
String channelId = channelRenderer["channelId"]; | |
String thumbnail = channelRenderer["thumbnail"]["thumbnails"][0]["url"]; | |
if (thumbnail.startsWith("//")) { | |
thumbnail = "https:${thumbnail}"; | |
} | |
String displayName = channelRenderer["displayName"]["runs"][0]["text"]; | |
var videoCountTextData = channelRenderer["videoCountText"]["runs"]; | |
String videoCountText = | |
"${videoCountTextData[0]["text"]} ${videoCountTextData[1]["text"]} ${videoCountTextData[2]["text"]}"; | |
String subscriberCountText = channelRenderer["subscriberCountText"]["runs"][0]["text"]; | |
result.add(Channel( | |
channelId: channelId, | |
thumbnail: thumbnail, | |
displayName: displayName, | |
videoCountText: videoCountText, | |
subscriberCountText: subscriberCountText, | |
)); | |
}); | |
} | |
} catch (e) { | |
print("get videos error : $e"); | |
} | |
return result; | |
} | |
Future<List<Video>> getChannelVideos() async { | |
final List<Channel> channels = _keyStorageService.subscriptionChannels ?? MemoryStorageService().defaultChannels; | |
final List<Video> result = []; | |
var futures = <Future>[]; | |
try { | |
channels.forEach((channel) { | |
futures.add(getYoutubeContents("/channel/${channel.channelId}/videos").then((data) { | |
if (data != null) { | |
List<dynamic> videoDataList = data["contents"]["singleColumnBrowseResultsRenderer"]["tabs"][1] | |
["tabRenderer"]["content"]["sectionListRenderer"]["contents"][0]["itemSectionRenderer"]["contents"]; | |
Future.forEach(videoDataList, (videoData) async { | |
try { | |
final Video video = _parseVideoData(videoData); | |
result.add(video); | |
} catch (e) { | |
print("$videoData : extract url error : $e"); | |
} | |
}); | |
} | |
})); | |
}); | |
} catch (e) { | |
print("get videos error : $e"); | |
} | |
await Future.wait(futures); | |
return result; | |
} | |
Future<List<Video>> searchVideos(String searchQuery) async { | |
final List<Video> result = []; | |
try { | |
Map<String, dynamic> data = await getYoutubeContents("/results?search_query=${Uri.encodeComponent(searchQuery)}"); | |
if (data != null) { | |
List<dynamic> videoDataList = | |
data["contents"]["sectionListRenderer"]["contents"][0]["itemSectionRenderer"]["contents"]; | |
Future.forEach(videoDataList, (videoData) async { | |
try { | |
final Video video = _parseVideoData(videoData); | |
result.add(video); | |
} catch (e) { | |
print("$videoData : extract url error : $e"); | |
} | |
}); | |
} | |
} catch (e) { | |
print("get videos error : $e"); | |
} | |
return result; | |
} | |
static final String youtubeUrl = "https://m.youtube.com"; | |
Future<Map<String, dynamic>> getYoutubeContents(String uri) async { | |
final String url = "${youtubeUrl}${uri}"; | |
Map<String, dynamic> result; | |
try { | |
var response = await http.get( | |
url, | |
headers: { | |
"user-agent": | |
"Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Mobile Safari/537.36", | |
}, | |
); | |
if (response.statusCode == 200) { | |
final String htmlString = response.body; | |
var document = parse(htmlString); | |
String initData = document.querySelector("#initial-data").nodes[0].text; | |
initData = initData.replaceAll("<!--", "").replaceAll("-->", "").trim(); | |
result = jsonDecode(initData); | |
} | |
} catch (e) { | |
print("$url => get html error : $e"); | |
} | |
return result; | |
} | |
Video _parseVideoData(Map<String, dynamic> videoData) { | |
Map<String, dynamic> data = videoData["compactVideoRenderer"]; | |
String videoId = data["videoId"]; | |
String title = data["title"]["runs"][0]["text"]; | |
String thumbnail = "https://i.ytimg.com/vi_webp/${videoId}/sddefault.webp"; | |
String viewCountText = data["viewCountText"]["runs"][0]["text"]; | |
String lengthText = data["lengthText"]["runs"][0]["text"]; | |
String publishedTimeText = ""; | |
try { | |
publishedTimeText = data["publishedTimeText"]["runs"][0]["text"]; | |
} catch (e) { | |
print("parse publishedTimeText error: $e"); | |
} | |
// String videoPlayUrl = await CommonUtils.extractYoutubeUrl(videoId); | |
return Video( | |
videoId: videoId, | |
title: title, | |
thumbnail: thumbnail, | |
publishedTimeText: publishedTimeText, | |
viewCountText: viewCountText, | |
lengthText: lengthText, | |
// videoPlayUrl: videoPlayUrl, | |
); | |
} | |
Future<List<Video>> getNextVideos(String videoId) async { | |
final List<Video> result = []; | |
try { | |
Map<String, dynamic> data = await getYoutubeContents("/watch?v=${videoId}"); | |
if (data != null) { | |
List<dynamic> contents = data["contents"]["singleColumnWatchNextResults"]["results"]["results"]["contents"]; | |
for (Map<String, dynamic> content in contents) { | |
bool hasKey = content.containsKey("itemSectionRenderer"); | |
if (hasKey) { | |
List<dynamic> videoDataList = content["itemSectionRenderer"]["contents"]; | |
videoDataList = videoDataList.sublist(1, videoDataList.length); | |
videoDataList.forEach((videoData) { | |
try { | |
final Video video = _parseVideoData(videoData); | |
result.add(video); | |
} catch (e) { | |
print("$videoData : extract url error : $e"); | |
} | |
}); | |
} | |
} | |
} | |
} catch (e) { | |
print("get videos error : $e"); | |
} | |
return result; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment