using System.Text.Json;
using Application.Abstractions.YouTube;
using StackExchange.Redis;
namespace Infrastructure.YouTube;
///
/// Redis Hash 기반 라이브 상태 저장소
/// Key: "youtube:live:channels" (Hash — channelId → LiveStreamInfo JSON)
/// PubSub 알림이 없으면 데이터가 없음 = 라이브 아님
///
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 GetLiveAsync(string channelId)
{
var db = redis.GetDatabase();
var json = await db.HashGetAsync(HashKey, channelId);
if (json.IsNullOrEmpty)
{
return null;
}
return JsonSerializer.Deserialize(json.ToString());
}
public async Task> GetAllLiveAsync()
{
var db = redis.GetDatabase();
var entries = await db.HashGetAllAsync(HashKey);
var result = new List();
foreach (var entry in entries)
{
var info = JsonSerializer.Deserialize(entry.Value.ToString());
if (info is not null)
{
result.Add(info);
}
}
return result.AsReadOnly();
}
}