reimmich/lib/reimmich.ex
2026-05-21 20:00:17 +02:00

114 lines
2.9 KiB
Elixir

defmodule Reimmich do
import Reimmich.Sql
alias Reimmich.{Album, Asset}
alias Reimmich.Api
alias Reimmich.ProgressCounter
def albums do
select("id, name, thumbnail_asset_id FROM remote_album_entity")
|> Enum.map(fn [album_id, name, thumbnail_asset_id] ->
assets =
select("asset_id FROM remote_album_asset_entity WHERE album_id='#{album_id}'")
|> List.flatten()
|> Enum.map(&asset/1)
%Album{name: name, thumbnail: asset(thumbnail_asset_id), assets: assets}
end)
end
def asset(id) do
[[filename, checksum, width, height, thumbhash]] =
select(
"name, checksum, width, height, thumb_hash FROM remote_asset_entity WHERE id='#{id}'"
)
%Asset{
filename: filename,
checksum: checksum,
width: width,
height: height,
thumbhash: thumbhash
}
end
def match do
t_start = System.os_time(:second)
albums = albums()
no_albums = Enum.count(albums)
no_album_assets = albums |> Enum.flat_map(& &1.assets) |> Enum.count()
total = no_albums + no_album_assets
total_digits = total |> Integer.digits() |> Enum.count()
pad = fn n -> to_string(n) |> String.pad_leading(total_digits) end
lookup_and_log = fn asset ->
result = lookup(asset)
IO.write("\e[2K\r")
if is_nil(result), do: IO.puts("MISS #{asset.filename}")
progress = if is_nil(result), do: ProgressCounter.miss(), else: ProgressCounter.hit()
IO.write("[#{pad.(progress)}/#{total}]")
result
end
IO.puts("~> looking up #{total} assets in #{no_albums} albums")
IO.puts("")
albums =
albums
|> Enum.map(fn album ->
album
|> Map.replace_lazy(:thumbnail, lookup_and_log)
|> Map.replace_lazy(:assets, &Enum.map(&1, lookup_and_log))
end)
{hit, miss} = ProgressCounter.result()
t_lookup = System.os_time(:second)
IO.puts("")
IO.puts("----------------------------------------------------")
IO.puts("HIT: #{hit}, MISS: #{miss} (took #{fmt_interval(t_start, t_lookup)})")
IO.puts("")
IO.puts("~> creating albums")
IO.puts("")
albums
|> Enum.each(fn album ->
Api.create_album(album.name, album.assets)
|> Api.set_album_thumbnail(album.thumbnail)
IO.write("\e[2K\r")
IO.write("CREATE #{album.name}")
end)
t_finish = System.os_time(:second)
IO.puts("")
IO.puts("----------------------------------------------------")
IO.puts("")
IO.puts("~> done (took #{fmt_interval(t_start, t_finish)})")
end
defp lookup(%Asset{} = asset) do
with nil <- Api.find_by(%{checksum: asset.checksum}),
nil <- Api.find_by(%{originalFileName: asset.filename}) do
nil
end
end
defp fmt_interval(t1, t2) do
seconds = t2 - t1
mm = div(seconds, 60) |> to_string() |> String.pad_leading(2, "0")
ss = rem(seconds, 60) |> to_string() |> String.pad_leading(2, "0")
"#{mm}:#{ss}"
end
end