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
- Fix issue #144: get_video_info was removed from yt.
- Min sdk version now is 2.13.0

View File

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

View File

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

View File

@ -1,15 +1,14 @@
import 'package:collection/collection.dart';
import 'package:html/dom.dart';
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 '../../exceptions/exceptions.dart';
import '../../extensions/helpers_extension.dart';
import '../../retry.dart';
import '../../videos/videos.dart';
import '../youtube_http_client.dart';
import '../models/initial_data.dart';
import '../models/youtube_page.dart';
import '../youtube_http_client.dart';
///
class ChannelUploadPage extends YoutubePage<_InitialData> {

View File

@ -1,12 +1,12 @@
import 'package:collection/collection.dart';
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 '../../extensions/helpers_extension.dart';
import '../../retry.dart';
import '../youtube_http_client.dart';
import '../models/initial_data.dart';
import '../models/youtube_page.dart';
import '../youtube_http_client.dart';
///
class PlaylistPage extends YoutubePage<_InitialData> {

View File

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

View File

@ -1,16 +1,16 @@
import 'package:collection/collection.dart';
import 'package:html/dom.dart';
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 '../../extensions/helpers_extension.dart';
import '../../retry.dart';
import '../../videos/video_id.dart';
import '../models/initial_data.dart';
import '../models/youtube_page.dart';
import '../player/player_response.dart';
import '../youtube_http_client.dart';
import 'player_config_base.dart';
import '../models/initial_data.dart';
///
class WatchPage extends YoutubePage<_InitialData> {
@ -21,12 +21,11 @@ class WatchPage extends YoutubePage<_InitialData> {
static final RegExp _visitorInfoLiveExp =
RegExp('VISITOR_INFO1_LIVE=([^;]+)');
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
// Overridden to be non-nullable.
// ignore: overridden_fields
final Document root;
///
@ -48,18 +47,6 @@ class WatchPage extends YoutubePage<_InitialData> {
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;

View File

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

View File

@ -1,15 +1,16 @@
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:http_parser/http_parser.dart';
import '../../exceptions/exceptions.dart';
import '../../extensions/helpers_extension.dart';
import '../../retry.dart';
import '../youtube_http_client.dart';
import '../player/player_response.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 {
final Map<String, String> root;
@ -51,6 +52,7 @@ class VideoInfoClient {
VideoInfoClient.parse(String raw) : root = Uri.splitQueryString(raw);
///
@alwaysThrows
static Future<VideoInfoClient> get(
YoutubeHttpClient httpClient, String videoId,
[String? sts]) {

View File

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

View File

@ -72,6 +72,7 @@ mixin _$SearchVideo {
String? get uploadDate => throw _privateConstructorUsedError;
/// True if this video is a live stream.
// ignore: avoid_positional_boolean_parameters
bool get isLive => throw _privateConstructorUsedError;
/// Channel id
@ -317,6 +318,7 @@ class _$_SearchVideo with BaseSearchContent implements _SearchVideo {
@override
/// True if this video is a live stream.
// ignore: avoid_positional_boolean_parameters
final bool isLive;
@override
@ -428,6 +430,7 @@ abstract class _SearchVideo implements SearchVideo, BaseSearchContent {
@override
/// True if this video is a live stream.
// ignore: avoid_positional_boolean_parameters
bool get isLive => throw _privateConstructorUsedError;
@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 '../../reverse_engineering/responses/closed_caption_client.dart' as re
show ClosedCaptionClient;
import '../../reverse_engineering/responses/video_info_client.dart';
import '../../reverse_engineering/youtube_http_client.dart';
import '../videos.dart';
import 'closed_caption.dart';
@ -34,16 +35,16 @@ class ClosedCaptionClient {
]}) async {
videoId = VideoId.fromString(videoId);
var tracks = <ClosedCaptionTrackInfo>{};
var videoInfoResponse =
await VideoInfoClient.get(_httpClient, videoId.value);
var playerResponse = videoInfoResponse.playerResponse;
var watchPage =
await WatchPage.get(_httpClient, videoId.value);
var playerResponse = watchPage.playerResponse!;
for (final track in playerResponse.closedCaptionTrack) {
for (final ext in formats) {
tracks.add(ClosedCaptionTrackInfo(
Uri.parse(track.url)
.replaceQueryParameters({'fmt': ext.formatCode}),
Language(track.languageCode, track.languageName),
Language(track.languageCode, track.languageName ?? ''),
isAutoGenerated: track.autoGenerated,
format: ext));
}

View File

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

View File

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

View File

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

View File

@ -26,9 +26,6 @@ class Bitrate with Comparable<Bitrate>, _$Bitrate {
@override
int compareTo(Bitrate other) => bitsPerSecond.compareTo(other.bitsPerSecond);
@override
List<Object> get props => [bitsPerSecond];
String _getLargestSymbol() {
if (gigaBitsPerSecond.abs() >= 1) {
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/heuristics.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/player/player_source.dart';
import '../../reverse_engineering/responses/video_info_client.dart';
import '../../reverse_engineering/youtube_http_client.dart';
import '../video_id.dart';
import 'bitrate.dart';
@ -37,7 +35,8 @@ class StreamsClient {
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 playerConfig = embedPage.playerConfig;
if (playerConfig == null) {
@ -79,7 +78,7 @@ class StreamsClient {
streamInfoProviders.addAll(dashManifest.streams);
}
return StreamContext(streamInfoProviders, cipherOperations);
}
}*/
Future<StreamContext> _getStreamContextFromWatchPage(VideoId videoId) async {
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 '../common/common.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 _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.
factory VideoId(String idOrUrl) {
final id = parseVideoId(idOrUrl);
@ -29,6 +22,13 @@ class VideoId with _$VideoId {
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.
/// If it is already a [VideoId], [obj] is returned
factory VideoId.fromString(dynamic obj) {

View File

@ -1,6 +1,6 @@
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.
version: 1.10.0
version: 1.10.1
homepage: https://github.com/Hexer10/youtube_explode_dart