Version 1.10.1

This commit is contained in:
Mattia 2021-07-23 12:54:29 +02:00
parent 73504b2a44
commit c640d08f29
20 changed files with 55 additions and 61 deletions

View File

@ -1,3 +1,8 @@
## 1.10.1
- Fix issue #146: Closed Captions couldn't be extracted anymore.
- Code cleanup.
-
## 1.10.0 ## 1.10.0
- Fix issue #144: get_video_info was removed from yt. - Fix issue #144: get_video_info was removed from yt.
- Min sdk version now is 2.13.0 - Min sdk version now is 2.13.0

View File

@ -5,7 +5,6 @@ part 'engagement.freezed.dart';
/// User activity statistics. /// User activity statistics.
@freezed @freezed
class Engagement with _$Engagement { class Engagement with _$Engagement {
const Engagement._();
const factory Engagement( const factory Engagement(
/// View count. /// View count.
@ -18,6 +17,8 @@ class Engagement with _$Engagement {
int? dislikeCount, int? dislikeCount,
) = _Engagement; ) = _Engagement;
const Engagement._();
/// Average user rating in stars (1 star to 5 stars). /// Average user rating in stars (1 star to 5 stars).
/// Returns -1 if likeCount or dislikeCount is null. /// Returns -1 if likeCount or dislikeCount is null.
num get avgRating { num get avgRating {

View File

@ -1,11 +1,11 @@
import 'package:html/parser.dart' as parser; import 'package:html/parser.dart' as parser;
import 'package:youtube_explode_dart/src/reverse_engineering/models/youtube_page.dart';
import '../../exceptions/exceptions.dart'; import '../../exceptions/exceptions.dart';
import '../../extensions/helpers_extension.dart'; import '../../extensions/helpers_extension.dart';
import '../../retry.dart'; import '../../retry.dart';
import '../youtube_http_client.dart';
import '../models/initial_data.dart'; import '../models/initial_data.dart';
import '../models/youtube_page.dart';
import '../youtube_http_client.dart';
/// ///
class ChannelPage extends YoutubePage<_InitialData> { class ChannelPage extends YoutubePage<_InitialData> {

View File

@ -1,15 +1,14 @@
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:html/dom.dart';
import 'package:html/parser.dart' as parser; import 'package:html/parser.dart' as parser;
import 'package:youtube_explode_dart/src/reverse_engineering/models/youtube_page.dart';
import '../../channels/channel_video.dart'; import '../../channels/channel_video.dart';
import '../../exceptions/exceptions.dart'; import '../../exceptions/exceptions.dart';
import '../../extensions/helpers_extension.dart'; import '../../extensions/helpers_extension.dart';
import '../../retry.dart'; import '../../retry.dart';
import '../../videos/videos.dart'; import '../../videos/videos.dart';
import '../youtube_http_client.dart';
import '../models/initial_data.dart'; import '../models/initial_data.dart';
import '../models/youtube_page.dart';
import '../youtube_http_client.dart';
/// ///
class ChannelUploadPage extends YoutubePage<_InitialData> { class ChannelUploadPage extends YoutubePage<_InitialData> {

View File

@ -1,12 +1,12 @@
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:html/parser.dart' as parser; import 'package:html/parser.dart' as parser;
import 'package:youtube_explode_dart/src/reverse_engineering/models/youtube_page.dart';
import '../../../youtube_explode_dart.dart'; import '../../../youtube_explode_dart.dart';
import '../../extensions/helpers_extension.dart'; import '../../extensions/helpers_extension.dart';
import '../../retry.dart'; import '../../retry.dart';
import '../youtube_http_client.dart';
import '../models/initial_data.dart'; import '../models/initial_data.dart';
import '../models/youtube_page.dart';
import '../youtube_http_client.dart';
/// ///
class PlaylistPage extends YoutubePage<_InitialData> { class PlaylistPage extends YoutubePage<_InitialData> {

View File

@ -11,8 +11,8 @@ import '../../search/related_query.dart';
import '../../search/search_filter.dart'; import '../../search/search_filter.dart';
import '../../search/search_video.dart'; import '../../search/search_video.dart';
import '../../videos/videos.dart'; import '../../videos/videos.dart';
import '../youtube_http_client.dart';
import '../models/initial_data.dart'; import '../models/initial_data.dart';
import '../youtube_http_client.dart';
/// ///
class SearchPage extends YoutubePage<_InitialData> { class SearchPage extends YoutubePage<_InitialData> {

View File

@ -1,16 +1,16 @@
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:html/dom.dart'; import 'package:html/dom.dart';
import 'package:html/parser.dart' as parser; import 'package:html/parser.dart' as parser;
import 'package:youtube_explode_dart/src/reverse_engineering/models/youtube_page.dart';
import '../../../youtube_explode_dart.dart'; import '../../../youtube_explode_dart.dart';
import '../../extensions/helpers_extension.dart'; import '../../extensions/helpers_extension.dart';
import '../../retry.dart'; import '../../retry.dart';
import '../../videos/video_id.dart'; import '../../videos/video_id.dart';
import '../models/initial_data.dart';
import '../models/youtube_page.dart';
import '../player/player_response.dart'; import '../player/player_response.dart';
import '../youtube_http_client.dart'; import '../youtube_http_client.dart';
import 'player_config_base.dart'; import 'player_config_base.dart';
import '../models/initial_data.dart';
/// ///
class WatchPage extends YoutubePage<_InitialData> { class WatchPage extends YoutubePage<_InitialData> {
@ -21,12 +21,11 @@ class WatchPage extends YoutubePage<_InitialData> {
static final RegExp _visitorInfoLiveExp = static final RegExp _visitorInfoLiveExp =
RegExp('VISITOR_INFO1_LIVE=([^;]+)'); RegExp('VISITOR_INFO1_LIVE=([^;]+)');
static final RegExp _yscExp = RegExp('YSC=([^;]+)'); static final RegExp _yscExp = RegExp('YSC=([^;]+)');
static final RegExp _playerResponseExp =
RegExp(r'var\s+ytInitialPlayerResponse\s*=\s*(\{.*\})');
static final _xsfrTokenExp = RegExp(r'"XSRF_TOKEN"\s*:\s*"(.+?)"');
@override @override
// Overridden to be non-nullable.
// ignore: overridden_fields
final Document root; final Document root;
/// ///
@ -48,18 +47,6 @@ class WatchPage extends YoutubePage<_InitialData> {
return 'https://youtube.com$url'; return 'https://youtube.com$url';
} }
late final String xsfrToken = getXsfrToken()!.replaceAll(r'\u003d', '=');
///
String? getXsfrToken() {
return _xsfrTokenExp
.firstMatch(root
.querySelectorAll('script')
.firstWhere((e) => _xsfrTokenExp.hasMatch(e.text))
.text)
?.group(1);
}
/// ///
bool get isOk => root.body?.querySelector('#player') != null; bool get isOk => root.body?.querySelector('#player') != null;

View File

@ -148,8 +148,8 @@ class ClosedCaptionTrack {
String get languageCode => root.getT<String>('languageCode')!; String get languageCode => root.getT<String>('languageCode')!;
/// ///
String get languageName => String? get languageName =>
root.get('name')!.getT<List<dynamic>>('runs')!.parseRuns(); root.get('name')!.getT<String>('simpleText');
/// ///
bool get autoGenerated => bool get autoGenerated =>

View File

@ -1,15 +1,16 @@
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:http_parser/http_parser.dart'; import 'package:http_parser/http_parser.dart';
import '../../exceptions/exceptions.dart'; import '../../exceptions/exceptions.dart';
import '../../extensions/helpers_extension.dart'; import '../../extensions/helpers_extension.dart';
import '../../retry.dart'; import '../../retry.dart';
import '../youtube_http_client.dart';
import '../player/player_response.dart';
import '../models/stream_info_provider.dart'; import '../models/stream_info_provider.dart';
import '../player/player_response.dart';
import '../youtube_http_client.dart';
/// ///
/// ///
@deprecated @Deprecated('This endpoint is not supported anymore.')
class VideoInfoClient { class VideoInfoClient {
final Map<String, String> root; final Map<String, String> root;
@ -51,6 +52,7 @@ class VideoInfoClient {
VideoInfoClient.parse(String raw) : root = Uri.splitQueryString(raw); VideoInfoClient.parse(String raw) : root = Uri.splitQueryString(raw);
/// ///
@alwaysThrows
static Future<VideoInfoClient> get( static Future<VideoInfoClient> get(
YoutubeHttpClient httpClient, String videoId, YoutubeHttpClient httpClient, String videoId,
[String? sts]) { [String? sts]) {

View File

@ -37,6 +37,7 @@ class SearchVideo with _$SearchVideo, BaseSearchContent {
String? uploadDate, String? uploadDate,
/// True if this video is a live stream. /// True if this video is a live stream.
// ignore: avoid_positional_boolean_parameters
bool isLive, bool isLive,
/// Channel id /// Channel id

View File

@ -72,6 +72,7 @@ mixin _$SearchVideo {
String? get uploadDate => throw _privateConstructorUsedError; String? get uploadDate => throw _privateConstructorUsedError;
/// True if this video is a live stream. /// True if this video is a live stream.
// ignore: avoid_positional_boolean_parameters
bool get isLive => throw _privateConstructorUsedError; bool get isLive => throw _privateConstructorUsedError;
/// Channel id /// Channel id
@ -317,6 +318,7 @@ class _$_SearchVideo with BaseSearchContent implements _SearchVideo {
@override @override
/// True if this video is a live stream. /// True if this video is a live stream.
// ignore: avoid_positional_boolean_parameters
final bool isLive; final bool isLive;
@override @override
@ -428,6 +430,7 @@ abstract class _SearchVideo implements SearchVideo, BaseSearchContent {
@override @override
/// True if this video is a live stream. /// True if this video is a live stream.
// ignore: avoid_positional_boolean_parameters
bool get isLive => throw _privateConstructorUsedError; bool get isLive => throw _privateConstructorUsedError;
@override @override

View File

@ -1,7 +1,8 @@
import 'package:youtube_explode_dart/src/reverse_engineering/pages/watch_page.dart';
import '../../extensions/helpers_extension.dart'; import '../../extensions/helpers_extension.dart';
import '../../reverse_engineering/responses/closed_caption_client.dart' as re import '../../reverse_engineering/responses/closed_caption_client.dart' as re
show ClosedCaptionClient; show ClosedCaptionClient;
import '../../reverse_engineering/responses/video_info_client.dart';
import '../../reverse_engineering/youtube_http_client.dart'; import '../../reverse_engineering/youtube_http_client.dart';
import '../videos.dart'; import '../videos.dart';
import 'closed_caption.dart'; import 'closed_caption.dart';
@ -34,16 +35,16 @@ class ClosedCaptionClient {
]}) async { ]}) async {
videoId = VideoId.fromString(videoId); videoId = VideoId.fromString(videoId);
var tracks = <ClosedCaptionTrackInfo>{}; var tracks = <ClosedCaptionTrackInfo>{};
var videoInfoResponse = var watchPage =
await VideoInfoClient.get(_httpClient, videoId.value); await WatchPage.get(_httpClient, videoId.value);
var playerResponse = videoInfoResponse.playerResponse; var playerResponse = watchPage.playerResponse!;
for (final track in playerResponse.closedCaptionTrack) { for (final track in playerResponse.closedCaptionTrack) {
for (final ext in formats) { for (final ext in formats) {
tracks.add(ClosedCaptionTrackInfo( tracks.add(ClosedCaptionTrackInfo(
Uri.parse(track.url) Uri.parse(track.url)
.replaceQueryParameters({'fmt': ext.formatCode}), .replaceQueryParameters({'fmt': ext.formatCode}),
Language(track.languageCode, track.languageName), Language(track.languageCode, track.languageName ?? ''),
isAutoGenerated: track.autoGenerated, isAutoGenerated: track.autoGenerated,
format: ext)); format: ext));
} }

View File

@ -13,14 +13,11 @@ class Language with _$Language {
/// ISO 639-1 code of this language. /// ISO 639-1 code of this language.
String code, String code,
/// Full English name of this language. /// Full English name of this language. This could be an empty string.
String name) = _Language; String name) = _Language;
const Language._(); const Language._();
@override
String toString() => 'Language: $name';
/// ///
factory Language.fromJson(Map<String, dynamic> json) => factory Language.fromJson(Map<String, dynamic> json) =>
_$LanguageFromJson(json); _$LanguageFromJson(json);

View File

@ -40,7 +40,7 @@ mixin _$Language {
/// ISO 639-1 code of this language. /// ISO 639-1 code of this language.
String get code => throw _privateConstructorUsedError; String get code => throw _privateConstructorUsedError;
/// Full English name of this language. /// Full English name of this language. This could be an empty string.
String get name => throw _privateConstructorUsedError; String get name => throw _privateConstructorUsedError;
Map<String, dynamic> toJson() => throw _privateConstructorUsedError; Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@ -131,9 +131,14 @@ class _$_Language extends _Language {
final String code; final String code;
@override @override
/// Full English name of this language. /// Full English name of this language. This could be an empty string.
final String name; final String name;
@override
String toString() {
return 'Language(code: $code, name: $name)';
}
@override @override
bool operator ==(dynamic other) { bool operator ==(dynamic other) {
return identical(this, other) || return identical(this, other) ||
@ -173,7 +178,7 @@ abstract class _Language extends Language {
String get code => throw _privateConstructorUsedError; String get code => throw _privateConstructorUsedError;
@override @override
/// Full English name of this language. /// Full English name of this language. This could be an empty string.
String get name => throw _privateConstructorUsedError; String get name => throw _privateConstructorUsedError;
@override @override
@JsonKey(ignore: true) @JsonKey(ignore: true)

View File

@ -1,7 +1,6 @@
import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:freezed_annotation/freezed_annotation.dart';
import '../../channels/channel_id.dart'; import '../../channels/channel_id.dart';
import '../../extensions/helpers_extension.dart';
import '../../reverse_engineering/responses/comments_client.dart' as re; import '../../reverse_engineering/responses/comments_client.dart' as re;
import '../../reverse_engineering/youtube_http_client.dart'; import '../../reverse_engineering/youtube_http_client.dart';
import '../videos.dart'; import '../videos.dart';

View File

@ -26,9 +26,6 @@ class Bitrate with Comparable<Bitrate>, _$Bitrate {
@override @override
int compareTo(Bitrate other) => bitsPerSecond.compareTo(other.bitsPerSecond); int compareTo(Bitrate other) => bitsPerSecond.compareTo(other.bitsPerSecond);
@override
List<Object> get props => [bitsPerSecond];
String _getLargestSymbol() { String _getLargestSymbol() {
if (gigaBitsPerSecond.abs() >= 1) { if (gigaBitsPerSecond.abs() >= 1) {
return 'Gbit/s'; return 'Gbit/s';

View File

@ -4,10 +4,8 @@ import '../../reverse_engineering/cipher/cipher_operations.dart';
import '../../reverse_engineering/dash_manifest.dart'; import '../../reverse_engineering/dash_manifest.dart';
import '../../reverse_engineering/heuristics.dart'; import '../../reverse_engineering/heuristics.dart';
import '../../reverse_engineering/models/stream_info_provider.dart'; import '../../reverse_engineering/models/stream_info_provider.dart';
import '../../reverse_engineering/pages/embed_page.dart';
import '../../reverse_engineering/pages/watch_page.dart'; import '../../reverse_engineering/pages/watch_page.dart';
import '../../reverse_engineering/player/player_source.dart'; import '../../reverse_engineering/player/player_source.dart';
import '../../reverse_engineering/responses/video_info_client.dart';
import '../../reverse_engineering/youtube_http_client.dart'; import '../../reverse_engineering/youtube_http_client.dart';
import '../video_id.dart'; import '../video_id.dart';
import 'bitrate.dart'; import 'bitrate.dart';
@ -37,7 +35,8 @@ class StreamsClient {
return DashManifest.get(_httpClient, dashManifestUrl); return DashManifest.get(_httpClient, dashManifestUrl);
} }
Future<StreamContext> _getStreamContextFromVideoInfo(VideoId videoId) async { // Not used anymore since Youtube removed the `video_info` endpoint.
/* Future<StreamContext> _getStreamContextFromVideoInfo(VideoId videoId) async {
var embedPage = await EmbedPage.get(_httpClient, videoId.toString()); var embedPage = await EmbedPage.get(_httpClient, videoId.toString());
var playerConfig = embedPage.playerConfig; var playerConfig = embedPage.playerConfig;
if (playerConfig == null) { if (playerConfig == null) {
@ -79,7 +78,7 @@ class StreamsClient {
streamInfoProviders.addAll(dashManifest.streams); streamInfoProviders.addAll(dashManifest.streams);
} }
return StreamContext(streamInfoProviders, cipherOperations); return StreamContext(streamInfoProviders, cipherOperations);
} }*/
Future<StreamContext> _getStreamContextFromWatchPage(VideoId videoId) async { Future<StreamContext> _getStreamContextFromWatchPage(VideoId videoId) async {
final watchPage = await WatchPage.get(_httpClient, videoId.toString()); final watchPage = await WatchPage.get(_httpClient, videoId.toString());

View File

@ -1,5 +1,3 @@
import 'package:youtube_explode_dart/src/reverse_engineering/player/player_response.dart';
import '../channels/channel_id.dart'; import '../channels/channel_id.dart';
import '../common/common.dart'; import '../common/common.dart';
import '../extensions/helpers_extension.dart'; import '../extensions/helpers_extension.dart';

View File

@ -11,13 +11,6 @@ class VideoId with _$VideoId {
static final _shortMatchExp = RegExp(r'youtu\.be/(.*?)(?:\?|&|/|$)'); static final _shortMatchExp = RegExp(r'youtu\.be/(.*?)(?:\?|&|/|$)');
static final _embedMatchExp = RegExp(r'youtube\..+?/embed/(.*?)(?:\?|&|/|$)'); static final _embedMatchExp = RegExp(r'youtube\..+?/embed/(.*?)(?:\?|&|/|$)');
const VideoId._();
const factory VideoId._internal(
/// ID as string.
String value) = _VideoId;
/// Initializes an instance of [VideoId] with a url or video id. /// Initializes an instance of [VideoId] with a url or video id.
factory VideoId(String idOrUrl) { factory VideoId(String idOrUrl) {
final id = parseVideoId(idOrUrl); final id = parseVideoId(idOrUrl);
@ -29,6 +22,13 @@ class VideoId with _$VideoId {
return VideoId._internal(id); return VideoId._internal(id);
} }
const VideoId._();
const factory VideoId._internal(
/// ID as string.
String value) = _VideoId;
/// Converts [obj] to a [VideoId] by calling .toString on that object. /// Converts [obj] to a [VideoId] by calling .toString on that object.
/// If it is already a [VideoId], [obj] is returned /// If it is already a [VideoId], [obj] is returned
factory VideoId.fromString(dynamic obj) { factory VideoId.fromString(dynamic obj) {

View File

@ -1,6 +1,6 @@
name: youtube_explode_dart name: youtube_explode_dart
description: A port in dart of the youtube explode library. Supports several API functions without the need of Youtube API Key. description: A port in dart of the youtube explode library. Supports several API functions without the need of Youtube API Key.
version: 1.10.0 version: 1.10.1
homepage: https://github.com/Hexer10/youtube_explode_dart homepage: https://github.com/Hexer10/youtube_explode_dart