diff --git a/IntroSkipper/Configuration/configPage.html b/IntroSkipper/Configuration/configPage.html index 36b41ca..a2a1398 100644 --- a/IntroSkipper/Configuration/configPage.html +++ b/IntroSkipper/Configuration/configPage.html @@ -714,6 +714,9 @@ +
+ +


@@ -758,6 +761,7 @@ var btnEraseRecapTimestamps = document.querySelector("button#btnEraseRecapTimestamps"); var btnEraseCreditTimestamps = document.querySelector("button#btnEraseCreditTimestamps"); var btnErasePreviewTimestamps = document.querySelector("button#btnErasePreviewTimestamps"); + var btnRebuildDatabase = document.querySelector("button#btnRebuildDatabase"); // all plugin configuration fields that can be get or set with .value (i.e. strings or numbers). var configurationFields = [ @@ -1504,6 +1508,9 @@ eraseTimestamps("Preview"); e.preventDefault(); }); + btnRebuildDatabase.addEventListener("click", () => { + fetchWithAuth("Intros/RebuildDatabase", "POST", null); + }); btnSeasonEraseTimestamps.addEventListener("click", () => { Dashboard.confirm("Are you sure you want to erase all timestamps for this season?", "Confirm timestamp erasure", (result) => { if (!result) { diff --git a/IntroSkipper/Controllers/SkipIntroController.cs b/IntroSkipper/Controllers/SkipIntroController.cs index 9a10e54..4aeafa1 100644 --- a/IntroSkipper/Controllers/SkipIntroController.cs +++ b/IntroSkipper/Controllers/SkipIntroController.cs @@ -251,6 +251,20 @@ public class SkipIntroController(MediaSegmentUpdateManager mediaSegmentUpdateMan return NoContent(); } + /// + /// Rebuilds the database. + /// + /// Database rebuilt. + /// No content. + [Authorize(Policy = Policies.RequiresElevation)] + [HttpPost("Intros/RebuildDatabase")] + public ActionResult RebuildDatabase() + { + using var db = new IntroSkipperDbContext(Plugin.Instance!.DbPath); + db.RebuildDatabase(); + return NoContent(); + } + /// /// Gets the user interface configuration. /// diff --git a/IntroSkipper/Db/IntroSkipperDbContext.cs b/IntroSkipper/Db/IntroSkipperDbContext.cs index 920d926..58da67c 100644 --- a/IntroSkipper/Db/IntroSkipperDbContext.cs +++ b/IntroSkipper/Db/IntroSkipperDbContext.cs @@ -109,39 +109,73 @@ public class IntroSkipperDbContext : DbContext /// public void ApplyMigrations() { - // If migrations table exists, just apply pending migrations normally - if (Database.GetAppliedMigrations().Any() || !Database.CanConnect()) + // If database doesn't exist or can't connect, create it with migrations + if (!Database.CanConnect()) + { + Database.Migrate(); + return; + } + + // If migrations table exists, apply pending migrations normally + if (Database.GetAppliedMigrations().Any()) { Database.Migrate(); return; } // For databases without migration history - try + RebuildDatabase(); + } + + /// + /// Rebuilds the database while preserving valid segments and season information. + /// + public void RebuildDatabase() + { + // Backup existing data + List segments = []; + List seasonInfos = []; + using (var db = new IntroSkipperDbContext(_dbPath)) { - // Backup existing data - List segments; - using (var db = new IntroSkipperDbContext(_dbPath)) + try { segments = [.. db.DbSegment.AsEnumerable().Where(s => s.ToSegment().Valid)]; } - - // Delete old database - Database.EnsureDeleted(); - - // Create new database with proper migration history - Database.Migrate(); - - // Restore the data - using (var db = new IntroSkipperDbContext(_dbPath)) + catch (Exception ex) { - db.DbSegment.AddRange(segments); - db.SaveChanges(); + throw new InvalidOperationException("Failed to read DbSegment data", ex); + } + + try + { + seasonInfos = [.. db.DbSeasonInfo]; + } + catch (Exception ex) + { + throw new InvalidOperationException("Failed to read DbSeasonInfo data", ex); } } - catch (Exception ex) + + // Delete old database + Database.EnsureDeleted(); + + // Create new database with proper migration history + Database.Migrate(); + + // Restore the data + using (var db = new IntroSkipperDbContext(_dbPath)) { - throw new InvalidOperationException("Failed to apply migrations", ex); + if (segments.Count > 0) + { + db.DbSegment.AddRange(segments); + } + + if (seasonInfos.Count > 0) + { + db.DbSeasonInfo.AddRange(seasonInfos); + } + + db.SaveChanges(); } } }