diff --git a/AudioCuesheetEditor.End2EndTests/AudioCuesheetEditor.End2EndTests.csproj b/AudioCuesheetEditor.End2EndTests/AudioCuesheetEditor.End2EndTests.csproj index 342f1e9f..1611da6b 100644 --- a/AudioCuesheetEditor.End2EndTests/AudioCuesheetEditor.End2EndTests.csproj +++ b/AudioCuesheetEditor.End2EndTests/AudioCuesheetEditor.End2EndTests.csproj @@ -41,23 +41,11 @@ PreserveNewest PreserveNewest - - PreserveNewest - true PreserveNewest PreserveNewest - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - diff --git a/AudioCuesheetEditor.End2EndTests/Pages/IndexTest.cs b/AudioCuesheetEditor.End2EndTests/Pages/IndexTest.cs index 4e1a39be..dd2b7265 100644 --- a/AudioCuesheetEditor.End2EndTests/Pages/IndexTest.cs +++ b/AudioCuesheetEditor.End2EndTests/Pages/IndexTest.cs @@ -153,74 +153,5 @@ public async Task RenameAudiofileTestAsync() await Page.GetByRole(AriaRole.Button, new() { Name = "Ok" }).ClickAsync(); await Expect(Page.Locator("#app")).ToMatchAriaSnapshotAsync("- textbox \"Cuesheet artist\"\n- group \"Cuesheet artist\"\n- text: Cuesheet artist\n- textbox \"Cuesheet title\"\n- group \"Cuesheet title\"\n- text: Cuesheet title\n- group:\n - button \"Choose File\"\n - textbox \"Audiofile\": /Kalimba test \\d+\\.mp3/\n - group \"Audiofile\"\n - text: Audiofile\n - button \"Search\"\n - button\n - button\n- group:\n - button \"Choose File\"\n - textbox \"CD Textfile\": No file selected\n - group \"CD Textfile\"\n - text: CD Textfile\n - button \"Search\"\n - button [disabled]\n - button\n- textbox \"Cataloguenumber\"\n- group \"Cataloguenumber\"\n- text: Cataloguenumber"); } - - [TestMethod] - public async Task ImportUndoRedoTestAsync() - { - await Page.GotoAsync("http://localhost:5132/"); - await Page.GetByText("Import view").ClickAsync(); - await Page.GetByRole(AriaRole.Button, new() { Name = "Choose File" }).ClickAsync(); - await Page.GetByRole(AriaRole.Button, new() { Name = "Choose File" }).SetInputFilesAsync(new[] { "Textimport with Cuesheetdata.txt" }); - await Page.GetByRole(AriaRole.Textbox, new() { Name = "Scheme common data" }).FillAsync("Artist - Title - "); - await Page.GetByRole(AriaRole.Group).Filter(new() { HasText = "Scheme common dataScheme" }).GetByRole(AriaRole.Button).Nth(1).ClickAsync(); - await Page.GetByRole(AriaRole.Paragraph).Filter(new() { HasText = "Cataloguenumber" }).ClickAsync(); - await Expect(Page.GetByRole(AriaRole.Toolbar)).ToMatchAriaSnapshotAsync("- button \"undo\" [disabled]"); - await Expect(Page.GetByRole(AriaRole.Toolbar)).ToMatchAriaSnapshotAsync("- button \"redo\" [disabled]"); - await Page.GetByRole(AriaRole.Button, new() { Name = "Complete" }).ClickAsync(); - await Expect(Page.GetByRole(AriaRole.Button, new() { Name = "undo" })).ToMatchAriaSnapshotAsync("- button \"undo\""); - await Expect(Page.GetByRole(AriaRole.Toolbar)).ToMatchAriaSnapshotAsync("- button \"redo\" [disabled]"); - await Page.GetByRole(AriaRole.Button, new() { Name = "undo" }).ClickAsync(); - await Expect(Page.GetByRole(AriaRole.Toolbar)).ToMatchAriaSnapshotAsync("- button \"undo\" [disabled]"); - await Expect(Page.GetByRole(AriaRole.Button, new() { Name = "redo" })).ToMatchAriaSnapshotAsync("- button \"redo\""); - await Page.GetByRole(AriaRole.Button, new() { Name = "redo" }).ClickAsync(); - await Expect(Page.GetByRole(AriaRole.Textbox, new() { Name = "Cuesheet artist" })).ToHaveValueAsync("DJFreezeT"); - await Expect(Page.GetByRole(AriaRole.Textbox, new() { Name = "Cuesheet title" })).ToHaveValueAsync("Rabbit Hole Mix"); - await Expect(Page.GetByRole(AriaRole.Textbox, new() { Name = "Cataloguenumber" })).ToHaveValueAsync("0123456789123"); - await Expect(Page.GetByRole(AriaRole.Cell, new() { Name = "Einmusik Clear" })).ToBeVisibleAsync(); - await Expect(Page.GetByRole(AriaRole.Cell, new() { Name = "Missing Path (Original Mix)" })).ToBeVisibleAsync(); - } - - [TestMethod] - public async Task ImportTestBug54Async() - { - await Page.GotoAsync("http://localhost:5132/"); - await Page.GetByText("Import view").ClickAsync(); - await Page.GetByRole(AriaRole.Button, new() { Name = "Choose File" }).ClickAsync(); - await Page.GetByRole(AriaRole.Button, new() { Name = "Choose File" }).SetInputFilesAsync(new[] { "Textimport-Bug-#54.txt" }); - await Page.GetByText("Textfile (common data in").ClickAsync(); - await Page.GetByText("Textfile (just track data)").ClickAsync(); - await Expect(Page.GetByRole(AriaRole.Tabpanel)).ToMatchAriaSnapshotAsync("- table:\n - rowgroup:\n - row \"# Sort Show Column Options Artist Sort Show Column Options Title Sort Show Column Options Begin Sort Show Column Options End Sort Show Column Options Length Sort Show Column Options\":\n - columnheader:\n - checkbox\n - columnheader \"# Sort Show Column Options\":\n - button \"Sort\"\n - button \"Show Column Options\"\n - columnheader \"Artist Sort Show Column Options\":\n - button \"Sort\"\n - button \"Show Column Options\"\n - columnheader \"Title Sort Show Column Options\":\n - button \"Sort\"\n - button \"Show Column Options\"\n - columnheader \"Begin Sort Show Column Options\":\n - button \"Sort\"\n - button \"Show Column Options\"\n - columnheader \"End Sort Show Column Options\":\n - button \"Sort\"\n - button \"Show Column Options\"\n - columnheader \"Length Sort Show Column Options\":\n - button \"Sort\"\n - button \"Show Column Options\"\n - rowgroup:\n - row /Increment Decrement Adriatique Clear X\\. Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: \"1\"\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"Adriatique Clear\":\n - textbox: Adriatique\n - button \"Clear\"\n - button\n - cell \"X. Clear\":\n - textbox: X.\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement Third Harmony Clear Fears And Dreams \\(Original Mix\\) Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: \"2\"\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"Third Harmony Clear\":\n - textbox: Third Harmony\n - button \"Clear\"\n - button\n - cell \"Fears And Dreams (Original Mix) Clear\":\n - textbox: Fears And Dreams (Original Mix)\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement Dele Sosimi Afrobeat Orchestra Clear Too Much Information \\(Laolu Remix; Edit\\) Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: \"3\"\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"Dele Sosimi Afrobeat Orchestra Clear\":\n - textbox: Dele Sosimi Afrobeat Orchestra\n - button \"Clear\"\n - button\n - cell \"Too Much Information (Laolu Remix; Edit) Clear\":\n - textbox: Too Much Information (Laolu Remix; Edit)\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement Edem, Govan Clear Ankh \\(Onetwo MX Remix\\) Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: \"4\"\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"Edem, Govan Clear\":\n - textbox: Edem, Govan\n - button \"Clear\"\n - button\n - cell \"Ankh (Onetwo MX Remix) Clear\":\n - textbox: Ankh (Onetwo MX Remix)\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement Jody Wisternoff Clear For All Time \\(feat\\. Hendrik Burkhard\\) \\(Extended Mix\\) Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: \"5\"\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"Jody Wisternoff Clear\":\n - textbox: Jody Wisternoff\n - button \"Clear\"\n - button\n - cell \"For All Time (feat. Hendrik Burkhard) (Extended Mix) Clear\":\n - textbox: For All Time (feat. Hendrik Burkhard) (Extended Mix)\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement Einmusik Clear Bead \\(Original Mix\\) Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: \"6\"\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"Einmusik Clear\":\n - textbox: Einmusik\n - button \"Clear\"\n - button\n - cell \"Bead (Original Mix) Clear\":\n - textbox: Bead (Original Mix)\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement Sebastien Leger Clear La Danse du Scorpion Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: \"7\"\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"Sebastien Leger Clear\":\n - textbox: Sebastien Leger\n - button \"Clear\"\n - button\n - cell \"La Danse du Scorpion Clear\":\n - textbox: La Danse du Scorpion\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement Paul Thomas & Solid Stone Clear La Bombo \\(Solid Stone Remix\\) Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: \"8\"\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"Paul Thomas & Solid Stone Clear\":\n - textbox: Paul Thomas & Solid Stone\n - button \"Clear\"\n - button\n - cell \"La Bombo (Solid Stone Remix) Clear\":\n - textbox: La Bombo (Solid Stone Remix)\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement GusGus Clear Crossfade \\(Maceo Plex Mix\\) Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: \"9\"\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"GusGus Clear\":\n - textbox: GusGus\n - button \"Clear\"\n - button\n - cell \"Crossfade (Maceo Plex Mix) Clear\":\n - textbox: Crossfade (Maceo Plex Mix)\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement Klangkarussell Clear Time \\(Original Mix\\) Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: /\\d+/\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"Klangkarussell Clear\":\n - textbox: Klangkarussell\n - button \"Clear\"\n - button\n - cell \"Time (Original Mix) Clear\":\n - textbox: Time (Original Mix)\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement Anysense & Un:said Clear Missing Path \\(Original Mix\\) Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: /\\d+/\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"Anysense & Un:said Clear\":\n - textbox: Anysense & Un:said\n - button \"Clear\"\n - button\n - cell \"Missing Path (Original Mix) Clear\":\n - textbox: Missing Path (Original Mix)\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement Space Food Clear Bombay Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: /\\d+/\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"Space Food Clear\":\n - textbox: Space Food\n - button \"Clear\"\n - button\n - cell \"Bombay Clear\":\n - textbox: Bombay\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement SHDW & Obscure Shape Clear Wächter der Nacht \\(Original Mix\\) Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: /\\d+/\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"SHDW & Obscure Shape Clear\":\n - textbox: SHDW & Obscure Shape\n - button \"Clear\"\n - button\n - cell \"Wächter der Nacht (Original Mix) Clear\":\n - textbox: Wächter der Nacht (Original Mix)\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement HOSH Clear Karma Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: /\\d+/\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"HOSH Clear\":\n - textbox: HOSH\n - button \"Clear\"\n - button\n - cell \"Karma Clear\":\n - textbox: Karma\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement Alexey Union Clear Olympia \\(Original Mix\\) Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: /\\d+/\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"Alexey Union Clear\":\n - textbox: Alexey Union\n - button \"Clear\"\n - button\n - cell \"Olympia (Original Mix) Clear\":\n - textbox: Olympia (Original Mix)\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement Paul Taylor Clear Afterglow Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: /\\d+/\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"Paul Taylor Clear\":\n - textbox: Paul Taylor\n - button \"Clear\"\n - button\n - cell \"Afterglow Clear\":\n - textbox: Afterglow\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement Philter Clear Stranger Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: /\\d+/\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"Philter Clear\":\n - textbox: Philter\n - button \"Clear\"\n - button\n - cell \"Stranger Clear\":\n - textbox: Stranger\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement Skizologic Clear Hypersphere \\(Original Mix\\) Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: /\\d+/\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"Skizologic Clear\":\n - textbox: Skizologic\n - button \"Clear\"\n - button\n - cell \"Hypersphere (Original Mix) Clear\":\n - textbox: Hypersphere (Original Mix)\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement Thomas Schumacher, Caitlin Clear All of You \\(Remix\\) Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: /\\d+/\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"Thomas Schumacher, Caitlin Clear\":\n - textbox: Thomas Schumacher, Caitlin\n - button \"Clear\"\n - button\n - cell \"All of You (Remix) Clear\":\n - textbox: All of You (Remix)\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement A\\. Skomoroh Clear White Horse Conquest \\(Original Mix\\) Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: /\\d+/\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"A. Skomoroh Clear\":\n - textbox: A. Skomoroh\n - button \"Clear\"\n - button\n - cell \"White Horse Conquest (Original Mix) Clear\":\n - textbox: White Horse Conquest (Original Mix)\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement Patrik Berg Clear Bright \\(Original Mix\\) Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: /\\d+/\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"Patrik Berg Clear\":\n - textbox: Patrik Berg\n - button \"Clear\"\n - button\n - cell \"Bright (Original Mix) Clear\":\n - textbox: Bright (Original Mix)\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement Hidden Empire Clear Bengal Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: /\\d+/\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"Hidden Empire Clear\":\n - textbox: Hidden Empire\n - button \"Clear\"\n - button\n - cell \"Bengal Clear\":\n - textbox: Bengal\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement Mario Ochoa Clear Levitate Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: /\\d+/\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"Mario Ochoa Clear\":\n - textbox: Mario Ochoa\n - button \"Clear\"\n - button\n - cell \"Levitate Clear\":\n - textbox: Levitate\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement Raul Facio Clear Eyes Wide Shut \\(Original Mix\\) Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: /\\d+/\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"Raul Facio Clear\":\n - textbox: Raul Facio\n - button \"Clear\"\n - button\n - cell \"Eyes Wide Shut (Original Mix) Clear\":\n - textbox: Eyes Wide Shut (Original Mix)\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement Soolver Clear Regular \\(Original Mix\\) Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: /\\d+/\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"Soolver Clear\":\n - textbox: Soolver\n - button \"Clear\"\n - button\n - cell \"Regular (Original Mix) Clear\":\n - textbox: Regular (Original Mix)\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement Weska Clear EQ64 \\(Original Mix\\) Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: /\\d+/\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"Weska Clear\":\n - textbox: Weska\n - button \"Clear\"\n - button\n - cell \"EQ64 (Original Mix) Clear\":\n - textbox: EQ64 (Original Mix)\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement Tempo Giusto Clear The Fall \\(Extended Mix\\) Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: /\\d+/\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"Tempo Giusto Clear\":\n - textbox: Tempo Giusto\n - button \"Clear\"\n - button\n - cell \"The Fall (Extended Mix) Clear\":\n - textbox: The Fall (Extended Mix)\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement Vlind & Asteroid & Gary Leroy Clear Trinity \\(Extended Mix\\) Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: /\\d+/\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"Vlind & Asteroid & Gary Leroy Clear\":\n - textbox: Vlind & Asteroid & Gary Leroy\n - button \"Clear\"\n - button\n - cell \"Trinity (Extended Mix) Clear\":\n - textbox: Trinity (Extended Mix)\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement Astral Legacy Clear Vaveyla \\(Original Mix\\) Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: /\\d+/\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"Astral Legacy Clear\":\n - textbox: Astral Legacy\n - button \"Clear\"\n - button\n - cell \"Vaveyla (Original Mix) Clear\":\n - textbox: Vaveyla (Original Mix)\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement Gerrox Clear Chakra \\(Original Mix\\) Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: /\\d+/\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"Gerrox Clear\":\n - textbox: Gerrox\n - button \"Clear\"\n - button\n - cell \"Chakra (Original Mix) Clear\":\n - textbox: Chakra (Original Mix)\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement Charlotte De Witte Clear Pattern Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: /\\d+/\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"Charlotte De Witte Clear\":\n - textbox: Charlotte De Witte\n - button \"Clear\"\n - button\n - cell \"Pattern Clear\":\n - textbox: Pattern\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement Space Food Clear Amabey Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: /\\d+/\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"Space Food Clear\":\n - textbox: Space Food\n - button \"Clear\"\n - button\n - cell \"Amabey Clear\":\n - textbox: Amabey\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement ARTBAT Clear Papilion \\(Original Mix\\) Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: /\\d+/\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"ARTBAT Clear\":\n - textbox: ARTBAT\n - button \"Clear\"\n - button\n - cell \"Papilion (Original Mix) Clear\":\n - textbox: Papilion (Original Mix)\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement PETER PAHN Clear Enjoy Infinity \\(Original Mix\\) Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: /\\d+/\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"PETER PAHN Clear\":\n - textbox: PETER PAHN\n - button \"Clear\"\n - button\n - cell \"Enjoy Infinity (Original Mix) Clear\":\n - textbox: Enjoy Infinity (Original Mix)\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement Solitek Clear Instinct \\(Original Mix\\) Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: /\\d+/\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"Solitek Clear\":\n - textbox: Solitek\n - button \"Clear\"\n - button\n - cell \"Instinct (Original Mix) Clear\":\n - textbox: Instinct (Original Mix)\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement Veerus Clear Heavy Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: /\\d+/\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"Veerus Clear\":\n - textbox: Veerus\n - button \"Clear\"\n - button\n - cell \"Heavy Clear\":\n - textbox: Heavy\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement Secret Cinema & Reinier Zonneveld Clear Pain Thing \\(Original Mix\\) Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: /\\d+/\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"Secret Cinema & Reinier Zonneveld Clear\":\n - textbox: Secret Cinema & Reinier Zonneveld\n - button \"Clear\"\n - button\n - cell \"Pain Thing (Original Mix) Clear\":\n - textbox: Pain Thing (Original Mix)\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement Amelie Lens Clear Hypnotized Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: /\\d+/\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"Amelie Lens Clear\":\n - textbox: Amelie Lens\n - button \"Clear\"\n - button\n - cell \"Hypnotized Clear\":\n - textbox: Hypnotized\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement Nikolay Kirov Clear Chasing the Sun \\(Original Mix\\) Clear \\d+:\\d+:\\d+ End has no value! Length has no value!/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: /\\d+/\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"Nikolay Kirov Clear\":\n - textbox: Nikolay Kirov\n - button \"Clear\"\n - button\n - cell \"Chasing the Sun (Original Mix) Clear\":\n - textbox: Chasing the Sun (Original Mix)\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell \"End has no value!\":\n - textbox\n - cell \"Length has no value!\":\n - textbox\n - rowgroup:\n - row"); - } - - [TestMethod] - public async Task ImportTestSampleInputfile2Async() - { - await Page.GotoAsync("http://localhost:5132/"); - await Page.GetByText("Import view").ClickAsync(); - await Page.GetByRole(AriaRole.Button, new() { Name = "Choose File" }).SetInputFilesAsync(new[] { "Sample_Inputfile2.txt" }); - await Page.GetByRole(AriaRole.Group).Filter(new() { HasText = "Scheme common dataScheme" }).GetByLabel("Clear").ClickAsync(); - await Expect(Page.GetByRole(AriaRole.Textbox, new() { Name = "Cuesheet artist" })).ToBeEmptyAsync(); - await Expect(Page.GetByRole(AriaRole.Textbox, new() { Name = "Cuesheet title" })).ToBeEmptyAsync(); - await Expect(Page.GetByRole(AriaRole.Cell, new() { Name = "Sample Title 1 Clear" })).ToBeVisibleAsync(); - await Expect(Page.GetByRole(AriaRole.Cell, new() { Name = ":09:23" }).Nth(1)).ToBeVisibleAsync(); - await Expect(Page.GetByRole(AriaRole.Cell, new() { Name = "01:15:" })).ToBeVisibleAsync(); - await Expect(Page.GetByRole(AriaRole.Cell, new() { Name = "Sample Artist 8 Clear" })).ToBeVisibleAsync(); - } - - [TestMethod] - public async Task ImportTestTraktorAsync() - { - await Page.GotoAsync("http://localhost:5132/"); - await Page.GetByText("Import view").ClickAsync(); - await Page.GetByRole(AriaRole.Button, new() { Name = "Choose File" }).SetInputFilesAsync(new[] { "Traktor Export.html" }); - await Page.GetByText("Textfile (common data in").ClickAsync(); - await Page.GetByText("Traktor history").ClickAsync(); - await Expect(Page.GetByRole(AriaRole.Cell, new() { Name = "Arba Han Clear" })).ToBeVisibleAsync(); - await Expect(Page.GetByRole(AriaRole.Cell, new() { Name = ":48:53" }).Nth(1)).ToBeVisibleAsync(); - await Expect(Page.GetByRole(AriaRole.Cell, new() { Name = "Acid Rain Clear" })).ToBeVisibleAsync(); - await Expect(Page.GetByRole(AriaRole.Cell, new() { Name = ":48:40" }).Nth(1)).ToBeVisibleAsync(); - await Page.GetByRole(AriaRole.Button, new() { Name = "Complete" }).ClickAsync(); - await Expect(Page.GetByRole(AriaRole.Cell, new() { Name = "Space Yoda (Snyl Remix) Clear" })).ToBeVisibleAsync(); - } } } diff --git a/AudioCuesheetEditor.End2EndTests/Textimport with Cuesheetdata.txt b/AudioCuesheetEditor.End2EndTests/Textimport with Cuesheetdata.txt deleted file mode 100644 index f476fd54..00000000 --- a/AudioCuesheetEditor.End2EndTests/Textimport with Cuesheetdata.txt +++ /dev/null @@ -1,40 +0,0 @@ -DJFreezeT - Rabbit Hole Mix - 0123456789123 -Adriatique - X. 00:05:24.250 -Third Harmony - Fears And Dreams (Original Mix) 00:10:39 -Dele Sosimi Afrobeat Orchestra - Too Much Information (Laolu Remix; Edit) 00:17:06 -Edem, Govan - Ankh (Onetwo MX Remix) 00:23:21 -Jody Wisternoff - For All Time (feat. Hendrik Burkhard) (Extended Mix) 00:29:02 -Einmusik - Bead (Original Mix) 00:34:27 -Sebastien Leger - La Danse du Scorpion 00:40:59 -Paul Thomas & Solid Stone - La Bombo (Solid Stone Remix) 00:46:19 -GusGus - Crossfade (Maceo Plex Mix) 00:52:20 -Klangkarussell - Time (Original Mix) 00:56:19 -Anysense & Un:said - Missing Path (Original Mix) 01:01:41 -Space Food - Bombay 01:06:33 -SHDW & Obscure Shape - Wächter der Nacht (Original Mix) 01:11:04 -HOSH - Karma 01:15:28 -Alexey Union - Olympia (Original Mix) 01:21:08 -Paul Taylor - Afterglow 01:25:38 -Philter - Stranger 01:31:52 -Skizologic - Hypersphere (Original Mix) 01:36:40 -Thomas Schumacher, Caitlin - All of You (Remix) 01:42:16 -A. Skomoroh - White Horse Conquest (Original Mix) 01:47:04 -Patrik Berg - Bright (Original Mix) 01:52:37 -Hidden Empire - Bengal 01:58:05 -Mario Ochoa - Levitate 02:03:00 -Raul Facio - Eyes Wide Shut (Original Mix) 02:08:21 -Soolver - Regular (Original Mix) 02:14:31 -Weska - EQ64 (Original Mix) 02:18:35 -Tempo Giusto - The Fall (Extended Mix) 02:24:12 -Vlind & Asteroid & Gary Leroy - Trinity (Extended Mix) 02:29:38 -Astral Legacy - Vaveyla (Original Mix) 02:32:52 -Gerrox - Chakra (Original Mix) 02:37:00 -Charlotte De Witte - Pattern 02:41:55 -Space Food - Amabey 02:46:55 -ARTBAT - Papilion (Original Mix) 02:51:13 -PETER PAHN - Enjoy Infinity (Original Mix) 02:56:08 -Solitek - Instinct (Original Mix) 03:00:57 -Veerus - Heavy 03:05:19 -Secret Cinema & Reinier Zonneveld - Pain Thing (Original Mix) 03:09:38 -Amelie Lens - Hypnotized 03:13:13 -Nikolay Kirov - Chasing the Sun (Original Mix) diff --git a/AudioCuesheetEditor.End2EndTests/Textimport-Bug-#54.txt b/AudioCuesheetEditor.End2EndTests/Textimport-Bug-#54.txt deleted file mode 100644 index 9cda0a9e..00000000 --- a/AudioCuesheetEditor.End2EndTests/Textimport-Bug-#54.txt +++ /dev/null @@ -1,40 +0,0 @@ - -Adriatique - X. 00:05:24 -Third Harmony - Fears And Dreams (Original Mix) 00:10:39 -Dele Sosimi Afrobeat Orchestra - Too Much Information (Laolu Remix; Edit) 00:17:06 -Edem, Govan - Ankh (Onetwo MX Remix) 00:23:21 -Jody Wisternoff - For All Time (feat. Hendrik Burkhard) (Extended Mix) 00:29:02 -Einmusik - Bead (Original Mix) 00:34:27 -Sebastien Leger - La Danse du Scorpion 00:40:59 -Paul Thomas & Solid Stone - La Bombo (Solid Stone Remix) 00:46:19 -GusGus - Crossfade (Maceo Plex Mix) 00:52:20 -Klangkarussell - Time (Original Mix) 00:56:19 -Anysense & Un:said - Missing Path (Original Mix) 01:01:41 -Space Food - Bombay 01:06:33 -SHDW & Obscure Shape - Wächter der Nacht (Original Mix) 01:11:04 -HOSH - Karma 01:15:28 -Alexey Union - Olympia (Original Mix) 01:21:08 -Paul Taylor - Afterglow 01:25:38 -Philter - Stranger 01:31:52 -Skizologic - Hypersphere (Original Mix) 01:36:40 -Thomas Schumacher, Caitlin - All of You (Remix) 01:42:16 -A. Skomoroh - White Horse Conquest (Original Mix) 01:47:04 -Patrik Berg - Bright (Original Mix) 01:52:37 -Hidden Empire - Bengal 01:58:05 -Mario Ochoa - Levitate 02:03:00 -Raul Facio - Eyes Wide Shut (Original Mix) 02:08:21 -Soolver - Regular (Original Mix) 02:14:31 -Weska - EQ64 (Original Mix) 02:18:35 -Tempo Giusto - The Fall (Extended Mix) 02:24:12 -Vlind & Asteroid & Gary Leroy - Trinity (Extended Mix) 02:29:38 -Astral Legacy - Vaveyla (Original Mix) 02:32:52 -Gerrox - Chakra (Original Mix) 02:37:00 -Charlotte De Witte - Pattern 02:41:55 -Space Food - Amabey 02:46:55 -ARTBAT - Papilion (Original Mix) 02:51:13 -PETER PAHN - Enjoy Infinity (Original Mix) 02:56:08 -Solitek - Instinct (Original Mix) 03:00:57 -Veerus - Heavy 03:05:19 -Secret Cinema & Reinier Zonneveld - Pain Thing (Original Mix) 03:09:38 -Amelie Lens - Hypnotized 03:13:13 -Nikolay Kirov - Chasing the Sun (Original Mix) diff --git a/AudioCuesheetEditor.End2EndTests/Traktor Export.html b/AudioCuesheetEditor.End2EndTests/Traktor Export.html deleted file mode 100644 index bdc497c7..00000000 --- a/AudioCuesheetEditor.End2EndTests/Traktor Export.html +++ /dev/null @@ -1,306 +0,0 @@ - - - - - - - - Track List HISTORY - - - - -

