| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667 |
- using System.Text.Json;
- using Application.Abstractions.YouTube;
- using StackExchange.Redis;
- namespace Infrastructure.YouTube;
- /// <summary>
- /// Redis Hash 기반 라이브 상태 저장소
- /// Key: "youtube:live:channels" (Hash — channelId → LiveStreamInfo JSON)
- /// PubSub 알림이 없으면 데이터가 없음 = 라이브 아님
- /// </summary>
- internal sealed class YouTubeLiveStateStore(IConnectionMultiplexer redis) : IYouTubeLiveStateStore
- {
- private const string HashKey = "youtube:live:channels";
- private static readonly TimeSpan MaxLiveDuration = TimeSpan.FromHours(12); // 안전장치: 12시간 후 자동 만료
- public async Task SetLiveAsync(string channelId, YouTubeLiveStreamInfo liveInfo)
- {
- var db = redis.GetDatabase();
- var json = JsonSerializer.Serialize(liveInfo);
- await db.HashSetAsync(HashKey, channelId, json);
- // 전체 Hash에 만료 설정 (최소 12시간은 유지)
- var ttl = await db.KeyTimeToLiveAsync(HashKey);
- if (ttl is null || ttl < MaxLiveDuration)
- {
- await db.KeyExpireAsync(HashKey, MaxLiveDuration);
- }
- }
- public async Task ClearLiveAsync(string channelId)
- {
- var db = redis.GetDatabase();
- await db.HashDeleteAsync(HashKey, channelId);
- }
- public async Task<YouTubeLiveStreamInfo?> GetLiveAsync(string channelId)
- {
- var db = redis.GetDatabase();
- var json = await db.HashGetAsync(HashKey, channelId);
- if (json.IsNullOrEmpty)
- {
- return null;
- }
- return JsonSerializer.Deserialize<YouTubeLiveStreamInfo>(json.ToString());
- }
- public async Task<IReadOnlyList<YouTubeLiveStreamInfo>> GetAllLiveAsync()
- {
- var db = redis.GetDatabase();
- var entries = await db.HashGetAllAsync(HashKey);
- var result = new List<YouTubeLiveStreamInfo>();
- foreach (var entry in entries)
- {
- var info = JsonSerializer.Deserialize<YouTubeLiveStreamInfo>(entry.Value.ToString());
- if (info is not null)
- {
- result.Add(info);
- }
- }
- return result.AsReadOnly();
- }
- }
|