Add Windows desktop version

This commit is contained in:
renato97
2025-12-17 19:20:55 +00:00
parent 93dbe0941e
commit 8921d7f2a6
36 changed files with 2760 additions and 0 deletions

View File

@@ -0,0 +1,198 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using StreamPlayer.Desktop.Models;
namespace StreamPlayer.Desktop.Services;
public sealed class EventService
{
private static readonly Uri EventsUri = new("https://streamtpmedia.com/eventos.json");
private static readonly TimeSpan CacheDuration = TimeSpan.FromHours(24);
private static readonly string CachePath = Path.Combine(GetDataDirectory(), "events-cache.json");
private static readonly TimeZoneInfo EventZone = ResolveEventZone();
private static readonly HttpClient HttpClient = CreateHttpClient();
public async Task<IReadOnlyList<LiveEvent>> GetEventsAsync(bool forceRefresh, CancellationToken cancellationToken)
{
if (!forceRefresh && TryLoadFromCache(out var cached))
{
return cached;
}
try
{
string json = await DownloadJsonAsync(cancellationToken).ConfigureAwait(false);
var events = ParseEvents(json);
SaveCache(json);
return events;
}
catch
{
if (TryLoadFromCache(out var cachedEvents))
{
return cachedEvents;
}
throw;
}
}
private static bool TryLoadFromCache(out IReadOnlyList<LiveEvent> events)
{
events = Array.Empty<LiveEvent>();
if (!File.Exists(CachePath))
{
return false;
}
var age = DateTimeOffset.UtcNow - File.GetLastWriteTimeUtc(CachePath);
if (age > CacheDuration)
{
return false;
}
try
{
string json = File.ReadAllText(CachePath, Encoding.UTF8);
events = ParseEvents(json);
return true;
}
catch
{
return false;
}
}
private static void SaveCache(string json)
{
try
{
Directory.CreateDirectory(Path.GetDirectoryName(CachePath)!);
File.WriteAllText(CachePath, json, Encoding.UTF8);
}
catch
{
// Not critical if the cache cannot be persisted.
}
}
private static async Task<string> DownloadJsonAsync(CancellationToken cancellationToken)
{
using var response = await HttpClient.GetAsync(EventsUri, HttpCompletionOption.ResponseHeadersRead, cancellationToken)
.ConfigureAwait(false);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);
}
private static IReadOnlyList<LiveEvent> ParseEvents(string json)
{
using var document = JsonDocument.Parse(json);
var results = new List<LiveEvent>();
foreach (var item in document.RootElement.EnumerateArray())
{
string title = item.GetPropertyOrDefault("title");
string time = item.GetPropertyOrDefault("time");
string category = item.GetPropertyOrDefault("category");
string status = item.GetPropertyOrDefault("status");
string link = NormalizeLink(item.GetPropertyOrDefault("link"));
string channelName = ExtractChannelName(link);
long startMillis = ParseEventTime(time);
results.Add(new LiveEvent(title, time, category, status, link, channelName, startMillis));
}
return results;
}
private static string NormalizeLink(string link)
{
if (string.IsNullOrWhiteSpace(link))
{
return string.Empty;
}
return link.Replace("global1.php", "global2.php", StringComparison.OrdinalIgnoreCase);
}
private static string ExtractChannelName(string link)
{
if (string.IsNullOrWhiteSpace(link))
{
return string.Empty;
}
int index = link.IndexOf("stream=", StringComparison.OrdinalIgnoreCase);
if (index < 0 || index + 7 >= link.Length)
{
return string.Empty;
}
return link[(index + 7)..].Replace("_", " ").ToUpperInvariant();
}
private static long ParseEventTime(string time)
{
if (string.IsNullOrWhiteSpace(time))
{
return -1;
}
try
{
var parsed = DateTime.ParseExact(time.Trim(), "HH:mm", CultureInfo.InvariantCulture, DateTimeStyles.None);
var today = TimeZoneInfo.ConvertTime(DateTimeOffset.UtcNow, EventZone).Date;
var localCandidate = new DateTime(today.Year, today.Month, today.Day, parsed.Hour, parsed.Minute, 0, DateTimeKind.Unspecified);
var utcDateTime = TimeZoneInfo.ConvertTimeToUtc(localCandidate, EventZone);
var start = new DateTimeOffset(utcDateTime, TimeSpan.Zero);
if (start < DateTimeOffset.UtcNow.AddHours(-12))
{
start = start.AddDays(1);
}
return start.ToUnixTimeMilliseconds();
}
catch
{
return -1;
}
}
private static string GetDataDirectory()
{
var folder = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
"StreamPlayerDesktop");
Directory.CreateDirectory(folder);
return folder;
}
private static HttpClient CreateHttpClient()
{
var handler = new HttpClientHandler
{
AutomaticDecompression = System.Net.DecompressionMethods.All
};
var client = new HttpClient(handler)
{
Timeout = TimeSpan.FromSeconds(15)
};
client.DefaultRequestHeaders.UserAgent.ParseAdd("StreamPlayerDesktop/1.0");
client.DefaultRequestHeaders.ConnectionClose = false;
return client;
}
private static TimeZoneInfo ResolveEventZone()
{
string[] candidates = { "America/Argentina/Buenos_Aires", "Argentina Standard Time" };
foreach (var id in candidates)
{
try
{
return TimeZoneInfo.FindSystemTimeZoneById(id);
}
catch
{
// try next
}
}
return TimeZoneInfo.Local;
}
}