Track List: HISTORY

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Num.ArtistTitleStart Time
1NachapGlass2025/1/29 18:52:10
2Progressive + Melodic DECK2025/1/29 19:04:06
3Nomer 21Depersonalization2025/1/29 19:05:41
4SevenEver, Nopopstar, 2JOHN'S & Eugene JayShowing Off2025/1/29 19:11:24
5Carlo WhaleUnconscious2025/1/29 19:16:12
6Arba HanTimelaps2025/1/29 19:21:47
7SavillEnergy Surrounds2025/1/29 19:25:23
8TeklixThe Tribal Code2025/1/29 19:32:40
9NeuralisI'm Looking for Answers2025/1/29 19:41:03
10Nopopstar & ArsiaDirty Moves2025/1/29 19:46:44
11SevenEver, Nopopstar, 2JOHN'S & Eugene JayLost (Maze 28 Remix)2025/1/29 19:52:34
12SevenEver, Nopopstar, 2JOHN'S & Eugene JayLost (Redspace Remix)2025/1/29 19:57:27
13Gadoz5d2025/1/29 20:00:32
14DJ DanzikOut of Space2025/1/29 20:08:44
15Enis ÇobanInternet2025/1/29 20:11:32
16Cold Mind & Alex YikkerRage2025/1/29 20:17:24
17Maze 28Sol (JAHAYA Remix)2025/1/29 20:23:43
18Alex GraftonHi Baby2025/1/29 20:25:01
19Che&Mos & Halo FarDaddy2025/1/29 20:34:23
20K KARDENAcid Rain2025/1/29 20:37:32
21Dobrov & Gar1ssonAnalogic (Redspace Remix)2025/1/29 20:40:50
22GazfluzVargan2025/1/29 20:48:50
23SHKAPOVControl2025/1/29 20:52:49
24QazaQOn the Line2025/1/29 20:58:57
25Alex SchaufelElizabeth (Larsun Hesh Remix)2025/1/29 21:01:19
26OiroJust Business2025/1/29 21:06:28
27MolexMind Split (Redspace Remix)2025/1/29 21:11:32
28SOLIGive Me Your Mind2025/1/29 21:19:58
29MANDUJacky2025/1/29 21:25:58
30SOLISpacetoon2025/1/29 21:31:15
31Sasha FashionMoqton2025/1/29 21:33:41
32NAASAPoison2025/1/29 21:42:28
33Nopopstar, 2JOHN'S & Eugene JayNightlong2025/1/29 21:48:55
34Skillz jayChoir2025/1/29 21:51:24
35KovaxController2025/1/29 21:57:59
36MumboiJust a Beat2025/1/29 22:00:41
37EcleptSprut2025/1/29 22:07:28
38RudenskyDark Escort2025/1/29 22:12:45
39Alexey Union, Kinky Sound & KOCHETOVConnected2025/1/29 22:17:12
40ANMA (MD)Space Yoda (Snyl Remix)2025/1/29 22:24:40
41InacheAndale (MONTA (TN) Remix)2025/1/29 22:30:03
- - - diff --git a/AudioCuesheetEditor.Tests/AudioCuesheetEditor.Tests.csproj b/AudioCuesheetEditor.Tests/AudioCuesheetEditor.Tests.csproj index ce6c191f..4ebd0bab 100644 --- a/AudioCuesheetEditor.Tests/AudioCuesheetEditor.Tests.csproj +++ b/AudioCuesheetEditor.Tests/AudioCuesheetEditor.Tests.csproj @@ -36,4 +36,13 @@ + + + PreserveNewest + + + PreserveNewest + + + diff --git a/AudioCuesheetEditor.Tests/Model/AudioCuesheet/CuesheetTests.cs b/AudioCuesheetEditor.Tests/Model/AudioCuesheet/CuesheetTests.cs index 1849ce87..7b6f0454 100644 --- a/AudioCuesheetEditor.Tests/Model/AudioCuesheet/CuesheetTests.cs +++ b/AudioCuesheetEditor.Tests/Model/AudioCuesheet/CuesheetTests.cs @@ -71,6 +71,90 @@ public void EmptyCuesheetTracksValidationTest() Assert.AreEqual(ValidationStatus.Success, validationErrorTracks.Status); } + [TestMethod()] + public void ImportTest() + { + // Arrange + var fileContent = new List + { + "CuesheetArtist - CuesheetTitle c:\\tmp\\Testfile.mp3", + "Sample Artist 1 - Sample Title 1 00:05:00", + "Sample Artist 2 - Sample Title 2 00:09:23", + "Sample Artist 3 - Sample Title 3 00:15:54", + "Sample Artist 4 - Sample Title 4 00:20:13", + "Sample Artist 5 - Sample Title 5 00:24:54", + "Sample Artist 6 - Sample Title 6 00:31:54", + "Sample Artist 7 - Sample Title 7 00:45:54", + "Sample Artist 8 - Sample Title 8 01:15:54" + }; + + var traceChangeManager = new TraceChangeManager(TestHelper.CreateLogger()); + var sessionStateContainer = new SessionStateContainer(traceChangeManager); + var localStorageOptionsProviderMock = new Mock(); + var textImportScheme = new TextImportScheme() + { + SchemeCuesheet = TextImportScheme.DefaultSchemeCuesheet, + SchemeTracks = TextImportScheme.DefaultSchemeTracks + }; + var timeSpanFormat = new TimeSpanFormat(); + var options = new ApplicationOptions(); + localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync()).ReturnsAsync(options); + var fileInputManagerMock = new Mock(); + var importManager = new ImportManager(sessionStateContainer, localStorageOptionsProviderMock.Object, traceChangeManager, fileInputManagerMock.Object); + var testHelper = new TestHelper(); + // Act + importManager.ImportText(fileContent, textImportScheme, timeSpanFormat); + + // Assert + Assert.IsNull(sessionStateContainer.ImportCuesheet?.CDTextfile); + Assert.AreEqual(ValidationStatus.Success, sessionStateContainer.ImportCuesheet?.Validate().Status); + Assert.AreEqual(ValidationStatus.Success, sessionStateContainer.ImportCuesheet?.Tracks.ElementAt(0).Validate().Status); + Assert.AreEqual(ValidationStatus.Success, sessionStateContainer.ImportCuesheet?.Tracks.ElementAt(1).Validate().Status); + Assert.AreEqual(ValidationStatus.Success, sessionStateContainer.ImportCuesheet?.Tracks.ElementAt(2).Validate().Status); + Assert.AreEqual(ValidationStatus.Success, sessionStateContainer.ImportCuesheet?.Tracks.ElementAt(3).Validate().Status); + Assert.AreEqual(ValidationStatus.Success, sessionStateContainer.ImportCuesheet?.Tracks.ElementAt(4).Validate().Status); + Assert.AreEqual(ValidationStatus.Success, sessionStateContainer.ImportCuesheet?.Tracks.ElementAt(5).Validate().Status); + Assert.AreEqual(ValidationStatus.Success, sessionStateContainer.ImportCuesheet?.Tracks.ElementAt(6).Validate().Status); + Assert.AreEqual(ValidationStatus.Success, sessionStateContainer.ImportCuesheet?.Tracks.ElementAt(7).Validate().Status); + } + + [TestMethod()] + public void ImportTestCalculateEndCorrectly() + { + // Arrange + var textImportMemoryStream = new MemoryStream(Resources.Textimport_Bug_54); + using var reader = new StreamReader(textImportMemoryStream); + List lines = []; + while (reader.EndOfStream == false) + { + lines.Add(reader.ReadLine()); + } + var fileContent = lines.AsReadOnly(); + var testHelper = new TestHelper(); + var traceChangeManager = new TraceChangeManager(TestHelper.CreateLogger()); + var sessionStateContainer = new SessionStateContainer(traceChangeManager); + var localStorageOptionsProviderMock = new Mock(); + var options = new ApplicationOptions(); + var textImportScheme = new TextImportScheme() + { + SchemeCuesheet = null, + SchemeTracks = TextImportScheme.DefaultSchemeTracks + }; + options.ImportScheme = textImportScheme; + localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync()).ReturnsAsync(options); + var fileInputManagerMock = new Mock(); + var importManager = new ImportManager(sessionStateContainer, localStorageOptionsProviderMock.Object, traceChangeManager, fileInputManagerMock.Object); + var timeSpanFormat = new TimeSpanFormat(); + // Act + importManager.ImportText(fileContent, textImportScheme, timeSpanFormat); + // Assert + Assert.IsNull(sessionStateContainer.Importfile?.AnalyseException); + Assert.IsNotNull(sessionStateContainer.ImportCuesheet); + Assert.AreEqual(39, sessionStateContainer.ImportCuesheet.Tracks.Count); + Assert.AreEqual(new TimeSpan(0, 5, 24), sessionStateContainer.ImportCuesheet.Tracks.ElementAt(0).End); + Assert.AreEqual(new TimeSpan(3, 13, 13), sessionStateContainer.ImportCuesheet.Tracks.ElementAt(38).Begin); + } + [TestMethod()] public void RecordTest() { @@ -342,6 +426,68 @@ public void TrackLengthChangedWithIsLinkedToPreivousTest() Assert.AreEqual(track1.End, track2.Begin); Assert.AreEqual(editedTrack.End, track2.Begin); } + [TestMethod()] + public void ImportSamplesTest() + { + // Arrange + var fileContent = File.ReadAllLines("Sample_Inputfile.txt"); + var testHelper = new TestHelper(); + var traceChangeManager = new TraceChangeManager(TestHelper.CreateLogger()); + var sessionStateContainer = new SessionStateContainer(traceChangeManager); + var localStorageOptionsProviderMock = new Mock(); + var textImportScheme = new TextImportScheme() + { + SchemeCuesheet = TextImportScheme.DefaultSchemeCuesheet, + SchemeTracks = TextImportScheme.DefaultSchemeTracks + }; + var timeSpanFormat = new TimeSpanFormat(); + var options = new ApplicationOptions(); + localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync()).ReturnsAsync(options); + var fileInputManagerMock = new Mock(); + var importManager = new ImportManager(sessionStateContainer, localStorageOptionsProviderMock.Object, traceChangeManager, fileInputManagerMock.Object); + // Act + importManager.ImportText(fileContent, textImportScheme, timeSpanFormat); + // Assert + Assert.IsNull(sessionStateContainer.Importfile?.AnalyseException); + Assert.IsNotNull(sessionStateContainer.ImportCuesheet); + Assert.AreEqual("CuesheetArtist", sessionStateContainer.ImportCuesheet.Artist); + Assert.AreEqual("CuesheetTitle", sessionStateContainer.ImportCuesheet.Title); + Assert.AreEqual(8, sessionStateContainer.ImportCuesheet.Tracks.Count); + Assert.AreEqual(new TimeSpan(1, 15, 54), sessionStateContainer.ImportCuesheet.Tracks.Last().End); + } + + [TestMethod()] + public void ImportSamples2Test() + { + // Arrange + var fileContent = File.ReadAllLines("Sample_Inputfile2.txt"); + var testHelper = new TestHelper(); + var traceChangeManager = new TraceChangeManager(TestHelper.CreateLogger()); + var sessionStateContainer = new SessionStateContainer(traceChangeManager); + var localStorageOptionsProviderMock = new Mock(); + var textImportScheme = new TextImportScheme() + { + SchemeCuesheet = null, + SchemeTracks = TextImportScheme.DefaultSchemeTracks + }; + var timeSpanFormat = new TimeSpanFormat(); + var options = new ApplicationOptions + { + ImportScheme = textImportScheme + }; + localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync()).ReturnsAsync(options); + var fileInputManagerMock = new Mock(); + var importManager = new ImportManager(sessionStateContainer, localStorageOptionsProviderMock.Object, traceChangeManager, fileInputManagerMock.Object); + // Act + importManager.ImportText(fileContent, textImportScheme, timeSpanFormat); + // Assert + Assert.IsNull(sessionStateContainer.Importfile?.AnalyseException); + Assert.IsNotNull(sessionStateContainer.ImportCuesheet); + Assert.IsNull(sessionStateContainer.ImportCuesheet.Artist); + Assert.IsNull(sessionStateContainer.ImportCuesheet.Title); + Assert.AreEqual(8, sessionStateContainer.ImportCuesheet.Tracks.Count); + Assert.AreEqual(new TimeSpan(1, 15, 54), sessionStateContainer.ImportCuesheet.Tracks.Last().End); + } [TestMethod()] public void ValidateTest() diff --git a/AudioCuesheetEditor.Tests/Model/IO/Import/ImportprofileTests.cs b/AudioCuesheetEditor.Tests/Model/IO/Import/ImportprofileTests.cs deleted file mode 100644 index b25e9756..00000000 --- a/AudioCuesheetEditor.Tests/Model/IO/Import/ImportprofileTests.cs +++ /dev/null @@ -1,47 +0,0 @@ -//This file is part of AudioCuesheetEditor. - -//AudioCuesheetEditor is free software: you can redistribute it and/or modify -//it under the terms of the GNU General Public License as published by -//the Free Software Foundation, either version 3 of the License, or -//(at your option) any later version. - -//AudioCuesheetEditor is distributed in the hope that it will be useful, -//but WITHOUT ANY WARRANTY; without even the implied warranty of -//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -//GNU General Public License for more details. - -//You should have received a copy of the GNU General Public License -//along with Foobar. If not, see -//. - -using AudioCuesheetEditor.Model.Entity; -using AudioCuesheetEditor.Model.IO.Import; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System.Linq; - -namespace AudioCuesheetEditor.Tests.Model.IO.Import -{ - [TestClass()] - public class ImportprofileTests - { - [TestMethod()] - public void Validate_WithoutPlaceholder_ReturnsInvalid() - { - // Arrange - var importprofile = new Importprofile() - { - SchemeCuesheet = "Test 1", - SchemeTracks = "Here comes next track" - }; - // Act - var result = importprofile.Validate(); - // Assert - Assert.AreEqual(ValidationStatus.Error, result.Status); - Assert.AreEqual(2, result.ValidationMessages.Count); - Assert.AreEqual("{0} contains no placeholder!", result.ValidationMessages.First().Message); - Assert.AreEqual(nameof(Importprofile.SchemeCuesheet), result.ValidationMessages.First().Parameter?.First().ToString()); - Assert.AreEqual("{0} contains no placeholder!", result.ValidationMessages.Last().Message); - Assert.AreEqual(nameof(Importprofile.SchemeTracks), result.ValidationMessages.Last().Parameter?.First().ToString()); - } - } -} \ No newline at end of file diff --git a/AudioCuesheetEditor.Tests/Model/IO/Import/TextImportSchemeTests.cs b/AudioCuesheetEditor.Tests/Model/IO/Import/TextImportSchemeTests.cs new file mode 100644 index 00000000..17be72f0 --- /dev/null +++ b/AudioCuesheetEditor.Tests/Model/IO/Import/TextImportSchemeTests.cs @@ -0,0 +1,168 @@ +//This file is part of AudioCuesheetEditor. + +//AudioCuesheetEditor is free software: you can redistribute it and/or modify +//it under the terms of the GNU General Public License as published by +//the Free Software Foundation, either version 3 of the License, or +//(at your option) any later version. + +//AudioCuesheetEditor is distributed in the hope that it will be useful, +//but WITHOUT ANY WARRANTY; without even the implied warranty of +//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//GNU General Public License for more details. + +//You should have received a copy of the GNU General Public License +//along with Foobar. If not, see +//. +using AudioCuesheetEditor.Model.Entity; +using AudioCuesheetEditor.Model.IO.Import; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.Linq; + +namespace AudioCuesheetEditor.Tests.Model.IO.Import +{ + [TestClass()] + public class TextImportSchemeTests + { + [TestMethod] + public void Validate_SchemeCuesheet_WithValidPlaceholders_ShouldReturnSuccess() + { + // Arrange + var scheme = new TextImportScheme + { + SchemeCuesheet = "(?'Artist'.+) - (?'Title'.+)\\t+(?'Audiofile'.+)" + }; + + // Act + var result = scheme.Validate(nameof(TextImportScheme.SchemeCuesheet)); + + // Assert + Assert.AreEqual(ValidationStatus.Success, result.Status); + Assert.AreEqual(0, result.ValidationMessages.Count); + } + + [TestMethod] + public void Validate_SchemeCuesheet_WithSimplePlaceholders_ShouldReturnSuccess() + { + // Arrange + var scheme = new TextImportScheme + { + SchemeCuesheet = "Artist - Title" + }; + + // Act + var result = scheme.Validate(nameof(TextImportScheme.SchemeCuesheet)); + + // Assert + Assert.AreEqual(ValidationStatus.Success, result.Status); + Assert.AreEqual(0, result.ValidationMessages.Count); + } + + [TestMethod] + public void Validate_SchemeCuesheet_WithoutPlaceholders_ShouldReturnError() + { + // Arrange + var scheme = new TextImportScheme + { + SchemeCuesheet = "InvalidPattern" + }; + + // Act + var result = scheme.Validate(nameof(TextImportScheme.SchemeCuesheet)); + + // Assert + Assert.AreEqual(ValidationStatus.Error, result.Status); + Assert.AreEqual(1, result.ValidationMessages.Count); + var message = result.ValidationMessages.Single(); + Assert.AreEqual("{0} contains no placeholder!", message.Message); + Assert.AreEqual(nameof(TextImportScheme.SchemeCuesheet), message.Parameter?.FirstOrDefault()); + } + + [TestMethod] + public void Validate_SchemeTracks_WithValidPlaceholders_ShouldReturnSuccess() + { + // Arrange + var scheme = new TextImportScheme + { + SchemeTracks = "(?'Artist'.+) - (?'Title'.+)(?:...\\t)(?'End'.+)" + }; + + // Act + var result = scheme.Validate(nameof(TextImportScheme.SchemeTracks)); + + // Assert + Assert.AreEqual(ValidationStatus.Success, result.Status); + Assert.AreEqual(0, result.ValidationMessages.Count); + } + + [TestMethod] + public void Validate_SchemeTracks_WithSimplePlaceholders_ShouldReturnSuccess() + { + // Arrange + var scheme = new TextImportScheme + { + SchemeTracks = "Artist - Title\tEnd" + }; + + // Act + var result = scheme.Validate(nameof(TextImportScheme.SchemeTracks)); + + // Assert + Assert.AreEqual(ValidationStatus.Success, result.Status); + Assert.AreEqual(0, result.ValidationMessages.Count); + } + + [TestMethod] + public void Validate_SchemeTracks_WithoutPlaceholders_ShouldReturnError() + { + // Arrange + var scheme = new TextImportScheme + { + SchemeTracks = "InvalidPattern" + }; + + // Act + var result = scheme.Validate(nameof(TextImportScheme.SchemeTracks)); + + // Assert + Assert.AreEqual(ValidationStatus.Error, result.Status); + Assert.AreEqual(1, result.ValidationMessages.Count); + var message = result.ValidationMessages.Single(); + Assert.AreEqual("{0} contains no placeholder!", message.Message); + Assert.AreEqual(nameof(TextImportScheme.SchemeTracks), message.Parameter?.FirstOrDefault()); + } + + [TestMethod] + public void Validate_SchemeCuesheetEmpty_ShouldReturnSuccess() + { + // Arrange + var scheme = new TextImportScheme + { + SchemeCuesheet = string.Empty + }; + + // Act + var result = scheme.Validate(nameof(TextImportScheme.SchemeCuesheet)); + + // Assert + Assert.AreEqual(ValidationStatus.Success, result.Status); + Assert.AreEqual(0, result.ValidationMessages.Count); + } + + [TestMethod] + public void Validate_SchemeTrackstEmpty_ShouldReturnSuccess() + { + // Arrange + var scheme = new TextImportScheme + { + SchemeTracks = string.Empty + }; + + // Act + var result = scheme.Validate(nameof(TextImportScheme.SchemeTracks)); + + // Assert + Assert.AreEqual(ValidationStatus.Success, result.Status); + Assert.AreEqual(0, result.ValidationMessages.Count); + } + } +} diff --git a/AudioCuesheetEditor.Tests/Properties/Resources.Designer.cs b/AudioCuesheetEditor.Tests/Properties/Resources.Designer.cs index c8637f77..20d2a794 100644 --- a/AudioCuesheetEditor.Tests/Properties/Resources.Designer.cs +++ b/AudioCuesheetEditor.Tests/Properties/Resources.Designer.cs @@ -90,16 +90,6 @@ internal static byte[] Playlist_Bug_57 { } } - /// - /// Sucht eine lokalisierte Ressource vom Typ System.Byte[]. - /// - internal static byte[] Sample_Inputfile { - get { - object obj = ResourceManager.GetObject("Sample_Inputfile", resourceCulture); - return ((byte[])(obj)); - } - } - /// /// Sucht eine lokalisierte Ressource vom Typ System.Byte[]. /// @@ -135,17 +125,7 @@ internal static byte[] Textimport_Bug_54 { /// internal static byte[] Textimport_with_Cuesheetdata { get { - object obj = ResourceManager.GetObject("Textimport with Cuesheetdata", resourceCulture); - return ((byte[])(obj)); - } - } - - /// - /// Sucht eine lokalisierte Ressource vom Typ System.Byte[]. - /// - internal static byte[] Traktor_Export { - get { - object obj = ResourceManager.GetObject("Traktor Export", resourceCulture); + object obj = ResourceManager.GetObject("Textimport_with_Cuesheetdata", resourceCulture); return ((byte[])(obj)); } } diff --git a/AudioCuesheetEditor.Tests/Properties/Resources.resx b/AudioCuesheetEditor.Tests/Properties/Resources.resx index 0370cfee..243026f0 100644 --- a/AudioCuesheetEditor.Tests/Properties/Resources.resx +++ b/AudioCuesheetEditor.Tests/Properties/Resources.resx @@ -136,13 +136,7 @@ ..\Resources\Textimport_Bug_#233.txt;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - ..\Resources\Traktor Export.html;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - + ..\Resources\Textimport with Cuesheetdata.txt;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - ..\Resources\Sample_Inputfile.txt;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - \ No newline at end of file diff --git a/AudioCuesheetEditor.Tests/Resources/Textimport with Cuesheetdata.txt b/AudioCuesheetEditor.Tests/Resources/Textimport with Cuesheetdata.txt index f476fd54..754820c5 100644 --- a/AudioCuesheetEditor.Tests/Resources/Textimport with Cuesheetdata.txt +++ b/AudioCuesheetEditor.Tests/Resources/Textimport with Cuesheetdata.txt @@ -37,4 +37,4 @@ Solitek - Instinct (Original Mix) 03:00:57 Veerus - Heavy 03:05:19 Secret Cinema & Reinier Zonneveld - Pain Thing (Original Mix) 03:09:38 Amelie Lens - Hypnotized 03:13:13 -Nikolay Kirov - Chasing the Sun (Original Mix) +Nikolay Kirov - Chasing the Sun (Original Mix) diff --git a/AudioCuesheetEditor.Tests/Resources/Traktor Export.html b/AudioCuesheetEditor.Tests/Resources/Traktor Export.html deleted file mode 100644 index bdc497c7..00000000 --- a/AudioCuesheetEditor.Tests/Resources/Traktor Export.html +++ /dev/null @@ -1,306 +0,0 @@ - - - - - - - - Track List HISTORY - - - - -

Track List: HISTORY

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Num.ArtistTitleStart Time
1NachapGlass2025/1/29 18:52:10
2Progressive + Melodic DECK2025/1/29 19:04:06
3Nomer 21Depersonalization2025/1/29 19:05:41
4SevenEver, Nopopstar, 2JOHN'S & Eugene JayShowing Off2025/1/29 19:11:24
5Carlo WhaleUnconscious2025/1/29 19:16:12
6Arba HanTimelaps2025/1/29 19:21:47
7SavillEnergy Surrounds2025/1/29 19:25:23
8TeklixThe Tribal Code2025/1/29 19:32:40
9NeuralisI'm Looking for Answers2025/1/29 19:41:03
10Nopopstar & ArsiaDirty Moves2025/1/29 19:46:44
11SevenEver, Nopopstar, 2JOHN'S & Eugene JayLost (Maze 28 Remix)2025/1/29 19:52:34
12SevenEver, Nopopstar, 2JOHN'S & Eugene JayLost (Redspace Remix)2025/1/29 19:57:27
13Gadoz5d2025/1/29 20:00:32
14DJ DanzikOut of Space2025/1/29 20:08:44
15Enis ÇobanInternet2025/1/29 20:11:32
16Cold Mind & Alex YikkerRage2025/1/29 20:17:24
17Maze 28Sol (JAHAYA Remix)2025/1/29 20:23:43
18Alex GraftonHi Baby2025/1/29 20:25:01
19Che&Mos & Halo FarDaddy2025/1/29 20:34:23
20K KARDENAcid Rain2025/1/29 20:37:32
21Dobrov & Gar1ssonAnalogic (Redspace Remix)2025/1/29 20:40:50
22GazfluzVargan2025/1/29 20:48:50
23SHKAPOVControl2025/1/29 20:52:49
24QazaQOn the Line2025/1/29 20:58:57
25Alex SchaufelElizabeth (Larsun Hesh Remix)2025/1/29 21:01:19
26OiroJust Business2025/1/29 21:06:28
27MolexMind Split (Redspace Remix)2025/1/29 21:11:32
28SOLIGive Me Your Mind2025/1/29 21:19:58
29MANDUJacky2025/1/29 21:25:58
30SOLISpacetoon2025/1/29 21:31:15
31Sasha FashionMoqton2025/1/29 21:33:41
32NAASAPoison2025/1/29 21:42:28
33Nopopstar, 2JOHN'S & Eugene JayNightlong2025/1/29 21:48:55
34Skillz jayChoir2025/1/29 21:51:24
35KovaxController2025/1/29 21:57:59
36MumboiJust a Beat2025/1/29 22:00:41
37EcleptSprut2025/1/29 22:07:28
38RudenskyDark Escort2025/1/29 22:12:45
39Alexey Union, Kinky Sound & KOCHETOVConnected2025/1/29 22:17:12
40ANMA (MD)Space Yoda (Snyl Remix)2025/1/29 22:24:40
41InacheAndale (MONTA (TN) Remix)2025/1/29 22:30:03
- - - diff --git a/AudioCuesheetEditor.Tests/Resources/Sample_Inputfile.txt b/AudioCuesheetEditor.Tests/Sample_Inputfile.txt similarity index 100% rename from AudioCuesheetEditor.Tests/Resources/Sample_Inputfile.txt rename to AudioCuesheetEditor.Tests/Sample_Inputfile.txt diff --git a/AudioCuesheetEditor.End2EndTests/Sample_Inputfile2.txt b/AudioCuesheetEditor.Tests/Sample_Inputfile2.txt similarity index 100% rename from AudioCuesheetEditor.End2EndTests/Sample_Inputfile2.txt rename to AudioCuesheetEditor.Tests/Sample_Inputfile2.txt diff --git a/AudioCuesheetEditor.Tests/Services/IO/CuesheetImportServiceTests.cs b/AudioCuesheetEditor.Tests/Services/IO/CuesheetImportServiceTests.cs index 03f2ae32..2b373da0 100644 --- a/AudioCuesheetEditor.Tests/Services/IO/CuesheetImportServiceTests.cs +++ b/AudioCuesheetEditor.Tests/Services/IO/CuesheetImportServiceTests.cs @@ -18,6 +18,7 @@ using AudioCuesheetEditor.Tests.Properties; using Microsoft.VisualStudio.TestTools.UnitTesting; using System; +using System.Collections.Generic; using System.IO; using System.Linq; @@ -30,43 +31,46 @@ public class CuesheetImportServiceTests public void Analyse_WithSampleCuesheet_CreatesValidCuesheet() { // Arrange - var fileContent = @"PERFORMER ""Sample CD Artist"" -TITLE ""Sample CD Title"" -FILE ""AC DC - TNT.mp3"" MP3 -CDTEXTFILE ""Testfile.cdt"" -CATALOG 0123456789012 -TRACK 01 AUDIO - PERFORMER ""Sample Artist 1"" - TITLE ""Sample Title 1"" - INDEX 01 00:00:00 -TRACK 02 AUDIO - PERFORMER ""Sample Artist 2"" - TITLE ""Sample Title 2"" - INDEX 01 05:00:00 -TRACK 03 AUDIO - PERFORMER ""Sample Artist 3"" - TITLE ""Sample Title 3"" - INDEX 01 09:23:00 -TRACK 04 AUDIO - PERFORMER ""Sample Artist 4"" - TITLE ""Sample Title 4"" - INDEX 01 15:54:00 -TRACK 05 AUDIO - PERFORMER ""Sample Artist 5"" - TITLE ""Sample Title 5"" - INDEX 01 20:13:00 -TRACK 06 AUDIO - PERFORMER ""Sample Artist 6"" - TITLE ""Sample Title 6"" - INDEX 01 24:54:00 -TRACK 07 AUDIO - PERFORMER ""Sample Artist 7"" - TITLE ""Sample Title 7"" - INDEX 01 31:54:00 -TRACK 08 AUDIO - PERFORMER ""Sample Artist 8"" - TITLE ""Sample Title 8"" - INDEX 01 45:51:00"; + var fileContent = new List + { + "PERFORMER \"Sample CD Artist\"", + "TITLE \"Sample CD Title\"", + "FILE \"AC DC - TNT.mp3\" MP3", + "CDTEXTFILE \"Testfile.cdt\"", + "CATALOG 0123456789012", + "TRACK 01 AUDIO", + " PERFORMER \"Sample Artist 1\"", + " TITLE \"Sample Title 1\"", + " INDEX 01 00:00:00", + "TRACK 02 AUDIO", + " PERFORMER \"Sample Artist 2\"", + " TITLE \"Sample Title 2\"", + " INDEX 01 05:00:00", + "TRACK 03 AUDIO", + " PERFORMER \"Sample Artist 3\"", + " TITLE \"Sample Title 3\"", + " INDEX 01 09:23:00", + "TRACK 04 AUDIO", + " PERFORMER \"Sample Artist 4\"", + " TITLE \"Sample Title 4\"", + " INDEX 01 15:54:00", + "TRACK 05 AUDIO", + " PERFORMER \"Sample Artist 5\"", + " TITLE \"Sample Title 5\"", + " INDEX 01 20:13:00", + "TRACK 06 AUDIO", + " PERFORMER \"Sample Artist 6\"", + " TITLE \"Sample Title 6\"", + " INDEX 01 24:54:00", + "TRACK 07 AUDIO", + " PERFORMER \"Sample Artist 7\"", + " TITLE \"Sample Title 7\"", + " INDEX 01 31:54:00", + "TRACK 08 AUDIO", + " PERFORMER \"Sample Artist 8\"", + " TITLE \"Sample Title 8\"", + " INDEX 01 45:51:00" + }; // Act var importFile = CuesheetImportService.Analyse(fileContent); // Assert @@ -74,17 +78,15 @@ TRACK 08 AUDIO Assert.IsNull(importFile.AnalyseException); Assert.IsNotNull(importFile.AnalysedCuesheet); Assert.AreEqual(8, importFile.AnalysedCuesheet.Tracks.Count); - Assert.IsNotNull(importFile.FileContentRecognized); - var lines = importFile.FileContentRecognized.Split(Environment.NewLine); - Assert.AreEqual(string.Format(CuesheetConstants.RecognizedMarkHTML, "PERFORMER \"Sample CD Artist\""), lines.ElementAt(0)); - Assert.AreEqual(string.Format(CuesheetConstants.RecognizedMarkHTML, "TITLE \"Sample CD Title\""), lines.ElementAt(1)); - Assert.AreEqual(string.Format(CuesheetConstants.RecognizedMarkHTML, "FILE \"AC DC - TNT.mp3\" MP3"), lines.ElementAt(2)); - Assert.AreEqual(string.Format(CuesheetConstants.RecognizedMarkHTML, "CDTEXTFILE \"Testfile.cdt\""), lines.ElementAt(3)); - Assert.AreEqual(string.Format(CuesheetConstants.RecognizedMarkHTML, "CATALOG 0123456789012"), lines.ElementAt(4)); - Assert.AreEqual(string.Format(CuesheetConstants.RecognizedMarkHTML, "TRACK 01 AUDIO"), lines.ElementAt(5)); - Assert.AreEqual(string.Format(" {0}", string.Format(CuesheetConstants.RecognizedMarkHTML, "PERFORMER \"Sample Artist 1\"")), lines.ElementAt(6)); - Assert.AreEqual(string.Format(" {0}", string.Format(CuesheetConstants.RecognizedMarkHTML, "TITLE \"Sample Title 1\"")), lines.ElementAt(7)); - Assert.AreEqual(string.Format(" {0}", string.Format(CuesheetConstants.RecognizedMarkHTML, "INDEX 01 00:00:00")), lines.ElementAt(8)); + Assert.AreEqual(string.Format(CuesheetConstants.RecognizedMarkHTML, "PERFORMER \"Sample CD Artist\""), importFile.FileContentRecognized?.ElementAt(0)); + Assert.AreEqual(string.Format(CuesheetConstants.RecognizedMarkHTML, "TITLE \"Sample CD Title\""), importFile.FileContentRecognized?.ElementAt(1)); + Assert.AreEqual(string.Format(CuesheetConstants.RecognizedMarkHTML, "FILE \"AC DC - TNT.mp3\" MP3"), importFile.FileContentRecognized?.ElementAt(2)); + Assert.AreEqual(string.Format(CuesheetConstants.RecognizedMarkHTML, "CDTEXTFILE \"Testfile.cdt\""), importFile.FileContentRecognized?.ElementAt(3)); + Assert.AreEqual(string.Format(CuesheetConstants.RecognizedMarkHTML, "CATALOG 0123456789012"), importFile.FileContentRecognized?.ElementAt(4)); + Assert.AreEqual(string.Format(CuesheetConstants.RecognizedMarkHTML, "TRACK 01 AUDIO"), importFile.FileContentRecognized?.ElementAt(5)); + Assert.AreEqual(string.Format(" {0}", string.Format(CuesheetConstants.RecognizedMarkHTML, "PERFORMER \"Sample Artist 1\"")), importFile.FileContentRecognized?.ElementAt(6)); + Assert.AreEqual(string.Format(" {0}", string.Format(CuesheetConstants.RecognizedMarkHTML, "TITLE \"Sample Title 1\"")), importFile.FileContentRecognized?.ElementAt(7)); + Assert.AreEqual(string.Format(" {0}", string.Format(CuesheetConstants.RecognizedMarkHTML, "INDEX 01 00:00:00")), importFile.FileContentRecognized?.ElementAt(8)); } [TestMethod()] @@ -93,7 +95,12 @@ public void Analyse_WithCuesheetBug30_CreatesValidCuesheet() //Arrange var textImportMemoryStream = new MemoryStream(Resources.Playlist_Bug_30); using var reader = new StreamReader(textImportMemoryStream); - var fileContent = reader.ReadToEnd(); + List lines = []; + while (reader.EndOfStream == false) + { + lines.Add(reader.ReadLine()); + } + var fileContent = lines.AsReadOnly(); //Act var importFile = CuesheetImportService.Analyse(fileContent); //Assert @@ -107,7 +114,12 @@ public void Analyse_WithCuesheetBug57_CreatesValidCuesheet() //Arrange var textImportMemoryStream = new MemoryStream(Resources.Playlist_Bug_57); using var reader = new StreamReader(textImportMemoryStream); - var fileContent = reader.ReadToEnd(); + List lines = []; + while (reader.EndOfStream == false) + { + lines.Add(reader.ReadLine()); + } + var fileContent = lines.AsReadOnly(); //Act var importFile = CuesheetImportService.Analyse(fileContent); //Assert @@ -123,7 +135,12 @@ public void Analyse_WithCuesheetBug36_CreatesValidCuesheet() //Arrange var textImportMemoryStream = new MemoryStream(Resources.Playlist__36_Frames); using var reader = new StreamReader(textImportMemoryStream); - var fileContent = reader.ReadToEnd(); + List lines = []; + while (reader.EndOfStream == false) + { + lines.Add(reader.ReadLine()); + } + var fileContent = lines.AsReadOnly(); //Act var importFile = CuesheetImportService.Analyse(fileContent); //Assert @@ -137,56 +154,57 @@ public void Analyse_WithCuesheetBug36_CreatesValidCuesheet() public void Analyse_WithCDTextFileCatalogueNumberAndPreAndPostGap_CreatesValidCuesheet() { // Arrange - var fileContent = @"PERFORMER ""Sample CD Artist"" -TITLE ""Sample CD Title"" -FILE ""AC DC - TNT.mp3"" MP3 -CDTEXTFILE ""Testfile.cdt"" -CATALOG 0123456789012 -TRACK 01 AUDIO - PERFORMER ""Sample Artist 1"" - TITLE ""Sample Title 1"" - FLAGS 4CH DCP PRE SCMS - INDEX 01 00:00:00 -TRACK 02 AUDIO - PERFORMER ""Sample Artist 2"" - TITLE ""Sample Title 2"" - FLAGS DCP PRE - INDEX 01 05:00:00 -TRACK 03 AUDIO - PERFORMER ""Sample Artist 3"" - TITLE ""Sample Title 3"" - INDEX 01 09:23:00 -TRACK 04 AUDIO - PERFORMER ""Sample Artist 4"" - TITLE ""Sample Title 4"" - INDEX 01 15:54:00 -TRACK 05 AUDIO - PERFORMER ""Sample Artist 5"" - TITLE ""Sample Title 5"" - INDEX 01 20:13:00 - POSTGAP 00:02:00 -TRACK 06 AUDIO - PERFORMER ""Sample Artist 6"" - TITLE ""Sample Title 6"" - INDEX 01 24:54:00 -TRACK 07 AUDIO - PERFORMER ""Sample Artist 7"" - TITLE ""Sample Title 7"" - PREGAP 00:04:00 - INDEX 01 31:54:00 -TRACK 08 AUDIO - PERFORMER ""Sample Artist 8"" - TITLE ""Sample Title 8"" - INDEX 01 45:51:00"; + var fileContent = new List + { + "PERFORMER \"Sample CD Artist\"", + "TITLE \"Sample CD Title\"", + "FILE \"AC DC - TNT.mp3\" MP3", + "CDTEXTFILE \"Testfile.cdt\"", + "CATALOG 0123456789012", + "TRACK 01 AUDIO", + " PERFORMER \"Sample Artist 1\"", + " TITLE \"Sample Title 1\"", + " FLAGS 4CH DCP PRE SCMS", + " INDEX 01 00:00:00", + "TRACK 02 AUDIO", + " PERFORMER \"Sample Artist 2\"", + " TITLE \"Sample Title 2\"", + " FLAGS DCP PRE", + " INDEX 01 05:00:00", + "TRACK 03 AUDIO", + " PERFORMER \"Sample Artist 3\"", + " TITLE \"Sample Title 3\"", + " INDEX 01 09:23:00", + "TRACK 04 AUDIO", + " PERFORMER \"Sample Artist 4\"", + " TITLE \"Sample Title 4\"", + " INDEX 01 15:54:00", + "TRACK 05 AUDIO", + " PERFORMER \"Sample Artist 5\"", + " TITLE \"Sample Title 5\"", + " INDEX 01 20:13:00", + " POSTGAP 00:02:00", + "TRACK 06 AUDIO", + " PERFORMER \"Sample Artist 6\"", + " TITLE \"Sample Title 6\"", + " INDEX 01 24:54:00", + "TRACK 07 AUDIO", + " PERFORMER \"Sample Artist 7\"", + " TITLE \"Sample Title 7\"", + " PREGAP 00:04:00", + " INDEX 01 31:54:00", + "TRACK 08 AUDIO", + " PERFORMER \"Sample Artist 8\"", + " TITLE \"Sample Title 8\"", + " INDEX 01 45:51:00" + }; // Act var importFile = CuesheetImportService.Analyse(fileContent); // Assert Assert.IsNull(importFile.AnalyseException); Assert.IsNotNull(importFile.AnalysedCuesheet); - Assert.IsNotNull(importFile.FileContentRecognized); - var lines = importFile.FileContentRecognized.Split(Environment.NewLine); - Assert.AreEqual(string.Format(" {0}", string.Format(CuesheetConstants.RecognizedMarkHTML, "FLAGS 4CH DCP PRE SCMS")), lines.ElementAt(8)); - Assert.AreEqual(string.Format(" {0}", string.Format(CuesheetConstants.RecognizedMarkHTML, "PREGAP 00:04:00")), lines.ElementAt(35)); + Assert.AreEqual(string.Format(" {0}", string.Format(CuesheetConstants.RecognizedMarkHTML, "FLAGS 4CH DCP PRE SCMS")), importFile.FileContentRecognized?.ElementAt(8)); + Assert.AreEqual(string.Format(" {0}", string.Format(CuesheetConstants.RecognizedMarkHTML, "PREGAP 00:04:00")), importFile.FileContentRecognized?.ElementAt(35)); Assert.AreEqual(8, importFile.AnalysedCuesheet.Tracks.Count); Assert.IsNotNull(importFile.AnalysedCuesheet.CDTextfile); Assert.AreEqual(4, importFile.AnalysedCuesheet.Tracks.ElementAt(0).Flags.Count()); diff --git a/AudioCuesheetEditor.Tests/Services/IO/FileInputManagerTests.cs b/AudioCuesheetEditor.Tests/Services/IO/FileInputManagerTests.cs index f78b881c..2b293be9 100644 --- a/AudioCuesheetEditor.Tests/Services/IO/FileInputManagerTests.cs +++ b/AudioCuesheetEditor.Tests/Services/IO/FileInputManagerTests.cs @@ -13,7 +13,6 @@ //You should have received a copy of the GNU General Public License //along with Foobar. If not, see //. -using AudioCuesheetEditor.Model.IO.Audio; using AudioCuesheetEditor.Services.IO; using Microsoft.AspNetCore.Components.Forms; using Microsoft.Extensions.Logging; @@ -38,7 +37,7 @@ public void CheckFileMimeType_ReturnsTrue_WhenContentTypeDoesNotMatchButExtensio var manager = new FileInputManager(jsRuntimeMock.Object, httpClientMock.Object, loggerMock.Object); // Act - var result = manager.CheckFileMimeType(file, "audio/mpeg", [".mp3"]); + var result = manager.CheckFileMimeType(file, "audio/mpeg", ".mp3"); // Assert Assert.IsTrue(result); @@ -55,7 +54,7 @@ public void CheckFileMimeType_ReturnsTrue_WhenContentTypeDoesMatchButNotExtensio var manager = new FileInputManager(jsRuntimeMock.Object, httpClientMock.Object, loggerMock.Object); // Act - var result = manager.CheckFileMimeType(file, "audio/mpeg", [".mp3", ".txt"]); + var result = manager.CheckFileMimeType(file, "audio/mpeg", ".mp3"); // Assert Assert.IsTrue(result); @@ -72,7 +71,7 @@ public void CheckFileMimeType_ReturnsFalse_WhenExtensionDoesNotMatchAndContentTy var manager = new FileInputManager(jsRuntimeMock.Object, httpClientMock.Object, loggerMock.Object); // Act - var result = manager.CheckFileMimeType(file, "audio/flac", [".mp3"]); + var result = manager.CheckFileMimeType(file, "audio/flac", ".mp3"); // Assert Assert.IsFalse(result); @@ -89,150 +88,12 @@ public void CheckFileMimeType_ReturnsTrue_WhenContentTypeAndExtensionMatch() var manager = new FileInputManager(jsRuntimeMock.Object, httpClientMock.Object, loggerMock.Object); // Act - var result = manager.CheckFileMimeType(file, "audio/wave", [".wav"]); + var result = manager.CheckFileMimeType(file, "audio/wave", ".wav"); // Assert Assert.IsTrue(result); } - [TestMethod()] - public void CheckFileMimeType_ReturnsTrue_WhenContentMainTypeMatch() - { - // Arrange - var jsRuntimeMock = new Mock(); - var httpClientMock = new Mock(); - var loggerMock = new Mock>(); - var file = CreateBrowserFile("history.txt", "text/plain"); - var manager = new FileInputManager(jsRuntimeMock.Object, httpClientMock.Object, loggerMock.Object); - - // Act - var result = manager.CheckFileMimeType(file, "text/*", [".txt", ".text"]); - - // Assert - Assert.IsTrue(result); - } - - [TestMethod()] - public void IsValidAudiofile_ReturnsTrue_WithValidAudiocodec() - { - // Arrange - var jsRuntimeMock = new Mock(); - var httpClientMock = new Mock(); - var loggerMock = new Mock>(); - var file = CreateBrowserFile("test.wav", "audio/wav"); - var manager = new FileInputManager(jsRuntimeMock.Object, httpClientMock.Object, loggerMock.Object); - - // Act - var result = manager.IsValidAudiofile(file); - - // Assert - Assert.IsTrue(result); - } - - [TestMethod()] - public void IsValidAudiofile_ReturnsFalse_WithInvalidAudiocodecAndExtension() - { - // Arrange - var jsRuntimeMock = new Mock(); - var httpClientMock = new Mock(); - var loggerMock = new Mock>(); - var file = CreateBrowserFile("test.mock", "just a fantasy"); - var manager = new FileInputManager(jsRuntimeMock.Object, httpClientMock.Object, loggerMock.Object); - - // Act - var result = manager.IsValidAudiofile(file); - - // Assert - Assert.IsFalse(result); - } - - [TestMethod()] - public void GetAudioCodec_ReturnsAudiocodec_WhenContentTypeMatches() - { - // Arrange - var jsRuntimeMock = new Mock(); - var httpClientMock = new Mock(); - var loggerMock = new Mock>(); - var file = CreateBrowserFile("test.wbem", "audio/webm"); - var manager = new FileInputManager(jsRuntimeMock.Object, httpClientMock.Object, loggerMock.Object); - - // Act - var result = manager.GetAudioCodec(file); - - // Assert - Assert.IsNotNull(result); - Assert.AreEqual(Audiofile.AudioCodecWEBM, result); - } - - [TestMethod()] - public void GetAudioCodec_ReturnsAudiocodec_WhenContentTypeAndFileExtensionMatches() - { - // Arrange - var jsRuntimeMock = new Mock(); - var httpClientMock = new Mock(); - var loggerMock = new Mock>(); - var file = CreateBrowserFile("test.webm", "audio/webm"); - var manager = new FileInputManager(jsRuntimeMock.Object, httpClientMock.Object, loggerMock.Object); - - // Act - var result = manager.GetAudioCodec(file); - - // Assert - Assert.IsNotNull(result); - Assert.AreEqual(Audiofile.AudioCodecWEBM, result); - } - - [TestMethod()] - public void GetAudioCodec_ReturnsNull_WhenContentTypeAndFileExtensionNotMatch() - { - // Arrange - var jsRuntimeMock = new Mock(); - var httpClientMock = new Mock(); - var loggerMock = new Mock>(); - var file = CreateBrowserFile("test.acx", "fantasy stuff"); - var manager = new FileInputManager(jsRuntimeMock.Object, httpClientMock.Object, loggerMock.Object); - - // Act - var result = manager.GetAudioCodec(file); - - // Assert - Assert.IsNull(result); - } - - [TestMethod()] - public void IsValidForImportView_ReturnsTrue_WhenFileIsHtml() - { - // Arrange - var jsRuntimeMock = new Mock(); - var httpClientMock = new Mock(); - var loggerMock = new Mock>(); - var file = CreateBrowserFile("test.html", "text/html"); - var manager = new FileInputManager(jsRuntimeMock.Object, httpClientMock.Object, loggerMock.Object); - - // Act - var result = manager.IsValidForImportView(file); - - // Assert - Assert.IsTrue(result); - } - - [TestMethod()] - public void IsValidForImportView_ReturnsFalse_WhenFileIsBinary() - { - // Arrange - var jsRuntimeMock = new Mock(); - var httpClientMock = new Mock(); - var loggerMock = new Mock>(); - var file = CreateBrowserFile("test.dat", "application/octet-stream"); - var manager = new FileInputManager(jsRuntimeMock.Object, httpClientMock.Object, loggerMock.Object); - - // Act - var result = manager.IsValidForImportView(file); - - // Assert - Assert.IsFalse(result); - } - private static IBrowserFile CreateBrowserFile(string name, string contentType) { var fileMock = new Mock(); diff --git a/AudioCuesheetEditor.Tests/Services/IO/ImportManagerTests.cs b/AudioCuesheetEditor.Tests/Services/IO/ImportManagerTests.cs index 1dcf6fb8..08b24803 100644 --- a/AudioCuesheetEditor.Tests/Services/IO/ImportManagerTests.cs +++ b/AudioCuesheetEditor.Tests/Services/IO/ImportManagerTests.cs @@ -14,17 +14,18 @@ //along with Foobar. If not, see //. -using AudioCuesheetEditor.Model.AudioCuesheet; -using AudioCuesheetEditor.Model.AudioCuesheet.Import; +using AudioCuesheetEditor.Data.Options; using AudioCuesheetEditor.Model.IO.Import; +using AudioCuesheetEditor.Model.Options; +using AudioCuesheetEditor.Model.Utility; using AudioCuesheetEditor.Services.IO; using AudioCuesheetEditor.Services.UI; using AudioCuesheetEditor.Tests.Utility; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; using System; +using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; namespace AudioCuesheetEditor.Tests.Services.IO { @@ -32,86 +33,78 @@ namespace AudioCuesheetEditor.Tests.Services.IO public class ImportManagerTests { [TestMethod()] - public async Task ImportTextAsync_WithAnalysedCuesheet_SetsImportCuesheet() + public void ImportTextAsync_TextfileWithStartDateTime_CreatesValidCuesheet() { // Arrange - var fileContent = "This is just a test"; - var traceChangeManager = new TraceChangeManager(TestHelper.CreateLogger()); - var sessionStateContainer = new SessionStateContainer(traceChangeManager); - var fileInputManagerMock = new Mock(); - var textImportServiceMock = new Mock(); - var importCuesheet = new ImportCuesheet() + var fileContent = new List { - Artist = "Test Cuesheet Artist", - Title = "Test Cuesheet Title", - Audiofile = "Test Cuesheet Audiofile", - Cataloguenumber = "Test Cuesheet Cataloguenumber", - CDTextfile = "Test Cuesheet CDTextfile" + "Innellea~The Golden Fort~02.08.2024 20:10:48", + "Nora En Pure~Diving with Whales (Daniel Portman Remix)~02.08.2024 20:15:21", + "WhoMadeWho & Adriatique~Miracle (RÜFÜS DU SOL Remix)~02.08.2024 20:20:42", + "Ella Wild~Poison D'araignee (Original Mix)~02.08.2024 20:28:03", + "Stil & Bense~On The Edge (Original Mix)~02.08.2024 20:32:42", + "Nebula~Clairvoyant Dreams~02.08.2024 20:39:01", + "Valentina Black~I'm a Tree (Extended Mix)~02.08.2024 20:47:08", + "Nebula~Clairvoyant Dreams~02.08.2024 20:53:20", + "Kiko & Dave Davis feat. Phoebe~Living in Space (Dub Mix)~02.08.2024 20:58:11", + "Lilly Palmer~Before Acid~02.08.2024 21:03:53", + "Sofi Tukker~Drinkee (Vintage Culture & John Summit Extended Mix)~02.08.2024 21:09:52", + "CID & Truth x Lies~Caroline (Extended Mix)~02.08.2024 21:14:09", + "Moby~Why Does My Heart Feel So Bad? (Oxia Remix)~02.08.2024 21:17:15", + "Ammo Avenue~Little Gurl (Extended Mix)~02.08.2024 21:22:46", + "James Hurr & Smokin Jo & Stealth~Beggin' For Change~02.08.2024 21:28:37", + "Kristine Blond~Love Shy (Sam Divine & CASSIMM Extended Remix)~02.08.2024 21:30:47", + "Vanilla Ace~Work On You (Original Mix)~02.08.2024 21:36:28", + "Truth X Lies~Like This~02.08.2024 21:42:05", + "Terri-Anne~Round Round~02.08.2024 21:44:07", + "Joanna Magik~Maneater~02.08.2024 21:46:32", + "Jen Payne & Kevin McKay~Feed Your Soul~02.08.2024 21:48:45", + "Kevin McKay & Eppers & Notelle~On My Own~02.08.2024 21:51:37", + "Nader Razdar & Kevin McKay~Get Ur Freak On (Kevin McKay Extended Mix)~02.08.2024 21:53:49", + "Philip Z~Yala (Extended Mix)~02.08.2024 21:59:40", + "Kyle Kinch & Kevin McKay~Hella~02.08.2024 22:05:53", + "Roze Wild~B-O-D-Y~02.08.2024 22:08:26", + "Jey Kurmis~Snoop~02.08.2024 22:11:09", + "Bootie Brown & Tame Impala & Gorillaz~New Gold (Dom Dolla Remix Extended)~02.08.2024 22:16:23", + "Eli Brown & Love Regenerator~Don't You Want Me (Original Mix)~02.08.2024 22:21:23", + "Local Singles~Voices~02.08.2024 22:25:59" }; - importCuesheet.Tracks.Add(new() - { - Artist = "Test Track Artist 1", - Title = "Test Track Title 1", - Begin = new TimeSpan(0, 3, 20), - End = new TimeSpan(0, 7, 43), - Flags = [Flag.DCP], - Position = 1, - PreGap = new TimeSpan(0, 0, 2), - PostGap = new TimeSpan(0, 0, 4) - }); - var importfile = new Importfile() - { - FileContent = fileContent, - FileType = ImportFileType.Textfile, - AnalysedCuesheet = importCuesheet - }; - textImportServiceMock.Setup(x => x.AnalyseAsync(fileContent)).ReturnsAsync(importfile); - var importManager = new ImportManager(sessionStateContainer, traceChangeManager, fileInputManagerMock.Object, textImportServiceMock.Object); - var testHelper = new TestHelper(); - // Act - await importManager.ImportTextAsync(fileContent); - // Assert - Assert.AreEqual(importfile, sessionStateContainer.Importfile); - Assert.IsNotNull(sessionStateContainer.ImportCuesheet); - Assert.AreEqual(importCuesheet.Artist, sessionStateContainer.ImportCuesheet.Artist); - Assert.AreEqual(importCuesheet.Title, sessionStateContainer.ImportCuesheet.Title); - Assert.IsNotNull(sessionStateContainer.ImportCuesheet.Audiofile); - Assert.AreEqual(importCuesheet.Audiofile, sessionStateContainer.ImportCuesheet.Audiofile.Name); - Assert.AreEqual(importCuesheet.Cataloguenumber, sessionStateContainer.ImportCuesheet.Cataloguenumber); - Assert.IsNotNull(sessionStateContainer.ImportCuesheet.CDTextfile); - Assert.AreEqual(importCuesheet.CDTextfile, sessionStateContainer.ImportCuesheet.CDTextfile.Name); - Assert.AreEqual(importCuesheet.Tracks.First().Artist, sessionStateContainer.ImportCuesheet.Tracks.First().Artist); - Assert.AreEqual(importCuesheet.Tracks.First().Title, sessionStateContainer.ImportCuesheet.Tracks.First().Title); - Assert.AreEqual(importCuesheet.Tracks.First().Begin, sessionStateContainer.ImportCuesheet.Tracks.First().Begin); - Assert.AreEqual(importCuesheet.Tracks.First().End, sessionStateContainer.ImportCuesheet.Tracks.First().End); - CollectionAssert.AreEquivalent(importCuesheet.Tracks.First().Flags.ToList(), sessionStateContainer.ImportCuesheet.Tracks.First().Flags.ToList()); - Assert.AreEqual(importCuesheet.Tracks.First().Position, sessionStateContainer.ImportCuesheet.Tracks.First().Position); - Assert.AreEqual(importCuesheet.Tracks.First().PreGap, sessionStateContainer.ImportCuesheet.Tracks.First().PreGap); - Assert.AreEqual(importCuesheet.Tracks.First().PostGap, sessionStateContainer.ImportCuesheet.Tracks.First().PostGap); - } - [TestMethod()] - public async Task ImportTextAsync_WithoutAnalysedCuesheet_DoesNothing() - { - // Arrange - var fileContent = "This is just a test"; var traceChangeManager = new TraceChangeManager(TestHelper.CreateLogger()); var sessionStateContainer = new SessionStateContainer(traceChangeManager); - var fileInputManagerMock = new Mock(); - var textImportServiceMock = new Mock(); - var importfile = new Importfile() + var localStorageOptionsProviderMock = new Mock(); + var textImportScheme = new TextImportScheme() { - FileContent = fileContent, - FileType = ImportFileType.Textfile, + SchemeCuesheet = null, + SchemeTracks = @"(?'Artist'[a-zA-Z0-9_ .();äöü&:,'*-?:]{1,})~(?'Title'[a-zA-Z0-9_ .();äöü&'*-?:Ü]{1,})~(?'StartDateTime'.{1,})" }; - textImportServiceMock.Setup(x => x.AnalyseAsync(fileContent)).ReturnsAsync(importfile); - var importManager = new ImportManager(sessionStateContainer, traceChangeManager, fileInputManagerMock.Object, textImportServiceMock.Object); + var timeSpanFormat = new TimeSpanFormat(); + var options = new ApplicationOptions(); + localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync()).ReturnsAsync(options); + var fileInputManagerMock = new Mock(); + var importManager = new ImportManager(sessionStateContainer, localStorageOptionsProviderMock.Object, traceChangeManager, fileInputManagerMock.Object); var testHelper = new TestHelper(); // Act - await importManager.ImportTextAsync(fileContent); + importManager.ImportText(fileContent, textImportScheme, timeSpanFormat); // Assert - Assert.AreEqual(importfile, sessionStateContainer.Importfile); - Assert.IsNull(sessionStateContainer.ImportCuesheet); + Assert.IsNull(sessionStateContainer.Importfile?.AnalyseException); + Assert.IsNotNull(sessionStateContainer.ImportCuesheet); + Assert.AreEqual(30, sessionStateContainer.ImportCuesheet.Tracks.Count); + Assert.AreEqual("Innellea", sessionStateContainer.ImportCuesheet.Tracks.ElementAt(0).Artist); + Assert.AreEqual("The Golden Fort", sessionStateContainer.ImportCuesheet.Tracks.ElementAt(0).Title); + Assert.AreEqual(TimeSpan.Zero, sessionStateContainer.ImportCuesheet.Tracks.ElementAt(0).Begin); + Assert.AreEqual(new TimeSpan(0, 4, 33), sessionStateContainer.ImportCuesheet.Tracks.ElementAt(0).End); + Assert.AreEqual(new TimeSpan(0, 4, 33), sessionStateContainer.ImportCuesheet.Tracks.ElementAt(0).Length); + Assert.AreEqual("Nora En Pure", sessionStateContainer.ImportCuesheet.Tracks.ElementAt(1).Artist); + Assert.AreEqual("Diving with Whales (Daniel Portman Remix)", sessionStateContainer.ImportCuesheet.Tracks.ElementAt(1).Title); + Assert.AreEqual(new TimeSpan(0, 4, 33), sessionStateContainer.ImportCuesheet.Tracks.ElementAt(1).Begin); + Assert.AreEqual(new TimeSpan(0, 9, 54), sessionStateContainer.ImportCuesheet.Tracks.ElementAt(1).End); + Assert.AreEqual(new TimeSpan(0, 5, 21), sessionStateContainer.ImportCuesheet.Tracks.ElementAt(1).Length); + Assert.AreEqual("Local Singles", sessionStateContainer.ImportCuesheet.Tracks.ElementAt(29).Artist); + Assert.AreEqual("Voices", sessionStateContainer.ImportCuesheet.Tracks.ElementAt(29).Title); + Assert.AreEqual(new TimeSpan(2, 15, 11), sessionStateContainer.ImportCuesheet.Tracks.ElementAt(29).Begin); + Assert.IsNull(sessionStateContainer.ImportCuesheet.Tracks.ElementAt(29).End); + Assert.IsNull(sessionStateContainer.ImportCuesheet.Tracks.ElementAt(29).Length); } } } \ No newline at end of file diff --git a/AudioCuesheetEditor.Tests/Services/IO/TextImportServiceTests.cs b/AudioCuesheetEditor.Tests/Services/IO/TextImportServiceTests.cs index 19557500..e95cfd6c 100644 --- a/AudioCuesheetEditor.Tests/Services/IO/TextImportServiceTests.cs +++ b/AudioCuesheetEditor.Tests/Services/IO/TextImportServiceTests.cs @@ -13,49 +13,45 @@ //You should have received a copy of the GNU General Public License //along with Foobar. If not, see //. -using AudioCuesheetEditor.Data.Options; using AudioCuesheetEditor.Model.AudioCuesheet; -using AudioCuesheetEditor.Model.AudioCuesheet.Import; using AudioCuesheetEditor.Model.IO.Import; -using AudioCuesheetEditor.Model.Options; using AudioCuesheetEditor.Model.Utility; using AudioCuesheetEditor.Services.IO; using AudioCuesheetEditor.Tests.Properties; using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; using System; +using System.Collections.Generic; using System.IO; using System.Linq; -using System.Threading.Tasks; namespace AudioCuesheetEditor.Tests.Services.IO { [TestClass()] public class TextImportServiceTests { - [TestMethod()] - public async Task AnalyseAsync_SampleCuesheet_CreatesValidCuesheetAsync() + public void Analyse_SampleCuesheet_CreatesValidCuesheet() { // Arrange - var fileContent = @"CuesheetArtist - CuesheetTitle c:\tmp\Testfile.mp3 -Sample Artist 1 - Sample Title 1 00:05:00 -Sample Artist 2 - Sample Title 2 00:09:23 -Sample Artist 3 - Sample Title 3 00:15:54 -Sample Artist 4 - Sample Title 4 00:20:13 -Sample Artist 5 - Sample Title 5 00:24:54 -Sample Artist 6 - Sample Title 6 00:31:54 -Sample Artist 7 - Sample Title 7 00:45:54 -Sample Artist 8 - Sample Title 8 01:15:54"; - var localStorageOptionsProviderMock = new Mock(); - var options = new ApplicationOptions + var fileContent = new List { - SelectedImportProfile = ApplicationOptions.DefaultSelectedImportprofile + "CuesheetArtist - CuesheetTitle c:\\tmp\\Testfile.mp3", + "Sample Artist 1 - Sample Title 1 00:05:00", + "Sample Artist 2 - Sample Title 2 00:09:23", + "Sample Artist 3 - Sample Title 3 00:15:54", + "Sample Artist 4 - Sample Title 4 00:20:13", + "Sample Artist 5 - Sample Title 5 00:24:54", + "Sample Artist 6 - Sample Title 6 00:31:54", + "Sample Artist 7 - Sample Title 7 00:45:54", + "Sample Artist 8 - Sample Title 8 01:15:54" + }; + var textImportScheme = new TextImportScheme() + { + SchemeCuesheet = TextImportScheme.DefaultSchemeCuesheet, + SchemeTracks = TextImportScheme.DefaultSchemeTracks }; - localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync()).ReturnsAsync(options); - var service = new TextImportService(localStorageOptionsProviderMock.Object); // Act - var importfile = await service.AnalyseAsync(fileContent); + var importfile = TextImportService.Analyse(textImportScheme, fileContent); // Assert Assert.IsNull(importfile.AnalyseException); Assert.IsNotNull(importfile.AnalysedCuesheet); @@ -69,65 +65,55 @@ public async Task AnalyseAsync_SampleCuesheet_CreatesValidCuesheetAsync() } [TestMethod()] - public async Task AnalyseAsync_InvalidSchemeTracks_CreatesAnalyseExceptionAsync() + public void Analyse_InvalidSchemeTracks_CreatesAnalyseException() { // Arrange - var fileContent = @"CuesheetArtist|CuesheetTitle c:\tmp\TestTextFile.cdt -1|Sample Artist 1 - Sample Title 1 00:05:00 -2|Sample Artist 2 - Sample Title 2 00:09:23 -3|Sample Artist 3 - Sample Title 3 00:15:54 -4|Sample Artist 4 - Sample Title 4 00:20:13 -5|Sample Artist 5 - Sample Title 5 00:24:54 -6|Sample Artist 6 - Sample Title 6 00:31:54 -7|Sample Artist 7 - Sample Title 7 00:45:54 -8|Sample Artist 8 - Sample Title 8 01:15:54"; - var profile = new Importprofile() + var fileContent = new List { - UseRegularExpression = true, - SchemeCuesheet = @"(?'Cuesheet.Artist'\A.*)[|](?'Cuesheet.Title'\w{1,})\t{1,}(?'Cuesheet.CDTextfile'.{1,})", - SchemeTracks = @"(?'Track.Position'.{1,})|(?'Track.Artist'.{1,}) - (?'Track.Title'[a-zA-Z0-9_ ]{1,})\t{1,}(?'Track.End'.{1,})" + "CuesheetArtist|CuesheetTitle c:\\tmp\\TestTextFile.cdt", + "1|Sample Artist 1 - Sample Title 1 00:05:00", + "2|Sample Artist 2 - Sample Title 2 00:09:23", + "3|Sample Artist 3 - Sample Title 3 00:15:54", + "4|Sample Artist 4 - Sample Title 4 00:20:13", + "5|Sample Artist 5 - Sample Title 5 00:24:54", + "6|Sample Artist 6 - Sample Title 6 00:31:54", + "7|Sample Artist 7 - Sample Title 7 00:45:54", + "8|Sample Artist 8 - Sample Title 8 01:15:54" }; - var localStorageOptionsProviderMock = new Mock(); - var options = new ApplicationOptions + var textImportScheme = new TextImportScheme() { - SelectedImportProfile = profile + SchemeCuesheet = @"(?'Cuesheet.Artist'\A.*)[|](?'Cuesheet.Title'\w{1,})\t{1,}(?'Cuesheet.CDTextfile'.{1,})", + SchemeTracks = @"(?'Track.Position'.{1,})|(?'Track.Artist'.{1,}) - (?'Track.Title'[a-zA-Z0-9_ ]{1,})\t{1,}(?'Track.End'.{1,})" }; - localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync()).ReturnsAsync(options); - var service = new TextImportService(localStorageOptionsProviderMock.Object); // Act - var importfile = await service.AnalyseAsync(fileContent); + var importfile = TextImportService.Analyse(textImportScheme, fileContent); // Assert Assert.IsNotNull(importfile.AnalyseException); } [TestMethod()] - public async Task AnalyseAsync_InputfileWithExtraSeperator_CreatesValidCuesheetAsync() + public void Analyse_InputfileWithExtraSeperator_CreatesValidCuesheet() { // Arrange - var fileContent = @"CuesheetArtist|CuesheetTitle c:\tmp\TestTextFile.cdt -1|Sample Artist 1 - Sample Title 1 00:05:00 -2|Sample Artist 2 - Sample Title 2 00:09:23 -3|Sample Artist 3 - Sample Title 3 00:15:54 -4|Sample Artist 4 - Sample Title 4 00:20:13 -5|Sample Artist 5 - Sample Title 5 00:24:54 -6|Sample Artist 6 - Sample Title 6 00:31:54 -7|Sample Artist 7 - Sample Title 7 00:45:54 -8|Sample Artist 8 - Sample Title 8 01:15:54"; - var profile = new Importprofile() + var fileContent = new List { - UseRegularExpression = true, - SchemeCuesheet = @"(?'Artist'\A.*)[|](?'Title'\w{1,})\t{1,}(?'CDTextfile'[^\r\n]+)", - SchemeTracks = @"(?'Position'\d{1,})[|](?'Artist'.{1,}) - (?'Title'[a-zA-Z0-9_ ]{1,})\t{1,}(?'End'.{1,})" + "CuesheetArtist|CuesheetTitle c:\\tmp\\TestTextFile.cdt", + "1|Sample Artist 1 - Sample Title 1 00:05:00", + "2|Sample Artist 2 - Sample Title 2 00:09:23", + "3|Sample Artist 3 - Sample Title 3 00:15:54", + "4|Sample Artist 4 - Sample Title 4 00:20:13", + "5|Sample Artist 5 - Sample Title 5 00:24:54", + "6|Sample Artist 6 - Sample Title 6 00:31:54", + "7|Sample Artist 7 - Sample Title 7 00:45:54", + "8|Sample Artist 8 - Sample Title 8 01:15:54" }; - var localStorageOptionsProviderMock = new Mock(); - var options = new ApplicationOptions + var textImportScheme = new TextImportScheme() { - SelectedImportProfile = profile + SchemeCuesheet = @"(?'Artist'\A.*)[|](?'Title'\w{1,})\t{1,}(?'CDTextfile'.{1,})", + SchemeTracks = @"(?'Position'\d{1,})[|](?'Artist'.{1,}) - (?'Title'[a-zA-Z0-9_ ]{1,})\t{1,}(?'End'.{1,})" }; - localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync()).ReturnsAsync(options); - var service = new TextImportService(localStorageOptionsProviderMock.Object); // Act - var importfile = await service.AnalyseAsync(fileContent); + var importfile = TextImportService.Analyse(textImportScheme, fileContent); // Assert Assert.IsNull(importfile.AnalyseException); Assert.IsNotNull(importfile.AnalysedCuesheet); @@ -140,34 +126,30 @@ public async Task AnalyseAsync_InputfileWithExtraSeperator_CreatesValidCuesheetA Assert.AreEqual("Sample Title 1", importfile.AnalysedCuesheet.Tracks.ElementAt(0).Title); Assert.AreEqual(new TimeSpan(0, 5, 0), importfile.AnalysedCuesheet.Tracks.ElementAt(0).End); } - + [TestMethod()] - public async Task AnalyseAsync_InputfileWithSimplifiedScheme_CreatesValidCuesheetAsync() + public void Analyse_InputfileWithSimplifiedScheme_CreatesValidCuesheet() { - var fileContent = @"CuesheetArtist|CuesheetTitle c:\tmp\TestTextFile.cdt -1|Sample Artist 1 - Sample Title 1 00:05:00 -2|Sample Artist 2 - Sample Title 2 00:09:23 -3|Sample Artist 3 - Sample Title 3 00:15:54 -4|Sample Artist 4 - Sample Title 4 00:20:13 -5|Sample Artist 5 - Sample Title 5 00:24:54 -6|Sample Artist 6 - Sample Title 6 00:31:54 -7|Sample Artist 7 - Sample Title 7 00:45:54 -8|Sample Artist 8 - Sample Title 8 01:15:54"; - var profile = new Importprofile() + // Arrange + var fileContent = new List { - UseRegularExpression = false, - SchemeCuesheet = @"Artist|Title CDTextfile", - SchemeTracks = @"Position|Artist - Title End" + "CuesheetArtist|CuesheetTitle c:\\tmp\\TestTextFile.cdt", + "1|Sample Artist 1 - Sample Title 1 00:05:00", + "2|Sample Artist 2 - Sample Title 2 00:09:23", + "3|Sample Artist 3 - Sample Title 3 00:15:54", + "4|Sample Artist 4 - Sample Title 4 00:20:13", + "5|Sample Artist 5 - Sample Title 5 00:24:54", + "6|Sample Artist 6 - Sample Title 6 00:31:54", + "7|Sample Artist 7 - Sample Title 7 00:45:54", + "8|Sample Artist 8 - Sample Title 8 01:15:54" }; - var localStorageOptionsProviderMock = new Mock(); - var options = new ApplicationOptions + var textImportScheme = new TextImportScheme() { - SelectedImportProfile = profile + SchemeCuesheet = @"Artist|Title CDTextfile", + SchemeTracks = @"Position|Artist - Title End" }; - localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync()).ReturnsAsync(options); - var service = new TextImportService(localStorageOptionsProviderMock.Object); // Act - var importfile = await service.AnalyseAsync(fileContent); + var importfile = TextImportService.Analyse(textImportScheme, fileContent); // Assert Assert.IsNull(importfile.AnalyseException); Assert.IsNotNull(importfile.AnalysedCuesheet); @@ -182,83 +164,71 @@ public async Task AnalyseAsync_InputfileWithSimplifiedScheme_CreatesValidCueshee } [TestMethod()] - public async Task AnalyseAsync_InvalidScheme_CreatesAnalyseExceptionAsync() + public void Analyse_InvalidScheme_CreatesAnalyseException() { // Arrange - var fileContent = @"CuesheetArtist|CuesheetTitle c:\tmp\TestTextFile.cdt A83412346734 -1|Sample Artist 1 - Sample Title 1 00:05:00 -2|Sample Artist 2 - Sample Title 2 00:09:23 -3|Sample Artist 3 - Sample Title 3 00:15:54 -4|Sample Artist 4 - Sample Title 4 00:20:13 -5|Sample Artist 5 - Sample Title 5 00:24:54 -6|Sample Artist 6 - Sample Title 6 00:31:54 -7|Sample Artist 7 - Sample Title 7 00:45:54 -8|Sample Artist 8 - Sample Title 8 01:15:54 - - - - - - - - -"; - var profile = new Importprofile() + var fileContent = new List + { + "CuesheetArtist|CuesheetTitle c:\\tmp\\TestTextFile.cdt A83412346734", + "1|Sample Artist 1 - Sample Title 1 00:05:00", + "2|Sample Artist 2 - Sample Title 2 00:09:23", + "3|Sample Artist 3 - Sample Title 3 00:15:54", + "4|Sample Artist 4 - Sample Title 4 00:20:13", + "5|Sample Artist 5 - Sample Title 5 00:24:54", + "6|Sample Artist 6 - Sample Title 6 00:31:54", + "7|Sample Artist 7 - Sample Title 7 00:45:54", + "8|Sample Artist 8 - Sample Title 8 01:15:54", + string.Empty, + string.Empty, + string.Empty, + string.Empty, + string.Empty, + string.Empty, + string.Empty, + string.Empty + }; + var textImportScheme = new TextImportScheme() { - UseRegularExpression = true, SchemeCuesheet = @"(?'Cuesheet.Artist'\A.*)[|](?'Cuesheet.Title'\w{1,})\t{1,}(?'Cuesheet.CDTextfile'[a-zA-Z0-9_ .();äöü&:,\\]{1,})\t{1,}(?'Cuesheet.Cataloguenumber'.{1,})", SchemeTracks = @"(?'Track.Position'.{1,})|(?'Track.Artist'.{1,}) - (?'Track.Title'[a-zA-Z0-9_ ]{1,})\t{1,}(?'Track.End'.{1,})" }; - var localStorageOptionsProviderMock = new Mock(); - var options = new ApplicationOptions - { - SelectedImportProfile = profile - }; - localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync()).ReturnsAsync(options); - var service = new TextImportService(localStorageOptionsProviderMock.Object); // Act - var importfile = await service.AnalyseAsync(fileContent); + var importfile = TextImportService.Analyse(textImportScheme, fileContent); // Assert Assert.IsNotNull(importfile.AnalyseException); } [TestMethod()] - public async Task AnalyseAsync_CuesheetWithTextfileAndCatalogueNumber_CreatesValidCuesheetAsync() + public void Analyse_CuesheetWithTextfileAndCatalogueNumber_CreatesValidCuesheet() { // Arrange - var fileContent = @"CuesheetArtist|CuesheetTitle c:\tmp\TestTextFile.cdt A83412346734 -1|Sample Artist 1 - Sample Title 1 00:05:00 -2|Sample Artist 2 - Sample Title 2 00:09:23 -3|Sample Artist 3 - Sample Title 3 00:15:54 -4|Sample Artist 4 - Sample Title 4 00:20:13 -5|Sample Artist 5 - Sample Title 5 00:24:54 -6|Sample Artist 6 - Sample Title 6 00:31:54 -7|Sample Artist 7 - Sample Title 7 00:45:54 -8|Sample Artist 8 - Sample Title 8 01:15:54 - - - - - - - - -"; - var profile = new Importprofile() - { - UseRegularExpression = true, - SchemeCuesheet = @"(?'Artist'\A.*)[|](?'Title'\w{1,})\t{1,}(?'CDTextfile'[a-zA-Z0-9_ .();äöü&:,\\]{1,})\t{1,}(?'Cataloguenumber'[^\r\n]+)", + var fileContent = new List + { + "CuesheetArtist|CuesheetTitle c:\\tmp\\TestTextFile.cdt A83412346734", + "1|Sample Artist 1 - Sample Title 1 00:05:00", + "2|Sample Artist 2 - Sample Title 2 00:09:23", + "3|Sample Artist 3 - Sample Title 3 00:15:54", + "4|Sample Artist 4 - Sample Title 4 00:20:13", + "5|Sample Artist 5 - Sample Title 5 00:24:54", + "6|Sample Artist 6 - Sample Title 6 00:31:54", + "7|Sample Artist 7 - Sample Title 7 00:45:54", + "8|Sample Artist 8 - Sample Title 8 01:15:54", + string.Empty, + string.Empty, + string.Empty, + string.Empty, + string.Empty, + string.Empty, + string.Empty, + string.Empty + }; + var textImportScheme = new TextImportScheme() + { + SchemeCuesheet = @"(?'Artist'\A.*)[|](?'Title'\w{1,})\t{1,}(?'CDTextfile'[a-zA-Z0-9_ .();äöü&:,\\]{1,})\t{1,}(?'Cataloguenumber'.{1,})", SchemeTracks = @"(?'Position'.{1,})[|](?'Artist'.{1,}) - (?'Title'[a-zA-Z0-9_ ]{1,})\t{1,}(?'End'.{1,})" }; - var localStorageOptionsProviderMock = new Mock(); - var options = new ApplicationOptions - { - SelectedImportProfile = profile - }; - localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync()).ReturnsAsync(options); - var service = new TextImportService(localStorageOptionsProviderMock.Object); // Act - var importfile = await service.AnalyseAsync(fileContent); + var importfile = TextImportService.Analyse(textImportScheme, fileContent); // Assert Assert.IsNull(importfile.AnalyseException); Assert.IsNotNull(importfile.AnalysedCuesheet); @@ -274,31 +244,28 @@ public async Task AnalyseAsync_CuesheetWithTextfileAndCatalogueNumber_CreatesVal } [TestMethod()] - public async Task AnalyseAsync_CuesheeTracksOnly_CreatesValidCuesheetAsync() + public void Analyse_CuesheeTracksOnly_CreatesValidCuesheet() { // Arrange - var fileContent = @"Sample Artist 1 - Sample Title 1 00:05:00 -Sample Artist 2 - Sample Title 2 00:09:23 -Sample Artist 3 - Sample Title 3 00:15:54 -Sample Artist 4 - Sample Title 4 00:20:13 -Sample Artist 5 - Sample Title 5 00:24:54 -Sample Artist 6 - Sample Title 6 00:31:54 -Sample Artist 7 - Sample Title 7 00:45:54 -Sample Artist 8 - Sample Title 8 01:15:54 -Sample Artist 9 - Sample Title 9 "; - var profile = new Importprofile() + var fileContent = new List { - SchemeTracks = $"{nameof(ImportTrack.Artist)} - {nameof(ImportTrack.Title)}\t{nameof(ImportTrack.End)}" + "Sample Artist 1 - Sample Title 1 00:05:00", + "Sample Artist 2 - Sample Title 2 00:09:23", + "Sample Artist 3 - Sample Title 3 00:15:54", + "Sample Artist 4 - Sample Title 4 00:20:13", + "Sample Artist 5 - Sample Title 5 00:24:54", + "Sample Artist 6 - Sample Title 6 00:31:54", + "Sample Artist 7 - Sample Title 7 00:45:54", + "Sample Artist 8 - Sample Title 8 01:15:54", + "Sample Artist 9 - Sample Title 9" }; - var localStorageOptionsProviderMock = new Mock(); - var options = new ApplicationOptions + var textImportScheme = new TextImportScheme() { - SelectedImportProfile = profile + SchemeCuesheet = null, + SchemeTracks = TextImportScheme.DefaultSchemeTracks }; - localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync()).ReturnsAsync(options); - var service = new TextImportService(localStorageOptionsProviderMock.Object); // Act - var importfile = await service.AnalyseAsync(fileContent); + var importfile = TextImportService.Analyse(textImportScheme, fileContent); // Assert Assert.IsNull(importfile.AnalyseException); Assert.IsNotNull(importfile.AnalysedCuesheet); @@ -309,26 +276,25 @@ public async Task AnalyseAsync_CuesheeTracksOnly_CreatesValidCuesheetAsync() } [TestMethod()] - public async Task AnalyseAsync_CuesheetBug213_CreatesValidCuesheetAsync() + public void Analyse_CuesheetBug213_CreatesValidCuesheet() { // Arrange var textImportMemoryStream = new MemoryStream(Resources.Textimport_Bug_213); using var reader = new StreamReader(textImportMemoryStream); - var fileContent = reader.ReadToEnd(); - var profile = new Importprofile() + List lines = []; + while (reader.EndOfStream == false) { - SchemeTracks = $"{nameof(ImportTrack.Artist)} - {nameof(ImportTrack.Title)}\t{nameof(ImportTrack.End)}", - TimeSpanFormat = new() { Scheme = $"{nameof(TimeSpanFormat.Minutes)}:{nameof(TimeSpanFormat.Seconds)}" } - }; - var localStorageOptionsProviderMock = new Mock(); - var options = new ApplicationOptions + lines.Add(reader.ReadLine()); + } + var fileContent = lines.AsReadOnly(); + var timeSpanFormat = new TimeSpanFormat() { Scheme = "Minutes:Seconds" }; + var textImportScheme = new TextImportScheme() { - SelectedImportProfile = profile + SchemeCuesheet = null, + SchemeTracks = TextImportScheme.DefaultSchemeTracks }; - localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync()).ReturnsAsync(options); - var service = new TextImportService(localStorageOptionsProviderMock.Object); // Act - var importfile = await service.AnalyseAsync(fileContent); + var importfile = TextImportService.Analyse(textImportScheme, fileContent, timeSpanFormat); // Assert Assert.IsNull(importfile.AnalyseException); Assert.IsNotNull(importfile.AnalysedCuesheet); @@ -337,31 +303,27 @@ public async Task AnalyseAsync_CuesheetBug213_CreatesValidCuesheetAsync() } [TestMethod()] - public async Task AnalyseAsync_CuesheetWithFlags_CreatesValidCuesheetAsync() + public void Analyse_CuesheetWithFlags_CreatesValidCuesheet() { // Arrange - var fileContent = @"Sample Artist 1 - Sample Title 1 00:05:00 DCP -Sample Artist 2 - Sample Title 2 00:09:23 -Sample Artist 3 - Sample Title 3 00:15:54 PRE, DCP -Sample Artist 4 - Sample Title 4 00:20:13 4CH -Sample Artist 5 - Sample Title 5 00:24:54 -Sample Artist 6 - Sample Title 6 00:31:54 PRE DCP 4CH -Sample Artist 7 - Sample Title 7 00:45:54 -Sample Artist 8 - Sample Title 8 01:15:54 PRE DCP 4CH SCMS"; - var profile = new Importprofile() + var fileContent = new List { - UseRegularExpression = true, - SchemeTracks = "(?'Artist'[a-zA-Z0-9_ .();äöü&:,]+) - (?'Title'[a-zA-Z0-9_ .();äöü]+)[\t ]+(?'End'[0-9]{2}:[0-9]{2}:[0-9]{2})(?:[\t ]+(?'Flags'[A-Za-z0-9 ,]+))?(?=[\t ]*(?:\r?\n|$))" + "Sample Artist 1 - Sample Title 1 00:05:00 DCP", + "Sample Artist 2 - Sample Title 2 00:09:23", + "Sample Artist 3 - Sample Title 3 00:15:54 PRE, DCP", + "Sample Artist 4 - Sample Title 4 00:20:13 4CH", + "Sample Artist 5 - Sample Title 5 00:24:54", + "Sample Artist 6 - Sample Title 6 00:31:54 PRE DCP 4CH", + "Sample Artist 7 - Sample Title 7 00:45:54", + "Sample Artist 8 - Sample Title 8 01:15:54 PRE DCP 4CH SCMS" }; - var localStorageOptionsProviderMock = new Mock(); - var options = new ApplicationOptions + var textImportScheme = new TextImportScheme() { - SelectedImportProfile = profile + SchemeCuesheet = null, + SchemeTracks = "(?'Artist'[a-zA-Z0-9_ .();äöü&:,]{1,}) - (?'Title'[a-zA-Z0-9_ .();äöü]{1,})\t{1,}(?'End'[0-9]{2}[:][0-9]{2}[:][0-9]{2})\t{1,}(?'Flags'[a-zA-Z 0-9,]{1,})" }; - localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync()).ReturnsAsync(options); - var service = new TextImportService(localStorageOptionsProviderMock.Object); // Act - var importfile = await service.AnalyseAsync(fileContent); + var importfile = TextImportService.Analyse(textImportScheme, fileContent); // Assert Assert.IsNull(importfile.AnalyseException); Assert.IsNotNull(importfile.AnalysedCuesheet); @@ -380,31 +342,27 @@ public async Task AnalyseAsync_CuesheetWithFlags_CreatesValidCuesheetAsync() } [TestMethod()] - public async Task AnalyseAsync_CuesheetWithPreGapAndPostGap_CreatesValidCuesheetAsync() + public void Analyse_CuesheetWithPreGapAndPostGap_CreatesValidCuesheet() { // Arrange - var fileContent = @"Sample Artist 1 - Sample Title 1 00:00:02 00:05:00 00:00:00 -Sample Artist 2 - Sample Title 2 00:00:04 00:09:23 00:00:00 -Sample Artist 3 - Sample Title 3 00:00:00 00:15:54 00:00:02 -Sample Artist 4 - Sample Title 4 00:00:00 00:20:13 00:00:03 -Sample Artist 5 - Sample Title 5 00:00:00 00:24:54 00:00:04 -Sample Artist 6 - Sample Title 6 00:00:00 00:31:54 00:00:01 -Sample Artist 7 - Sample Title 7 00:00:00 00:45:54 00:00:00 -Sample Artist 8 - Sample Title 8 00:00:02 01:15:54 00:00:00"; - var profile = new Importprofile() + var fileContent = new List { - UseRegularExpression = true, - SchemeTracks = "(?'Artist'[a-zA-Z0-9_ .();äöü&:,]{1,}) - (?'Title'[a-zA-Z0-9_ .();äöü]{1,})\t{1,}(?'PreGap'[0-9]{2}[:][0-9]{2}[:][0-9]{2})\t{1,}(?'End'[0-9]{2}[:][0-9]{2}[:][0-9]{2})\t{1,}(?'PostGap'[0-9]{2}[:][0-9]{2}[:][0-9]{2})" + "Sample Artist 1 - Sample Title 1 00:00:02 00:05:00 00:00:00", + "Sample Artist 2 - Sample Title 2 00:00:04 00:09:23 00:00:00", + "Sample Artist 3 - Sample Title 3 00:00:00 00:15:54 00:00:02", + "Sample Artist 4 - Sample Title 4 00:00:00 00:20:13 00:00:03", + "Sample Artist 5 - Sample Title 5 00:00:00 00:24:54 00:00:04", + "Sample Artist 6 - Sample Title 6 00:00:00 00:31:54 00:00:01", + "Sample Artist 7 - Sample Title 7 00:00:00 00:45:54 00:00:00", + "Sample Artist 8 - Sample Title 8 00:00:02 01:15:54 00:00:00" }; - var localStorageOptionsProviderMock = new Mock(); - var options = new ApplicationOptions + var textImportScheme = new TextImportScheme() { - SelectedImportProfile = profile + SchemeCuesheet = null, + SchemeTracks = "(?'Artist'[a-zA-Z0-9_ .();äöü&:,]{1,}) - (?'Title'[a-zA-Z0-9_ .();äöü]{1,})\t{1,}(?'PreGap'[0-9]{2}[:][0-9]{2}[:][0-9]{2})\t{1,}(?'End'[0-9]{2}[:][0-9]{2}[:][0-9]{2})\t{1,}(?'PostGap'[0-9]{2}[:][0-9]{2}[:][0-9]{2})" }; - localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync()).ReturnsAsync(options); - var service = new TextImportService(localStorageOptionsProviderMock.Object); // Act - var importfile = await service.AnalyseAsync(fileContent); + var importfile = TextImportService.Analyse(textImportScheme, fileContent); // Assert Assert.IsNull(importfile.AnalyseException); Assert.IsNotNull(importfile.AnalysedCuesheet); @@ -428,76 +386,73 @@ public async Task AnalyseAsync_CuesheetWithPreGapAndPostGap_CreatesValidCuesheet } [TestMethod()] - public async Task AnalyseAsync_SchemeCuesheetOnly_CreatesFileContentRecognizedOnlyForCuesheetAsync() + public void Analyse_SchemeCuesheetOnly_CreatesFileContentRecognizedOnlyForCuesheet() { // Arrange - var fileContent = @"CuesheetArtist - CuesheetTitle c:\tmp\Testfile.mp3 -Sample Artist 1 - Sample Title 1 00:05:00 -Sample Artist 2 - Sample Title 2 00:09:23 -Sample Artist 3 - Sample Title 3 00:15:54 -Sample Artist 4 - Sample Title 4 00:20:13 -Sample Artist 5 - Sample Title 5 00:24:54 -Sample Artist 6 - Sample Title 6 00:31:54 -Sample Artist 7 - Sample Title 7 00:45:54 -Sample Artist 8 - Sample Title 8 01:15:54"; - var profile = new Importprofile() + var fileContent = new List { - SchemeCuesheet = $"{nameof(ImportCuesheet.Artist)} - {nameof(ImportCuesheet.Title)}\t{nameof(ImportCuesheet.Audiofile)}" + "CuesheetArtist - CuesheetTitle c:\\tmp\\Testfile.mp3", + "Sample Artist 1 - Sample Title 1 00:05:00", + "Sample Artist 2 - Sample Title 2 00:09:23", + "Sample Artist 3 - Sample Title 3 00:15:54", + "Sample Artist 4 - Sample Title 4 00:20:13", + "Sample Artist 5 - Sample Title 5 00:24:54", + "Sample Artist 6 - Sample Title 6 00:31:54", + "Sample Artist 7 - Sample Title 7 00:45:54", + "Sample Artist 8 - Sample Title 8 01:15:54" }; - var localStorageOptionsProviderMock = new Mock(); - var options = new ApplicationOptions + var textImportScheme = new TextImportScheme() { - SelectedImportProfile = profile + SchemeCuesheet = TextImportScheme.DefaultSchemeCuesheet, + SchemeTracks = null }; - localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync()).ReturnsAsync(options); - var service = new TextImportService(localStorageOptionsProviderMock.Object); // Act - var importfile = await service.AnalyseAsync(fileContent); + var importfile = TextImportService.Analyse(textImportScheme, fileContent); // Assert Assert.IsNull(importfile.AnalyseException); Assert.IsNotNull(importfile.AnalysedCuesheet); Assert.IsNotNull(importfile.FileContentRecognized); - var lines = importfile.FileContentRecognized.Split(Environment.NewLine); Assert.AreEqual(string.Format("{0} - {1} {2}", string.Format(CuesheetConstants.RecognizedMarkHTML, "CuesheetArtist"), string.Format(CuesheetConstants.RecognizedMarkHTML, "CuesheetTitle"), - string.Format(CuesheetConstants.RecognizedMarkHTML, "c:\\tmp\\Testfile.mp3")), lines.First()); + string.Format(CuesheetConstants.RecognizedMarkHTML, "c:\\tmp\\Testfile.mp3")), importfile.FileContentRecognized.First()); Assert.AreEqual("CuesheetArtist", importfile.AnalysedCuesheet.Artist); Assert.AreEqual("CuesheetTitle", importfile.AnalysedCuesheet.Title); Assert.AreEqual("c:\\tmp\\Testfile.mp3", importfile.AnalysedCuesheet.Audiofile); Assert.AreEqual(0, importfile.AnalysedCuesheet.Tracks.Count); - Assert.AreEqual("Sample Artist 1 - Sample Title 1 00:05:00", lines.ElementAt(1)); - Assert.AreEqual("Sample Artist 2 - Sample Title 2 00:09:23", lines.ElementAt(2)); - Assert.AreEqual("Sample Artist 3 - Sample Title 3 00:15:54", lines.ElementAt(3)); - Assert.AreEqual("Sample Artist 4 - Sample Title 4 00:20:13", lines.ElementAt(4)); - Assert.AreEqual("Sample Artist 5 - Sample Title 5 00:24:54", lines.ElementAt(5)); - Assert.AreEqual("Sample Artist 6 - Sample Title 6 00:31:54", lines.ElementAt(6)); - Assert.AreEqual("Sample Artist 7 - Sample Title 7 00:45:54", lines.ElementAt(7)); - Assert.AreEqual("Sample Artist 8 - Sample Title 8 01:15:54", lines.ElementAt(8)); + Assert.AreEqual("Sample Artist 1 - Sample Title 1 00:05:00", importfile.FileContentRecognized.ElementAt(1)); + Assert.AreEqual("Sample Artist 2 - Sample Title 2 00:09:23", importfile.FileContentRecognized.ElementAt(2)); + Assert.AreEqual("Sample Artist 3 - Sample Title 3 00:15:54", importfile.FileContentRecognized.ElementAt(3)); + Assert.AreEqual("Sample Artist 4 - Sample Title 4 00:20:13", importfile.FileContentRecognized.ElementAt(4)); + Assert.AreEqual("Sample Artist 5 - Sample Title 5 00:24:54", importfile.FileContentRecognized.ElementAt(5)); + Assert.AreEqual("Sample Artist 6 - Sample Title 6 00:31:54", importfile.FileContentRecognized.ElementAt(6)); + Assert.AreEqual("Sample Artist 7 - Sample Title 7 00:45:54", importfile.FileContentRecognized.ElementAt(7)); + Assert.AreEqual("Sample Artist 8 - Sample Title 8 01:15:54", importfile.FileContentRecognized.ElementAt(8)); } [TestMethod()] - public async Task AnalyseAsync_CuesheetWithoutTracks_CreatesValidFileContentRecognizedAsync() + public void Analyse_CuesheetWithoutTracks_CreatesValidFileContentRecognized() { // Arrange - var fileContent = @"CuesheetArtist - CuesheetTitle c:\tmp\Testfile.mp3 -Sample Artist 1 - Sample Title 1 00:05:00 -Sample Artist 2 - Sample Title 2 00:09:23 -Sample Artist 3 - Sample Title 3 00:15:54 -Sample Artist 4 - Sample Title 4 00:20:13 -Sample Artist 5 - Sample Title 5 00:24:54 -Sample Artist 6 - Sample Title 6 00:31:54 -Sample Artist 7 - Sample Title 7 00:45:54 -Sample Artist 8 - Sample Title 8 01:15:54"; - var localStorageOptionsProviderMock = new Mock(); - var options = new ApplicationOptions + var fileContent = new List + { + "CuesheetArtist - CuesheetTitle c:\\tmp\\Testfile.mp3", + "Sample Artist 1 - Sample Title 1 00:05:00", + "Sample Artist 2 - Sample Title 2 00:09:23", + "Sample Artist 3 - Sample Title 3 00:15:54", + "Sample Artist 4 - Sample Title 4 00:20:13", + "Sample Artist 5 - Sample Title 5 00:24:54", + "Sample Artist 6 - Sample Title 6 00:31:54", + "Sample Artist 7 - Sample Title 7 00:45:54", + "Sample Artist 8 - Sample Title 8 01:15:54" + }; + var textImportScheme = new TextImportScheme() { - SelectedImportProfile = ApplicationOptions.DefaultSelectedImportprofile + SchemeCuesheet = TextImportScheme.DefaultSchemeCuesheet, + SchemeTracks = TextImportScheme.DefaultSchemeTracks }; - localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync()).ReturnsAsync(options); - var service = new TextImportService(localStorageOptionsProviderMock.Object); // Act - var importfile = await service.AnalyseAsync(fileContent); + var importfile = TextImportService.Analyse(textImportScheme, fileContent); // Assert Assert.IsNull(importfile.AnalyseException); Assert.IsNotNull(importfile.AnalysedCuesheet); @@ -506,225 +461,39 @@ public async Task AnalyseAsync_CuesheetWithoutTracks_CreatesValidFileContentReco Assert.AreEqual("Sample Artist 1", importfile.AnalysedCuesheet.Tracks.ElementAt(0).Artist); Assert.AreEqual("Sample Title 1", importfile.AnalysedCuesheet.Tracks.ElementAt(0).Title); Assert.AreEqual(new TimeSpan(0, 5, 0), importfile.AnalysedCuesheet.Tracks.ElementAt(0).End); - var lines = importfile.FileContentRecognized.Split(Environment.NewLine); Assert.AreEqual(string.Format("{0} - {1} {2}", string.Format(CuesheetConstants.RecognizedMarkHTML, "Sample Artist 8"), string.Format(CuesheetConstants.RecognizedMarkHTML, "Sample Title 8"), - string.Format(CuesheetConstants.RecognizedMarkHTML, "01:15:54")), lines.Last()); + string.Format(CuesheetConstants.RecognizedMarkHTML, "01:15:54")), importfile.FileContentRecognized.Last()); } [TestMethod()] - public async Task AnalyseAsync_TextfileBug233_CreatesValidFileContentRecognizedAsync() + public void Analyse_TextfileBug233_CreatesValidFileContentRecognized() { // Arrange - var profile = new Importprofile() - { - SchemeTracks = $"{nameof(ImportTrack.Artist)} - {nameof(ImportTrack.Title)}\t{nameof(ImportTrack.End)}" - }; var textImportMemoryStream = new MemoryStream(Resources.Textimport_Bug__233); - using var reader = new StreamReader(textImportMemoryStream); - var fileContent = reader.ReadToEnd(); - var localStorageOptionsProviderMock = new Mock(); - var options = new ApplicationOptions + using var reader = new StreamReader(textImportMemoryStream); + List lines = []; + while (reader.EndOfStream == false) { - SelectedImportProfile = profile + lines.Add(reader.ReadLine()); + } + var fileContent = lines.AsReadOnly(); + var textImportScheme = new TextImportScheme() + { + SchemeCuesheet = null, + SchemeTracks = TextImportScheme.DefaultSchemeTracks }; - localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync()).ReturnsAsync(options); - var service = new TextImportService(localStorageOptionsProviderMock.Object); // Act - var importfile = await service.AnalyseAsync(fileContent); + var importfile = TextImportService.Analyse(textImportScheme, fileContent); // Assert Assert.IsNull(importfile.AnalyseException); Assert.IsNotNull(importfile.AnalysedCuesheet); Assert.IsNotNull(importfile.FileContentRecognized); - var lines = importfile.FileContentRecognized.Split(Environment.NewLine); Assert.AreEqual(string.Format("{0} - {1}\t\t\t\t\t\t\t\t{2}", string.Format(CuesheetConstants.RecognizedMarkHTML, "Age Of Love"), string.Format(CuesheetConstants.RecognizedMarkHTML, "The Age Of Love (Charlotte De Witte & Enrico Sangiuliano Remix)"), - string.Format(CuesheetConstants.RecognizedMarkHTML, "04:29:28")), lines.ElementAt(53)); - } - - [TestMethod()] - public async Task AnalyseAsync_WithRegularExpression_ReturnsCuesheetAsync() - { - // Arrange - var profile = new Importprofile() - { - UseRegularExpression = true, - SchemeTracks = "\\s*\\d+\\s*(?.*?)\\s*(?.*?)</td>\\s*<td>(?<StartDateTime>.*?)</td>\\s*</tr>" - }; - var textImportMemoryStream = new MemoryStream(Resources.Traktor_Export); - var reader = new StreamReader(textImportMemoryStream); - var fileContent = reader.ReadToEnd(); - var localStorageOptionsProviderMock = new Mock<ILocalStorageOptionsProvider>(); - var options = new ApplicationOptions - { - SelectedImportProfile = profile - }; - localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync<ApplicationOptions>()).ReturnsAsync(options); - var service = new TextImportService(localStorageOptionsProviderMock.Object); - // Act - var importfile = await service.AnalyseAsync(fileContent); - // Assert - Assert.AreEqual(fileContent, importfile.FileContent); - Assert.IsNull(importfile.AnalyseException); - Assert.IsNotNull(importfile.AnalysedCuesheet); - Assert.IsNull(importfile.AnalysedCuesheet.Artist); - Assert.IsNull(importfile.AnalysedCuesheet.Title); - Assert.AreEqual(41, importfile.AnalysedCuesheet.Tracks.Count); - Assert.AreEqual("Nachap", importfile.AnalysedCuesheet.Tracks.First().Artist); - Assert.AreEqual("Glass", importfile.AnalysedCuesheet.Tracks.First().Title); - Assert.AreEqual(new DateTime(2025, 1, 29, 18, 52, 10), importfile.AnalysedCuesheet.Tracks.First().StartDateTime); - Assert.AreEqual("Inache", importfile.AnalysedCuesheet.Tracks.Last().Artist); - Assert.AreEqual("Andale (MONTA (TN) Remix)", importfile.AnalysedCuesheet.Tracks.Last().Title); - Assert.AreEqual(new DateTime(2025, 1, 29, 22, 30, 3), importfile.AnalysedCuesheet.Tracks.Last().StartDateTime); - Assert.IsNotNull(importfile.FileContentRecognized); - Assert.IsTrue(importfile.FileContentRecognized.Contains(String.Format(CuesheetConstants.RecognizedMarkHTML, "Sasha Fashion"))); - Assert.IsTrue(importfile.FileContentRecognized.Contains(String.Format(CuesheetConstants.RecognizedMarkHTML, "2025/1/29 21:48:55"))); - } - - [TestMethod()] - public async Task AnalyseAsync_WithoutRegularExpression_ReturnsCuesheetAsync() - { - // Arrange - var profile = new Importprofile() - { - UseRegularExpression = false, - SchemeCuesheet = "Artist - Title - Cataloguenumber", - SchemeTracks = "Artist - Title\tBegin" - }; - var textImportMemoryStream = new MemoryStream(Resources.Textimport_with_Cuesheetdata); - var reader = new StreamReader(textImportMemoryStream); - var fileContent = reader.ReadToEnd(); - var localStorageOptionsProviderMock = new Mock<ILocalStorageOptionsProvider>(); - var options = new ApplicationOptions - { - SelectedImportProfile = profile - }; - localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync<ApplicationOptions>()).ReturnsAsync(options); - var service = new TextImportService(localStorageOptionsProviderMock.Object); - // Act - var importfile = await service.AnalyseAsync(fileContent); - // Assert - Assert.IsNull(importfile.AnalyseException); - Assert.IsNotNull(importfile.AnalysedCuesheet); - Assert.AreEqual("DJFreezeT", importfile.AnalysedCuesheet.Artist); - Assert.AreEqual("Rabbit Hole Mix", importfile.AnalysedCuesheet.Title); - Assert.AreEqual("0123456789123", importfile.AnalysedCuesheet.Cataloguenumber); - Assert.AreEqual("Adriatique", importfile.AnalysedCuesheet.Tracks.First().Artist); - Assert.AreEqual("X.", importfile.AnalysedCuesheet.Tracks.First().Title); - Assert.AreEqual(new TimeSpan(0, 0, 5, 24, 250), importfile.AnalysedCuesheet.Tracks.First().Begin); - Assert.AreEqual("Nikolay Kirov", importfile.AnalysedCuesheet.Tracks.Last().Artist); - Assert.AreEqual("Chasing the Sun (Original Mix)", importfile.AnalysedCuesheet.Tracks.Last().Title); - Assert.IsNull(importfile.AnalysedCuesheet.Tracks.Last().Begin); - Assert.IsNotNull(importfile.FileContentRecognized); - Assert.IsTrue(importfile.FileContentRecognized.Contains(String.Format(CuesheetConstants.RecognizedMarkHTML, "DJFreezeT"))); - Assert.IsTrue(importfile.FileContentRecognized.Contains(String.Format(CuesheetConstants.RecognizedMarkHTML, "Rabbit Hole Mix"))); - Assert.IsTrue(importfile.FileContentRecognized.Contains(String.Format(CuesheetConstants.RecognizedMarkHTML, "0123456789123"))); - Assert.IsTrue(importfile.FileContentRecognized.Contains(String.Format(CuesheetConstants.RecognizedMarkHTML, "Nikolay Kirov"))); - Assert.IsTrue(importfile.FileContentRecognized.Contains(String.Format(CuesheetConstants.RecognizedMarkHTML, "Chasing the Sun (Original Mix)"))); - Assert.IsTrue(importfile.FileContentRecognized.Contains(String.Format(CuesheetConstants.RecognizedMarkHTML, "SHDW & Obscure Shape"))); - Assert.IsTrue(importfile.FileContentRecognized.Contains(String.Format(CuesheetConstants.RecognizedMarkHTML, "Wächter der Nacht (Original Mix)"))); - } - - [TestMethod()] - public async Task AnalyseAsync_TextfileWithStartDateTime_CreatesValidCuesheetAsync() - { - // Arrange - var fileContent = $@"Innellea~The Golden Fort~{new DateTime(2024, 8, 14, 20, 10, 48)} -Nora En Pure~Diving with Whales (Daniel Portman Remix)~{new DateTime(2024, 8, 14, 20, 15, 21)} -WhoMadeWho & Adriatique~Miracle (RÜFÜS DU SOL Remix)~{new DateTime(2024, 8, 14, 20, 20, 42)} -Ella Wild~Poison D'araignee (Original Mix)~{new DateTime(2024, 8, 14, 20, 28, 03)} -Stil & Bense~On The Edge (Original Mix)~{new DateTime(2024, 8, 14, 20, 32, 42)} -Nebula~Clairvoyant Dreams~{new DateTime(2024, 8, 14, 20, 39, 1)} -Valentina Black~I'm a Tree (Extended Mix)~{new DateTime(2024, 8, 14, 20, 47, 08)} -Nebula~Clairvoyant Dreams~{new DateTime(2024, 8, 14, 20, 53, 20)} -Kiko & Dave Davis feat. Phoebe~Living in Space (Dub Mix)~{new DateTime(2024, 8, 14, 20, 58, 11)} -Lilly Palmer~Before Acid~{new DateTime(2024, 8, 14, 21, 03, 53)} -Sofi Tukker~Drinkee (Vintage Culture & John Summit Extended Mix)~{new DateTime(2024, 8, 14, 21, 09, 52)} -CID & Truth x Lies~Caroline (Extended Mix)~{new DateTime(2024, 8, 14, 21, 14, 09)} -Moby~Why Does My Heart Feel So Bad? (Oxia Remix)~{new DateTime(2024, 8, 14, 21, 17, 15)} -Ammo Avenue~Little Gurl (Extended Mix)~{new DateTime(2024, 8, 14, 21, 22, 46)} -James Hurr & Smokin Jo & Stealth~Beggin' For Change~{new DateTime(2024, 8, 14, 21, 28, 37)} -Kristine Blond~Love Shy (Sam Divine & CASSIMM Extended Remix)~{new DateTime(2024, 8, 14, 21, 30, 47)} -Vanilla Ace~Work On You (Original Mix)~{new DateTime(2024, 8, 14, 21, 36, 28)} -Truth X Lies~Like This~{new DateTime(2024, 8, 14, 21, 42, 05)} -Terri-Anne~Round Round~{new DateTime(2024, 8, 14, 21, 44, 07)} -Joanna Magik~Maneater~{new DateTime(2024, 8, 14, 21, 46, 32)} -Jen Payne & Kevin McKay~Feed Your Soul~1{new DateTime(2024, 8, 14, 21, 48, 45)} -Kevin McKay & Eppers & Notelle~On My Own~{new DateTime(2024, 8, 14, 21, 51, 37)} -Nader Razdar & Kevin McKay~Get Ur Freak On (Kevin McKay Extended Mix)~{new DateTime(2024, 8, 14, 21, 53, 49)} -Philip Z~Yala (Extended Mix)~{new DateTime(2024, 8, 14, 21, 59, 40)} -Kyle Kinch & Kevin McKay~Hella~{new DateTime(2024, 8, 14, 22, 05, 53)} -Roze Wild~B-O-D-Y~{new DateTime(2024, 8, 14, 22, 08, 26)} -Jey Kurmis~Snoop~{new DateTime(2024, 8, 14, 22, 11, 09)} -Bootie Brown & Tame Impala & Gorillaz~New Gold (Dom Dolla Remix Extended)~{new DateTime(2024, 8, 14, 22, 16, 23)} -Eli Brown & Love Regenerator~Don't You Want Me (Original Mix)~{new DateTime(2024, 8, 14, 22, 21, 23)} -Local Singles~Voices~{new DateTime(2024, 8, 14, 22, 25, 59)}"; - - var profile = new Importprofile() - { - UseRegularExpression = false, - SchemeTracks = $"{nameof(ImportTrack.Artist)}~{nameof(ImportTrack.Title)}~{nameof(ImportTrack.StartDateTime)}" - }; - var localStorageOptionsProviderMock = new Mock<ILocalStorageOptionsProvider>(); - var options = new ApplicationOptions - { - SelectedImportProfile = profile - }; - localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync<ApplicationOptions>()).ReturnsAsync(options); - var service = new TextImportService(localStorageOptionsProviderMock.Object); - // Act - var importfile = await service.AnalyseAsync(fileContent); - // Assert - Assert.IsNull(importfile.AnalyseException); - Assert.IsNotNull(importfile.AnalysedCuesheet); - Assert.AreEqual(30, importfile.AnalysedCuesheet.Tracks.Count); - Assert.AreEqual("Innellea", importfile.AnalysedCuesheet.Tracks.ElementAt(0).Artist); - Assert.AreEqual("The Golden Fort", importfile.AnalysedCuesheet.Tracks.ElementAt(0).Title); - Assert.AreEqual(new DateTime(2024, 8, 14, 20, 10, 48), importfile.AnalysedCuesheet.Tracks.ElementAt(0).StartDateTime); - Assert.AreEqual("Nora En Pure", importfile.AnalysedCuesheet.Tracks.ElementAt(1).Artist); - Assert.AreEqual("Diving with Whales (Daniel Portman Remix)", importfile.AnalysedCuesheet.Tracks.ElementAt(1).Title); - Assert.AreEqual(new DateTime(2024, 8, 14, 20, 15, 21), importfile.AnalysedCuesheet.Tracks.ElementAt(1).StartDateTime); - Assert.AreEqual("Local Singles", importfile.AnalysedCuesheet.Tracks.ElementAt(29).Artist); - Assert.AreEqual("Voices", importfile.AnalysedCuesheet.Tracks.ElementAt(29).Title); - Assert.AreEqual(new DateTime(2024, 8, 14, 22, 25, 59), importfile.AnalysedCuesheet.Tracks.ElementAt(29).StartDateTime); - } - - [TestMethod()] - public async Task AnalyseAsync_WithCommonDataMatchingMultipleLines_SetsCommonDataOnceAsync() - { - // Arrange - var profile = new Importprofile() - { - UseRegularExpression = false, - SchemeCuesheet = "Artist - Title\tAudiofile", - SchemeTracks = "Artist - Title\tBegin" - }; - var textImportMemoryStream = new MemoryStream(Resources.Sample_Inputfile); - var reader = new StreamReader(textImportMemoryStream); - var fileContent = reader.ReadToEnd(); - var localStorageOptionsProviderMock = new Mock<ILocalStorageOptionsProvider>(); - var options = new ApplicationOptions - { - SelectedImportProfile = profile - }; - localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync<ApplicationOptions>()).ReturnsAsync(options); - var service = new TextImportService(localStorageOptionsProviderMock.Object); - // Act - var importfile = await service.AnalyseAsync(fileContent); - // Assert - Assert.IsNull(importfile.AnalyseException); - Assert.IsNotNull(importfile.AnalysedCuesheet); - Assert.AreEqual("CuesheetArtist", importfile.AnalysedCuesheet.Artist); - Assert.AreEqual("CuesheetTitle", importfile.AnalysedCuesheet.Title); - Assert.AreEqual("c:\\AudioFile.mp3", importfile.AnalysedCuesheet.Audiofile); - Assert.AreEqual(8, importfile.AnalysedCuesheet.Tracks.Count); - Assert.IsNotNull(importfile.FileContentRecognized); - Assert.IsTrue(importfile.FileContentRecognized.Contains(String.Format(CuesheetConstants.RecognizedMarkHTML, "CuesheetArtist"))); - Assert.IsTrue(importfile.FileContentRecognized.Contains(String.Format(CuesheetConstants.RecognizedMarkHTML, "CuesheetTitle"))); - Assert.IsTrue(importfile.FileContentRecognized.Contains(String.Format(CuesheetConstants.RecognizedMarkHTML, "c:\\AudioFile.mp3"))); - Assert.IsTrue(importfile.FileContentRecognized.Contains(String.Format(CuesheetConstants.RecognizedMarkHTML, "Sample Artist 8"))); + string.Format(CuesheetConstants.RecognizedMarkHTML, "04:29:28")), importfile.FileContentRecognized.ElementAt(53)); } } } \ No newline at end of file diff --git a/AudioCuesheetEditor.Tests/Services/UI/TraceChangeManagerTests.cs b/AudioCuesheetEditor.Tests/Services/UI/TraceChangeManagerTests.cs index 155ed70c..76c4e24b 100644 --- a/AudioCuesheetEditor.Tests/Services/UI/TraceChangeManagerTests.cs +++ b/AudioCuesheetEditor.Tests/Services/UI/TraceChangeManagerTests.cs @@ -13,13 +13,21 @@ //You should have received a copy of the GNU General Public License //along with Foobar. If not, see //<http: //www.gnu.org/licenses />. +using AudioCuesheetEditor.Data.Options; using AudioCuesheetEditor.Model.AudioCuesheet; using AudioCuesheetEditor.Model.Entity; +using AudioCuesheetEditor.Model.IO.Import; +using AudioCuesheetEditor.Model.Options; +using AudioCuesheetEditor.Model.Utility; +using AudioCuesheetEditor.Services.IO; using AudioCuesheetEditor.Services.UI; +using AudioCuesheetEditor.Tests.Properties; using AudioCuesheetEditor.Tests.Utility; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; using System; using System.Collections.Generic; +using System.IO; using System.Linq; namespace AudioCuesheetEditor.Tests.Services.UI @@ -156,6 +164,115 @@ public void TrackListTest() Assert.IsFalse(manager.CanRedo); } + [TestMethod()] + public void Import_ValidTextfile_IsUndoable() + { + // Arrange + var traceChangeManager = new TraceChangeManager(TestHelper.CreateLogger<TraceChangeManager>()); + var sessionStateContainer = new SessionStateContainer(traceChangeManager); + var textImportMemoryStream = new MemoryStream(Resources.Textimport_with_Cuesheetdata); + using var reader = new StreamReader(textImportMemoryStream); + List<string?> lines = []; + while (reader.EndOfStream == false) + { + lines.Add(reader.ReadLine()); + } + var fileContent = lines.AsReadOnly(); + var localStorageOptionsProviderMock = new Mock<ILocalStorageOptionsProvider>(); + var textImportScheme = new TextImportScheme() + { + SchemeCuesheet = "(?'Artist'\\A.*) - (?'Title'[a-zA-Z0-9_ .();äöü&:,]{1,}) - (?'Cataloguenumber'.{1,})", + SchemeTracks = TextImportScheme.DefaultSchemeTracks + }; + var timeSpanFormat = new TimeSpanFormat(); + var options = new ApplicationOptions(); + localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync<ApplicationOptions>()).ReturnsAsync(options); + var fileInputManagerMock = new Mock<IFileInputManager>(); + var importManager = new ImportManager(sessionStateContainer, localStorageOptionsProviderMock.Object, traceChangeManager, fileInputManagerMock.Object); + // Act + importManager.ImportText(fileContent, textImportScheme, timeSpanFormat); + // Assert + Assert.IsFalse(traceChangeManager.CanUndo); + Assert.IsFalse(traceChangeManager.CanRedo); + Assert.IsNotNull(sessionStateContainer.ImportCuesheet); + Assert.AreEqual("DJFreezeT", sessionStateContainer.ImportCuesheet.Artist); + Assert.AreEqual("0123456789123", sessionStateContainer.ImportCuesheet.Cataloguenumber); + Assert.AreNotEqual(0, sessionStateContainer.ImportCuesheet.Tracks.Count); + } + + [TestMethod()] + public void UndoImport_ValidTextfile_ResetsToEmptyCuesheet() + { + // Arrange + var traceChangeManager = new TraceChangeManager(TestHelper.CreateLogger<TraceChangeManager>()); + var sessionStateContainer = new SessionStateContainer(traceChangeManager); + var textImportMemoryStream = new MemoryStream(Resources.Textimport_with_Cuesheetdata); + using var reader = new StreamReader(textImportMemoryStream); + List<string?> lines = []; + while (reader.EndOfStream == false) + { + lines.Add(reader.ReadLine()); + } + var fileContent = lines.AsReadOnly(); + var localStorageOptionsProviderMock = new Mock<ILocalStorageOptionsProvider>(); + var textImportScheme = new TextImportScheme() + { + SchemeCuesheet = "(?'Artist'\\A.*) - (?'Title'[a-zA-Z0-9_ .();äöü&:,]{1,}) - (?'Cataloguenumber'.{1,})", + SchemeTracks = TextImportScheme.DefaultSchemeTracks + }; + var timeSpanFormat = new TimeSpanFormat(); + var options = new ApplicationOptions(); + localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync<ApplicationOptions>()).ReturnsAsync(options); + var fileInputManagerMock = new Mock<IFileInputManager>(); + var importManager = new ImportManager(sessionStateContainer, localStorageOptionsProviderMock.Object, traceChangeManager, fileInputManagerMock.Object); + importManager.ImportText(fileContent, textImportScheme, timeSpanFormat); + importManager.ImportCuesheet(); + // Act + traceChangeManager.Undo(); + // Assert + Assert.AreEqual(0, sessionStateContainer.Cuesheet.Tracks.Count); + Assert.IsTrue(string.IsNullOrEmpty(sessionStateContainer.Cuesheet.Artist)); + Assert.IsTrue(string.IsNullOrEmpty(sessionStateContainer.Cuesheet.Cataloguenumber)); + Assert.IsFalse(traceChangeManager.CanUndo); + Assert.IsTrue(traceChangeManager.CanRedo); + } + + [TestMethod()] + public void UndoAndRedoImport_ValidTextfile_ResetsTextfileValues() + { + // Arrange + var testhelper = new TestHelper(); + var traceChangeManager = new TraceChangeManager(TestHelper.CreateLogger<TraceChangeManager>()); + var sessionStateContainer = new SessionStateContainer(traceChangeManager); + var textImportMemoryStream = new MemoryStream(Resources.Textimport_with_Cuesheetdata); + using var reader = new StreamReader(textImportMemoryStream); + List<string?> lines = []; + while (reader.EndOfStream == false) + { + lines.Add(reader.ReadLine()); + } + var fileContent = lines.AsReadOnly(); + var localStorageOptionsProviderMock = new Mock<ILocalStorageOptionsProvider>(); + var textImportScheme = new TextImportScheme() + { + SchemeCuesheet = "(?'Artist'\\A.*) - (?'Title'[a-zA-Z0-9_ .();äöü&:,]{1,}) - (?'Cataloguenumber'.{1,})", + SchemeTracks = TextImportScheme.DefaultSchemeTracks + }; + var timeSpanFormat = new TimeSpanFormat(); + var options = new ApplicationOptions(); + localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync<ApplicationOptions>()).ReturnsAsync(options); + var fileInputManagerMock = new Mock<IFileInputManager>(); + var importManager = new ImportManager(sessionStateContainer, localStorageOptionsProviderMock.Object, traceChangeManager, fileInputManagerMock.Object); + importManager.ImportText(fileContent, textImportScheme, timeSpanFormat); + traceChangeManager.Undo(); + // Act + traceChangeManager.Redo(); + // Assert + Assert.AreEqual("DJFreezeT", sessionStateContainer.ImportCuesheet?.Artist); + Assert.AreEqual("0123456789123", sessionStateContainer.ImportCuesheet?.Cataloguenumber); + Assert.AreEqual(39, sessionStateContainer.ImportCuesheet?.Tracks.Count); + } + [TestMethod()] public void RemoveTracksTest() { diff --git a/AudioCuesheetEditor/AudioCuesheetEditor.csproj b/AudioCuesheetEditor/AudioCuesheetEditor.csproj index 0b8da88c..826022e8 100644 --- a/AudioCuesheetEditor/AudioCuesheetEditor.csproj +++ b/AudioCuesheetEditor/AudioCuesheetEditor.csproj @@ -20,7 +20,6 @@ </ItemGroup> <ItemGroup> - <PackageReference Include="HtmlSanitizer" Version="9.0.886" /> <PackageReference Include="MetaBrainz.MusicBrainz" Version="6.1.0" /> <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.5" /> <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="9.0.5" PrivateAssets="all" /> diff --git a/AudioCuesheetEditor/Model/AudioCuesheet/CuesheetConstants.cs b/AudioCuesheetEditor/Model/AudioCuesheet/CuesheetConstants.cs index f0121747..2994ab3d 100644 --- a/AudioCuesheetEditor/Model/AudioCuesheet/CuesheetConstants.cs +++ b/AudioCuesheetEditor/Model/AudioCuesheet/CuesheetConstants.cs @@ -31,7 +31,6 @@ public class CuesheetConstants public static readonly String Tab = "\t"; public static readonly String CuesheetCDTextfile = "CDTEXTFILE"; public static readonly String CuesheetCatalogueNumber = "CATALOG"; - public static readonly String MarkHTMLStart = "<Mark>"; - public static readonly String RecognizedMarkHTML = MarkHTMLStart + "{0}" + "</Mark>"; + public static readonly String RecognizedMarkHTML = "<Mark>{0}</Mark>"; } } diff --git a/AudioCuesheetEditor/Model/IO/FileExtensions.cs b/AudioCuesheetEditor/Model/IO/FileExtensions.cs index 980f574d..439bf6df 100644 --- a/AudioCuesheetEditor/Model/IO/FileExtensions.cs +++ b/AudioCuesheetEditor/Model/IO/FileExtensions.cs @@ -21,6 +21,5 @@ public class FileExtensions public const string Projectfile = ".ace"; public const string Cuesheet = ".cue"; public const string CDTextfile = ".cdt"; - public const string HTML = ".html"; } } diff --git a/AudioCuesheetEditor/Model/IO/FileMimeTypes.cs b/AudioCuesheetEditor/Model/IO/FileMimeTypes.cs index 37483831..1dc3c20d 100644 --- a/AudioCuesheetEditor/Model/IO/FileMimeTypes.cs +++ b/AudioCuesheetEditor/Model/IO/FileMimeTypes.cs @@ -17,9 +17,9 @@ namespace AudioCuesheetEditor.Model.IO { public static class FileMimeTypes { - public const string TextPlain = "text/plain"; + public const string Text = "text/plain"; public const string Projectfile = "application/x-ace"; public const string Cuesheet = "application/x-cue"; - public const string Text = "text/*"; + public const string CDTextfile = "text/*"; } } diff --git a/AudioCuesheetEditor/Model/IO/Import/IImportfile.cs b/AudioCuesheetEditor/Model/IO/Import/IImportfile.cs index a10f1d99..b99d07b0 100644 --- a/AudioCuesheetEditor/Model/IO/Import/IImportfile.cs +++ b/AudioCuesheetEditor/Model/IO/Import/IImportfile.cs @@ -21,15 +21,15 @@ namespace AudioCuesheetEditor.Model.IO.Import public interface IImportfile { /// <summary> - /// File content + /// File content (each element is a file line) /// </summary> - String? FileContent { get; set; } + IEnumerable<String?>? FileContent { get; set; } /// <summary> /// File content with marking which passages has been reconized by scheme /// </summary> - String? FileContentRecognized { get; set; } + IEnumerable<String?>? FileContentRecognized { get; set; } /// <summary> - /// Exception that has been thrown while reading out the file + /// Exception that has been thrown while readinng out the file /// </summary> Exception? AnalyseException { get; set; } /// <summary> diff --git a/AudioCuesheetEditor/Model/IO/Import/Importfile.cs b/AudioCuesheetEditor/Model/IO/Import/Importfile.cs index c0726e8e..033e1989 100644 --- a/AudioCuesheetEditor/Model/IO/Import/Importfile.cs +++ b/AudioCuesheetEditor/Model/IO/Import/Importfile.cs @@ -20,14 +20,14 @@ namespace AudioCuesheetEditor.Model.IO.Import { public class Importfile : IImportfile { + /// <inheritdoc /> + public IEnumerable<String?>? FileContent { get; set; } + /// <inheritdoc /> + public IEnumerable<String?>? FileContentRecognized { get; set; } /// <inheritdoc /> public Exception? AnalyseException { get; set; } /// <inheritdoc /> public ImportCuesheet? AnalysedCuesheet { get; set; } public ImportFileType FileType { get; set; } - /// <inheritdoc /> - public string? FileContentRecognized { get; set; } - /// <inheritdoc /> - public string? FileContent { get; set; } } } diff --git a/AudioCuesheetEditor/Model/IO/Import/Importprofile.cs b/AudioCuesheetEditor/Model/IO/Import/TextImportScheme.cs similarity index 76% rename from AudioCuesheetEditor/Model/IO/Import/Importprofile.cs rename to AudioCuesheetEditor/Model/IO/Import/TextImportScheme.cs index b1f824c4..e63c3fce 100644 --- a/AudioCuesheetEditor/Model/IO/Import/Importprofile.cs +++ b/AudioCuesheetEditor/Model/IO/Import/TextImportScheme.cs @@ -16,26 +16,55 @@ using AudioCuesheetEditor.Model.AudioCuesheet; using AudioCuesheetEditor.Model.AudioCuesheet.Import; using AudioCuesheetEditor.Model.Entity; -using AudioCuesheetEditor.Model.Utility; namespace AudioCuesheetEditor.Model.IO.Import { - public class Importprofile : Validateable + public class TextImportScheme : Validateable { public static readonly IEnumerable<String> AvailableSchemeCuesheet; public static readonly IEnumerable<String> AvailableSchemesTrack; - static Importprofile() + static TextImportScheme() { AvailableSchemeCuesheet = [nameof(Cuesheet.Artist), nameof(Cuesheet.Title), nameof(Cuesheet.Audiofile), nameof(Cuesheet.CDTextfile), nameof(Cuesheet.Cataloguenumber)]; AvailableSchemesTrack = [nameof(Track.Artist), nameof(Track.Title), nameof(Track.Begin), nameof(Track.End), nameof(Track.Length), nameof(Track.Position), nameof(Track.Flags), nameof(Track.PreGap), nameof(Track.PostGap), nameof(ImportTrack.StartDateTime)]; } - public Guid Id { get; init; } = Guid.NewGuid(); - public String? Name { get; set; } - public Boolean UseRegularExpression { get; set; } - public String? SchemeCuesheet { get; set; } - public String? SchemeTracks { get; set; } - public TimeSpanFormat? TimeSpanFormat { get; set; } + + public static readonly String DefaultSchemeCuesheet = @"(?'Artist'\w*) - (?'Title'\w*)\t{1,}(?'Audiofile'.*)"; + public static readonly String DefaultSchemeTracks = @"(?'Artist'.+?) - (?'Title'.+?)\s*\t+(?'End'.+)"; + + public static readonly TextImportScheme DefaultTextImportScheme = new() + { + SchemeCuesheet = DefaultSchemeCuesheet, + SchemeTracks = DefaultSchemeTracks + }; + + private string? schemeTracks; + private string? schemeCuesheet; + + public event EventHandler<String>? SchemeChanged; + + public String? SchemeTracks + { + get { return schemeTracks; } + set + { + schemeTracks = value; + SchemeChanged?.Invoke(this, nameof(SchemeTracks)); + OnValidateablePropertyChanged(); + } + } + + public String? SchemeCuesheet + { + get { return schemeCuesheet; } + set + { + schemeCuesheet = value; + SchemeChanged?.Invoke(this, nameof(SchemeCuesheet)); + OnValidateablePropertyChanged(); + } + } public override ValidationResult Validate(string property) { ValidationStatus validationStatus = ValidationStatus.NoValidation; diff --git a/AudioCuesheetEditor/Model/Options/ApplicationOptions.cs b/AudioCuesheetEditor/Model/Options/ApplicationOptions.cs index db2d3fb3..97af692a 100644 --- a/AudioCuesheetEditor/Model/Options/ApplicationOptions.cs +++ b/AudioCuesheetEditor/Model/Options/ApplicationOptions.cs @@ -13,7 +13,6 @@ //You should have received a copy of the GNU General Public License //along with Foobar. If not, see //<http: //www.gnu.org/licenses />. -using AudioCuesheetEditor.Model.AudioCuesheet.Import; using AudioCuesheetEditor.Model.Entity; using AudioCuesheetEditor.Model.IO; using AudioCuesheetEditor.Model.IO.Export; @@ -34,35 +33,6 @@ public enum ViewMode public class ApplicationOptions : Validateable, IOptions { public const LogLevel DefaultLogLevel = LogLevel.Information; - public static readonly Importprofile DefaultSelectedImportprofile = new() - { - Name = "Textfile (common data in first line)", - UseRegularExpression = false, - SchemeCuesheet = $"{nameof(ImportCuesheet.Artist)} - {nameof(ImportCuesheet.Title)}\t{nameof(ImportCuesheet.Audiofile)}", - SchemeTracks = $"{nameof(ImportTrack.Artist)} - {nameof(ImportTrack.Title)}\t{nameof(ImportTrack.End)}" - }; - public static readonly ICollection<Importprofile> DefaultImportprofiles = - [ - DefaultSelectedImportprofile, - new() - { - Name = "Textfile (just track data)", - UseRegularExpression = false, - SchemeTracks = $"{nameof(ImportTrack.Artist)} - {nameof(ImportTrack.Title)}\t{nameof(ImportTrack.End)}" - }, - new() - { - Name = "Textfile (track data seperated by ~)", - UseRegularExpression = false, - SchemeTracks = $"{nameof(ImportTrack.Artist)}~{nameof(ImportTrack.Title)}~{nameof(ImportTrack.StartDateTime)}" - }, - new() - { - Name = "Traktor history", - UseRegularExpression = true, - SchemeTracks = @$"<tr>\s*<td>(?<{nameof(ImportTrack.Position)}>\d+)</td>\s*<td>(?<{nameof(ImportTrack.Artist)}>.*?)</td>\s*<td>(?<{nameof(ImportTrack.Title)}>.*?)</td>\s*<td>(?<{nameof(ImportTrack.StartDateTime)}>.*?)</td>\s*</tr>" - } - ]; private string? projectFilename = Projectfile.DefaultFilename; private string? cuesheetFilename = Exportfile.DefaultCuesheetFilename; public String? CuesheetFilename @@ -132,29 +102,12 @@ public String? ProjectFilename } public TimeSpanFormat? TimeSpanFormat { get; set; } public Boolean LinkTracks { get; set; } = true; + public TextImportScheme ImportScheme { get; set; } = TextImportScheme.DefaultTextImportScheme; + public TimeSpanFormat ImportTimeSpanFormat { get; set; } = new(); public uint RecordCountdownTimer { get; set; } = 5; public Boolean FixedTracksTableHeader { get; set; } = false; public String? DisplayTimeSpanFormat { get; set; } public LogLevel MinimumLogLevel { get; set; } = DefaultLogLevel; - [JsonInclude] - public Guid? SelectedImportProfileId { get; private set; } = DefaultSelectedImportprofile.Id; - public ICollection<Importprofile> ImportProfiles { get; set; } = DefaultImportprofiles; - [JsonIgnore] - public Importprofile? SelectedImportProfile - { - get => SelectedImportProfileId.HasValue ? ImportProfiles.FirstOrDefault(x => x.Id == SelectedImportProfileId) : null; - set - { - if (ImportProfiles.Any(x => x.Id == value?.Id) == false) - { - if (value != null) - { - ImportProfiles.Add(value); - } - } - SelectedImportProfileId = value?.Id; - } - } public override ValidationResult Validate(string property) { diff --git a/AudioCuesheetEditor/Model/Options/ExportOptions.cs b/AudioCuesheetEditor/Model/Options/ExportOptions.cs index dfa267a2..eb36939b 100644 --- a/AudioCuesheetEditor/Model/Options/ExportOptions.cs +++ b/AudioCuesheetEditor/Model/Options/ExportOptions.cs @@ -67,17 +67,7 @@ public ExportOptions(ICollection<Exportprofile> exportProfiles, Guid? selectedPr public Exportprofile? SelectedExportProfile { get => SelectedProfileId.HasValue ? ExportProfiles.FirstOrDefault(x => x.Id == SelectedProfileId) : null; - set - { - if (ExportProfiles.Any(x => x.Id == value?.Id) == false) - { - if (value != null) - { - ExportProfiles.Add(value); - } - } - SelectedProfileId = value?.Id; - } + set => SelectedProfileId = value?.Id; } public Guid? SelectedProfileId { get; private set; } } diff --git a/AudioCuesheetEditor/Program.cs b/AudioCuesheetEditor/Program.cs index 785f743c..a6a022fe 100644 --- a/AudioCuesheetEditor/Program.cs +++ b/AudioCuesheetEditor/Program.cs @@ -27,8 +27,10 @@ using Microsoft.AspNetCore.Components.Web; using Microsoft.AspNetCore.Components.WebAssembly.Hosting; using MudBlazor.Services; +using System.ComponentModel.Design; using System.Globalization; using System.Reflection; +using System.Reflection.Emit; using Toolbelt.Blazor.Extensions.DependencyInjection; var builder = WebAssemblyHostBuilder.CreateDefault(args); @@ -50,7 +52,7 @@ builder.Services.AddScoped<ISessionStateContainer, SessionStateContainer>(); builder.Services.AddScoped<ITraceChangeManager, TraceChangeManager>(); builder.Services.AddScoped<ImportManager>(); -builder.Services.AddScoped<ITextImportService, TextImportService>(); +builder.Services.AddScoped<TextImportService>(); builder.Services.AddScoped<CuesheetImportService>(); builder.Services.AddScoped<CuesheetExportService>(); builder.Services.AddScoped<ApplicationOptionsTimeSpanParser>(); diff --git a/AudioCuesheetEditor/Services/IO/CuesheetImportService.cs b/AudioCuesheetEditor/Services/IO/CuesheetImportService.cs index 9f6af21d..47ad4494 100644 --- a/AudioCuesheetEditor/Services/IO/CuesheetImportService.cs +++ b/AudioCuesheetEditor/Services/IO/CuesheetImportService.cs @@ -16,18 +16,16 @@ using AudioCuesheetEditor.Model.AudioCuesheet; using AudioCuesheetEditor.Model.AudioCuesheet.Import; using AudioCuesheetEditor.Model.IO.Import; -using System.Text; using System.Text.RegularExpressions; namespace AudioCuesheetEditor.Services.IO { public class CuesheetImportService { - public static IImportfile Analyse(string fileContent) + public static IImportfile Analyse(IEnumerable<String?> fileContent) { Importfile importfile = new() { - FileContent = fileContent, FileType = ImportFileType.Cuesheet }; try @@ -57,9 +55,11 @@ public static IImportfile Analyse(string fileContent) var regexCDTextfile = new Regex("^" + CuesheetConstants.CuesheetCDTextfile + " \"(?'" + cuesheetCDTextfileGroupName + "'.{0,})\""); var regexCatalogueNumber = new Regex("^" + CuesheetConstants.CuesheetCatalogueNumber + " (?'" + cuesheetCatalogueNumberGroupName + "'.{0,})"); ImportTrack? track = null; - StringBuilder recognizedContent = new(); - foreach (var line in fileContent.Split(Environment.NewLine)) + List<String?> lines = []; + List<String?>? recognizedLines = []; + foreach (var line in fileContent) { + lines.Add(line); String? recognizedLine = line; if (String.IsNullOrEmpty(line) == false) { @@ -277,9 +277,10 @@ public static IImportfile Analyse(string fileContent) } } } - recognizedContent.AppendLine(recognizedLine); + recognizedLines.Add(recognizedLine); } - importfile.FileContentRecognized = recognizedContent.ToString().TrimEnd(Environment.NewLine.ToCharArray()); + importfile.FileContent = lines.AsReadOnly(); + importfile.FileContentRecognized = recognizedLines.AsReadOnly(); } catch (Exception ex) { diff --git a/AudioCuesheetEditor/Services/IO/FileInputManager.cs b/AudioCuesheetEditor/Services/IO/FileInputManager.cs index 96129e13..d3ef5d12 100644 --- a/AudioCuesheetEditor/Services/IO/FileInputManager.cs +++ b/AudioCuesheetEditor/Services/IO/FileInputManager.cs @@ -27,7 +27,7 @@ public class FileInputManager(IJSRuntime jsRuntime, HttpClient httpClient, ILogg private readonly HttpClient _httpClient = httpClient; private readonly ILogger<FileInputManager> _logger = logger; - public AudioCodec? GetAudioCodec(IBrowserFile browserFile) + public static AudioCodec? GetAudioCodec(IBrowserFile browserFile) { AudioCodec? foundAudioCodec = null; var extension = Path.GetExtension(browserFile.Name); @@ -37,7 +37,7 @@ public class FileInputManager(IJSRuntime jsRuntime, HttpClient httpClient, ILogg { foundAudioCodec = audioCodecsFound.FirstOrDefault(); } - if (foundAudioCodec == null) + else { // Second search with mime type or file extension audioCodecsFound = Audiofile.AudioCodecs.Where(x => x.MimeType.Equals(browserFile.ContentType, StringComparison.OrdinalIgnoreCase) || x.FileExtension.Equals(extension, StringComparison.OrdinalIgnoreCase)); @@ -46,35 +46,21 @@ public class FileInputManager(IJSRuntime jsRuntime, HttpClient httpClient, ILogg return foundAudioCodec; } - public bool IsValidAudiofile(IBrowserFile browserFile) + public Boolean CheckFileMimeType(IBrowserFile file, String mimeType, String fileExtension) { - var codec = GetAudioCodec(browserFile); - return codec != null; - } - - public Boolean CheckFileMimeType(IBrowserFile file, String mimeType, IEnumerable<String> fileExtensions) - { - _logger.LogDebug("CheckFileMimeType called with file: file.Name: '{FileName}', file.ContentType: '{ContentType}', mimeType: '{MimeType}', fileExtensions: '{fileExtensions}'", file.Name, file.ContentType, mimeType, fileExtensions); + _logger.LogDebug("CheckFileMimeType called with file: file.Name: '{FileName}', file.ContentType: '{ContentType}', mimeType: '{MimeType}', fileExtension: '{FileExtension}'", file.Name, file.ContentType, mimeType, fileExtension); Boolean fileMimeTypeMatches = false; - if ((file != null) && (String.IsNullOrEmpty(mimeType) == false)) + if ((file != null) && (String.IsNullOrEmpty(mimeType) == false) && (String.IsNullOrEmpty(fileExtension) == false)) { if (String.IsNullOrEmpty(file.ContentType) == false) { - if (mimeType.EndsWith("/*")) - { - var mainType = mimeType.Substring(0, mimeType.Length - 1); - fileMimeTypeMatches = file.ContentType.StartsWith(mainType, StringComparison.CurrentCultureIgnoreCase); - } - else - { - fileMimeTypeMatches = file.ContentType.Equals(mimeType, StringComparison.CurrentCultureIgnoreCase); - } + fileMimeTypeMatches = file.ContentType.Equals(mimeType, StringComparison.CurrentCultureIgnoreCase); } - if ((fileMimeTypeMatches == false) && (fileExtensions.Any())) + if (fileMimeTypeMatches == false) { //Try to find by file extension var extension = Path.GetExtension(file.Name); - fileMimeTypeMatches = fileExtensions.Any(x => x.Equals(extension, StringComparison.CurrentCultureIgnoreCase)); + fileMimeTypeMatches = extension.Equals(fileExtension, StringComparison.CurrentCultureIgnoreCase); } } return fileMimeTypeMatches; @@ -115,7 +101,7 @@ public Boolean CheckFileMimeType(IBrowserFile file, String mimeType, IEnumerable CDTextfile? cdTextfile = null; if (browserFile != null) { - if (CheckFileMimeType(browserFile, FileMimeTypes.Text, [FileExtensions.CDTextfile])) + if (CheckFileMimeType(browserFile, FileMimeTypes.CDTextfile, FileExtensions.CDTextfile)) { cdTextfile = new CDTextfile(browserFile.Name); } @@ -126,11 +112,5 @@ public Boolean CheckFileMimeType(IBrowserFile file, String mimeType, IEnumerable } return cdTextfile; } - - /// <inheritdoc/> - public bool IsValidForImportView(IBrowserFile browserFile) - { - return CheckFileMimeType(browserFile, FileMimeTypes.Text, [FileExtensions.Text, FileExtensions.HTML]); - } } } diff --git a/AudioCuesheetEditor/Services/IO/IFileInputManager.cs b/AudioCuesheetEditor/Services/IO/IFileInputManager.cs index 6bf15fef..7a7326c8 100644 --- a/AudioCuesheetEditor/Services/IO/IFileInputManager.cs +++ b/AudioCuesheetEditor/Services/IO/IFileInputManager.cs @@ -22,16 +22,8 @@ namespace AudioCuesheetEditor.Services.IO { public interface IFileInputManager { - bool IsValidAudiofile(IBrowserFile browserFile); - AudioCodec? GetAudioCodec(IBrowserFile browserFile); - bool CheckFileMimeType(IBrowserFile file, string mimeType, IEnumerable<string> fileExtensions); + bool CheckFileMimeType(IBrowserFile file, string mimeType, string fileExtension); Task<Audiofile?> CreateAudiofileAsync(string? fileInputId, IBrowserFile? browserFile, Action<Task<Stream>>? afterContentStreamLoaded = null); CDTextfile? CreateCDTextfile(IBrowserFile? browserFile); - /// <summary> - /// Checks if the file can be used for the import view - /// </summary> - /// <param name="browserFile"></param> - /// <returns></returns> - bool IsValidForImportView(IBrowserFile browserFile); } } \ No newline at end of file diff --git a/AudioCuesheetEditor/Services/IO/ITextImportService.cs b/AudioCuesheetEditor/Services/IO/ITextImportService.cs deleted file mode 100644 index 3f4f31a6..00000000 --- a/AudioCuesheetEditor/Services/IO/ITextImportService.cs +++ /dev/null @@ -1,25 +0,0 @@ -//This file is part of AudioCuesheetEditor. - -//AudioCuesheetEditor is free software: you can redistribute it and/or modify -//it under the terms of the GNU General Public License as published by -//the Free Software Foundation, either version 3 of the License, or -//(at your option) any later version. - -//AudioCuesheetEditor is distributed in the hope that it will be useful, -//but WITHOUT ANY WARRANTY; without even the implied warranty of -//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -//GNU General Public License for more details. - -//You should have received a copy of the GNU General Public License -//along with Foobar. If not, see -//<http: //www.gnu.org/licenses />. - -using AudioCuesheetEditor.Model.IO.Import; - -namespace AudioCuesheetEditor.Services.IO -{ - public interface ITextImportService - { - Task<IImportfile> AnalyseAsync(string fileContent); - } -} \ No newline at end of file diff --git a/AudioCuesheetEditor/Services/IO/ImportManager.cs b/AudioCuesheetEditor/Services/IO/ImportManager.cs index 729392cf..eef38039 100644 --- a/AudioCuesheetEditor/Services/IO/ImportManager.cs +++ b/AudioCuesheetEditor/Services/IO/ImportManager.cs @@ -13,10 +13,14 @@ //You should have received a copy of the GNU General Public License //along with Foobar. If not, see //<http: //www.gnu.org/licenses />. +using AudioCuesheetEditor.Data.Options; using AudioCuesheetEditor.Model.AudioCuesheet; using AudioCuesheetEditor.Model.AudioCuesheet.Import; using AudioCuesheetEditor.Model.IO; using AudioCuesheetEditor.Model.IO.Audio; +using AudioCuesheetEditor.Model.IO.Import; +using AudioCuesheetEditor.Model.Options; +using AudioCuesheetEditor.Model.Utility; using AudioCuesheetEditor.Services.UI; using Microsoft.AspNetCore.Components.Forms; @@ -30,20 +34,21 @@ public enum ImportFileType Textfile, Audiofile } - public class ImportManager(ISessionStateContainer sessionStateContainer, ITraceChangeManager traceChangeManager, IFileInputManager fileInputManager, ITextImportService textImportService) + public class ImportManager(ISessionStateContainer sessionStateContainer, ILocalStorageOptionsProvider localStorageOptionsProvider, ITraceChangeManager traceChangeManager, IFileInputManager fileInputManager) { private readonly ISessionStateContainer _sessionStateContainer = sessionStateContainer; + private readonly ILocalStorageOptionsProvider _localStorageOptionsProvider = localStorageOptionsProvider; private readonly ITraceChangeManager _traceChangeManager = traceChangeManager; private readonly IFileInputManager _fileInputManager = fileInputManager; - private readonly ITextImportService _textImportService = textImportService; public async Task<Dictionary<IBrowserFile, ImportFileType>> ImportFilesAsync(IEnumerable<IBrowserFile> files) { Dictionary<IBrowserFile, ImportFileType> importFileTypes = []; foreach (var file in files) { - if (_fileInputManager.CheckFileMimeType(file, FileMimeTypes.Projectfile, [FileExtensions.Projectfile])) + if (_fileInputManager.CheckFileMimeType(file, FileMimeTypes.Projectfile, FileExtensions.Projectfile)) { + //TODO: can not be undone var fileContent = await ReadFileContentAsync(file); var cuesheet = Projectfile.ImportFile(fileContent.ToArray()); if (cuesheet != null) @@ -52,31 +57,40 @@ public async Task<Dictionary<IBrowserFile, ImportFileType>> ImportFilesAsync(IEn } importFileTypes.Add(file, ImportFileType.ProjectFile); } - if (_fileInputManager.CheckFileMimeType(file, FileMimeTypes.Cuesheet, [FileExtensions.Cuesheet])) + if (_fileInputManager.CheckFileMimeType(file, FileMimeTypes.Cuesheet, FileExtensions.Cuesheet)) { var fileContent = await ReadFileContentAsync(file); fileContent.Position = 0; using var reader = new StreamReader(fileContent); - var stringFileContent = reader.ReadToEnd(); - ImportCuesheet(stringFileContent); + List<String?> lines = []; + while (reader.EndOfStream == false) + { + lines.Add(reader.ReadLine()); + } + ImportCuesheet(lines); importFileTypes.Add(file, ImportFileType.Cuesheet); } - if (_fileInputManager.IsValidForImportView(file)) + if (_fileInputManager.CheckFileMimeType(file, FileMimeTypes.Text, FileExtensions.Text)) { var fileContent = await ReadFileContentAsync(file); fileContent.Position = 0; using var reader = new StreamReader(fileContent); - var stringFileContent = reader.ReadToEnd(); - await ImportTextAsync(stringFileContent); + List<String?> lines = []; + while (reader.EndOfStream == false) + { + lines.Add(reader.ReadLine()); + } + var options = await _localStorageOptionsProvider.GetOptionsAsync<ApplicationOptions>(); + ImportText([.. lines], options.ImportScheme, options.ImportTimeSpanFormat); importFileTypes.Add(file, ImportFileType.Textfile); } } return importFileTypes; } - public async Task ImportTextAsync(string fileContent) + public void ImportText(IEnumerable<String?> fileContent, TextImportScheme textImportScheme, TimeSpanFormat timeSpanFormat) { - _sessionStateContainer.Importfile = await _textImportService.AnalyseAsync(fileContent); + _sessionStateContainer.Importfile = TextImportService.Analyse(textImportScheme, fileContent, timeSpanFormat); if (_sessionStateContainer.Importfile.AnalysedCuesheet != null) { var importCuesheet = new Cuesheet(); @@ -96,7 +110,7 @@ public void ImportCuesheet() _sessionStateContainer.ResetImport(); } - private void ImportCuesheet(String fileContent) + private void ImportCuesheet(IEnumerable<String?> fileContent) { _sessionStateContainer.Importfile = CuesheetImportService.Analyse(fileContent); if (_sessionStateContainer.Importfile.AnalysedCuesheet != null) diff --git a/AudioCuesheetEditor/Services/IO/TextImportService.cs b/AudioCuesheetEditor/Services/IO/TextImportService.cs index 92946a85..5d46e3c4 100644 --- a/AudioCuesheetEditor/Services/IO/TextImportService.cs +++ b/AudioCuesheetEditor/Services/IO/TextImportService.cs @@ -14,184 +14,120 @@ //along with Foobar. If not, see //<http: //www.gnu.org/licenses />. -using AudioCuesheetEditor.Data.Options; using AudioCuesheetEditor.Model.AudioCuesheet; using AudioCuesheetEditor.Model.AudioCuesheet.Import; using AudioCuesheetEditor.Model.IO.Audio; using AudioCuesheetEditor.Model.IO.Import; -using AudioCuesheetEditor.Model.Options; using AudioCuesheetEditor.Model.Utility; using System.Reflection; -using System.Text; using System.Text.RegularExpressions; namespace AudioCuesheetEditor.Services.IO { - public class TextImportService(ILocalStorageOptionsProvider localStorageOptionsProvider) : ITextImportService + public class TextImportService { - private readonly ILocalStorageOptionsProvider _localStorageOptionsProvider = localStorageOptionsProvider; - - public async Task<IImportfile> AnalyseAsync(string fileContent) + public static IImportfile Analyse(TextImportScheme textImportScheme, IEnumerable<String?> fileContent, TimeSpanFormat? timeSpanFormat = null) { Importfile importfile = new() { - FileContent = fileContent, - FileContentRecognized = fileContent, - AnalysedCuesheet = new ImportCuesheet(), FileType = ImportFileType.Textfile }; try { - var options = await _localStorageOptionsProvider.GetOptionsAsync<ApplicationOptions>(); - var importprofile = options.SelectedImportProfile ?? throw new InvalidOperationException("Selected import profiles is not set!"); - SearchForCuesheetData(ref importfile, fileContent, importprofile); - SearchForTrackData(ref importfile, fileContent, importprofile); - } - catch (Exception ex) - { - importfile.FileContentRecognized = fileContent; - importfile.AnalyseException = ex; - importfile.AnalysedCuesheet = null; - } - return importfile; - } - - private static string ApplyRegexAndMarkGroups(object entity, Regex regex, string input, TimeSpanFormat? timeSpanFormat) - { - return regex.Replace(input, match => - { - string result = match.Value; - var groupInfos = new List<(int RelIndex, int Length, string Value, string Key)>(); - for (int matchCounter = 1; matchCounter < match.Groups.Count; matchCounter++) - { - var group = match.Groups[matchCounter]; - var key = regex.GroupNameFromNumber(matchCounter); - if (!string.IsNullOrEmpty(key) && key != matchCounter.ToString()) - { - var property = entity.GetType().GetProperty(key, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); - if (property != null) - { - SetValue(entity, property, group.Value, timeSpanFormat); - } - int relIndex = group.Index - match.Index; - groupInfos.Add((relIndex, group.Length, group.Value, key)); - } - } - if (groupInfos.Count == 0) - { - return result; - } - groupInfos.Sort((a, b) => b.RelIndex.CompareTo(a.RelIndex)); - var sb = new StringBuilder(result); - foreach (var (RelIndex, Length, Value, Key) in groupInfos) - { - sb.Remove(RelIndex, Length); - sb.Insert(RelIndex, string.Format(CuesheetConstants.RecognizedMarkHTML, Value)); - } - return sb.ToString(); - }); - } - - private static void SearchForCuesheetData(ref Importfile importfile, string fileContent, Importprofile importprofile) - { - if (string.IsNullOrWhiteSpace(importprofile.SchemeCuesheet) == false) - { - var cuesheet = importfile.AnalysedCuesheet; - Regex regex; - if (importprofile.UseRegularExpression == true) + importfile.FileContent = fileContent; + importfile.AnalysedCuesheet = new ImportCuesheet(); + Boolean cuesheetRecognized = false; + List<String?> recognizedFileContent = []; + Regex? regExCuesheet = null, regExTracks = null; + if (String.IsNullOrEmpty(textImportScheme.SchemeCuesheet) == false) { - regex = new Regex(importprofile.SchemeCuesheet, RegexOptions.Multiline); + regExCuesheet = CreateCuesheetRegexPattern(textImportScheme.SchemeCuesheet); } - else + if (String.IsNullOrEmpty(textImportScheme.SchemeTracks) == false) { - regex = CreateCuesheetRegexPattern(importprofile.SchemeCuesheet); + regExTracks = CreateTrackRegexPattern(textImportScheme.SchemeTracks); } - - if (importprofile.UseRegularExpression) + foreach (var line in fileContent) { - importfile.FileContentRecognized = ApplyRegexAndMarkGroups(cuesheet!, regex, fileContent, importprofile.TimeSpanFormat); - } - else - { - var sb = new StringBuilder(); - using (var reader = new StringReader(fileContent)) + var recognizedLine = line; + if (String.IsNullOrEmpty(line) == false) { - string? line; - while ((line = reader.ReadLine()) != null) + Boolean recognized = false; + if ((recognized == false) && (cuesheetRecognized == false) && (regExCuesheet != null)) { - var markedLine = ApplyRegexAndMarkGroups(cuesheet!, regex, line, importprofile.TimeSpanFormat); - sb.AppendLine(markedLine); - if (!string.Equals(markedLine, line)) - { - //We found the first occurence, break the loop - //Attach the rest of the file to FileContentRecognized - sb.Append(reader.ReadToEnd()); - break; - } + recognizedLine = AnalyseLine(line, importfile.AnalysedCuesheet, regExCuesheet, timeSpanFormat); + recognized = recognizedLine != null; + cuesheetRecognized = recognizedLine != null; + } + if ((recognized == false) && (regExTracks != null)) + { + var track = new ImportTrack(); + recognizedLine = AnalyseLine(line, track, regExTracks, timeSpanFormat); + recognized = recognizedLine != null; + importfile.AnalysedCuesheet.Tracks.Add(track); } } - importfile.FileContentRecognized = sb.ToString(); + recognizedFileContent.Add(recognizedLine); + } + if (recognizedFileContent.Count > 0) + { + importfile.FileContentRecognized = recognizedFileContent.AsReadOnly(); } } + catch (Exception ex) + { + importfile.FileContent = fileContent; + importfile.FileContentRecognized = fileContent; + importfile.AnalyseException = ex; + importfile.AnalysedCuesheet = null; + } + return importfile; } - private static void SearchForTrackData(ref Importfile importfile, string fileContent, Importprofile importprofile) + private static String? AnalyseLine(String line, object entity, Regex regex, TimeSpanFormat? timeSpanFormat) { - if (string.IsNullOrWhiteSpace(importprofile.SchemeTracks) == false) + String? recognized = null; + string? recognizedLine = line; + if (String.IsNullOrEmpty(line) == false) { - Regex regex; - if (importprofile.UseRegularExpression == true) - { - regex = new Regex(importprofile.SchemeTracks, RegexOptions.Multiline); - } - else - { - regex = CreateTrackRegexPattern(importprofile.SchemeTracks); - } - - var cuesheet = importfile.AnalysedCuesheet; - importfile.FileContentRecognized ??= fileContent; - if (importprofile.UseRegularExpression) - { - - importfile.FileContentRecognized = regex.Replace(importfile.FileContentRecognized, - match => - { - var track = new ImportTrack(); - string marked = ApplyRegexAndMarkGroups(track, regex, match.Value, importprofile.TimeSpanFormat); - cuesheet!.Tracks.Add(track); - return marked; - } - ); - } - else + var match = regex.Match(line); + if (match.Success) { - var sb = new StringBuilder(); - using (var reader = new StringReader(importfile.FileContentRecognized)) + for (int groupCounter = 1; groupCounter < match.Groups.Count; groupCounter++) { - string? line; - while ((line = reader.ReadLine()) != null) + var key = match.Groups.Keys.ElementAt(groupCounter); + var group = match.Groups.GetValueOrDefault(key); + if ((group != null) && (group.Success)) { - // Check if this line is already analyzed - if (line.Contains(CuesheetConstants.MarkHTMLStart) == false) + var property = entity.GetType().GetProperty(key, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + if (property != null) { - var track = new ImportTrack(); - var markedLine = ApplyRegexAndMarkGroups(track, regex, line, importprofile.TimeSpanFormat); - if (!string.Equals(markedLine, line)) - { - cuesheet!.Tracks.Add(track); - } - sb.AppendLine(markedLine); + SetValue(entity, property, group.Value, timeSpanFormat); + recognizedLine = string.Concat(recognizedLine.AsSpan(0, group.Index + (13 * (groupCounter - 1))) + , String.Format(CuesheetConstants.RecognizedMarkHTML, group.Value) + , recognizedLine.AsSpan(group.Index + (13 * (groupCounter - 1)) + group.Length)); } else { - sb.AppendLine(line); + throw new NullReferenceException(String.Format("Property '{0}' was not found for line content {1}", key, line)); } } + else + { + throw new NullReferenceException(String.Format("Group '{0}' could not be found!", key)); + } + } + if (recognizedLine.Contains(CuesheetConstants.RecognizedMarkHTML.Substring(0, CuesheetConstants.RecognizedMarkHTML.IndexOf("{0}")))) + { + recognized = recognizedLine; } - importfile.FileContentRecognized = sb.ToString().TrimEnd(Environment.NewLine.ToCharArray()); + } + else + { + recognized = line; } } + return recognized; } private static void SetValue(object entity, PropertyInfo property, string value, TimeSpanFormat? timeSpanFormat) @@ -227,135 +163,57 @@ private static void SetValue(object entity, PropertyInfo property, string value, private static Regex CreateCuesheetRegexPattern(string scheme) { - string[] fieldNames = - [ - nameof(ImportCuesheet.Artist), - nameof(ImportCuesheet.Title), - nameof(ImportCuesheet.Audiofile), - nameof(ImportCuesheet.CDTextfile), - nameof(ImportCuesheet.Cataloguenumber) - ]; - var parts = new List<string>(); - int idx = 0; - while (idx < scheme.Length) + var regex = new Regex(scheme); + var groupNames = regex.GetGroupNames(); + //GroupNames always has a group "0", so we count for more than one group + if (groupNames.Any(x => x != "0")) { - var field = fieldNames.FirstOrDefault(fn => scheme.IndexOf(fn, idx, StringComparison.Ordinal) == idx); - if (field != null) - { - parts.Add(field); - idx += field.Length; - } - else - { - int nextFieldIdx = scheme.Length; - foreach (var fn in fieldNames) - { - int pos = scheme.IndexOf(fn, idx, StringComparison.Ordinal); - if (pos >= 0 && pos < nextFieldIdx) - { - nextFieldIdx = pos; - } - } - string separator = scheme.Substring(idx, nextFieldIdx - idx); - parts.Add(separator); - idx = nextFieldIdx; - } + return regex; } - - var regexBuilder = new StringBuilder("^"); - for (int i = 0; i < parts.Count; i++) + else { - var part = parts[i]; - if (fieldNames.Contains(part)) - { - bool isLast = i == parts.Count - 1 || parts.Skip(i + 1).All(p => !fieldNames.Contains(p)); - if (isLast) - { - regexBuilder.Append($@"(?<{part}>.+)"); - } - else - { - regexBuilder.Append($@"(?<{part}>.+?)"); - } - } - else - { - string sep = Regex.Escape(part).Replace("\\t", @"\t{1,}"); - regexBuilder.Append(sep); - } - } - regexBuilder.Append('$'); + var regexString = Regex.Escape(scheme); - return new Regex(regexBuilder.ToString()); + regexString = regexString.Replace(nameof(Cuesheet.Artist), $"(?<{nameof(Cuesheet.Artist)}>.+)"); + regexString = regexString.Replace(nameof(Cuesheet.Title), $"(?<{nameof(Cuesheet.Title)}>.+)"); + regexString = regexString.Replace(nameof(Cuesheet.Audiofile), $"(?<{nameof(Cuesheet.Audiofile)}>.+)"); + regexString = regexString.Replace(nameof(Cuesheet.CDTextfile), $"(?<{nameof(Cuesheet.CDTextfile)}>.+)"); + regexString = regexString.Replace(nameof(Cuesheet.Cataloguenumber), $"(?<{nameof(Cuesheet.Cataloguenumber)}>.+)"); + //Replace tab with non matching group + regexString = regexString.Replace("\\t", "(?:...\\t)"); + + return new Regex(regexString); + } } private static Regex CreateTrackRegexPattern(string scheme) { - string[] fieldNames = - [ - nameof(ImportTrack.Artist), - nameof(ImportTrack.Title), - nameof(ImportTrack.Begin), - nameof(ImportTrack.End), - nameof(ImportTrack.Length), - nameof(ImportTrack.Position), - nameof(ImportTrack.Flags), - nameof(ImportTrack.PreGap), - nameof(ImportTrack.PostGap), - nameof(ImportTrack.StartDateTime) - ]; - var parts = new List<string>(); - int idx = 0; - while (idx < scheme.Length) + var regex = new Regex(scheme); + var groupNames = regex.GetGroupNames(); + //GroupNames always has a group "0", so we count for more than one group + if (groupNames.Any(x => x != "0")) { - var field = fieldNames.FirstOrDefault(fn => scheme.IndexOf(fn, idx, StringComparison.Ordinal) == idx); - if (field != null) - { - parts.Add(field); - idx += field.Length; - } - else - { - int nextFieldIdx = scheme.Length; - foreach (var fn in fieldNames) - { - int pos = scheme.IndexOf(fn, idx, StringComparison.Ordinal); - if (pos >= 0 && pos < nextFieldIdx) - { - nextFieldIdx = pos; - } - } - string separator = scheme.Substring(idx, nextFieldIdx - idx); - parts.Add(separator); - idx = nextFieldIdx; - } + return regex; } - - var regexBuilder = new StringBuilder("^"); - for (int i = 0; i < parts.Count; i++) - { - var part = parts[i]; - if (fieldNames.Contains(part)) - { - bool isLast = i == parts.Count - 1 || parts.Skip(i + 1).All(p => !fieldNames.Contains(p)); - if (isLast) - { - regexBuilder.Append($@"(?<{part}>.+)"); - } - else - { - regexBuilder.Append($@"(?<{part}>.+?)"); - } - } - else - { - string sep = Regex.Escape(part).Replace("\\t", @"\t{1,}"); - regexBuilder.Append(sep); - } + else + { + var regexString = Regex.Escape(scheme); + + regexString = regexString.Replace(nameof(ImportTrack.Artist), $"(?<{nameof(ImportTrack.Artist)}>.+)"); + regexString = regexString.Replace(nameof(ImportTrack.Title), $"(?<{nameof(ImportTrack.Title)}>.+)"); + regexString = regexString.Replace(nameof(ImportTrack.Begin), $"(?<{nameof(ImportTrack.Begin)}>.+)"); + regexString = regexString.Replace(nameof(ImportTrack.End), $"(?<{nameof(ImportTrack.End)}>.+)"); + regexString = regexString.Replace(nameof(ImportTrack.Length), $"(?<{nameof(ImportTrack.Length)}>.+)"); + regexString = regexString.Replace(nameof(ImportTrack.Position), $"(?<{nameof(ImportTrack.Position)}>.+)"); + regexString = regexString.Replace(nameof(ImportTrack.Flags), $"(?<{nameof(ImportTrack.Flags)}>.+)"); + regexString = regexString.Replace(nameof(ImportTrack.PreGap), $"(?<{nameof(ImportTrack.PreGap)}>.+)"); + regexString = regexString.Replace(nameof(ImportTrack.PostGap), $"(?<{nameof(ImportTrack.PostGap)}>.+)"); + regexString = regexString.Replace(nameof(ImportTrack.StartDateTime), $"(?<{nameof(ImportTrack.StartDateTime)}>.+)"); + //Replace tab with non matching group + regexString = regexString.Replace("\\t", "(?:...\\t)"); + + return new Regex(regexString); } - regexBuilder.Append('$'); - - return new Regex(regexBuilder.ToString()); } } } diff --git a/AudioCuesheetEditor/Shared/AppBar.razor b/AudioCuesheetEditor/Shared/AppBar.razor index 522e2cf8..c4413df8 100644 --- a/AudioCuesheetEditor/Shared/AppBar.razor +++ b/AudioCuesheetEditor/Shared/AppBar.razor @@ -35,10 +35,10 @@ along with Foobar. If not, see { <MudButtonGroup Color="Color.Primary" Variant="Variant.Filled" OverrideStyles> <MudTooltip Text="@_localizer["Undo last edit"]"> - <MudIconButton Icon="@Icons.Material.Outlined.Undo" aria-label="undo" Disabled="!TraceChangeManager.CanUndo" OnClick="() => TraceChangeManager.Undo()" /> + <MudIconButton Icon="@Icons.Material.Outlined.Undo" Disabled="!TraceChangeManager.CanUndo" OnClick="() => TraceChangeManager.Undo()" /> </MudTooltip> <MudTooltip Text="@_localizer["Redo last edit"]"> - <MudIconButton Icon="@Icons.Material.Outlined.Redo" aria-label="redo" Disabled="!TraceChangeManager.CanRedo" OnClick="() => TraceChangeManager.Redo()" /> + <MudIconButton Icon="@Icons.Material.Outlined.Redo" Disabled="!TraceChangeManager.CanRedo" OnClick="() => TraceChangeManager.Redo()" /> </MudTooltip> </MudButtonGroup> } diff --git a/AudioCuesheetEditor/Shared/Cuesheet/EditSections.razor b/AudioCuesheetEditor/Shared/Cuesheet/EditSections.razor index 7f5d21f4..412434c1 100644 --- a/AudioCuesheetEditor/Shared/Cuesheet/EditSections.razor +++ b/AudioCuesheetEditor/Shared/Cuesheet/EditSections.razor @@ -21,7 +21,6 @@ along with Foobar. If not, see @inject ApplicationOptionsTimeSpanParser _applicationOptionsTimeSpanParser @inject ValidationService _validationService @inject IDialogService _dialogService -@inject IFileInputManager _fileInputManager <MudButtonGroup OverrideStyles="false"> <MudTooltip Text="@_localizer["Add new section"]"> @@ -129,9 +128,13 @@ along with Foobar. If not, see void AudiofileSelected(CuesheetSection section, IBrowserFile? browserFile) { - if ((browserFile != null) && (_fileInputManager.IsValidAudiofile(browserFile) == true)) + if (browserFile != null) { - section.AudiofileName = browserFile?.Name; + var codec = FileInputManager.GetAudioCodec(browserFile); + if (codec != null) + { + section.AudiofileName = browserFile?.Name; + } } else { diff --git a/AudioCuesheetEditor/Shared/Dialogs/SelectFileDialog.razor b/AudioCuesheetEditor/Shared/Dialogs/SelectFileDialog.razor index 8198bb4f..68e64b89 100644 --- a/AudioCuesheetEditor/Shared/Dialogs/SelectFileDialog.razor +++ b/AudioCuesheetEditor/Shared/Dialogs/SelectFileDialog.razor @@ -51,8 +51,8 @@ along with Foobar. If not, see invalidDropFileNames.Clear(); foreach (var file in files) { - if ((_fileInputManager.CheckFileMimeType(file, FileMimeTypes.Projectfile, [FileExtensions.Projectfile]) == false) - && (_fileInputManager.CheckFileMimeType(file, FileMimeTypes.Cuesheet, [FileExtensions.Cuesheet]) == false)) + if ((_fileInputManager.CheckFileMimeType(file, FileMimeTypes.Projectfile, FileExtensions.Projectfile) == false) + && (_fileInputManager.CheckFileMimeType(file, FileMimeTypes.Cuesheet, FileExtensions.Cuesheet) == false)) { invalidDropFileNames.Add(file.Name); } diff --git a/AudioCuesheetEditor/Shared/Import/ImportFileContent.razor b/AudioCuesheetEditor/Shared/Import/ImportFileContent.razor index c8c5c83a..d90881ca 100644 --- a/AudioCuesheetEditor/Shared/Import/ImportFileContent.razor +++ b/AudioCuesheetEditor/Shared/Import/ImportFileContent.razor @@ -21,34 +21,43 @@ along with Foobar. If not, see @inject ISessionStateContainer _sessionStateContainer <MudTabs Rounded ApplyEffectsToContainer Outlined Color="Color.Success"> - <MudTabPanel Text="@_localizer["Analyzed file content"]" Icon="@Icons.Material.Outlined.Analytics"> - <MudField Class="ml-2 mr-2"> - <pre> - @if (FileContentRecognized != null) - { - @((MarkupString)SanitizeHTML(FileContentRecognized)) - } - </pre> - </MudField> - </MudTabPanel> - <MudTabPanel Text="@_localizer["Edit"]" Icon="@Icons.Material.Outlined.Edit"> - <MudTextField Class="ml-2 mr-2" T="string" AutoGrow Text="@_sessionStateContainer.Importfile?.FileContent" TextChanged="FileContent_TextChangedAsync" /> - </MudTabPanel> + @if (FileContentRecognized != null) + { + <MudTabPanel Text="@_localizer["Analyzed file content"]" Icon="@Icons.Material.Outlined.Analytics"> + <MudField Class="ml-2 mr-2"> + <pre> + @foreach (var line in FileContentRecognized) + { + @((MarkupString)String.Format("{0}<br />", line)) + } + </pre> + </MudField> + </MudTabPanel> + <MudTabPanel Text="@_localizer["Edit"]" Icon="@Icons.Material.Outlined.Edit"> + <MudTextField Class="ml-2 mr-2" T="string" Lines="@FileContentRecognized.Count()" AutoGrow Text="@FileContent" TextChanged="FileContent_TextChangedAsync" /> + </MudTabPanel> + } </MudTabs> @code { [Parameter] public EventCallback<string> FileContentChanged { get; set; } - public String? FileContentRecognized => _sessionStateContainer.Importfile?.FileContentRecognized; - - async Task FileContent_TextChangedAsync(string newFileContent) + public IEnumerable<String?>? FileContentRecognized => _sessionStateContainer.Importfile?.FileContentRecognized; + public String FileContent { - await FileContentChanged.InvokeAsync(newFileContent); + get + { + String fileContent = String.Empty; + if (_sessionStateContainer.Importfile?.FileContent != null) + { + fileContent = String.Join(Environment.NewLine, _sessionStateContainer.Importfile.FileContent); + } + return fileContent; + } } - string SanitizeHTML(string input) + async Task FileContent_TextChangedAsync(string newFileContent) { - var sanitizer = new HtmlSanitizer(); - return sanitizer.Sanitize(input); + await FileContentChanged.InvokeAsync(newFileContent); } } diff --git a/AudioCuesheetEditor/Shared/Import/Importprofiles.de.resx b/AudioCuesheetEditor/Shared/Import/ImportSchemes.de.resx similarity index 82% rename from AudioCuesheetEditor/Shared/Import/Importprofiles.de.resx rename to AudioCuesheetEditor/Shared/Import/ImportSchemes.de.resx index a7736980..1262de70 100644 --- a/AudioCuesheetEditor/Shared/Import/Importprofiles.de.resx +++ b/AudioCuesheetEditor/Shared/Import/ImportSchemes.de.resx @@ -117,8 +117,11 @@ <resheader name="writer"> <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> </resheader> - <data name="Are you sure you want to reset the import profiles to factory default?" xml:space="preserve"> - <value>Möchten Sie wirklich die Importprofile auf Werkseinstellungen zurücksetzen?</value> + <data name="Add placeholder" xml:space="preserve"> + <value>Platzhalter hinzufügen</value> + </data> + <data name="Are you sure you want to reset the import schemes to factory default?" xml:space="preserve"> + <value>Möchten Sie wirklich die Importschemata auf Werkseinstellungen zurücksetzen?</value> </data> <data name="Artist" xml:space="preserve"> <value>Künstler</value> @@ -127,7 +130,7 @@ <value>Audiodatei</value> </data> <data name="Begin" xml:space="preserve"> - <value>Beginn</value> + <value>Start</value> </data> <data name="Cataloguenumber" xml:space="preserve"> <value>Katalognummer</value> @@ -144,26 +147,14 @@ <data name="End" xml:space="preserve"> <value>Ende</value> </data> - <data name="Enter the common data scheme for this profile here" xml:space="preserve"> - <value>Geben Sie hier das Allgemeine Informationen Schema für das Profil an</value> - </data> - <data name="Enter the name for this profile here" xml:space="preserve"> - <value>Geben Sie hier den Namen für das Profil an</value> - </data> - <data name="Enter the time format for this profile here" xml:space="preserve"> - <value>Geben Sie hier das Zeitformat für das Profil an</value> - </data> - <data name="Enter the track scheme for this profile here" xml:space="preserve"> - <value>Geben Sie hier das Titel Schema für das Profil an</value> - </data> <data name="Flags" xml:space="preserve"> <value>Markierungen</value> </data> <data name="Hours" xml:space="preserve"> <value>Stunden</value> </data> - <data name="Import profile" xml:space="preserve"> - <value>Import Profil</value> + <data name="Import schemes" xml:space="preserve"> + <value>Importschemata</value> </data> <data name="Length" xml:space="preserve"> <value>Länge</value> @@ -174,12 +165,6 @@ <data name="Minutes" xml:space="preserve"> <value>Minuten</value> </data> - <data name="Name" xml:space="preserve"> - <value>Name</value> - </data> - <data name="New import profile" xml:space="preserve"> - <value>Neues Importprofil</value> - </data> <data name="Position" xml:space="preserve"> <value>Position</value> </data> @@ -189,24 +174,18 @@ <data name="PreGap" xml:space="preserve"> <value>Vorlücke</value> </data> - <data name="Profile name" xml:space="preserve"> - <value>Profil Name</value> - </data> - <data name="Scheme common data" xml:space="preserve"> - <value>Schema Allgemeine Informationen</value> - </data> - <data name="Scheme tracks" xml:space="preserve"> - <value>Schema Titel</value> - </data> - <data name="Search using regular expressions" xml:space="preserve"> - <value>Suche mit regulären Ausdrücken</value> - </data> <data name="Seconds" xml:space="preserve"> <value>Sekunden</value> </data> <data name="StartDateTime" xml:space="preserve"> <value>Startzeitpunkt</value> </data> + <data name="Textimport scheme cuesheet" xml:space="preserve"> + <value>Textimportschema Cuesheet</value> + </data> + <data name="Textimport scheme tracks" xml:space="preserve"> + <value>Textimportschema Titel</value> + </data> <data name="Time input format for import" xml:space="preserve"> <value>Zeitformat für den Import</value> </data> diff --git a/AudioCuesheetEditor/Shared/Import/ImportSchemes.razor b/AudioCuesheetEditor/Shared/Import/ImportSchemes.razor new file mode 100644 index 00000000..29de29ff --- /dev/null +++ b/AudioCuesheetEditor/Shared/Import/ImportSchemes.razor @@ -0,0 +1,133 @@ +@using System.Linq.Expressions +<!-- +This file is part of AudioCuesheetEditor. + +AudioCuesheetEditor is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +AudioCuesheetEditor is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Foobar. If not, see +<http: //www.gnu.org/licenses />. +--> +@inherits BaseLocalizedComponent + +@inject IStringLocalizer<ImportSchemes> _localizer +@inject ValidationService _validationService +@inject IDialogService _dialogService + +<MudStack Row AlignItems="AlignItems.Baseline" Justify="Justify.SpaceBetween"> + <MudText Typo="Typo.h5">@_localizer["Import schemes"]</MudText> + <MudIconButton Icon="@Icons.Material.Outlined.LockReset" Color="Color.Warning" Variant="Variant.Filled" OnClick="ResetSchemes" /> +</MudStack> +<MudStack Row AlignItems="AlignItems.Baseline"> + <MudTextField T="string" @ref="importSchemeCuesheetTextField" Label="@_localizer["Textimport scheme cuesheet"]" Text="@ApplicationOptions?.ImportScheme.SchemeCuesheet" + TextChanged="ImportSchemeCuesheetTextChangedAsync" Clearable Error="String.IsNullOrEmpty(GetValidationErrorMessage(ApplicationOptions?.ImportScheme, nameof(TextImportScheme.SchemeCuesheet))) == false" + ErrorText="@GetValidationErrorMessage(ApplicationOptions?.ImportScheme, nameof(TextImportScheme.SchemeCuesheet))" /> + <MudMenu Label="@_localizer["Add placeholder"]" Color="Color.Primary" Variant="Variant.Filled" EndIcon="@Icons.Material.Outlined.KeyboardArrowDown"> + @foreach (var scheme in TextImportScheme.AvailableSchemeCuesheet) + { + <MudMenuItem OnClick="() => AppendPlaceholderToTextField(importSchemeCuesheetTextField, scheme)">@_localizer[scheme]</MudMenuItem> + } + </MudMenu> +</MudStack> +<MudStack Row AlignItems="AlignItems.Baseline"> + <MudTextField T="string" @ref="importSchemeTracksTextField" Label="@_localizer["Textimport scheme tracks"]" Text="@ApplicationOptions?.ImportScheme.SchemeTracks" + TextChanged="ImportSchemeTracksTextChangedAsync" Clearable Error="String.IsNullOrEmpty(GetValidationErrorMessage(ApplicationOptions?.ImportScheme, nameof(TextImportScheme.SchemeTracks))) == false" + ErrorText="@GetValidationErrorMessage(ApplicationOptions?.ImportScheme, nameof(TextImportScheme.SchemeTracks))" /> + <MudMenu Label="@_localizer["Add placeholder"]" Color="Color.Primary" Variant="Variant.Filled" EndIcon="@Icons.Material.Outlined.KeyboardArrowDown"> + @foreach (var scheme in TextImportScheme.AvailableSchemesTrack) + { + <MudMenuItem OnClick="() => AppendPlaceholderToTextField(importSchemeTracksTextField, scheme)">@_localizer[scheme]</MudMenuItem> + } + </MudMenu> +</MudStack> +<MudStack Row AlignItems="AlignItems.Baseline"> + <MudTextField T="string" @ref="importTimeInputFormatTextField" Label="@_localizer["Time input format for import"]" Text="@ApplicationOptions?.ImportTimeSpanFormat?.Scheme" + TextChanged="ImportTimeInputFormatChangedAsync" Clearable Error="String.IsNullOrEmpty(GetValidationErrorMessage(ApplicationOptions?.ImportTimeSpanFormat, nameof(TimeSpanFormat.Scheme))) == false" + ErrorText="@GetValidationErrorMessage(ApplicationOptions?.ImportTimeSpanFormat, nameof(TimeSpanFormat.Scheme))" /> + <MudMenu Label="@_localizer["Add placeholder"]" Color="Color.Primary" Variant="Variant.Filled" EndIcon="@Icons.Material.Outlined.KeyboardArrowDown"> + @foreach (var scheme in TimeSpanFormat.AvailableTimespanScheme) + { + <MudMenuItem OnClick="() => AppendPlaceholderToTextField(importTimeInputFormatTextField, scheme)">@_localizer[scheme]</MudMenuItem> + } + </MudMenu> +</MudStack> + +@code { + [Parameter] + public EventCallback<string> ImportSchemeCuesheetChanged { get; set; } + + [Parameter] + public EventCallback<string> ImportSchemeTracksChanged { get; set; } + + [Parameter] + public EventCallback<string> ImportTimeInputFormatChanged { get; set; } + + MudTextField<string>? importSchemeCuesheetTextField, importSchemeTracksTextField, importTimeInputFormatTextField; + + void AppendPlaceholderToTextField(MudTextField<string>? mudTextField, string placeholder) + { + mudTextField?.SetText($"{mudTextField.Text}{placeholder}"); + } + + async Task ImportSchemeCuesheetTextChangedAsync(string newScheme) + { + await LocalStorageOptionsProvider.SaveOptionsValueAsync<ApplicationOptions>(x => x.ImportScheme.SchemeCuesheet, newScheme); + await ImportSchemeCuesheetChanged.InvokeAsync(newScheme); + } + + async Task ImportSchemeTracksTextChangedAsync(string newScheme) + { + await LocalStorageOptionsProvider.SaveOptionsValueAsync<ApplicationOptions>(x => x.ImportScheme.SchemeTracks, newScheme); + await ImportSchemeTracksChanged.InvokeAsync(newScheme); + } + + async Task ImportTimeInputFormatChangedAsync(string newScheme) + { + await LocalStorageOptionsProvider.SaveOptionsValueAsync<ApplicationOptions>(x => x.ImportTimeSpanFormat.Scheme, newScheme); + await ImportTimeInputFormatChanged.InvokeAsync(newScheme); + } + + String? GetValidationErrorMessage(object? model, string propertyName) + { + String? validationErrorMessage = null; + if (model != null) + { + var validationMessages = _validationService.Validate(model, propertyName); + if (validationMessages.Count() > 0) + { + validationErrorMessage = String.Join(Environment.NewLine, validationMessages); + } + } + return validationErrorMessage; + } + + async Task ResetSchemes() + { + if (ApplicationOptions != null) + { + var parameters = new DialogParameters<ConfirmDialog> + { + { x => x.ConfirmText, _localizer["Are you sure you want to reset the import schemes to factory default?"] }, + }; + var dialog = await _dialogService.ShowAsync<ConfirmDialog>(_localizer["Confirm"], parameters); + var result = await dialog.Result; + if (result?.Canceled == false) + { + ApplicationOptions.ImportScheme = TextImportScheme.DefaultTextImportScheme; + ApplicationOptions.ImportTimeSpanFormat = new(); + await LocalStorageOptionsProvider.SaveOptionsAsync(ApplicationOptions); + await ImportSchemeCuesheetChanged.InvokeAsync(ApplicationOptions.ImportScheme.SchemeCuesheet); + await ImportSchemeTracksChanged.InvokeAsync(ApplicationOptions.ImportScheme.SchemeTracks); + await ImportTimeInputFormatChanged.InvokeAsync(ApplicationOptions.ImportTimeSpanFormat.Scheme); + } + } + } +} diff --git a/AudioCuesheetEditor/Shared/Import/Importprofiles.resx b/AudioCuesheetEditor/Shared/Import/ImportSchemes.resx similarity index 83% rename from AudioCuesheetEditor/Shared/Import/Importprofiles.resx rename to AudioCuesheetEditor/Shared/Import/ImportSchemes.resx index 5bd62cc2..e6597bc9 100644 --- a/AudioCuesheetEditor/Shared/Import/Importprofiles.resx +++ b/AudioCuesheetEditor/Shared/Import/ImportSchemes.resx @@ -117,8 +117,11 @@ <resheader name="writer"> <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> </resheader> - <data name="Are you sure you want to reset the import profiles to factory default?" xml:space="preserve"> - <value>Are you sure you want to reset the import profiles to factory default?</value> + <data name="Add placeholder" xml:space="preserve"> + <value>Add placeholder</value> + </data> + <data name="Are you sure you want to reset the import schemes to factory default?" xml:space="preserve"> + <value>Are you sure you want to reset the import schemes to factory default?</value> </data> <data name="Artist" xml:space="preserve"> <value>Artist</value> @@ -144,26 +147,14 @@ <data name="End" xml:space="preserve"> <value>End</value> </data> - <data name="Enter the common data scheme for this profile here" xml:space="preserve"> - <value>Enter the common data scheme for this profile here</value> - </data> - <data name="Enter the name for this profile here" xml:space="preserve"> - <value>Enter the name for this profile here</value> - </data> - <data name="Enter the time format for this profile here" xml:space="preserve"> - <value>Enter the time format for this profile here</value> - </data> - <data name="Enter the track scheme for this profile here" xml:space="preserve"> - <value>Enter the track scheme for this profile here</value> - </data> <data name="Flags" xml:space="preserve"> <value>Flags</value> </data> <data name="Hours" xml:space="preserve"> <value>Hours</value> </data> - <data name="Import profile" xml:space="preserve"> - <value>Import profile</value> + <data name="Import schemes" xml:space="preserve"> + <value>Import schemes</value> </data> <data name="Length" xml:space="preserve"> <value>Length</value> @@ -174,12 +165,6 @@ <data name="Minutes" xml:space="preserve"> <value>Minutes</value> </data> - <data name="Name" xml:space="preserve"> - <value>Name</value> - </data> - <data name="New import profile" xml:space="preserve"> - <value>New import profile</value> - </data> <data name="Position" xml:space="preserve"> <value>Position</value> </data> @@ -189,24 +174,18 @@ <data name="PreGap" xml:space="preserve"> <value>PreGap</value> </data> - <data name="Profile name" xml:space="preserve"> - <value>Profile name</value> - </data> - <data name="Scheme common data" xml:space="preserve"> - <value>Scheme common data</value> - </data> - <data name="Scheme tracks" xml:space="preserve"> - <value>Scheme tracks</value> - </data> - <data name="Search using regular expressions" xml:space="preserve"> - <value>Search using regular expressions</value> - </data> <data name="Seconds" xml:space="preserve"> <value>Seconds</value> </data> <data name="StartDateTime" xml:space="preserve"> <value>StartDateTime</value> </data> + <data name="Textimport scheme cuesheet" xml:space="preserve"> + <value>Textimport scheme cuesheet</value> + </data> + <data name="Textimport scheme tracks" xml:space="preserve"> + <value>Textimport scheme tracks</value> + </data> <data name="Time input format for import" xml:space="preserve"> <value>Time input format for import</value> </data> diff --git a/AudioCuesheetEditor/Shared/Import/Importprofiles.razor b/AudioCuesheetEditor/Shared/Import/Importprofiles.razor deleted file mode 100644 index 9925e1c2..00000000 --- a/AudioCuesheetEditor/Shared/Import/Importprofiles.razor +++ /dev/null @@ -1,200 +0,0 @@ -@using System.Linq.Expressions -<!-- -This file is part of AudioCuesheetEditor. - -AudioCuesheetEditor is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -AudioCuesheetEditor is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar. If not, see -<http: //www.gnu.org/licenses />. ---> -@inherits BaseLocalizedComponent - -@inject IStringLocalizer<Importprofiles> _localizer -@inject ValidationService _validationService -@inject IDialogService _dialogService - -@if (ApplicationOptions != null) -{ - <MudStack Row> - <MudSelect T="Importprofile" Variant="Variant.Outlined" Label="@_localizer["Import profile"]" Value="ApplicationOptions.SelectedImportProfile" ValueChanged="SelectedImportProfileChangedAsync"> - @foreach (var profile in ApplicationOptions.ImportProfiles) - { - <MudSelectItem Value="profile">@profile.Name</MudSelectItem> - } - </MudSelect> - <MudButtonGroup OverrideStyles="false" Class="mt-2" Style="height: 56px;"> - <MudIconButton Color="Color.Primary" Variant="Variant.Filled" Icon="@Icons.Material.Outlined.Add" OnClick="AddImportprofileClick" /> - <MudIconButton Color="Color.Warning" Variant="Variant.Filled" Icon="@Icons.Material.Outlined.Delete" Disabled="ApplicationOptions.SelectedImportProfile == null" OnClick="DeleteImportprofileClick" /> - <MudIconButton Color="Color.Error" Variant="Variant.Filled" Icon="@Icons.Material.Outlined.LockReset" OnClick="ResetSchemes" /> - </MudButtonGroup> - </MudStack> - <MudTextField T="string" Value="ApplicationOptions.SelectedImportProfile?.Name" Disabled="ApplicationOptions.SelectedImportProfile == null" ValueChanged="(newValue) => LocalStorageOptionsProvider.SaveNestedOptionValueAsync<ApplicationOptions, Importprofile?, String?>(x => x.SelectedImportProfile, x => x!.Name, newValue)" Error="String.IsNullOrEmpty(GetValidationErrorMessage(ApplicationOptions.SelectedImportProfile, nameof(Importprofile.Name))) == false" ErrorText="@GetValidationErrorMessage(ApplicationOptions.SelectedImportProfile, nameof(Importprofile.Name))" Label="@_localizer["Profile name"]" Placeholder="@_localizer["Enter the name for this profile here"]" Variant="Variant.Outlined" /> - <MudSwitch T="Boolean?" Color="Color.Secondary" Value="ApplicationOptions.SelectedImportProfile?.UseRegularExpression" Disabled="ApplicationOptions.SelectedImportProfile == null" ValueChanged="UseRegularExpressionChangedAsync" Label="@_localizer["Search using regular expressions"]" /> - <MudStack Row> - <MudTextField T="string" @ref="schemeCuesheetTextField" Text="@ApplicationOptions.SelectedImportProfile?.SchemeCuesheet" TextChanged="SchemeCuesheetChangedAsync" - Error="String.IsNullOrEmpty(GetValidationErrorMessage(ApplicationOptions.SelectedImportProfile, nameof(Importprofile.SchemeCuesheet))) == false" ErrorText="@GetValidationErrorMessage(ApplicationOptions.SelectedImportProfile, nameof(Importprofile.SchemeCuesheet))" - Label="@_localizer["Scheme common data"]" Placeholder="@_localizer["Enter the common data scheme for this profile here"]" Variant="Variant.Outlined" - Clearable Disabled="ApplicationOptions.SelectedImportProfile == null" /> - <MudMenu Icon="@Icons.Material.Outlined.Add" Color="Color.Primary" Variant="Variant.Filled" Disabled="ApplicationOptions.SelectedImportProfile == null" Class="mt-1" Style="height: 56px;"> - @foreach (var scheme in Importprofile.AvailableSchemeCuesheet) - { - <MudMenuItem OnClick="() => AppendPlaceholderToTextField(schemeCuesheetTextField, scheme)">@_localizer[scheme]</MudMenuItem> - } - </MudMenu> - </MudStack> - <MudStack Row> - <MudTextField T="string" @ref="schemeTracksTextField" Text="@ApplicationOptions.SelectedImportProfile?.SchemeTracks" TextChanged="SchemeTracksChangedAsync" - Error="String.IsNullOrEmpty(GetValidationErrorMessage(ApplicationOptions.SelectedImportProfile, nameof(Importprofile.SchemeTracks))) == false" ErrorText="@GetValidationErrorMessage(ApplicationOptions.SelectedImportProfile, nameof(Importprofile.SchemeTracks))" - Label="@_localizer["Scheme tracks"]" Placeholder="@_localizer["Enter the track scheme for this profile here"]" Variant="Variant.Outlined" - Clearable Disabled="ApplicationOptions.SelectedImportProfile == null" /> - <MudMenu Icon="@Icons.Material.Outlined.Add" Color="Color.Primary" Variant="Variant.Filled" Disabled="ApplicationOptions.SelectedImportProfile == null" Class="mt-1" Style="height: 56px;"> - @foreach (var scheme in Importprofile.AvailableSchemesTrack) - { - <MudMenuItem OnClick="() => AppendPlaceholderToTextField(schemeTracksTextField, scheme)">@_localizer[scheme]</MudMenuItem> - } - </MudMenu> - </MudStack> - <MudStack Row> - <MudTextField T="string" @ref="timeSpanFormatTextField" Text="@ApplicationOptions.SelectedImportProfile?.TimeSpanFormat?.Scheme" TextChanged="ImportTimeInputFormatChangedAsync" - Error="String.IsNullOrEmpty(GetValidationErrorMessage(ApplicationOptions.SelectedImportProfile?.TimeSpanFormat, nameof(TimeSpanFormat.Scheme))) == false" ErrorText="@GetValidationErrorMessage(ApplicationOptions.SelectedImportProfile?.TimeSpanFormat, nameof(TimeSpanFormat.Scheme))" - Label="@_localizer["Time input format for import"]" Placeholder="@_localizer["Enter the time format for this profile here"]" Variant="Variant.Outlined" - Clearable Disabled="ApplicationOptions.SelectedImportProfile == null" /> - <MudMenu Icon="@Icons.Material.Outlined.Add" Color="Color.Primary" Variant="Variant.Filled" Disabled="ApplicationOptions.SelectedImportProfile == null" Class="mt-1" Style="height: 56px;"> - @foreach (var scheme in TimeSpanFormat.AvailableTimespanScheme) - { - <MudMenuItem OnClick="() => AppendPlaceholderToTextField(timeSpanFormatTextField, scheme)">@_localizer[scheme]</MudMenuItem> - } - </MudMenu> - </MudStack> -} - -@code { - [Parameter] - public EventCallback ImportprofileChanged { get; set; } - - MudTextField<string>? schemeCuesheetTextField, schemeTracksTextField, timeSpanFormatTextField; - - void AppendPlaceholderToTextField(MudTextField<string>? mudTextField, string placeholder) - { - mudTextField?.SetText($"{mudTextField.Text}{placeholder}"); - } - - async Task SelectedImportProfileChangedAsync(Importprofile? newSelectedProfile) - { - await LocalStorageOptionsProvider.SaveOptionsValueAsync<ApplicationOptions>(x => x.SelectedImportProfile, newSelectedProfile); - await ImportprofileChanged.InvokeAsync(); - } - - async Task UseRegularExpressionChangedAsync(Boolean? newValue) - { - await LocalStorageOptionsProvider.SaveNestedOptionValueAsync<ApplicationOptions, Importprofile?, Boolean>(x => x.SelectedImportProfile, x => x!.UseRegularExpression, newValue!.Value); - await ImportprofileChanged.InvokeAsync(); - } - - async Task SchemeCuesheetChangedAsync(string newScheme) - { - await LocalStorageOptionsProvider.SaveNestedOptionValueAsync<ApplicationOptions, Importprofile?, String?>(x => x.SelectedImportProfile, x => x!.SchemeCuesheet, newScheme); - await ImportprofileChanged.InvokeAsync(); - } - - async Task SchemeTracksChangedAsync(string newScheme) - { - await LocalStorageOptionsProvider.SaveNestedOptionValueAsync<ApplicationOptions, Importprofile?, String?>(x => x.SelectedImportProfile, x => x!.SchemeTracks, newScheme); - await ImportprofileChanged.InvokeAsync(); - } - - async Task ImportTimeInputFormatChangedAsync(string newScheme) - { - var profile = ApplicationOptions?.SelectedImportProfile; - if (profile != null) - { - TimeSpanFormat? format = null; - if (string.IsNullOrEmpty(newScheme)) - { - format = null; - } - else - { - format = new() { Scheme = newScheme }; - } - await LocalStorageOptionsProvider.SaveOptionsValueAsync<ApplicationOptions>(x => x.SelectedImportProfile!.TimeSpanFormat, format); - await ImportprofileChanged.InvokeAsync(); - } - } - - String? GetValidationErrorMessage(object? model, string propertyName) - { - String? validationErrorMessage = null; - if (model != null) - { - var validationMessages = _validationService.Validate(model, propertyName); - if (validationMessages.Count() > 0) - { - validationErrorMessage = String.Join(Environment.NewLine, validationMessages); - } - } - return validationErrorMessage; - } - - async Task ResetSchemes() - { - var parameters = new DialogParameters<ConfirmDialog> - { - { x => x.ConfirmText, _localizer["Are you sure you want to reset the import profiles to factory default?"] }, - }; - var dialog = await _dialogService.ShowAsync<ConfirmDialog>(_localizer["Confirm"], parameters); - var result = await dialog.Result; - if (result?.Canceled == false) - { - ApplicationOptions!.ImportProfiles = ApplicationOptions.DefaultImportprofiles; - ApplicationOptions!.SelectedImportProfile = ApplicationOptions.DefaultSelectedImportprofile; - await LocalStorageOptionsProvider.SaveOptionsAsync(ApplicationOptions); - await ImportprofileChanged.InvokeAsync(); - } - } - - async Task AddImportprofileClick() - { - var newProfilesNames = ApplicationOptions!.ImportProfiles.Where(x => x.Name?.StartsWith(_localizer["New import profile"]) == true).Select(x => x.Name); - var regex = new System.Text.RegularExpressions.Regex(@"(\d+)$"); - int maxNumber = newProfilesNames - .Select(name => - { - var match = regex.Match(name ?? ""); - return match.Success ? int.Parse(match.Value) : 0; - }) - .DefaultIfEmpty(0) - .Max(); - var profile = new Importprofile() - { - Name = $"{_localizer["New import profile"]} {maxNumber + 1}" - }; - ApplicationOptions!.SelectedImportProfile = profile; - await LocalStorageOptionsProvider.SaveOptionsAsync(ApplicationOptions); - await ImportprofileChanged.InvokeAsync(); - } - - async Task DeleteImportprofileClick() - { - var selectedProfile = ApplicationOptions?.SelectedImportProfile; - if (selectedProfile != null) - { - ApplicationOptions!.ImportProfiles.Remove(selectedProfile); - var lastProfile = ApplicationOptions.ImportProfiles.LastOrDefault(); - if (lastProfile != null) - { - ApplicationOptions!.SelectedImportProfile = lastProfile; - } - await LocalStorageOptionsProvider.SaveOptionsAsync(ApplicationOptions); - } - } -} diff --git a/AudioCuesheetEditor/Shared/Import/SelectImportFiles.razor b/AudioCuesheetEditor/Shared/Import/SelectImportFiles.razor index 94f9b212..590b8275 100644 --- a/AudioCuesheetEditor/Shared/Import/SelectImportFiles.razor +++ b/AudioCuesheetEditor/Shared/Import/SelectImportFiles.razor @@ -29,7 +29,7 @@ along with Foobar. If not, see </CardHeaderContent> </MudCardHeader> <MudCardContent id="@dropFileInputId"> - <DropFileInput OnFilesSelected="InputFilesChanged" Filter="@FileMimeTypes.TextPlain" /> + <DropFileInput OnFilesSelected="InputFilesChanged" Filter="@FileMimeTypes.Text" /> @foreach (var invalidFileName in invalidDropFileNames) { <MudAlert Variant="Variant.Filled" Severity="Severity.Error" ShowCloseIcon CloseIconClicked="() => CloseInvalidFileClicked(invalidFileName)"> @@ -54,7 +54,8 @@ along with Foobar. If not, see invalidDropFileNames.Clear(); foreach (var file in files) { - if (_fileInputManager.IsValidForImportView(file) == false) + if ((_fileInputManager.CheckFileMimeType(file, FileMimeTypes.Text, FileExtensions.Text) == false) + && (FileInputManager.GetAudioCodec(file) == null)) { invalidDropFileNames.Add(file.Name); } @@ -76,7 +77,7 @@ along with Foobar. If not, see // Audio file is handled seperatly foreach (var file in files) { - var codec = _fileInputManager.GetAudioCodec(file); + var codec = FileInputManager.GetAudioCodec(file); if (codec != null) { var audiofile = await _fileInputManager.CreateAudiofileAsync(dropFileInputId, file); diff --git a/AudioCuesheetEditor/Shared/ViewModes/ViewModeImport.razor b/AudioCuesheetEditor/Shared/ViewModes/ViewModeImport.razor index 4404698a..470d5c84 100644 --- a/AudioCuesheetEditor/Shared/ViewModes/ViewModeImport.razor +++ b/AudioCuesheetEditor/Shared/ViewModes/ViewModeImport.razor @@ -33,7 +33,7 @@ along with Foobar. If not, see </TitleContent> <ChildContent> <ImportFileContent FileContentChanged="ImportFileContent_FileContentChanged" /> - <Importprofiles ImportprofileChanged="ReanalyseImportfile" /> + <ImportSchemes ImportSchemeCuesheetChanged="ReanalyseImportfile" ImportSchemeTracksChanged="ReanalyseImportfile" ImportTimeInputFormatChanged="ReanalyseImportfile" /> @if (_sessionStateContainer.Importfile?.AnalyseException != null) { <MudAlert Severity="Severity.Error" Variant="Variant.Filled"> @@ -68,17 +68,18 @@ along with Foobar. If not, see Boolean fileContentExpanded = false, cuesheetDataExpanded = false, cuesheetTracksExpanded = false; Boolean selectFilesStepCompleted = false, selectFilesStepError = false; - async Task ImportFileContent_FileContentChanged(string newFileContent) + void ImportFileContent_FileContentChanged(string newFileContent) { - await _importManager.ImportTextAsync(newFileContent); + var textToAnalyse = newFileContent.Split(Environment.NewLine); + _importManager.ImportText(textToAnalyse, ApplicationOptions!.ImportScheme, ApplicationOptions!.ImportTimeSpanFormat); } - async Task ReanalyseImportfile() + void ReanalyseImportfile() { var fileContent = _sessionStateContainer.Importfile?.FileContent; if (fileContent != null) { - await _importManager.ImportTextAsync(fileContent); + _importManager.ImportText(fileContent, ApplicationOptions!.ImportScheme, ApplicationOptions.ImportTimeSpanFormat); } } diff --git a/AudioCuesheetEditor/_Imports.razor b/AudioCuesheetEditor/_Imports.razor index 4dd0e275..4cd9df9e 100644 --- a/AudioCuesheetEditor/_Imports.razor +++ b/AudioCuesheetEditor/_Imports.razor @@ -43,5 +43,4 @@ @using Howler.Blazor.Components @using Markdig @using Toolbelt.Blazor.HotKeys2 -@using MudBlazor -@using Ganss.Xss \ No newline at end of file +@using MudBlazor \ No newline at end of file