import 'package:http/http.dart'; import '../exceptions/exceptions.dart'; import '../videos/streams/streams.dart'; class YoutubeHttpClient { final Client _httpClient = Client(); final Map _defaultHeaders = const { 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36', 'accept-language': 'en-US,en;q=1.0' }; /// Throws if something is wrong with the response. void _validateResponse(BaseResponse response, int statusCode) { var request = response.request; if (request.url.host.endsWith('.google.com') && request.url.path.startsWith('/sorry/')) { throw RequestLimitExceededException.httpRequest(response); } if (statusCode >= 500) { throw TransientFailureException.httpRequest(response); } if (statusCode == 429) { throw RequestLimitExceededException.httpRequest(response); } if (statusCode >= 400) { throw FatalFailureException.httpRequest(response); } } Future get(dynamic url, {Map headers}) { return _httpClient.get(url, headers: {...?headers, ..._defaultHeaders}); } Future post(dynamic url, {Map headers}) { return _httpClient.post(url, headers: {...?headers, ..._defaultHeaders}); } Future head(dynamic url, {Map headers}) { return _httpClient.head(url, headers: {...?headers, ..._defaultHeaders}); } Future getString(dynamic url, {Map headers, bool validate = true}) async { var response = await _httpClient.get(url, headers: {...?headers, ..._defaultHeaders}); if (validate) { _validateResponse(response, response.statusCode); } return response.body; } Future postString(dynamic url, {Map headers, bool validate = true}) async { var response = await _httpClient.post(url, headers: {...?headers, ..._defaultHeaders}); if (validate) { _validateResponse(response, response.statusCode); } return response.body; } Stream> getStream(StreamInfo streamInfo, {Map headers, bool validate = true}) async* { var url = streamInfo.url; if (!streamInfo.isRateLimited()) { var request = Request('get', url); request.headers.addAll(_defaultHeaders); var response = await request.send(); if (validate) { _validateResponse(response, response.statusCode); } yield* response.stream; } else { for (var i = 0; i < streamInfo.size.totalBytes; i += 9898989) { var request = Request('get', url); request.headers['range'] = 'bytes=$i-${i + 9898989}'; request.headers.addAll(_defaultHeaders); var response = await request.send(); if (validate) { _validateResponse(response, response.statusCode); } yield* response.stream; } } } Future getContentLength(dynamic url, {Map headers, bool validate = true}) async { var response = await head(url, headers: headers); if (validate) { _validateResponse(response, response.statusCode); } return int.tryParse(response.headers['content-length'] ?? ''); } /// Closes the [Client] assigned to this [YoutubeHttpClient]. /// Should be called after this is not used anymore. void close() => _httpClient.close(); }