diff --git a/Services/TvSyncService.cs b/Services/TvSyncService.cs new file mode 100644 index 0000000..4d47aeb --- /dev/null +++ b/Services/TvSyncService.cs @@ -0,0 +1,152 @@ +using System.Text.Json; +using dbsync.Config; +using Npgsql; +using Serilog; + +namespace TvSyncApp.Services; + +public class TvSyncService +{ + private readonly string _connectionString; + + public TvSyncService(string connectionString) + { + _connectionString = connectionString; + } + + // ----------------------------------------- + // 1. Download Updates + // ----------------------------------------- + public async Task> DownloadTvMazeUpdatesAsync( + string updatesUrl, + string tempDir) + { + using var http = new HttpClient(); + + var json = await http.GetStringAsync(updatesUrl); + + // Deserialize first to validate and normalize + var parsed = JsonSerializer.Deserialize>(json) + ?? new Dictionary(); + + // Convert to int-key dictionary (what your app uses) + var result = parsed + .Where(k => int.TryParse(k.Key, out _)) + .ToDictionary(k => int.Parse(k.Key), v => v.Value); + + // Pretty-print JSON before saving + var options = new JsonSerializerOptions + { + WriteIndented = true + }; + + var formattedJson = JsonSerializer.Serialize(result, options); + + var filePath = Path.Combine(tempDir, "tvmaze_updates.json"); + await File.WriteAllTextAsync(filePath, formattedJson); + + return result; + } + + + // ----------------------------------------- + // 2. Update DB + // ----------------------------------------- + public void UpdateTvUpdatesTable(Dictionary updates) + { + using var conn = new NpgsqlConnection(_connectionString); + conn.Open(); + + using var transaction = conn.BeginTransaction(); + + using var batch = new NpgsqlBatch(conn, transaction); + + foreach (var kvp in updates) + { + var cmd = new NpgsqlBatchCommand(@" + INSERT INTO updates.tvupdates (seriesid, timestamp) + VALUES (@id, @ts) + ON CONFLICT (seriesid) + DO UPDATE SET timestamp = EXCLUDED.timestamp;"); + + cmd.Parameters.AddWithValue("id", kvp.Key); + cmd.Parameters.AddWithValue("ts", kvp.Value); + + batch.BatchCommands.Add(cmd); + } + + batch.ExecuteNonQuery(); + + transaction.Commit(); + + Log.Information("Batch updated tvupdates table with {Count} entries.", updates.Count); + } + + + // ----------------------------------------- + // 3. Determine Files Needed + // ----------------------------------------- + public List<(string Directory, string FileName)> DetermineFilesNeeded( + Dictionary downloadedUpdates, + Dictionary previousUpdates, + Dictionary directories) + { + var filesNeeded = new List<(string, string)>(); + var directoryList = directories.Values.ToList(); + + foreach (var kvp in downloadedUpdates) + { + var seriesId = kvp.Key; + var newTs = kvp.Value; + + if (previousUpdates.TryGetValue(seriesId, out var oldTs)) + { + if (newTs > oldTs) + foreach (var dir in directoryList) + filesNeeded.Add((dir, $"{seriesId}.json")); + } + else + { + foreach (var dir in directoryList) + filesNeeded.Add((dir, $"{seriesId}.json")); + } + } + + return filesNeeded; + } + + // ----------------------------------------- + // 4. Download Files + // ----------------------------------------- + public async Task DownloadSeriesFilesAsync( + List<(string Directory, string FileName)> filesNeeded, + Dictionary skipIds, + Settings settings) + { + using var http = new HttpClient(); + + foreach (var item in filesNeeded) + { + var seriesId = item.FileName.Replace(".json", ""); + var url = settings.Json!.SeriesApi! + .Replace("", seriesId); + + if (skipIds.ContainsKey(seriesId)) + { + Log.Information("Skipping series {SeriesId}", seriesId); + continue; + } + + try + { + var json = await http.GetStringAsync(url); + var savePath = Path.Combine(item.Directory, item.FileName); + await File.WriteAllTextAsync(savePath, json); + } + catch (Exception ex) + { + Log.Error(ex, "Failed downloading {SeriesId}", seriesId); + } + } + } +} \ No newline at end of file