Version 1.10.1
This commit is contained in:
parent
73504b2a44
commit
c640d08f29
|
@ -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
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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> {
|
||||||
|
|
|
@ -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> {
|
||||||
|
|
|
@ -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> {
|
||||||
|
|
|
@ -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> {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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 =>
|
||||||
|
|
|
@ -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]) {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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';
|
||||||
|
|
|
@ -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';
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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';
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue