This commit is contained in:
KosmonautX 2024-01-21 12:11:21 +08:00 committed by GitHub
commit 2450a9dbfd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 233 additions and 81 deletions

View file

@ -12,13 +12,13 @@
# - https://pkgs.org/ - resource for finding needed packages
# - Ex: hexpm/elixir:1.12.0-erlang-24.0.1-debian-bullseye-20210902-slim
#
ARG BUILDER_IMAGE="hexpm/elixir:1.12.0-erlang-24.0.1-debian-bullseye-20210902-slim"
ARG BUILDER_IMAGE="hexpm/elixir:1.14.0-erlang-24.0.1-debian-bullseye-20210902-slim"
ARG RUNNER_IMAGE="debian:bullseye-20210902-slim"
FROM ${BUILDER_IMAGE} as builder
# install build dependencies
RUN apt-get update -y && apt-get install -y build-essential git \
RUN apt-get update -y && apt-get install -y build-essential git curl ffmpeg \
&& apt-get clean && rm -f /var/lib/apt/lists/*_*
# prepare build dir
@ -30,6 +30,7 @@ RUN mix local.hex --force && \
# set build ENV
ENV MIX_ENV="prod"
ENV BUMBLEBEE_CACHE_DIR="/app/.bumblebee"
# install mix dependencies
COPY mix.exs mix.lock ./
@ -57,6 +58,7 @@ COPY assets assets
RUN mix assets.deploy
RUN mix compile
RUN mix run -e 'LiveBeats.Application.load_serving()' --no-start
# Changes to config/runtime.exs don't require recompiling the code
COPY config/runtime.exs config/
@ -68,7 +70,7 @@ RUN mix release
# the compiled release and other runtime necessities
FROM ${RUNNER_IMAGE}
RUN apt-get update -y && apt-get install -y libstdc++6 openssl libncurses5 locales \
RUN apt-get update -y && apt-get install -y libstdc++6 openssl libncurses5 locales curl ffmpeg \
&& apt-get clean && rm -f /var/lib/apt/lists/*_*
# Set the locale
@ -80,9 +82,11 @@ ENV LC_ALL en_US.UTF-8
WORKDIR "/app"
RUN chown nobody /app
ENV BUMBLEBEE_CACHE_DIR="/app/.bumblebee"
# Only copy the final release from the build stage
COPY --from=builder --chown=nobody:root /app/_build/prod/rel/live_beats ./
COPY --from=builder --chown=nobody:root /app/.bumblebee/ ./.bumblebee
USER nobody

View file

@ -34,7 +34,7 @@ config :esbuild,
# Configure tailwind (the version is required)
config :tailwind,
version: "3.1.8",
version: "3.2.7",
default: [
args: ~w(
--config=tailwind.config.js

View file

@ -2,7 +2,7 @@ import Config
config :live_beats, :files,
uploads_dir: Path.expand("../priv/uploads", __DIR__),
host: [scheme: "http", host: "localhost", port: 4000],
host: [scheme: "http", host: "localhost", port: 4001],
server_ip: "127.0.0.1",
hostname: "localhost",
transport_opts: []

View file

@ -1,45 +1,46 @@
app = "livebeats"
# fly.toml app configuration file generated for livebeats on 2023-05-19T22:42:40-04:00
#
# See https://fly.io/docs/reference/configuration/ for information about how to use this file.
#
app = "livebeats"
primary_region = "ord"
kill_signal = "SIGTERM"
kill_timeout = 5
processes = []
kill_timeout = "5s"
[experimental]
auto_rollback = true
[deploy]
release_command = "/app/bin/migrate"
[env]
BUMBLEBEE_CACHE_DIR = "/app/.bumblebee"
PHX_HOST = "livebeats.fly.dev"
[mounts]
source="data"
destination="/app/uploads"
[experimental]
allowed_public_ports = []
auto_rollback = true
[[services]]
http_checks = []
protocol = "tcp"
internal_port = 4000
processes = ["app"]
protocol = "tcp"
script_checks = []
[[services.ports]]
port = 80
handlers = ["http"]
[[services.ports]]
port = 443
handlers = ["tls", "http"]
[services.concurrency]
type = "connections"
hard_limit = 2500
soft_limit = 2000
type = "connections"
[[services.ports]]
handlers = ["http"]
port = 80
[[services.ports]]
handlers = ["tls", "http"]
port = 443
[[services.tcp_checks]]
grace_period = "20s" # allow some time for startup
interval = "15s"
timeout = "2s"
grace_period = "20s"
restart_limit = 0
timeout = "2s"

View file

@ -5,32 +5,46 @@ defmodule LiveBeats.Application do
use Application
def load_serving do
{:ok, whisper} = Bumblebee.load_model({:hf, "openai/whisper-tiny"})
{:ok, featurizer} = Bumblebee.load_featurizer({:hf, "openai/whisper-tiny"})
{:ok, tokenizer} = Bumblebee.load_tokenizer({:hf, "openai/whisper-tiny"})
Bumblebee.Audio.speech_to_text(whisper, featurizer, tokenizer,
compile: [batch_size: 1],
max_new_tokens: 100,
defn_options: [compiler: EXLA]
)
end
@impl true
def start(_type, _args) do
LiveBeats.MediaLibrary.attach()
topologies = Application.get_env(:libcluster, :topologies) || []
children = [
{Cluster.Supervisor, [topologies, [name: LiveBeats.ClusterSupervisor]]},
{Task.Supervisor, name: LiveBeats.TaskSupervisor},
# Start the Ecto repository
LiveBeats.Repo,
LiveBeats.ReplicaRepo,
# Start the Telemetry supervisor
LiveBeatsWeb.Telemetry,
# Start the PubSub system
{Phoenix.PubSub, name: LiveBeats.PubSub},
# start presence
LiveBeatsWeb.Presence,
{Finch, name: LiveBeats.Finch},
# Start the Endpoint (http/https)
LiveBeatsWeb.Endpoint,
# Expire songs every six hours
{LiveBeats.SongsCleaner, interval: {3600 * 6, :second}}
# Start a worker by calling: LiveBeats.Worker.start_link(arg)
# {LiveBeats.Worker, arg}
]
children =
[
{Nx.Serving, name: WhisperServing, serving: load_serving()},
{Cluster.Supervisor, [topologies, [name: LiveBeats.ClusterSupervisor]]},
{Task.Supervisor, name: LiveBeats.TaskSupervisor},
{Task.Supervisor, name: Fly.Machine.TaskSupervisor},
# Start the Ecto repository
LiveBeats.Repo,
LiveBeats.ReplicaRepo,
# Start the Telemetry supervisor
LiveBeatsWeb.Telemetry,
# Start the PubSub system
{Phoenix.PubSub, name: LiveBeats.PubSub},
# start presence
LiveBeatsWeb.Presence,
{Finch, name: LiveBeats.Finch},
# Start the Endpoint (http/https)
LiveBeatsWeb.Endpoint,
# Expire songs every six hours
{LiveBeats.SongsCleaner, interval: {3600 * 6, :second}}
# Start a worker by calling: LiveBeats.Worker.start_link(arg)
# {LiveBeats.Worker, arg}
]
# See https://hexdocs.pm/elixir/Supervisor.html
# for other strategies and supported options

17
lib/live_beats/audio.ex Normal file
View file

@ -0,0 +1,17 @@
defmodule LiveBeats.Audio do
def speech_to_text(path, chunk_time, func) do
{:ok, stat} = LiveBeats.MP3Stat.parse(path)
0..stat.duration//chunk_time
|> Task.async_stream(
fn ss ->
args = ~w(-ac 1 -ar 16k -f f32le -ss #{ss} -t #{chunk_time} -v quiet -)
{data, 0} = System.cmd("ffmpeg", ["-i", path] ++ args)
{ss, Nx.Serving.batched_run(WhisperServing, Nx.from_binary(data, :f32))}
end,
max_concurrency: 2,
timeout: :infinity
)
|> Enum.map(fn {:ok, {ss, %{results: [%{text: text}]}}} -> func.(ss, text) end)
end
end

View file

@ -52,13 +52,7 @@ defmodule LiveBeats.MediaLibrary do
user.id == song.user_id
end
def play_song(%Song{id: id}) do
play_song(id)
end
def play_song(id) do
song = get_song!(id)
def play_song(%Song{} = song) do
played_at =
cond do
playing?(song) ->
@ -79,9 +73,10 @@ defmodule LiveBeats.MediaLibrary do
})
stopped_query =
from s in Song,
from(s in Song,
where: s.user_id == ^song.user_id and s.status in [:playing, :paused],
update: [set: [status: :stopped]]
)
{:ok, %{now_playing: new_song}} =
Multi.new()
@ -96,15 +91,22 @@ defmodule LiveBeats.MediaLibrary do
new_song
end
def play_song(id) do
id
|> get_song!()
|> play_song()
end
def pause_song(%Song{} = song) do
now = DateTime.truncate(DateTime.utc_now(), :second)
set = [status: :paused, paused_at: now]
pause_query = from(s in Song, where: s.id == ^song.id, update: [set: ^set])
stopped_query =
from s in Song,
from(s in Song,
where: s.user_id == ^song.user_id and s.status in [:playing, :paused],
update: [set: [status: :stopped]]
)
{:ok, _} =
Multi.new()
@ -165,7 +167,7 @@ defmodule LiveBeats.MediaLibrary do
Ecto.Multi.new()
|> lock_playlist(user.id)
|> Ecto.Multi.run(:starting_position, fn repo, _changes ->
count = repo.one(from s in Song, where: s.user_id == ^user.id, select: count(s.id))
count = repo.one(from(s in Song, where: s.user_id == ^user.id, select: count(s.id)))
{:ok, count - 1}
end)
@ -209,6 +211,8 @@ defmodule LiveBeats.MediaLibrary do
|> Enum.filter(&match?({{:song, _ref}, _}, &1))
|> Enum.map(fn {{:song, ref}, song} ->
consume_file.(ref, fn tmp_path -> store_mp3(song, tmp_path) end)
async_transcribe(song, user)
{ref, song}
end)
@ -228,6 +232,22 @@ defmodule LiveBeats.MediaLibrary do
end
end
defp async_transcribe(%Song{} = song, %Accounts.User{} = user) do
Task.Supervisor.start_child(LiveBeats.TaskSupervisor, fn ->
segments =
LiveBeats.Audio.speech_to_text(song.mp3_filepath, 20, fn ss, text ->
segment = %Song.TranscriptSegment{ss: ss, text: text}
broadcast!(user.id, {segment, song.id})
segment
end)
Repo.update_all(from(s in Song, where: s.id == ^song.id),
set: [transcript_segments: segments]
)
end)
end
defp broadcast_imported(%Accounts.User{} = user, songs) do
songs = Enum.map(songs, fn {_ref, song} -> song end)
broadcast!(user.id, %Events.SongsImported{user_id: user.id, songs: songs})
@ -274,7 +294,7 @@ defmodule LiveBeats.MediaLibrary do
def get_current_active_song(%Profile{user_id: user_id}) do
Repo.replica().one(
from s in Song, where: s.user_id == ^user_id and s.status in [:playing, :paused]
from(s in Song, where: s.user_id == ^user_id and s.status in [:playing, :paused])
)
end
@ -361,7 +381,7 @@ defmodule LiveBeats.MediaLibrary do
Ecto.Multi.new()
|> lock_playlist(song.user_id)
|> Ecto.Multi.run(:index, fn repo, _changes ->
case repo.one(from s in Song, where: s.user_id == ^song.user_id, select: count(s.id)) do
case repo.one(from(s in Song, where: s.user_id == ^song.user_id, select: count(s.id))) do
count when new_index < count -> {:ok, new_index}
count -> {:ok, count - 1}
end

View file

@ -25,6 +25,11 @@ defmodule LiveBeats.MediaLibrary.Song do
belongs_to :user, Accounts.User
belongs_to :genre, LiveBeats.MediaLibrary.Genre
embeds_many :transcript_segments, TranscriptSegment do
field :ss, :integer
field :text, :string
end
timestamps()
end
@ -50,9 +55,19 @@ defmodule LiveBeats.MediaLibrary.Song do
def put_stats(%Ecto.Changeset{} = changeset, %LiveBeats.MP3Stat{} = stat) do
changeset
|> put_duration(stat.duration)
|> maybe_put(:artist, stat.artist)
|> maybe_put(:attribution, stat.attrib)
|> Ecto.Changeset.put_change(:mp3_filesize, stat.size)
end
def maybe_put(%Ecto.Changeset{} = changeset, field, value) do
if Ecto.Changeset.get_field(changeset, field) in [nil, ""] do
Ecto.Changeset.change(changeset, %{field => value})
else
changeset
end
end
defp put_duration(%Ecto.Changeset{} = changeset, duration) when is_integer(duration) do
changeset
|> Ecto.Changeset.change(%{duration: duration})

View file

@ -8,7 +8,7 @@ defmodule LiveBeats.MP3Stat do
import Bitwise
alias LiveBeats.MP3Stat
defstruct duration: 0, size: 0, path: nil, title: nil, artist: nil, tags: nil
defstruct duration: 0, size: 0, path: nil, title: nil, artist: nil, tags: nil, attrib: nil
@declared_frame_ids ~w(AENC APIC ASPI COMM COMR ENCR EQU2 ETCO GEOB GRID LINK MCDI MLLT OWNE PRIV PCNT POPM POSS RBUF RVA2 RVRB SEEK SIGN SYLT SYTC TALB TBPM TCOM TCON TCOP TDEN TDLY TDOR TDRC TDRL TDTG TENC TEXT TFLT TIPL TIT1 TIT2 TIT3 TKEY TLAN TLEN TMCL TMED TMOO TOAL TOFN TOLY TOPE TOWN TPE1 TPE2 TPE3 TPE4 TPOS TPRO TPUB TRCK TRSN TRSO TSOA TSOP TSOT TSRC TSSE TSST TXXX UFID USER USLT WCOM WCOP WOAF WOAR WOAS WORS WPAY WPUB WXXX)
@ -42,6 +42,13 @@ defmodule LiveBeats.MP3Stat do
duration when is_float(duration) and duration > 0 ->
title = Enum.at(tag_info["TIT2"] || [], 0)
artist = Enum.at(tag_info["TPE1"] || [], 0)
attrib =
case tag_info["COMM"] do
{_, _, info} -> String.trim_leading(info, "//")
_ -> nil
end
seconds = round(duration)
{:ok,
@ -51,7 +58,8 @@ defmodule LiveBeats.MP3Stat do
path: path,
tags: tag_info,
title: title,
artist: artist
artist: artist,
attrib: attrib
}}
_other ->

View file

@ -305,6 +305,17 @@ defmodule LiveBeatsWeb.CoreComponents do
)
end
def fade_in, do: fade_in(nil)
def fade_in(js \\ %JS{}, id) do
JS.show(js,
to: id && "##{id}",
time: 1000,
display: "inline-block",
transition: {"ease-out duration-1000", "opacity-0", "opacity-100"}
)
end
def hide(js \\ %JS{}, selector) do
JS.hide(js,
to: selector,
@ -597,7 +608,7 @@ defmodule LiveBeatsWeb.CoreComponents do
phx-click={@row_click && @row_click.(row)}
class={
col[:class!] ||
"px-6 py-3 whitespace-nowrap text-sm font-medium text-gray-900 #{if i == 0, do: "max-w-0 w-full"} #{col[:class]}"
"px-6 py-3 whitespace-nowrap text-sm font-medium text-gray-900 #{col[:class]}"
}
>
<div class="flex items-center space-x-3 lg:pl-2">

View file

@ -15,7 +15,7 @@ defmodule LiveBeatsWeb.OAuthCallbackController do
|> LiveBeatsWeb.UserAuth.log_in_user(user)
else
{:error, %Ecto.Changeset{} = changeset} ->
Logger.debug("failed GitHub insert #{inspect(changeset.errors)}")
Logger.info("failed GitHub insert #{inspect(changeset.errors)}")
conn
|> put_flash(
@ -25,7 +25,7 @@ defmodule LiveBeatsWeb.OAuthCallbackController do
|> redirect(to: "/")
{:error, reason} ->
Logger.debug("failed GitHub exchange #{inspect(reason)}")
Logger.info("failed GitHub exchange #{inspect(reason)}")
conn
|> put_flash(:error, "We were unable to contact GitHub. Please try again later")

View file

@ -332,6 +332,14 @@ defmodule LiveBeatsWeb.PlayerLive do
{:noreply, play_song(socket, play.song, play.elapsed)}
end
def handle_info({MediaLibrary, %MediaLibrary.Events.SongDeleted{song: song}}, socket) do
if socket.assigns.song && socket.assigns.song.id == song.id do
{:noreply, stop_song(socket)}
else
{:noreply, socket}
end
end
def handle_info({MediaLibrary, _}, socket), do: {:noreply, socket}
defp play_song(socket, %Song{} = song, elapsed) do

View file

@ -60,6 +60,13 @@ defmodule LiveBeatsWeb.ProfileLive do
total_count={@presences_count}
/>
<div id={"trascript-#{@active_song_id}"} phx-update="stream" class="mt-10 px-6">
<div :for={{id, segment} <- @streams.transcript_segments} id={id}>
<span class="text-gray-400">[<%= LiveBeats.MP3Stat.to_mmss(segment.ss) %>]</span>
<%= segment.text %>
</div>
</div>
<div id="dialogs" phx-update="stream">
<%= for {_id, song} <- if(@owns_profile?, do: @streams.songs, else: []), id = "delete-modal-#{song.id}" do %>
<.modal
@ -84,7 +91,6 @@ defmodule LiveBeatsWeb.ProfileLive do
rows={@streams.songs}
row_id={fn {id, _song} -> id end}
row_click={fn {_id, song} -> JS.push("play_or_pause", value: %{id: song.id}) end}
row_remove={fn {id, _song} -> hide("##{id}") end}
streamable
sortable_drop="row_dropped"
>
@ -151,23 +157,22 @@ defmodule LiveBeatsWeb.ProfileLive do
Presence.subscribe(profile)
end
active_song_id =
if song = MediaLibrary.get_current_active_song(profile) do
song.id
end
active_song = MediaLibrary.get_current_active_song(profile)
segments = if active_song, do: active_song.transcript_segments, else: []
songs = MediaLibrary.list_profile_songs(profile, 50)
socket =
socket
|> assign(
active_song_id: active_song_id,
active_song_id: active_song && active_song.id,
active_profile_id: current_user.active_profile_user_id,
profile: profile,
owns_profile?: MediaLibrary.owns_profile?(current_user, profile),
songs_count: Enum.count(songs)
)
|> stream(:songs, songs)
|> stream(:transcript_segments, segments, dom_id: &"ss-#{&1.ss}")
|> assign_presences()
{:ok, socket, temporary_assigns: [presences: %{}]}
@ -203,7 +208,11 @@ defmodule LiveBeatsWeb.ProfileLive do
:ok = MediaLibrary.delete_song(song)
end
{:noreply, socket}
if song.id == socket.assigns.active_song_id do
{:noreply, assign(socket, :active_song_id, nil)}
else
{:noreply, socket}
end
end
def handle_event("row_dropped", %{"id" => dom_id, "old" => old_idx, "new" => new_idx}, socket) do
@ -255,7 +264,22 @@ defmodule LiveBeatsWeb.ProfileLive do
{:noreply, pause_song(socket, song)}
end
def handle_info({MediaLibrary, {%MediaLibrary.Song.TranscriptSegment{} = seg, song_id}}, socket) do
if socket.assigns.active_song_id == song_id do
{:noreply, stream_insert(socket, :transcript_segments, seg)}
else
{:noreply, socket}
end
end
def handle_info({MediaLibrary, %MediaLibrary.Events.SongsImported{songs: songs}}, socket) do
%{current_user: current_user, active_song_id: active_song_id} = socket.assigns
first = hd(songs)
if !active_song_id && MediaLibrary.can_control_playback?(current_user, first) do
MediaLibrary.play_song(first)
end
{:noreply,
Enum.reduce(songs, socket, fn song, acc ->
acc
@ -305,20 +329,21 @@ defmodule LiveBeatsWeb.ProfileLive do
defp play_song(socket, %MediaLibrary.Song{} = song) do
%{active_song_id: active_song_id} = socket.assigns
song = %MediaLibrary.Song{song | status: :playing}
cond do
active_song_id == song.id ->
stream_insert(socket, :songs, %MediaLibrary.Song{song | status: :playing})
stream_insert(socket, :songs, song)
active_song_id ->
socket
|> stop_song(active_song_id)
|> stream_insert(:songs, %MediaLibrary.Song{song | status: :playing})
|> stream_insert(:songs, song)
|> assign(active_song_id: song.id)
true ->
socket
|> stream_insert(:songs, %MediaLibrary.Song{song | status: :playing})
|> stream_insert(:songs, song)
|> assign(active_song_id: song.id)
end
end

View file

@ -2,7 +2,8 @@
<p class="inline text-gray-500 text-sm">(songs expire every six hours)</p>
<.form
for={:songs}
for={%{}}
as={:songs}
id="song-form"
class="space-y-8"
phx-target={@myself}

View file

@ -33,7 +33,7 @@ defmodule LiveBeats.MixProject do
defp deps do
[
{:phoenix, "~> 1.7.1"},
{:phoenix_live_view, "~> 0.18.16"},
{:phoenix_live_view, "~> 0.18.17"},
{:phoenix_live_dashboard, "~> 0.7.2"},
{:phoenix_ecto, "~> 4.4"},
{:ecto_sql, "~> 3.6"},
@ -53,8 +53,11 @@ defmodule LiveBeats.MixProject do
{:mint, "~> 1.0"},
{:heroicons, "~> 0.2.2"},
{:castore, "~> 0.1.13"},
{:tailwind, "~> 0.1"},
{:libcluster, "~> 3.3.1"}
{:tailwind, "~> 0.2.0"},
{:libcluster, "~> 3.3.1"},
{:bumblebee, github: "elixir-nx/bumblebee"},
{:exla, ">= 0.0.0"},
{:req, "~> 0.3.7"}
]
end

View file

@ -1,15 +1,21 @@
%{
"axon": {:hex, :axon, "0.5.1", "1ae3a2193df45e51fca912158320b2ca87cb7fba4df242bd3ebe245504d0ea1a", [:mix], [{:kino, "~> 0.7", [hex: :kino, repo: "hexpm", optional: true]}, {:kino_vega_lite, "~> 0.1.7", [hex: :kino_vega_lite, repo: "hexpm", optional: true]}, {:nx, "~> 0.5.0", [hex: :nx, repo: "hexpm", optional: false]}, {:table_rex, "~> 3.1.1", [hex: :table_rex, repo: "hexpm", optional: true]}], "hexpm", "d36f2a11c34c6c2b458f54df5c71ffdb7ed91c6a9ccd908faba909c84cc6a38e"},
"bumblebee": {:git, "https://github.com/elixir-nx/bumblebee.git", "fd9a8b1d149cdcebd5170e103b49e9f6f64ee482", []},
"castore": {:hex, :castore, "0.1.22", "4127549e411bedd012ca3a308dede574f43819fe9394254ca55ab4895abfa1a2", [:mix], [], "hexpm", "c17576df47eb5aa1ee40cc4134316a99f5cad3e215d5c77b8dd3cfef12a22cac"},
"complex": {:hex, :complex, "0.5.0", "af2d2331ff6170b61bb738695e481b27a66780e18763e066ee2cd863d0b1dd92", [:mix], [], "hexpm", "2683bd3c184466cfb94fad74cbfddfaa94b860e27ad4ca1bffe3bff169d91ef1"},
"connection": {:hex, :connection, "1.1.0", "ff2a49c4b75b6fb3e674bfc5536451607270aac754ffd1bdfe175abe4a6d7a68", [:mix], [], "hexpm", "722c1eb0a418fbe91ba7bd59a47e28008a189d47e37e0e7bb85585a016b2869c"},
"cowboy": {:hex, :cowboy, "2.9.0", "865dd8b6607e14cf03282e10e934023a1bd8be6f6bacf921a7e2a96d800cd452", [:make, :rebar3], [{:cowlib, "2.11.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "2c729f934b4e1aa149aff882f57c6372c15399a20d54f65c8d67bef583021bde"},
"cowboy_telemetry": {:hex, :cowboy_telemetry, "0.4.0", "f239f68b588efa7707abce16a84d0d2acf3a0f50571f8bb7f56a15865aae820c", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7d98bac1ee4565d31b62d59f8823dfd8356a169e7fcbb83831b8a5397404c9de"},
"cowlib": {:hex, :cowlib, "2.11.0", "0b9ff9c346629256c42ebe1eeb769a83c6cb771a6ee5960bd110ab0b9b872063", [:make, :rebar3], [], "hexpm", "2b3e9da0b21c4565751a6d4901c20d1b4cc25cbb7fd50d91d2ab6dd287bc86a9"},
"db_connection": {:hex, :db_connection, "2.4.2", "f92e79aff2375299a16bcb069a14ee8615c3414863a6fef93156aee8e86c2ff3", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "4fe53ca91b99f55ea249693a0229356a08f4d1a7931d8ffa79289b145fe83668"},
"decimal": {:hex, :decimal, "2.0.0", "a78296e617b0f5dd4c6caf57c714431347912ffb1d0842e998e9792b5642d697", [:mix], [], "hexpm", "34666e9c55dea81013e77d9d87370fe6cb6291d1ef32f46a1600230b1d44f577"},
"ecto": {:hex, :ecto, "3.9.1", "67173b1687afeb68ce805ee7420b4261649d5e2deed8fe5550df23bab0bc4396", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c80bb3d736648df790f7f92f81b36c922d9dd3203ca65be4ff01d067f54eb304"},
"ecto": {:hex, :ecto, "3.9.4", "3ee68e25dbe0c36f980f1ba5dd41ee0d3eb0873bccae8aeaf1a2647242bffa35", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "de5f988c142a3aa4ec18b85a4ec34a2390b65b24f02385c1144252ff6ff8ee75"},
"ecto_network": {:hex, :ecto_network, "1.3.0", "1e77fa37c20e0f6a426d3862732f3317b0fa4c18f123d325f81752a491d7304e", [:mix], [{:ecto_sql, ">= 3.0.0", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:phoenix_html, ">= 0.0.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:postgrex, ">= 0.14.0", [hex: :postgrex, repo: "hexpm", optional: false]}], "hexpm", "053a5e46ef2837e8ea5ea97c82fa0f5494699209eddd764e663c85f11b2865bd"},
"ecto_sql": {:hex, :ecto_sql, "3.9.0", "2bb21210a2a13317e098a420a8c1cc58b0c3421ab8e3acfa96417dab7817918c", [:mix], [{:db_connection, "~> 2.5 or ~> 2.4.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.9.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "a8f3f720073b8b1ac4c978be25fa7960ed7fd44997420c304a4a2e200b596453"},
"elixir_make": {:hex, :elixir_make, "0.7.5", "784cc00f5fa24239067cc04d449437dcc5f59353c44eb08f188b2b146568738a", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: true]}], "hexpm", "c3d63e8d5c92fa3880d89ecd41de59473fa2e83eeb68148155e25e8b95aa2887"},
"erlexec": {:hex, :erlexec, "2.0.2", "995e40477de94c37ec1264cc3e52eb6273938e80c9bcc4f94110a3f1c0d9aba3", [:rebar3], [], "hexpm", "cc829a7c6c23d399832da2e998ea5ebc552232a6fe3eb1edb400178ec8287dcb"},
"esbuild": {:hex, :esbuild, "0.5.0", "d5bb08ff049d7880ee3609ed5c4b864bd2f46445ea40b16b4acead724fb4c4a3", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}], "hexpm", "f183a0b332d963c4cfaf585477695ea59eef9a6f2204fdd0efa00e099694ffe5"},
"exla": {:hex, :exla, "0.5.1", "8832aa299fe06ed9b772e004760b7c97e9d8dcbe40e9a4bfcbbe10b320b9c342", [:make, :mix], [{:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:nx, "~> 0.5.1", [hex: :nx, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:xla, "~> 0.4.4", [hex: :xla, repo: "hexpm", optional: false]}], "hexpm", "48a990dbaf02bf5f288aa1360b5237c2f55db8bf52d4f63072f2b6a15d4e8375"},
"file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"},
"finch": {:hex, :finch, "0.13.0", "c881e5460ec563bf02d4f4584079e62201db676ed4c0ef3e59189331c4eddf7b", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: false]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.3", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2.6", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "49957dcde10dcdc042a123a507a9c5ec5a803f53646d451db2f7dea696fba6cc"},
"floki": {:hex, :floki, "0.34.0", "002d0cc194b48794d74711731db004fafeb328fe676976f160685262d43706a8", [:mix], [], "hexpm", "9c3a9f43f40dde00332a589bd9d389b90c1f518aef500364d00636acc5ebc99c"},
@ -22,24 +28,34 @@
"mint": {:hex, :mint, "1.4.2", "50330223429a6e1260b2ca5415f69b0ab086141bc76dc2fbf34d7c389a6675b2", [:mix], [{:castore, "~> 0.1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "ce75a5bbcc59b4d7d8d70f8b2fc284b1751ffb35c7b6a6302b5192f8ab4ddd80"},
"nimble_options": {:hex, :nimble_options, "0.4.0", "c89babbab52221a24b8d1ff9e7d838be70f0d871be823165c94dd3418eea728f", [:mix], [], "hexpm", "e6701c1af326a11eea9634a3b1c62b475339ace9456c1a23ec3bc9a847bca02d"},
"nimble_pool": {:hex, :nimble_pool, "0.2.6", "91f2f4c357da4c4a0a548286c84a3a28004f68f05609b4534526871a22053cde", [:mix], [], "hexpm", "1c715055095d3f2705c4e236c18b618420a35490da94149ff8b580a2144f653f"},
"nx": {:hex, :nx, "0.5.1", "118134b8c97c2a8f86c87aa8434994c1cbbe139a306b89cca04e08dd46228067", [:mix], [{:complex, "~> 0.5", [hex: :complex, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ceb8fbbe19b3c4252a7188d8b0e059fac9da0f4a4f3bb770fc665fdd0b29f0c5"},
"nx_image": {:hex, :nx_image, "0.1.0", "ae10fa41fa95126f934d6160ef4320f7db583535fb868415f2562fe19969d245", [:mix], [{:nx, "~> 0.4", [hex: :nx, repo: "hexpm", optional: false]}], "hexpm", "60a2928164cdca540b4c180ff25579b97a5f2a650fc890d40db3e1a7798c93ad"},
"nx_signal": {:hex, :nx_signal, "0.1.0", "403ac73140e2f368e827e0aca1a3035abaf6d890b00376742b359a6838e00d7f", [:mix], [{:nx, "~> 0.5", [hex: :nx, repo: "hexpm", optional: false]}], "hexpm", "1c68f2f0d186700819287f37ee6154a11e06bf5dbb30b73fcc92776293309a05"},
"phoenix": {:hex, :phoenix, "1.7.1", "a029bde19d9c3b559e5c3d06c78b76e81396bedd456a6acedb42f9c7b2e535a9", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.4", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "ea9d4a85c3592e37efa07d0dc013254fda445885facaefddcbf646375c116457"},
"phoenix_ecto": {:hex, :phoenix_ecto, "4.4.0", "0672ed4e4808b3fbed494dded89958e22fb882de47a97634c0b13e7b0b5f7720", [:mix], [{:ecto, "~> 3.3", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "09864e558ed31ee00bd48fcc1d4fc58ae9678c9e81649075431e69dbabb43cc1"},
"phoenix_html": {:hex, :phoenix_html, "3.3.1", "4788757e804a30baac6b3fc9695bf5562465dd3f1da8eb8460ad5b404d9a2178", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "bed1906edd4906a15fd7b412b85b05e521e1f67c9a85418c55999277e553d0d3"},
"phoenix_live_dashboard": {:hex, :phoenix_live_dashboard, "0.7.2", "97cc4ff2dba1ebe504db72cb45098cb8e91f11160528b980bd282cc45c73b29c", [:mix], [{:ecto, "~> 3.6.2 or ~> 3.7", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_mysql_extras, "~> 0.5", [hex: :ecto_mysql_extras, repo: "hexpm", optional: true]}, {:ecto_psql_extras, "~> 0.7", [hex: :ecto_psql_extras, repo: "hexpm", optional: true]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.18.3", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6 or ~> 1.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "0e5fdf063c7a3b620c566a30fcf68b7ee02e5e46fe48ee46a6ec3ba382dc05b7"},
"phoenix_live_reload": {:hex, :phoenix_live_reload, "1.4.0", "4fe222c0be55fdc3f9c711e24955fc42a7cd9b7a2f5f406f2580a567c335a573", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "bebf0fc2d2113b61cb5968f585367234b7b4c21d963d691de7b4b2dc6cdaae6f"},
"phoenix_live_view": {:hex, :phoenix_live_view, "0.18.16", "781c6a3ac49e0451ca403848b40807171caea400896fe8ed8e5ddd6106ad5580", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.15 or ~> 1.7.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "09e6ae2babe62f74bfcd1e3cac1a9b0e2c262557cc566300a843425c9cb6842a"},
"phoenix_live_view": {:hex, :phoenix_live_view, "0.18.17", "74938b02f3c531bed3f87fe1ea39af6b5b2d26ab1405e77e76b8ef5df9ffa8a1", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.15 or ~> 1.7.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "f4b5710e19a29b8dc93b7af4bab4739c067a3cb759af01ffc3057165453dce38"},
"phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.1", "ba04e489ef03763bf28a17eb2eaddc2c20c6d217e2150a61e3298b0f4c2012b5", [:mix], [], "hexpm", "81367c6d1eea5878ad726be80808eb5a787a23dee699f96e72b1109c57cdd8d9"},
"phoenix_template": {:hex, :phoenix_template, "1.0.1", "85f79e3ad1b0180abb43f9725973e3b8c2c3354a87245f91431eec60553ed3ef", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "157dc078f6226334c91cb32c1865bf3911686f8bcd6bcff86736f6253e6993ee"},
"plug": {:hex, :plug, "1.14.0", "ba4f558468f69cbd9f6b356d25443d0b796fbdc887e03fa89001384a9cac638f", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "bf020432c7d4feb7b3af16a0c2701455cbbbb95e5b6866132cb09eb0c29adc14"},
"plug_cowboy": {:hex, :plug_cowboy, "2.6.0", "d1cf12ff96a1ca4f52207c5271a6c351a4733f413803488d75b70ccf44aebec2", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "073cf20b753ce6682ed72905cd62a2d4bd9bad1bf9f7feb02a1b8e525bd94fa6"},
"plug_crypto": {:hex, :plug_crypto, "1.2.3", "8f77d13aeb32bfd9e654cb68f0af517b371fb34c56c9f2b58fe3df1235c1251a", [:mix], [], "hexpm", "b5672099c6ad5c202c45f5a403f21a3411247f164e4a8fab056e5cd8a290f4a2"},
"postgrex": {:hex, :postgrex, "0.16.5", "fcc4035cc90e23933c5d69a9cd686e329469446ef7abba2cf70f08e2c4b69810", [:mix], [{:connection, "~> 1.1", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "edead639dc6e882618c01d8fc891214c481ab9a3788dfe38dd5e37fd1d5fb2e8"},
"progress_bar": {:hex, :progress_bar, "2.0.1", "7b40200112ae533d5adceb80ff75fbe66dc753bca5f6c55c073bfc122d71896d", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm", "2519eb58a2f149a3a094e729378256d8cb6d96a259ec94841bd69fdc71f18f87"},
"ranch": {:hex, :ranch, "1.8.0", "8c7a100a139fd57f17327b6413e4167ac559fbc04ca7448e9be9057311597a1d", [:make, :rebar3], [], "hexpm", "49fbcfd3682fab1f5d109351b61257676da1a2fdbe295904176d5e521a2ddfe5"},
"req": {:hex, :req, "0.3.7", "e4ea5d73e3f434c0a15601bb85330ffd0e57860c098283e98c28d21172a1f749", [:mix], [{:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.9", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "a7d3c0bec7d2d23198ef12676d2c950bec258308c6a5123eb98465030205f39c"},
"rustler_precompiled": {:hex, :rustler_precompiled, "0.6.1", "160b545bce8bf9a3f1b436b2c10f53574036a0db628e40f393328cbbe593602f", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:rustler, "~> 0.23", [hex: :rustler, repo: "hexpm", optional: true]}], "hexpm", "0dd269fa261c4e3df290b12031c575fff07a542749f7b0e8b744d72d66c43600"},
"swoosh": {:hex, :swoosh, "1.8.2", "af9a22ab2c0d20b266f61acca737fa11a121902de9466a39e91bacdce012101c", [:mix], [{:cowboy, "~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:ex_aws, "~> 2.1", [hex: :ex_aws, repo: "hexpm", optional: true]}, {:finch, "~> 0.6", [hex: :finch, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.13 or ~> 1.0", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mail, "~> 0.2", [hex: :mail, repo: "hexpm", optional: true]}, {:mime, "~> 1.1 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_cowboy, ">= 1.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "d058ba750eafadb6c09a84a352c14c5d1eeeda6e84945fcc95785b7f3067b7db"},
"tailwind": {:hex, :tailwind, "0.1.9", "25ba09d42f7bfabe170eb67683a76d6ec2061952dc9bd263a52a99ba3d24bd4d", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}], "hexpm", "9213f87709c458aaec313bb5f2df2b4d2cedc2b630e4ae821bf3c54c47a56d0b"},
"tailwind": {:hex, :tailwind, "0.2.0", "95f9e4a32020c5bec480f1d6a43a49ac8030b13183127b577605f506d6e13a66", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}], "hexpm", "385e939fcd7fe4654be5130b187e358aaabade385513f9d200ffecdbb9552a9e"},
"telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"},
"telemetry_metrics": {:hex, :telemetry_metrics, "0.6.1", "315d9163a1d4660aedc3fee73f33f1d355dcc76c5c3ab3d59e76e3edf80eef1f", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7be9e0871c41732c233be71e4be11b96e56177bf15dde64a8ac9ce72ac9834c6"},
"telemetry_poller": {:hex, :telemetry_poller, "1.0.0", "db91bb424e07f2bb6e73926fcafbfcbcb295f0193e0a00e825e589a0a47e8453", [:rebar3], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "b3a24eafd66c3f42da30fc3ca7dda1e9d546c12250a2d60d7b81d264fbec4f6e"},
"tokenizers": {:hex, :tokenizers, "0.3.0", "1aebe61c68cf36e3ea4423a357b196e226c18a8b3206afe59d257f038833deb4", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:rustler, ">= 0.0.0", [hex: :rustler, repo: "hexpm", optional: true]}, {:rustler_precompiled, "~> 0.6", [hex: :rustler_precompiled, repo: "hexpm", optional: false]}], "hexpm", "6c6de26911fd875fc1aead92ee40efb3e4271f54a312cc52f073012f2a134201"},
"unpickler": {:hex, :unpickler, "0.1.0", "c2262c0819e6985b761e7107546cef96a485f401816be5304a65fdd200d5bd6a", [:mix], [], "hexpm", "e2b3f61e62406187ac52afead8a63bfb4e49394028993f3c4c42712743cab79e"},
"unzip": {:hex, :unzip, "0.8.0", "ee21d87c21b01567317387dab4228ac570ca15b41cfc221a067354cbf8e68c4d", [:mix], [], "hexpm", "ffa67a483efcedcb5876971a50947222e104d5f8fea2c4a0441e6f7967854827"},
"websock": {:hex, :websock, "0.5.0", "f6bbce90226121d62a0715bca7c986c5e43de0ccc9475d79c55381d1796368cc", [:mix], [], "hexpm", "b51ac706df8a7a48a2c622ee02d09d68be8c40418698ffa909d73ae207eb5fb8"},
"websock_adapter": {:hex, :websock_adapter, "0.4.5", "30038a3715067f51a9580562c05a3a8d501126030336ffc6edb53bf57d6d2d26", [:mix], [{:bandit, "~> 0.6", [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.4", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "1d9812dc7e703c205049426fd4fe0852a247a825f91b099e53dc96f68bafe4c8"},
"xla": {:hex, :xla, "0.4.4", "c3a8ed1f579bda949df505e49ff65415c8281d991fbd6ae1d8f3c5d0fd155f54", [:make, :mix], [{:elixir_make, "~> 0.4", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "484f3f9011db3c9f1ff1e98eecefd382f3882a07ada540fd58803db1d2dab671"},
}

View file

@ -0,0 +1,9 @@
defmodule LiveBeats.Repo.Migrations.AddTranscriptsToSongs do
use Ecto.Migration
def change do
alter table(:songs) do
add :transcript_segments, {:array, :map}, null: false, default: []
end
end
end