Implement custom view counters

This commit is contained in:
2023-07-13 23:50:45 +01:00
parent 61a3df1cf8
commit dae8f99d33
4 changed files with 93 additions and 9 deletions

View File

@ -16,9 +16,10 @@ public class PlaylistController : Controller
private readonly IServiceScopeFactory _scopeFactory; private readonly IServiceScopeFactory _scopeFactory;
private readonly HttpClient _client; private readonly HttpClient _client;
private readonly SrsApi _srsApi; private readonly SrsApi _srsApi;
private readonly ViewCounter _viewCounter;
public PlaylistController(Config config, IMemoryCache cache, ILogger<PlaylistController> logger, IServiceScopeFactory scopeFactory, public PlaylistController(Config config, IMemoryCache cache, ILogger<PlaylistController> logger, IServiceScopeFactory scopeFactory,
HttpClient client, SrsApi srsApi) HttpClient client, SrsApi srsApi, ViewCounter viewCounter)
{ {
_config = config; _config = config;
_cache = cache; _cache = cache;
@ -26,10 +27,11 @@ public class PlaylistController : Controller
_scopeFactory = scopeFactory; _scopeFactory = scopeFactory;
_client = client; _client = client;
_srsApi = srsApi; _srsApi = srsApi;
_viewCounter = viewCounter;
} }
[HttpGet("{variant}/{pubkey}.m3u8")] [HttpGet("{variant}/{pubkey}.m3u8")]
public async Task RewritePlaylist([FromRoute] string pubkey, [FromRoute] string variant) public async Task RewritePlaylist([FromRoute] string pubkey, [FromRoute] string variant, [FromQuery(Name = "hls_ctx")] string hlsCtx)
{ {
var key = await GetStreamKey(pubkey); var key = await GetStreamKey(pubkey);
if (string.IsNullOrEmpty(key)) if (string.IsNullOrEmpty(key))
@ -74,6 +76,7 @@ public class PlaylistController : Controller
} }
Response.Body.Close(); Response.Body.Close();
_viewCounter.Activity(key, hlsCtx);
} }
[HttpGet("{pubkey}.m3u8")] [HttpGet("{pubkey}.m3u8")]
@ -92,6 +95,7 @@ public class PlaylistController : Controller
Response.StatusCode = 404; Response.StatusCode = 404;
return; return;
} }
Response.ContentType = "application/x-mpegurl"; Response.ContentType = "application/x-mpegurl";
await using var sw = new StreamWriter(Response.Body); await using var sw = new StreamWriter(Response.Body);
@ -113,7 +117,9 @@ public class PlaylistController : Controller
await sw.WriteLineAsync( await sw.WriteLineAsync(
$"#EXT-X-STREAM-INF:{string.Join(",", allArgs)},CODECS=\"avc1.640028,mp4a.40.2\""); $"#EXT-X-STREAM-INF:{string.Join(",", allArgs)},CODECS=\"avc1.640028,mp4a.40.2\"");
var u = new Uri(_config.DataHost, $"{variant.Name}/{pubkey}.m3u8{(!string.IsNullOrEmpty(hlsCtx) ? $"?hls_ctx={hlsCtx}" : "")}"); var u = new Uri(_config.DataHost,
$"{variant.Name}/{pubkey}.m3u8{(!string.IsNullOrEmpty(hlsCtx) ? $"?hls_ctx={hlsCtx}" : "")}");
await sw.WriteLineAsync(u.ToString()); await sw.WriteLineAsync(u.ToString());
} }
} }

View File

@ -50,6 +50,8 @@ internal static class Program
services.AddTransient<StreamManager>(); services.AddTransient<StreamManager>();
services.AddTransient<SrsApi>(); services.AddTransient<SrsApi>();
services.AddHostedService<BackgroundStreamManager>(); services.AddHostedService<BackgroundStreamManager>();
services.AddSingleton<ViewCounter>();
services.AddHostedService<ViewCounterDecay>();
// lnd services // lnd services
services.AddSingleton<LndNode>(); services.AddSingleton<LndNode>();

View File

@ -21,14 +21,13 @@ public class BackgroundStreamManager : BackgroundService
var streamManager = scope.ServiceProvider.GetRequiredService<StreamManager>(); var streamManager = scope.ServiceProvider.GetRequiredService<StreamManager>();
var srsApi = scope.ServiceProvider.GetRequiredService<SrsApi>(); var srsApi = scope.ServiceProvider.GetRequiredService<SrsApi>();
var viewCounter = scope.ServiceProvider.GetRequiredService<ViewCounter>();
var clients = await srsApi.ListClients(); var streams = await srsApi.ListStreams();
var streams = clients.Where(a => !a.Publish).GroupBy(a => a.Url); foreach (var stream in streams.GroupBy(a => a.Name))
foreach (var stream in streams)
{ {
var viewers = 0; //stream.Select(a => a.Ip).Distinct().Count(); var viewers = viewCounter.Current(stream.Key);
var streamKey = stream.Key.Split("/").Last(); await streamManager.UpdateViewers(stream.Key, viewers);
await streamManager.UpdateViewers(streamKey, viewers);
} }
} }
catch (Exception ex) catch (Exception ex)

View File

@ -0,0 +1,77 @@
using System.Collections.Concurrent;
namespace NostrStreamer.Services;
public class ViewCounter
{
private readonly ConcurrentDictionary<string, Dictionary<string, DateTime>> _sessions = new();
public void Activity(string key, string token)
{
if (!_sessions.ContainsKey(key))
{
_sessions.TryAdd(key, new());
}
if (_sessions.TryGetValue(key, out var x))
{
x[token] = DateTime.Now;
}
}
public void Decay()
{
foreach (var k in _sessions.Keys)
{
if (_sessions.TryGetValue(k, out var x))
{
_sessions[k] = x
.Where(a => a.Value > DateTime.Now.Subtract(TimeSpan.FromMinutes(2)))
.ToDictionary(a => a.Key, b => b.Value);
if (_sessions[k].Count == 0)
{
_sessions.TryRemove(k, out _);
}
}
}
}
public int Current(string key)
{
if (_sessions.TryGetValue(key, out var x))
{
return x.Count;
}
return 0;
}
}
public class ViewCounterDecay : BackgroundService
{
private readonly ViewCounter _viewCounter;
private readonly ILogger<ViewCounterDecay> _logger;
public ViewCounterDecay(ViewCounter viewCounter, ILogger<ViewCounterDecay> logger)
{
_viewCounter = viewCounter;
_logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
try
{
_viewCounter.Decay();
}
catch (Exception ex)
{
_logger.LogError(ex, "Something went wrong");
}
await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken);
}
}
}