From 170f195aae5442e5519e8b5c690502afc16c34c4 Mon Sep 17 00:00:00 2001 From: Claudio Ortolina Date: Sat, 14 Sep 2024 14:09:28 +0100 Subject: [PATCH] Fix edge cases with parser - Remove front_matter dependency, and use a hand-rolled simpler parser which is suitable for the data to parse (which is well-formed). - Fix a couple of edge cases with years and titles with commas --- lib/music_library/records/parser.ex | 35 +++++++++++++++--- mix.exs | 2 +- mix.lock | 3 +- test/music_library/records/parser_test.exs | 42 ++++++++++++++++++++-- 4 files changed, 72 insertions(+), 10 deletions(-) diff --git a/lib/music_library/records/parser.ex b/lib/music_library/records/parser.ex index 389e8008..ccddec77 100644 --- a/lib/music_library/records/parser.ex +++ b/lib/music_library/records/parser.ex @@ -1,24 +1,49 @@ defmodule MusicLibrary.Records.Parser do - alias MusicLibrary.Records.Record - def from_entry_contents(entry_contents) do - with {:ok, meta, _body} <- FrontMatter.parse(entry_contents) do + with {:ok, [meta]} <- parse_frontmatter(entry_contents) do {:ok, - %Record{ + %{ type: parse_subtype(meta["subType"]), musicbrainz_id: meta["id"], title: meta["title"], - year: meta["year"], + year: meta["year"] |> maybe_parse_year(), image: meta["image"], genres: meta["genres"] }} end end + defp parse_frontmatter(entry_contents) do + case entry_contents do + "---\n" <> rest -> + case String.split(rest, "\n---\n") do + [frontmatter, _] -> + case YamlElixir.read_all_from_string(frontmatter) do + {:ok, meta} -> {:ok, meta} + {:error, _} -> {:error, "Invalid frontmatter"} + end + + _ -> + {:error, "Invalid frontmatter"} + end + + _ -> + {:error, "Invalid frontmatter"} + end + end + defp parse_subtype("Album"), do: :album defp parse_subtype("ep"), do: :ep defp parse_subtype("Live"), do: :live defp parse_subtype("Compilation"), do: :compilation defp parse_subtype("Single"), do: :single defp parse_subtype(_), do: :other + + defp maybe_parse_year(nil), do: nil + defp maybe_parse_year(year) when is_integer(year), do: year + + defp maybe_parse_year(year) when is_binary(year) do + {integer, _remainder} = Integer.parse(year, 10) + integer + end end diff --git a/mix.exs b/mix.exs index 58008df8..5c63dcff 100644 --- a/mix.exs +++ b/mix.exs @@ -38,7 +38,7 @@ defmodule MusicLibrary.MixProject do {:ecto_sqlite3, ">= 0.0.0"}, {:phoenix_html, "~> 4.1"}, {:phoenix_live_reload, "~> 1.2", only: :dev}, - {:front_matter, "~> 1.0"}, + {:yaml_elixir, "~> 2.11"}, # TODO bump on release to {:phoenix_live_view, "~> 1.0.0"}, {:phoenix_live_view, "~> 1.0.0-rc.1", override: true}, {:floki, ">= 0.30.0", only: :test}, diff --git a/mix.lock b/mix.lock index 8093efa1..8098ccdc 100644 --- a/mix.lock +++ b/mix.lock @@ -14,7 +14,6 @@ "exqlite": {:hex, :exqlite, "0.23.0", "6e851c937a033299d0784994c66da24845415072adbc455a337e20087bce9033", [:make, :mix], [{:cc_precompiler, "~> 0.1", [hex: :cc_precompiler, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.8", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "404341cceec5e6466aaed160cf0b58be2019b60af82588c215e1224ebd3ec831"}, "file_system": {:hex, :file_system, "1.0.1", "79e8ceaddb0416f8b8cd02a0127bdbababe7bf4a23d2a395b983c1f8b3f73edd", [:mix], [], "hexpm", "4414d1f38863ddf9120720cd976fce5bdde8e91d8283353f0e31850fa89feb9e"}, "floki": {:hex, :floki, "0.36.2", "a7da0193538c93f937714a6704369711998a51a6164a222d710ebd54020aa7a3", [:mix], [], "hexpm", "a8766c0bc92f074e5cb36c4f9961982eda84c5d2b8e979ca67f5c268ec8ed580"}, - "front_matter": {:hex, :front_matter, "1.0.1", "caca2cb16336e1d82ca5c5e9d94f88fd6d4ce84c1f05253e76d357650d4c46a9", [:mix], [{:yaml_elixir, "~> 2.5.0", [hex: :yaml_elixir, repo: "hexpm", optional: false]}], "hexpm", "1c4daa17c071b245e3706795b98814392fe0ce6bbbfb7c6543be63db86a4e953"}, "gettext": {:hex, :gettext, "0.26.1", "38e14ea5dcf962d1fc9f361b63ea07c0ce715a8ef1f9e82d3dfb8e67e0416715", [:mix], [{:expo, "~> 0.5.1 or ~> 1.0", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "01ce56f188b9dc28780a52783d6529ad2bc7124f9744e571e1ee4ea88bf08734"}, "heroicons": {:git, "https://github.com/tailwindlabs/heroicons.git", "88ab3a0d790e6a47404cba02800a6b25d2afae50", [tag: "v2.1.1", sparse: "optimized", depth: 1]}, "hpax": {:hex, :hpax, "1.0.0", "28dcf54509fe2152a3d040e4e3df5b265dcb6cb532029ecbacf4ce52caea3fd2", [:mix], [], "hexpm", "7f1314731d711e2ca5fdc7fd361296593fc2542570b3105595bb0bc6d0fad601"}, @@ -38,5 +37,5 @@ "websock": {:hex, :websock, "0.5.3", "2f69a6ebe810328555b6fe5c831a851f485e303a7c8ce6c5f675abeb20ebdadc", [:mix], [], "hexpm", "6105453d7fac22c712ad66fab1d45abdf049868f253cf719b625151460b8b453"}, "websock_adapter": {:hex, :websock_adapter, "0.5.7", "65fa74042530064ef0570b75b43f5c49bb8b235d6515671b3d250022cb8a1f9e", [:mix], [{:bandit, ">= 0.6.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "d0f478ee64deddfec64b800673fd6e0c8888b079d9f3444dd96d2a98383bdbd1"}, "yamerl": {:hex, :yamerl, "0.10.0", "4ff81fee2f1f6a46f1700c0d880b24d193ddb74bd14ef42cb0bcf46e81ef2f8e", [:rebar3], [], "hexpm", "346adb2963f1051dc837a2364e4acf6eb7d80097c0f53cbdc3046ec8ec4b4e6e"}, - "yaml_elixir": {:hex, :yaml_elixir, "2.5.0", "45de762be6d75fa5a8b5f44ddff8c30f64c26526eab5b1d72e36d616007b7796", [:mix], [{:yamerl, "~> 0.7", [hex: :yamerl, repo: "hexpm", optional: false]}], "hexpm", "80fe4e43f05582f2a90f2dcd73fc6171fbd65f2e6836f71fe4ce2154ef358c36"}, + "yaml_elixir": {:hex, :yaml_elixir, "2.11.0", "9e9ccd134e861c66b84825a3542a1c22ba33f338d82c07282f4f1f52d847bd50", [:mix], [{:yamerl, "~> 0.10", [hex: :yamerl, repo: "hexpm", optional: false]}], "hexpm", "53cc28357ee7eb952344995787f4bb8cc3cecbf189652236e9b163e8ce1bc242"}, } diff --git a/test/music_library/records/parser_test.exs b/test/music_library/records/parser_test.exs index 66df826b..9c8e1d1c 100644 --- a/test/music_library/records/parser_test.exs +++ b/test/music_library/records/parser_test.exs @@ -1,6 +1,6 @@ defmodule MusicLibrary.Records.ParserTest do use ExUnit.Case, async: true - alias MusicLibrary.Records.{Parser, Record} + alias MusicLibrary.Records.Parser @obsidian_entry_path Path.expand("../../support/fixtures/marillion-marbles.md", __DIR__) @@ -9,7 +9,7 @@ defmodule MusicLibrary.Records.ParserTest do assert Parser.from_entry_contents(entry_contents) == {:ok, - %Record{ + %{ type: :album, musicbrainz_id: "20790e26-98e4-3ad3-a67f-b674758b942d", title: "Marbles", @@ -27,4 +27,42 @@ defmodule MusicLibrary.Records.ParserTest do ] }} end + + test "handles special characters in titles" do + entry_contents = """ + --- + type: "musicRelease" + subType: "Album" + title: "Guardians of the Galaxy: Awesome Mix, Vol. 1" + englishTitle: "Guardians of the Galaxy: Awesome Mix, Vol. 1" + year: "2014" + dataSource: "MusicBrainz API" + url: "https://musicbrainz.org/release-group/950092d6-45f6-4269-87da-99a9ff2fcc52" + id: "950092d6-45f6-4269-87da-99a9ff2fcc52" + genres: + - "classic rock" + - "pop" + - "pop rock" + - "rock" + artists: + - "Various Artists" + image: "https://coverartarchive.org/release-group/950092d6-45f6-4269-87da-99a9ff2fcc52/front" + rating: 9.6 + personalRating: 0 + tags: "mediaDB/music/Album" + --- + """ + + assert Parser.from_entry_contents(entry_contents) == + {:ok, + %{ + genres: ["classic rock", "pop", "pop rock", "rock"], + image: + "https://coverartarchive.org/release-group/950092d6-45f6-4269-87da-99a9ff2fcc52/front", + musicbrainz_id: "950092d6-45f6-4269-87da-99a9ff2fcc52", + title: "Guardians of the Galaxy: Awesome Mix, Vol. 1", + type: :album, + year: 2014 + }} + end end