Skip to content

Commit 332ed5b

Browse files
author
Andrii Bondarchuk
committed
Youtube: use OAuth2 instead of apikey
1 parent 757ae87 commit 332ed5b

File tree

3 files changed

+58
-28
lines changed

3 files changed

+58
-28
lines changed

src/FitSyncHub.Youtube/Options/YoutubeOptions.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
public sealed record YoutubeOptions
44
{
5-
public required string ApiKey { get; init; }
6-
public required string ChannelId { get; init; }
5+
public required string ClientId { get; init; }
6+
public required string ClientSecret { get; init; }
7+
public required string RefreshToken { get; init; }
78
}
Lines changed: 31 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,56 @@
1-
using FitSyncHub.Youtube.Options;
2-
using Google.Apis.YouTube.v3;
3-
using Microsoft.Extensions.Options;
1+
using Google.Apis.YouTube.v3;
2+
using Google.Apis.YouTube.v3.Data;
43

54
namespace FitSyncHub.Youtube.Services;
65

76
public class YouTubeLiveService
87
{
98
private readonly YouTubeService _youtubeService;
10-
private readonly string _channelId;
119

12-
public YouTubeLiveService(YouTubeService youTubeService, IOptions<YoutubeOptions> options)
10+
public YouTubeLiveService(YouTubeService youTubeService)
1311
{
1412
_youtubeService = youTubeService;
15-
_channelId = options.Value.ChannelId;
1613
}
1714

1815
public async Task<string?> GetNextUpcomingVideoId(CancellationToken cancellationToken)
1916
{
20-
var searchListRequest = _youtubeService.Search.List("snippet");
21-
searchListRequest.ChannelId = _channelId;
22-
searchListRequest.EventType = SearchResource.ListRequest.EventTypeEnum.Upcoming;
23-
searchListRequest.Type = "video";
17+
var liveBroadcastsRequest = _youtubeService.LiveBroadcasts.List("snippet");
18+
liveBroadcastsRequest.BroadcastStatus = LiveBroadcastsResource.ListRequest.BroadcastStatusEnum.Upcoming;
2419

25-
var searchResponse = await searchListRequest.ExecuteAsync(cancellationToken);
20+
var response = await GetAllPagesAsync(liveBroadcastsRequest, cancellationToken);
2621

27-
var upcomingVideoIds = searchResponse.Items.Select(item => item.Id.VideoId).ToHashSet();
28-
var videosListRequest = _youtubeService.Videos.List("liveStreamingDetails");
29-
videosListRequest.Id = new Google.Apis.Util.Repeatable<string>(upcomingVideoIds);
30-
31-
var videosListResponse = await videosListRequest.ExecuteAsync(cancellationToken);
32-
33-
return videosListResponse.Items
34-
.OrderBy(x => x.LiveStreamingDetails.ScheduledStartTimeDateTimeOffset)
22+
return response
23+
.OrderBy(x => x.Snippet.ScheduledStartTimeDateTimeOffset)
3524
.Select(x => x.Id)
3625
.FirstOrDefault();
3726
}
3827

3928
public async Task<string?> GetLiveVideoId(CancellationToken cancellationToken)
4029
{
41-
var searchListRequest = _youtubeService.Search.List("snippet");
42-
searchListRequest.ChannelId = _channelId;
43-
searchListRequest.EventType = SearchResource.ListRequest.EventTypeEnum.Live;
44-
searchListRequest.Type = "video";
30+
var liveBroadcastsRequest = _youtubeService.LiveBroadcasts.List("snippet");
31+
liveBroadcastsRequest.BroadcastStatus = LiveBroadcastsResource.ListRequest.BroadcastStatusEnum.Active;
32+
33+
var response = await GetAllPagesAsync(liveBroadcastsRequest, cancellationToken);
34+
35+
return response.Select(x => x.Id).SingleOrDefault();
36+
}
37+
38+
private static async Task<List<LiveBroadcast>> GetAllPagesAsync(
39+
LiveBroadcastsResource.ListRequest liveBroadcastsRequest, CancellationToken cancellationToken)
40+
{
41+
string? nextPageToken = null;
42+
List<LiveBroadcast> result = [];
43+
44+
do
45+
{
46+
liveBroadcastsRequest.PageToken = nextPageToken;
47+
48+
var response = await liveBroadcastsRequest.ExecuteAsync(cancellationToken);
49+
result.AddRange(response.Items);
4550

46-
var searchResponse = await searchListRequest.ExecuteAsync(cancellationToken);
51+
nextPageToken = response.NextPageToken;
52+
} while (!string.IsNullOrEmpty(nextPageToken));
4753

48-
return searchResponse.Items.FirstOrDefault()?.Id.VideoId;
54+
return result;
4955
}
5056
}

src/FitSyncHub.Youtube/YoutubeModule.cs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
using FitSyncHub.Youtube.Options;
22
using FitSyncHub.Youtube.Services;
3+
using Google.Apis.Auth.OAuth2;
4+
using Google.Apis.Auth.OAuth2.Flows;
5+
using Google.Apis.Auth.OAuth2.Responses;
36
using Google.Apis.Services;
47
using Google.Apis.YouTube.v3;
58
using Microsoft.Extensions.Configuration;
@@ -21,9 +24,29 @@ public IServiceCollection ConfigureYoutubeModule(string youtubeOptionsPath)
2124
services.AddScoped(sp =>
2225
{
2326
var options = sp.GetRequiredService<IOptions<YoutubeOptions>>().Value;
27+
28+
var token = new TokenResponse
29+
{
30+
// Google Refresh token does NOT expire unless revoked
31+
RefreshToken = options.RefreshToken
32+
};
33+
34+
var initializer = new GoogleAuthorizationCodeFlow.Initializer
35+
{
36+
ClientSecrets = new ClientSecrets
37+
{
38+
ClientId = options.ClientId,
39+
ClientSecret = options.ClientSecret,
40+
},
41+
Scopes = [YouTubeService.Scope.YoutubeReadonly],
42+
};
43+
44+
var credential = new UserCredential(new GoogleAuthorizationCodeFlow(initializer), "user", token);
45+
46+
// Create the service.
2447
return new YouTubeService(new BaseClientService.Initializer()
2548
{
26-
ApiKey = options.ApiKey,
49+
HttpClientInitializer = credential,
2750
});
2851
});
2952

0 commit comments

Comments
 (0)