Add channel api
This commit is contained in:
parent
727211ee05
commit
d458e23f3b
|
@ -13,3 +13,4 @@ doc/api/
|
|||
.idea/
|
||||
.vscode/
|
||||
*.iml
|
||||
bin/
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
library youtube_explode.cipher;
|
||||
|
||||
import 'package:http/http.dart' as http;
|
||||
import '../extensions.dart';
|
||||
import '../extensions/helpers_extension.dart';
|
||||
import 'cipher_operations.dart';
|
||||
|
||||
final _deciphererFuncNameExp = RegExp(
|
||||
|
|
|
@ -0,0 +1,140 @@
|
|||
import 'package:html/dom.dart';
|
||||
import 'package:html/parser.dart' as html;
|
||||
|
||||
import '../models/models.dart';
|
||||
import '../youtube_explode_base.dart';
|
||||
import 'helpers_extension.dart';
|
||||
import 'playlist_extension.dart';
|
||||
|
||||
/// Channel extension for YoutubeExplode
|
||||
extension ChannelExtension on YoutubeExplode {
|
||||
static final _usernameRegMatchExp =
|
||||
RegExp(r'youtube\..+?/user/(.*?)(?:\?|&|/|$)');
|
||||
|
||||
static final _idRegMatchExp =
|
||||
RegExp(r'youtube\..+?/channel/(.*?)(?:\?|&|/|$)');
|
||||
|
||||
/// Returns the [Channel] associated with the given channelId.
|
||||
/// Throws an [ArgumentError] if the channel id is not valid.
|
||||
Future<Channel> getChannel(String channelId) async {
|
||||
if (!validateChannelId(channelId)) {
|
||||
throw ArgumentError.value(
|
||||
channelId, 'channelId', 'Invalid YouTube channel id.');
|
||||
}
|
||||
|
||||
var channelPageHtml = await _getChannelPageHtml(channelId);
|
||||
var channelTitle = channelPageHtml
|
||||
.querySelector('meta[property="og:title"]')
|
||||
.attributes['content'];
|
||||
var channelImage = channelPageHtml
|
||||
.querySelector('meta[property="og:image"]')
|
||||
.attributes['content'];
|
||||
|
||||
return Channel(channelId, channelTitle, Uri.parse(channelImage));
|
||||
}
|
||||
|
||||
/// Get a channel id from a username.
|
||||
/// Might not work properly.
|
||||
Future<String> getChannelId(String username) async {
|
||||
if (!validateUsername(username)) {
|
||||
throw ArgumentError.value(
|
||||
username, 'username', 'Invalid YouTube username.');
|
||||
}
|
||||
|
||||
var userPageHtml = await _getUserPageHtml(username);
|
||||
|
||||
var channelUrl = userPageHtml
|
||||
.querySelector('meta[property="og:url"]')
|
||||
.attributes['content'];
|
||||
|
||||
return channelUrl.replaceFirst('/channel/', '');
|
||||
}
|
||||
|
||||
/// Returns all the videos uploaded by a channel up to [maxPages] count.
|
||||
Future<List<Video>> getChannelUploads(String channelId,
|
||||
[int maxPages = 5]) async {
|
||||
if (!validateChannelId(channelId)) {
|
||||
throw ArgumentError.value(
|
||||
channelId, 'channelId', 'Invalid YouTube channel id.');
|
||||
}
|
||||
|
||||
var playlistId = 'UU${channelId.replaceFirst('UC', '')}';
|
||||
var playlist = await getPlaylist(playlistId, maxPages);
|
||||
|
||||
return playlist.videos;
|
||||
}
|
||||
|
||||
Future<Document> _getChannelPageHtml(String channelId) async {
|
||||
var url = 'https://www.youtube.com/channel/$channelId?hl=en';
|
||||
var raw = (await client.get(url)).body;
|
||||
|
||||
return html.parse(raw);
|
||||
}
|
||||
|
||||
Future<Document> _getUserPageHtml(String username) async {
|
||||
var url = 'https://www.youtube.com/user/$username?hl=en';
|
||||
var raw = (await client.get(url)).body;
|
||||
|
||||
return html.parse(raw);
|
||||
}
|
||||
|
||||
/// Returns true if [username] is a valid Youtube username.
|
||||
static bool validateUsername(String username) {
|
||||
if (username.isNullOrWhiteSpace) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (username.length > 20) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !RegExp(r'[^0-9a-zA-Z]').hasMatch(username);
|
||||
}
|
||||
|
||||
/// Parses a username from an url.
|
||||
/// Returns null if the username is not found.
|
||||
static String parseUsername(String url) {
|
||||
if (url.isNullOrWhiteSpace) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var regMatch = _usernameRegMatchExp.firstMatch(url)?.group(1);
|
||||
if (!regMatch.isNullOrWhiteSpace && validateUsername(regMatch)) {
|
||||
return regMatch;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Returns true if [channelId] is a valid Youtube channel id.
|
||||
static bool validateChannelId(String channelId) {
|
||||
if (channelId.isNullOrWhiteSpace) {
|
||||
return false;
|
||||
}
|
||||
|
||||
channelId = channelId.toLowerCase();
|
||||
|
||||
if (!channelId.startsWith('uc')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (channelId.length != 24) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !RegExp(r'[^0-9a-zA-Z]').hasMatch(channelId);
|
||||
}
|
||||
|
||||
/// Parses a channel id from an url.
|
||||
/// Returns null if the username is not found.
|
||||
static String parseChannelId(String url) {
|
||||
if (url.isNullOrWhiteSpace) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var regMatch = _idRegMatchExp.firstMatch(url)?.group(1);
|
||||
if (!regMatch.isNullOrWhiteSpace && validateChannelId(regMatch)) {
|
||||
return regMatch;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
export 'channel_extension.dart';
|
||||
export 'helpers_extension.dart';
|
||||
export 'playlist_extension.dart';
|
|
@ -1,4 +1,4 @@
|
|||
import 'cipher/cipher_operations.dart';
|
||||
import '../cipher/cipher_operations.dart';
|
||||
|
||||
/// Utility for Strings.
|
||||
extension StringUtility on String {
|
|
@ -1,9 +1,9 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'extensions.dart';
|
||||
import 'models/models.dart';
|
||||
import 'parser.dart' as parser;
|
||||
import 'youtube_explode_base.dart';
|
||||
import '../models/models.dart';
|
||||
import '../parser.dart' as parser;
|
||||
import '../youtube_explode_base.dart';
|
||||
import 'helpers_extension.dart';
|
||||
|
||||
/// Playlist extension for YoutubeExplode
|
||||
extension PlaylistExtension on YoutubeExplode {
|
||||
|
@ -29,8 +29,7 @@ extension PlaylistExtension on YoutubeExplode {
|
|||
/// If the id is not valid an [ArgumentError] is thrown.
|
||||
Future<Playlist> getPlaylist(String playlistId, [int maxPages = 500]) async {
|
||||
if (!validatePlaylistId(playlistId)) {
|
||||
throw ArgumentError.value(
|
||||
playlistId, 'videoId', 'Invalid video id');
|
||||
throw ArgumentError.value(playlistId, 'videoId', 'Invalid video id');
|
||||
}
|
||||
|
||||
Map<String, dynamic> playlistJson;
|
|
@ -0,0 +1,14 @@
|
|||
/// Information about a YouTube channel.
|
||||
class Channel {
|
||||
/// ID of this channel.
|
||||
final String id;
|
||||
|
||||
/// Title of this channel.
|
||||
final String title;
|
||||
|
||||
/// Logo image URL of this channel.
|
||||
final Uri logoUrl;
|
||||
|
||||
/// Initializes an instance of [Channel]
|
||||
Channel(this.id, this.title, this.logoUrl);
|
||||
}
|
|
@ -2,6 +2,7 @@ library youtube_explode.models;
|
|||
|
||||
export 'audio_encoding.dart';
|
||||
export 'audio_stream_info.dart';
|
||||
export 'channel.dart';
|
||||
export 'container.dart';
|
||||
export 'media_stream_info.dart';
|
||||
export 'media_stream_info_set.dart';
|
||||
|
|
|
@ -2,13 +2,13 @@ import 'dart:convert';
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:html/dom.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:html/parser.dart' as html;
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
import 'cipher/cipher.dart';
|
||||
import 'extensions.dart';
|
||||
import 'extensions/extensions.dart';
|
||||
import 'models/models.dart';
|
||||
import 'parser.dart' as parser;
|
||||
import 'playlist_extension.dart';
|
||||
|
||||
/// YoutubeExplode entry class.
|
||||
class YoutubeExplode {
|
||||
|
@ -389,13 +389,33 @@ class YoutubeExplode {
|
|||
return null;
|
||||
}
|
||||
|
||||
/// Closes the youtube explode's http client.
|
||||
void close() {
|
||||
client.close();
|
||||
}
|
||||
|
||||
/* Export the extension static members. */
|
||||
|
||||
/// Parses a playlist [url] returning its id.
|
||||
/// If the [url] is a valid it is returned itself.
|
||||
static String parsePlaylistId(String url) =>
|
||||
PlaylistExtension.parsePlaylistId(url);
|
||||
|
||||
/// Closes the youtube explode's http client.
|
||||
void close() {
|
||||
client.close();
|
||||
}
|
||||
/// Returns true if [username] is a valid Youtube username.
|
||||
static bool validateUsername(String username) =>
|
||||
ChannelExtension.validateUsername(username);
|
||||
|
||||
/// Parses a username from an url.
|
||||
/// Returns null if the username is not found.
|
||||
static String parseUsername(String url) =>
|
||||
ChannelExtension.parseUsername(url);
|
||||
|
||||
/// Returns true if [channelId] is a valid Youtube channel id.
|
||||
static bool validateChannelId(String channelId) =>
|
||||
ChannelExtension.validateChannelId(channelId);
|
||||
|
||||
/// Parses a channel id from an url.
|
||||
/// Returns null if the username is not found.
|
||||
static String parseChannelId(String url) =>
|
||||
ChannelExtension.parseChannelId(url);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
library youtube_explode;
|
||||
|
||||
export 'src/extensions/extensions.dart'
|
||||
hide StringUtility, ListDecipher, ListFirst; // Hide helper extensions.
|
||||
export 'src/models/models.dart';
|
||||
export 'src/playlist_extension.dart';
|
||||
export 'src/youtube_explode_base.dart';
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
name: youtube_explode_dart
|
||||
description: A port in dart of the youtube explode library. Support serveral API functions.
|
||||
version: 0.0.1
|
||||
version: 0.0.2
|
||||
homepage: https://github.com/Hexer10/youtube_explode_dart
|
||||
|
||||
environment:
|
||||
|
|
Loading…
Reference in New Issue