using System.Text.Json.Serialization; using Microsoft.EntityFrameworkCore; var builder = WebApplication.CreateBuilder(args); builder.Services.AddHttpClient(); builder.Services.AddDbContext(o => o.UseSqlite("Data Source=data/main.db")); builder.Services.AddHostedService(); var app = builder.Build(); try { using var scope = app.Services.CreateScope(); scope.ServiceProvider.GetRequiredService().Database.EnsureCreated(); } catch (Exception e) { Console.WriteLine(e); } app.MapStaticAssets(); app.MapGet("shows", (Database db) => db.Shows); app.MapGet("/", () => Results.Redirect("/index.html")); app.Run(); class Database(DbContextOptions options) : DbContext(options) { public DbSet Shows { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity().HasIndex(p => new { p.Title, p.StartDateTime }) .IsUnique(); base.OnModelCreating(modelBuilder); } } class Show { public int Id { get; set; } public string Title { get; set; } public string Scene { get; set; } public DateTimeOffset StartDateTime { get; set; } public string TicketUrl { get; set; } public string Type { get; set; } public string MovieVersion { get; set; } public string MovieMainVersion { get; set; } public string[] Tags { get; set; } } class DataWorker(IServiceScopeFactory serviceScopeFactory) : BackgroundService { protected override async Task ExecuteAsync(CancellationToken stoppingToken) { const int hour = 3600 * 60; while (!stoppingToken.IsCancellationRequested) { try { await Work(stoppingToken); await Task.Delay(hour, stoppingToken); } catch (Exception e) { Console.WriteLine(e); } } } async Task Work(CancellationToken ct) { using var scope = serviceScopeFactory.CreateScope(); var db = scope.ServiceProvider.GetRequiredService(); var http = scope.ServiceProvider.GetRequiredService(); var logger = scope.ServiceProvider.GetRequiredService>(); var refreshData = Environment.GetEnvironmentVariable("VEGADATA_REFRESH_DATA") == "1"; logger.LogInformation("Syncing data. refresh:{refreshData}", refreshData); var shows = await http.GetFromJsonAsync("https://www.vegascene.no/api/program", ct); var addedCount = 0; if (refreshData) { db.Shows.RemoveRange(db.Shows); await db.SaveChangesAsync(ct); File.Copy("data/main.db", $"data/main-{DateTime.UnixEpoch}.db"); } var count = await db.Shows.CountAsync(ct); foreach (var show in shows?.Shows ?? []) { var existing = await db.Shows.AnyAsync(s => s.Title == show.MovieTitle && s.StartDateTime == show.ShowStart, ct); if (existing) continue; logger.LogInformation("Adding show {title} at {startDateTime}", show.MovieTitle, show.ShowStart); db.Shows.Add(new Show() { Title = show.MovieTitle, Scene = show.ScreenName, StartDateTime = show.ShowStart.ToUniversalTime(), MovieMainVersion = show.MovieMainVersionId, MovieVersion = show.MovieVersionId, TicketUrl = show.TicketSaleUrl, Type = show.ShowType, Tags = show.VersionTags.Select(t => t.Tag).ToArray() }); addedCount++; } await db.SaveChangesAsync(ct); logger.LogInformation("Synced data, old count:{count}, new count:{addedCount}", count, addedCount); } class ShowsResponse { public List Shows { get; set; } } class VegaShow { [JsonPropertyName("screenName")] public string ScreenName { get; set; } [JsonPropertyName("ticketSaleUrl")] public string TicketSaleUrl { get; set; } [JsonPropertyName("showType")] public string ShowType { get; set; } [JsonPropertyName("showStart")] public DateTimeOffset ShowStart { get; set; } [JsonPropertyName("movieVersionId")] public string MovieVersionId { get; set; } [JsonPropertyName("movieMainVersionId")] public string MovieMainVersionId { get; set; } [JsonPropertyName("movieTitle")] public string MovieTitle { get; set; } [JsonPropertyName("versionTags")] public VersionTag[] VersionTags { get; set; } } public class VersionTag { [JsonPropertyName("tag")] public string Tag { get; set; } [JsonPropertyName("type")] public string Type { get; set; } } }