Initial commit
This commit is contained in:
commit
67af59610f
34
.gitignore
vendored
Normal file
34
.gitignore
vendored
Normal file
|
@ -0,0 +1,34 @@
|
|||
bin/
|
||||
obj/
|
||||
/packages/
|
||||
riderModule.iml
|
||||
.idea/
|
||||
/_ReSharper.Caches/
|
||||
|
||||
# Created by https://www.toptal.com/developers/gitignore/api/macos
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=macos
|
||||
|
||||
### macOS ###
|
||||
# General
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
# Files that might appear in the root of a volume
|
||||
.DocumentRevisions-V100
|
||||
.fseventsd
|
||||
.Spotlight-V100
|
||||
.TemporaryItems
|
||||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
.com.apple.timemachine.donotpresent
|
||||
|
||||
# Directories potentially created on remote AFP share
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
.apdisk
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/macos
|
24
LICENSE
Normal file
24
LICENSE
Normal file
|
@ -0,0 +1,24 @@
|
|||
Be Gay, Do Crimes License
|
||||
|
||||
Copyright (c) 2022 Laura Hausmann
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
Be Gay
|
||||
Do Crimes
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
67
Program.cs
Normal file
67
Program.cs
Normal file
|
@ -0,0 +1,67 @@
|
|||
using System.Globalization;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
List<User> users = new();
|
||||
|
||||
while (Console.In.ReadLine() is { } entry) {
|
||||
if (!entry.Contains("\"cdn.chaos.stream\" \"GET /hls/"))
|
||||
continue;
|
||||
if (!entry.Contains(".ts HTTP"))
|
||||
continue;
|
||||
|
||||
var match = Regex.Match(entry, "^\\[(.*?)\\].*?GET \\/hls\\/(.+?)-(\\d+)\\.ts HTTP\\/\\d\\.\\d\" 200");
|
||||
if (!match.Success)
|
||||
continue;
|
||||
|
||||
var timestamp = match.Groups[1].Value;
|
||||
var streamkey = match.Groups[2].Value;
|
||||
var fragmentId = match.Groups[3].Value;
|
||||
|
||||
if (users.All(p => p.Streamkey != streamkey))
|
||||
users.Add(new User(streamkey));
|
||||
|
||||
var user = users.First(p => p.Streamkey == streamkey);
|
||||
|
||||
if (user.Fragments.All(p => p.Id != fragmentId))
|
||||
user.Fragments.Add(new Fragment(timestamp, fragmentId));
|
||||
else
|
||||
user.Fragments.First(p => p.Id == fragmentId).Count++;
|
||||
|
||||
UpdateScreen();
|
||||
}
|
||||
|
||||
void UpdateScreen() {
|
||||
var output = new List<(string streamkey, int count)>();
|
||||
foreach (var user in users.OrderBy(p => p.Streamkey)) {
|
||||
user.Fragments.RemoveAll(p => p.Time < DateTime.Now - TimeSpan.FromMinutes(1));
|
||||
if (user.Fragments.Any())
|
||||
output.Add((user.Streamkey, user.Fragments.TakeLast(2).First().Count));
|
||||
}
|
||||
|
||||
var paddingOffset = users.Select(p => p.Streamkey).Aggregate("", (max, cur) => max.Length > cur.Length ? max : cur).Length;
|
||||
Console.Clear();
|
||||
Console.WriteLine($"Viewcount as of {DateTime.Now:yyyy-MM-dd HH:mm:ss}");
|
||||
foreach (var item in output) {
|
||||
Console.WriteLine($"{item.streamkey.PadRight(paddingOffset)} - {item.count}");
|
||||
}
|
||||
}
|
||||
|
||||
internal class User {
|
||||
public List<Fragment> Fragments = new();
|
||||
public string Streamkey;
|
||||
|
||||
public User(string streamkey) {
|
||||
Streamkey = streamkey;
|
||||
}
|
||||
}
|
||||
|
||||
internal class Fragment {
|
||||
public string Id;
|
||||
public DateTime Time;
|
||||
public int Count = 1;
|
||||
|
||||
public Fragment(string timestamp, string id) {
|
||||
Time = DateTime.ParseExact(timestamp, "dd/MMM/yyyy:HH:mm:ss K", DateTimeFormatInfo.InvariantInfo);
|
||||
Id = id;
|
||||
}
|
||||
}
|
2
README.md
Normal file
2
README.md
Normal file
|
@ -0,0 +1,2 @@
|
|||
# hls-viewcount
|
||||
CLI tool that shows how many times recent fragments were hit, by streamkey. To be used in conjunction with [nginx-mod-rtmp](https://git.ztn.sh/zotan/nginx-mod-rtmp) and [RTMPdash](https://git.ztn.sh/zotan/RTMPdash).
|
11
hls-viewcount.csproj
Normal file
11
hls-viewcount.csproj
Normal file
|
@ -0,0 +1,11 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<RootNamespace>hls_viewcount</RootNamespace>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
16
hls-viewcount.sln
Normal file
16
hls-viewcount.sln
Normal file
|
@ -0,0 +1,16 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "hls-viewcount", "hls-viewcount.csproj", "{9B985D01-E911-46BB-8043-D8AE3981F839}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{9B985D01-E911-46BB-8043-D8AE3981F839}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{9B985D01-E911-46BB-8043-D8AE3981F839}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{9B985D01-E911-46BB-8043-D8AE3981F839}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{9B985D01-E911-46BB-8043-D8AE3981F839}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
EndGlobal
|
Loading…
Reference in a new issue