Tyrrrz / YoutubeExplode

Abstraction layer over YouTube's internal API
MIT License
2.96k stars 496 forks source link

Download private video by passing list of cookies, but got 401 #781

Open codewithmecoder opened 8 months ago

codewithmecoder commented 8 months ago

Version

6.3.13

Platform

.NET 8.0 / Window 11

Steps to reproduce

Step 1

var postData = new DownloadMultiPlaylistsRq { Cookies = sysNetCookieCollection.ToImmutableList(), PlaylistUrls = [ "https://www.youtube.com/playlist?list=PLgomaFin7XDmN6yMSDH6vQ5vWBWsbFB0K" //"https://www.youtube.com/playlist?list=PLgomaFin7XDmVk3B20G9WtCxervsc1tt0" ] };

string json = Newtonsoft.Json.JsonConvert.SerializeObject(postData);

// Prepare the content var content = new StringContent(json, Encoding.UTF8, "application/json");

// Send POST request HttpResponseMessage response = await httpClient.PostAsync("https://localhost:7151/api/v1/VideoDownload/playlists", content);

I use WPF for get all the cookie then pass it to my dotnet 8.0 API.

# Step 2
- Init ```var youtubeClient2 = new YoutubeClient(cookies);```

# Step 2
 - Getting all videos from a playlist that contain private videos that I can access with my youtube account. All the videos are returned.
- Get each private video info is okay but when download I got 401.

`An unhandled exception has occurred while executing the request. System.Net.Http.HttpRequestException: Response status code does not indicate success: 401 (Unauthorized).
`

```C#
public async Task<BaseRpModel> DownloadMultiPlaylistsAsync(IReadOnlyList<Cookie> cookies, List<string> playlistUrls, CancellationToken cancellationToken = default)
{
    if (!Directory.Exists(setting.VideoPath)) Directory.CreateDirectory(setting.VideoPath);

    foreach (var playlistUrl in playlistUrls)
    {
        await DownloadPlaylistAsync(cookies, playlistUrl, cancellationToken);
    }
    return new BaseRpModel
    {
        Code = "OK",
        Message = "Done downloading ... ",
    };
}

public async Task<BaseRpModel> DownloadPlaylistAsync(IReadOnlyList<Cookie> cookies, string playlistUrl, CancellationToken cancellationToken = default)
{
    if (!Directory.Exists(setting.VideoPath)) Directory.CreateDirectory(setting.VideoPath);

    var youtubeClient2 = new YoutubeClient(cookies);

    var playlist = await youtubeClient2.Playlists.GetAsync(playlistUrl, cancellationToken);
    var playlistPath = Path.Combine(setting.VideoPath, playlist.Title);
    Directory.CreateDirectory(playlistPath);

    var videos = await youtubeClient2.Playlists.GetVideosAsync(playlist.Id, cancellationToken);

    logger.LogDebug("Total videos: {@total}", videos.Count);

    //var tasks = videos.Select(i => RunAsync(i, youtubeClient2, playlistPath, cancellationToken));

    //await Task.WhenAll(tasks);

    foreach (var i in videos)
    {
        await RunAsync(i, youtubeClient2, playlistPath, cancellationToken);
    }

    return new BaseRpModel
    {
        Code = "OK",
        Message = "Done downloading ... ",
    };
}

Details

Thank you so much for check and help this out.🙏🙏

Checklist

Tyrrrz commented 3 months ago

Is this still reproducible with the latest version?

If I had to guess, there might be an issue with (de-)serialization of cookies when sending them to your backend. However, if you can fetch the private videos in a playlist, it's unlikely for that to be the issue.

You can also try fetching and downloading just one video and see if it changes anything.

codewithmecoder commented 3 months ago

Is this still reproducible with the latest version?

If I had to guess, there might be an issue with (de-)serialization of cookies when sending them to your backend. However, if you can fetch the private videos in a playlist, it's unlikely for that to be the issue.

You can also try fetching and downloading just one video and see if it changes anything.

Hi @Tyrrrz thank you for reply.

I will try it again.

mixail167 commented 1 week ago

The problem exists in version 6.4.3. My code:

public Form1()
        {
            InitializeComponent();
        }

        private async void Form1_Load(object sender, EventArgs e)
        {
            await webView21.EnsureCoreWebView2Async();
        }

        private void Button1_Click(object sender, EventArgs e)
        {
            webView21.CoreWebView2.Navigate(@"https://accounts.google.com/ServiceLogin?continue=http://www.youtube.com");
        }

        private async void Button2_Click(object sender, EventArgs e)
        {
            try
            {
                List<CoreWebView2Cookie> coreWebView2Cookies = await webView21.CoreWebView2.CookieManager.GetCookiesAsync("");
                List<Cookie> cookies = new List<Cookie>();
                foreach (CoreWebView2Cookie item in coreWebView2Cookies)
                {
                    cookies.Add(item.ToSystemNetCookie());
                }
                YoutubeClient youtube = new YoutubeClient(cookies);
                string videoUrl = "https://www.youtube.com/watch?v=c9DIoSNoQNs";
                StreamManifest streamManifest = await youtube.Videos.Streams.GetManifestAsync(videoUrl);
                IStreamInfo streamInfo = streamManifest.GetVideoOnlyStreams().GetWithHighestVideoQuality();
                await youtube.Videos.Streams.DownloadAsync(streamInfo, $"video.{streamInfo.Container}");
            }
            catch(Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

I get an error in the line

StreamManifest streamManifest = await youtube.Videos.Streams.GetManifestAsync(videoUrl);

Screenshot of the error:

image