Skip to content

plugins.blasttv: new plugin #6547

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 4, 2025
Merged

plugins.blasttv: new plugin #6547

merged 1 commit into from
Jun 4, 2025

Conversation

Lense
Copy link
Contributor

@Lense Lense commented Jun 3, 2025

Blast is a video game (mainly Counter-Strike) tournament organizer who streams their matches on their website alongside the usual Twitch and YouTube. The streams on blast.tv are usually in 4K as opposed to 1080p on other platforms.

I've been using this plugin locally for a couple years. Over time, I've recorded different API (https://api.blast.tv/v1/broadcasts/live) responses in comments in the plugin code to make development and testing easier, but I'm happy to move those to a more appropriate place.

There are some caveats:

  • Streams don't run 24/7--they'll only run, idk, 8-12 hours a day, 10 weeks a year, which I imagine would be annoying for reviewers. I'm creating the PR now because there should be regular daytime CST streams from now through June 22.
  • The stream used to occasionally (once every hour or so, but with high variance) exit early when streamlink was run with default options. Adding --hls-segment-queue-threshold 8 completely eliminated the issues. My understanding is that plugins can't do anything about this.

Copy link
Member

@bastimeyer bastimeyer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR, appreciated.

Adding a plugin for this site should be fine. But next time, please open a plugin request first, so you don't risk wasting your time should a PR you've worked on be rejected.

Before this can be merged, a couple of things need to be changed, fixed and also cleaned up.

Are you fine with me pushing onto your PR branch with my changes? I had a look at the site and modified your plugin accordingly, and I also added support for VODs. Both live streams and VODs can also be hosted externally, e.g. on YouTube or Twitch, so the plugin should redirect towards that automatically. I also fixed that.

I really don't feel like annotating code in PRs for non-trivial changes that I have already fixed locally, but let me comment on some of the stuff anyway.

Results from my local changes:

$ ./script/test-plugin-urls.py blasttv
:: https://blast.tv
::  160p, 360p, 480p, 720p, 1080p, 1440p, 2160p, worst, best
:: https://blast.tv/
::  160p, 360p, 480p, 720p, 1080p, 1440p, 2160p, worst, best
:: https://blast.tv/cs/tournaments/open-2025-season-1/match/24ff9d6c/vitality-mouz
::  160p, 360p, 480p, 720p, 1080p, 1440p, worst, best
:: https://blast.tv/cs/tournaments/rivals-2025-season-1/match/bfaaa42e/falcons-vitality
::  160p, 360p, 480p, 720p, 1080p, 1440p, worst, best
:: https://blast.tv/dota/tournaments/blast-slam-iii/match/acabb915/falcons-tundra
::  audio, worst, best, 160p, 360p, 480p, 720p60, 1080p60
:: https://blast.tv/live
::  160p, 360p, 480p, 720p, 1080p, 1440p, 2160p, worst, best
:: https://blast.tv/live/
::  160p, 360p, 480p, 720p, 1080p, 1440p, 2160p, worst, best
:: https://blast.tv/live/a
::  160p, 360p, 480p, 720p, 1080p, 1440p, 2160p, worst, best
:: https://blast.tv/live/americas1
!! No streams found
:: https://blast.tv/live/co-stream
!! No streams found
:: https://blast.tv/live/f
::  worst, best, 144p, 240p, 360p, 480p, 720p, 1080p

Comment on lines 43 to 46
schema=validate.Schema(
validate.parse_json(),
validate.filter(lambda v: v.get("videoSrc")),
[
{
"id": str,
"slug": str,
"title": str,
"videoSrc": validate.url(),
},
],
),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Parsed JSON responses require a proper data structure before you can iterate the data (validate.filter). If they don't return an array, then this will raise an ugly error.

Comment on lines 56 to 58
if not channel and len(live_channels) > 1:
log.error("Multiple live games, please specify one in URL")
return
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Live streams have a priority value in the API response. This can be used to default to the stream with the highest priority (the lowest integer value).

Comment on lines 67 to 71
yield from HLSStream.parse_variant_playlist(
self.session,
live_channel["videoSrc"],
).items()
break
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Plugin._get_streams() can both be a generator and a regular method returning a dict[str, Stream] | Iterable[tuple[str, Stream]], so yield from dict.items();break is unnecessary. Just return the resulting dict.


should_not_match = [
"https://blast.tv/a",
"https://blast.tv",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Their front-page also embeds the primary live stream, so the plugin should support that input URL.

Co-Authored-By: bastimeyer <mail@bastimeyer.de>
@Lense
Copy link
Contributor Author

Lense commented Jun 4, 2025

Are you fine with me pushing onto your PR branch with my changes?

Please go ahead, thank you.

@bastimeyer
Copy link
Member

Had a second look and I think the plugin implementation should be fine. If you want, you can have a look too and see if there are any streams that don't work. Otherwise, I'm going to merge later this evening.

Thanks.

@Lense
Copy link
Contributor Author

Lense commented Jun 4, 2025

Everything seems to work great, thank you so much!

@bastimeyer
Copy link
Member

  • The stream used to occasionally (once every hour or so, but with high variance) exit early when streamlink was run with default options. Adding --hls-segment-queue-threshold 8 completely eliminated the issues. My understanding is that plugins can't do anything about this.

Forgot to comment on this...

It's the server's responsibility according to the HLS protocol to provide the client with new media segments within the playlist's targetduration. Streamlink can end streams early in two different ways: first, via the global buffer read-timeout, which is a time value for how long the stream's consumer can wait for new data, and second, via the hls-segment-queue-threshold option (to be renamed in the future) that's a multiplication factor (default is 3) of the targetduration time, which is already a rather liberal value.

The intention is to close streams early which don't announce the stream's end properly without causing a buffer read timeout to trigger, as it's not a graceful end of the stream.

Plugins can always change session options, but it doesn't make any sense increasing this value, because it's an issue with the server. Setting the value to 8 means that the stream is allowed to stall for eight times its actual targetduration.

@bastimeyer bastimeyer merged commit 226c2c2 into streamlink:master Jun 4, 2025
24 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants