diff --git a/lib/music_library/records_on_this_day_email.ex b/lib/music_library/records_on_this_day_email.ex
index 162c5dde..dbbcb992 100644
--- a/lib/music_library/records_on_this_day_email.ex
+++ b/lib/music_library/records_on_this_day_email.ex
@@ -14,6 +14,7 @@ defmodule MusicLibrary.RecordsOnThisDayEmail do
if records == [] do
{:ok, :no_records}
else
+ grouped = Collection.group_records_by_release_group(records)
conf = config()
from_email = Keyword.fetch!(conf, :from_email)
to_email = Keyword.fetch!(conf, :to_email)
@@ -26,7 +27,7 @@ defmodule MusicLibrary.RecordsOnThisDayEmail do
|> to(to_email)
|> from({"MusicLibrary", from_email})
|> subject("[MusicLibrary] #{heading}")
- |> html_body(build_html(records, date, conf))
+ |> html_body(build_html(grouped, date, conf))
case mailer.deliver(email) do
{:ok, _} ->
@@ -42,12 +43,15 @@ defmodule MusicLibrary.RecordsOnThisDayEmail do
# -- Private --
- defp build_html(records, date, conf) do
+ defp build_html(grouped, date, conf) do
base_url = Keyword.fetch!(conf, :base_url) |> String.trim_trailing("/")
heading = date |> Calendar.strftime("Records on %-d %B") |> html_escape()
records_html =
- Enum.map_join(records, "\n", fn record -> record_html(record, date, base_url) end)
+ Enum.map_join(grouped, "\n", fn
+ {:single, record} -> record_html(record, date, base_url)
+ {:group, group} -> grouped_record_html(group, date, base_url)
+ end)
"""
@@ -101,6 +105,58 @@ defmodule MusicLibrary.RecordsOnThisDayEmail do
"""
end
+ defp grouped_record_html(%{representative: rep, records: records}, date, base_url) do
+ years = Record.released_how_long_ago?(rep, date)
+ cover_url = cover_image_url(rep, base_url)
+ artist_names = rep |> Record.artist_names() |> html_escape()
+ title = rep.title |> html_escape()
+ years_label = years_ago_label(years)
+ {years_color, years_weight} = anniversary_style(years)
+
+ cover_html =
+ if cover_url do
+ ~s(
})
)
+ else
+ ~s(
)
+ end
+
+ releases_html =
+ Enum.map_join(records, "\n", fn record ->
+ record_url = record_detail_url(record, base_url)
+ format = format_label(record.format)
+ type = type_label(record.type)
+
+ purchased_label =
+ if record.purchased_at do
+ " · Purchased on #{Record.format_as_date(record.purchased_at)}"
+ else
+ ""
+ end
+
+ """
+
+ #{format} · #{type}#{purchased_label}
+
+ """
+ end)
+
+ """
+
+
+ #{cover_html}
+
+
#{artist_names}
+
#{title}
+
+ #{years_label}
+
+
+
+ #{releases_html}
+
+ """
+ end
+
defp record_detail_url(record, base_url) do
"#{base_url}/collection/#{record.id}"
end
diff --git a/test/music_library/records_on_this_day_email_test.exs b/test/music_library/records_on_this_day_email_test.exs
index a8ee5e8a..a780c2d2 100644
--- a/test/music_library/records_on_this_day_email_test.exs
+++ b/test/music_library/records_on_this_day_email_test.exs
@@ -52,6 +52,42 @@ defmodule MusicLibrary.RecordsOnThisDayEmailTest do
refute_email_sent()
end
+ test "groups records with the same musicbrainz_id" do
+ shared_mbid = Ecto.UUID.generate()
+
+ cd =
+ Fixtures.Records.record(%{
+ release_date: "2020-03-05",
+ title: "Grouped Album",
+ musicbrainz_id: shared_mbid,
+ format: :cd
+ })
+
+ vinyl =
+ Fixtures.Records.record(%{
+ release_date: "2020-03-05",
+ title: "Grouped Album",
+ musicbrainz_id: shared_mbid,
+ format: :vinyl
+ })
+
+ date = ~D[2025-03-05]
+ assert {:ok, :sent} = RecordsOnThisDayEmail.send(date)
+
+ assert_email_sent(fn email ->
+ html = email.html_body
+ # Title appears once as group header
+ assert html =~ "Grouped Album"
+ assert html =~ "5 years ago"
+ # Both formats appear as sub-items
+ assert html =~ "CD"
+ assert html =~ "Vinyl"
+ # Both records have detail links
+ assert html =~ "/collection/#{cd.id}"
+ assert html =~ "/collection/#{vinyl.id}"
+ end)
+ end
+
test "shows anniversary styling for milestone years" do
# 10 year anniversary (gold)
Fixtures.Records.record(%{