Youtubeexplode: Streaming with AspNetCore

Created on 27 Jun 2018  路  7Comments  路  Source: Tyrrrz/YoutubeExplode

Has anyone found a better way to stream file?
Here is what I came up with:

//http://localhost:5000/Youtube/Download/bcQQwYKkj_s
[Route("Youtube")]
public class YoutubeController : ControllerBase {
    [HttpGet("Download/{id}")]
    public async Task DownloadAudio(string id) {
        var client = new YoutubeClient();
        var mediaInfoSet = await client.GetVideoMediaStreamInfosAsync(id);
        var mediaStreamInfo = mediaInfoSet.Audio.WithHighestBitrate();
        var contentType = mediaStreamInfo is AudioStreamInfo ? "audio" : "video"; //With this example just put "audio" I use custom query parameters ;)
        HttpContext.Response.Headers.Add("Content-Disposition", $"attachment;filename={id}.{mediaStreamInfo.Container}");
        HttpContext.Response.Headers.Add("Content-Type", $"{contentType}/{mediaStreamInfo.Container}");
        await client.DownloadMediaStreamAsync(mediaStreamInfo, HttpContext.Response.Body, null);
    }
}

Maybe among you there is an AspNet Core guru and knows better way to do this? If No I would probably put this into examples (so I could find this later myself 馃槃).
I couldn't work it out with Task<ActionResult<FileStreamResult>> so had to do all header stuff manually.

Edit: I found a better solution for this, which allows returning IActionResult, this allows return BadRequest(...) if you do for example:

if (!ModelState.IsValid) {
    return BadRequest(ModelState);
}

Here is my better solution:

[Route("Youtube")]
public class YoutubeController : ControllerBase {
    [HttpGet("Download/{id}")]
    public IActionResult DownloadAudio(string id) {
        //TODO: validate id
        var client = new YoutubeClient(); //This should be initialized in YoutubeController constructor.
        return new StreamYoutubeVideo(client, id);
    }
}

public class StreamYoutubeVideo : ActionResult {
    private readonly IYoutubeClient _client;
    private readonly string _videoId;
    public StreamYoutubeVideo(IYoutubeClient client, string videoId) {
        _client = client;
        _videoId = videoId;
    }
    public override async Task ExecuteResultAsync(ActionContext context) {
        var mediaInfoSet = await _client.GetVideoMediaStreamInfosAsync(_videoId);
        var mediaStreamInfo = mediaInfoSet.Audio.WithHighestBitrate();
        context.HttpContext.Response.Headers.Add("Content-Disposition", $"attachment;filename={_videoId}.{mediaStreamInfo.Container.GetFileExtension()}");
        context.HttpContext.Response.Headers.Add("Content-Type", $"audio/{mediaStreamInfo.Container.GetFileExtension()}");
        await _client.DownloadMediaStreamAsync(mediaStreamInfo, context.HttpContext.Response.Body);
    }
}
question

All 7 comments

Regarding your edited version, I recommend using mediaStreamInfo.Container.GetFileExtension(), this will return the actual extension instead of enum name (which can theoretically change).

Since I don't have access to Edit Wiki, I uploaded this example: https://gist.github.com/SlowLogicBoy/54200343ce3a08b5ac15555641ad4a9b

@Tyrrrz you can add this to wiki or readme :) or put a link to this gist like you always do. If you find this useful of course.

I will close this since this pollutes issue list.

Pretty cool, will give this a try sometime.

Do you have a full working example? 馃槢

My fully working solution is quite a bit different, I made this as simple as possible.
Mine has quite a few wrappers, model validations, is separated to quite a few libraries:
Model, Repository, Repository Implementation, Controller which is loaded dynamically, and quite a few DI happens so.. I could of course make this bare bones solution as an example.

vscode, noice 馃槑

Added to wiki :+1:

Was this page helpful?
0 / 5 - 0 ratings

Related issues

DavidBarishev picture DavidBarishev  路  8Comments

jungjanos picture jungjanos  路  6Comments

NoobInTraining picture NoobInTraining  路  5Comments

Bierman27 picture Bierman27  路  6Comments

dev45 picture dev45  路  5Comments