09f61b6039
Closes #130
92 lines
2.3 KiB
Elixir
92 lines
2.3 KiB
Elixir
defmodule MusicLibrary.Assets do
|
|
@moduledoc """
|
|
Binary asset storage and retrieval for covers and artist images.
|
|
"""
|
|
|
|
alias MusicLibrary.Assets.{Asset, Cache}
|
|
alias MusicLibrary.Repo
|
|
|
|
import Ecto.Query
|
|
|
|
@doc """
|
|
Store any file type - the responsibility to correctly populate format and
|
|
properties is left to the caller.
|
|
"""
|
|
@spec store(map()) :: {:ok, Asset.t()} | {:error, Ecto.Changeset.t()}
|
|
def store(params) do
|
|
%Asset{}
|
|
|> Asset.changeset(params)
|
|
|> Repo.insert(on_conflict: :nothing, returning: true)
|
|
end
|
|
|
|
@doc """
|
|
Store image files - properties will be computed automatically.
|
|
"""
|
|
@spec store_image(map()) :: {:ok, Asset.t()} | {:error, Ecto.Changeset.t()}
|
|
def store_image(params) do
|
|
%Asset{}
|
|
|> Asset.image_changeset(params)
|
|
|> Repo.insert(on_conflict: :nothing, returning: true)
|
|
end
|
|
|
|
@spec get(String.t()) :: Asset.t() | nil
|
|
def get(hash) do
|
|
Repo.get(Asset, hash)
|
|
end
|
|
|
|
@spec get!(String.t()) :: Asset.t()
|
|
def get!(hash) do
|
|
Repo.get!(Asset, hash)
|
|
end
|
|
|
|
@spec total_content_size() :: non_neg_integer() | nil
|
|
def total_content_size do
|
|
q =
|
|
from p in Asset, select: sum(fragment("length(content)"))
|
|
|
|
Repo.one(q)
|
|
end
|
|
|
|
@spec track_total_content_size() :: :ok
|
|
def track_total_content_size do
|
|
:telemetry.execute(
|
|
[:music_library, :assets],
|
|
%{content_size: total_content_size()},
|
|
%{}
|
|
)
|
|
end
|
|
|
|
@doc """
|
|
Deletes all assets not referenced by any record cover or artist info image.
|
|
Returns the number of pruned assets.
|
|
"""
|
|
@spec prune_unreferenced() :: non_neg_integer()
|
|
def prune_unreferenced do
|
|
# Note that SQLite doesn't support left joins on delete, so we do it in two steps.
|
|
asset_hashes =
|
|
from a in Asset,
|
|
left_join: r in MusicLibrary.Records.Record,
|
|
on: r.cover_hash == a.hash,
|
|
left_join: ai in MusicLibrary.Artists.ArtistInfo,
|
|
on: ai.image_data_hash == a.hash,
|
|
where: is_nil(r.id) and is_nil(ai.id),
|
|
select: a.hash
|
|
|
|
q =
|
|
from a in Asset,
|
|
where: a.hash in subquery(asset_hashes)
|
|
|
|
{count, nil} = Repo.delete_all(q)
|
|
count
|
|
end
|
|
|
|
@spec track_total_cache_size() :: :ok
|
|
def track_total_cache_size do
|
|
:telemetry.execute(
|
|
[:music_library, :assets],
|
|
%{cache_size: Cache.total_content_size()},
|
|
%{}
|
|
)
|
|
end
|
|
end
|