A thin unofficial Dart client for Holodex API v2.
- Getting started
- Usage
- Get a video by its ID
- Get a list of videos
- Get a list of live videos
- Get a channel by its ID
- Get a list of channels
- Quickly Access Live / Upcoming for a set of Channels
- Get Videos Related To Channel
- Get Clips of a VTuber
- Get Collabs that mention a VTuber
- Get Videos From Channel
- Get a single Video's metadata
- Search For Videos
- Search For Videos With a Comment
- Testing with Coverage
- Additional information
- Get an API key at holodex.net, instructions here
- Add this package your
pubspec.yaml
file - Run
dart pub get
orflutter pub get
.
Get an instance of HolodexClient with your API key
final holodexClient = HolodexClient(apiKey: apiKey);
Remember to close the client when you are done with it, this closes the internal http client. Failing to close it can cause the Dart process to hang.
holodexClient.close();
Get a video by its video ID, alias of getVideos
Arguments:
videoId
The video ID as a stringincludes
Request extra data be included in the results. They are not guarenteed to be returned.
// Get one video and print it
final VideoFull video = await holodexClient.getVideoById(
'Gx_GPwpyLxw',
includes: [
Includes.channelStats,
Includes.clips,
// Includes.description,
Includes.liveInfo,
Includes.mentions,
Includes.refers,
Includes.simulcasts,
Includes.songs,
Includes.sources,
],
);
print('Requested Video: ${video.toString()}');
Arguments:
filter
Filter the results returns by the Holodex API/videos
endpoint
// Get a bunch of videos and print them
final videoFilter = VideoFilter(
// channelId: 'UCsYcCwDqv6Sg8KMIIMF54SA', // Kiriku Translation
includes: <Includes>[
Includes.channelStats,
Includes.clips,
// Includes.description,
Includes.liveInfo,
Includes.mentions,
Includes.refers,
Includes.simulcasts,
Includes.songs,
Includes.sources,
],
languages: <Language>[Language.all],
limit: 50,
maxUpcomingHours: 1000,
// mentionedChannelId: 'UCDqI2jOz0weumE8s7paEk6g', // Roboco
offset: 50,
order: Order.descending,
// organization: Organization.Hololive,
paginated: true,
sort: <VideoSort>[VideoSort.availableAt],
status: <VideoStatus>[VideoStatus.past],
// Videos of type VideoType.clip cannot not have topic. Streams may or may not have topic.
// topicId: 'singing',
// type: VideoType.stream,
);
final PaginatedVideos videoList = await holodexClient.getVideos(videoFilter);
print('Videos: ${videoList.items.length}');
print('Total Videos: ${videoList.total}');
This is somewhat similar to calling getVideos
, except this endpoint imposes default values on the query parameters. You can choose to override them by providing your own values.
final liveVideoFilter = VideoFilter(includes: [Includes.channelStats]);
final PaginatedVideos liveVideos = await holodexClient.getLiveVideos(liveVideoFilter);
print('Live videos: ${liveVideos.items.length}\n');
Returns Channel
Arguments:
filter
Filter the results returns by the API
final Channel ceresFauna = await holodexClient.getChannelById('UCO_aKKYxn4tvrqPjcTzZ6EQ');
print('Requested Channel Name: ${ceresFauna.name}\n');
Arguments:
filter
Filter the results returns by the API
final channelFilter = const ChannelFilter(
limit: 25,
offset: 0,
order: Order.ascending,
organization: Organization.AtelierLive,
sort: [ChannelSort.organization],
);
final List<Channel> channels = await holodexClient.getChannels(channelFilter);
print('Atelier Live Channels: ${channels.length}\n');
This endpoint is similar to the getLiveVideos
method and usually replies much faster. It is more friendly in general. The cost to execute a lookup is significantly cheaper. It's unfortunately less customizable as a result.
Holodex recommends using this if you have a fixed set of channel IDs to look up status for.
Arguments:
channelIds
List of channel IDs to get the live videos from.
final List<Video> quickLiveVideos = await holodexClient.getLiveVideosFromChannelsQuickly([
'UCQ0UDLQCjY0rmuxCDE38FGg', // Matsuri
'UCZlDXzGoo7d44bwdNObFacg', // Kanata
'UCqm3BQLlJfvkTsX_hvm0UmA' // Watame
]);
print('Requested Live Videos From Channels: ${quickLiveVideos.length}\n');
A simplified method for access channel specific data. If you want more customization, the same result can be obtained by calling the [getVideos] method.
Arguments
channelId
ID of the Youtube Channel that is being queriedtype
The type of video resource to fetch. Clips finds clip videos of a vtuber channel, Video finds thechannelId
channel's uploads, and collabs finds videos uploaded by other channels that mention thechannelId
filter
Filter the results returns by the API
final PaginatedResult<VideoFull> matsuriClips = await holodexClient.getVideosRelatedToChannel(
'UCQ0UDLQCjY0rmuxCDE38FGg', // Matsuri
type: VideoSearchType.clips
);
print('Clips including Matsuri: ${matsuriClips.total}');
print('Returned clips including Matsuri: ${matsuriClips.videos.length}');
Alias of getChannelRelatedVideos
Arguments:
channelId
ID of the Youtube Channel that is being queriedfilter
Filter the results returns by the API
final PaginatedVideos matsuriClips2 = await holodexClient.getVTuberClips(
'UCQ0UDLQCjY0rmuxCDE38FGg', // Matsuri
);
print('Clips including Matsuri: ${matsuriClips2.total}');
print('Returned clips including Matsuri: ${matsuriClips2.items.length}\n');
Alias of getChannelRelatedVideos
Arguments:
channelId
ID of the Youtube Channel that is being queriedfilter
Filter the results returns by the API
final PaginatedVideos matsuriCollabs = await holodexClient.getVTuberCollabs('UCQ0UDLQCjY0rmuxCDE38FGg');
print('Collabs including Matsuri: ${matsuriCollabs.total}');
print('Returned collabs including Matsuri: ${matsuriCollabs.items.length}\n');
Alias of getChannelRelatedVideos
Arguments:
channelId
ID of the Youtube Channel that is being queriedfilter
Filter the results returns by the API
final PaginatedVideos matsuriUploads = await holodexClient.getChannelVideos('UCQ0UDLQCjY0rmuxCDE38FGg');
print('Total Matsuri uploads: ${matsuriUploads.total}');
print('Returned uploads: ${matsuriUploads.items.length}\n');
Retrieves a video, optionally with comments and recommended videos.
Arguments:
videoId
ID of the videoincludeTimestampComments
If set totrue
, comments with timestamps will be returnedfilterRecommendationLanguages
Retrieves recommended videos if not empty. This is a list of language codes to filter channels/clips, official streams do not follow this parameter.
final VideoFull shionSingingStream = await holodexClient.getVideoMetadata(
'eJJuy5rY57w', // Shion's singing stream
includeTimestampComments: true,
filterRecommendationLanguages: [Language.all],
);
final List<Comment> timestampComments = shionSingingStream.comments;
final List<Video> recommendations = shionSingingStream.recommendations;
print('Songs: ${shionSingingStream.songcount}');
print('Video Comments With Timestamps: ${timestampComments.length}');
print('Video Recommendations: ${recommendations.length}\n');
Flexible endpoint to search for videos fulfilling multiple conditions.
Descriptions with "any" implies an OR
condition, and "all" implies an AND
condition. Note that searching for topics and clips is not supported, because clips do not contain topics.
Arguments:
conditions
Match all of the items. -> For each item: look for the text in video title or descriptionfilter
Filter video results from the API
final singingSearchFilter = SearchFilter(
sort: SearchSort.newest,
languages: [Language.all],
targets: [SearchTarget.clip, SearchTarget.stream],
topics: ['singing'],
videoChannels: <String>[],
organizations: [
Organization.Hololive,
Organization.Nijisanji,
],
paginated: true,
offset: 0,
limit: 25,
);
final PaginatedVideos searchVideos = await holodexClient.searchVideos(
conditions: ['karaoke'],
filter: singingSearchFilter,
);
print('Videos Found: ${searchVideos.items.length}\n');
Flexible endpoint to search for comments in videos fulfilling multiple conditions. Descriptions with "any" implies an OR
condition, and "all" implies an AND
condition. Note that searching for topics and clips is not supported, because clips do not contain topics.
Arguments:
comment
Find videos with comments containing specified string (case insensitive)filter
Filter video results from the API
final singingSearchFilter = SearchFilter(
sort: SearchSort.newest,
languages: [Language.all],
targets: [SearchTarget.clip, SearchTarget.stream],
topics: ['singing'],
videoChannels: <String>[],
organizations: [
Organization.Hololive,
Organization.Nijisanji,
],
paginated: true,
offset: 0,
limit: 25,
);
final PaginatedVideos searchComments = await holodexClient.searchComments(
comment: 'shion',
filter: singingSearchFilter,
);
print('Videos with Comment: ${searchComments.items.length}\n');
Generating code coverage:
- Activate the coverage package
dart pub global activate coverage
- Run the tests with coverage
dart pub global run coverage:test_with_coverage --branch-coverage --function-coverage
- Visualize the coverage with an extension such as Coverage Gutters
- Read the official API documentation here
- Visit Holodex.net
- Some parts of this package was inspired by the C# and Rust implementations of the API.