mirror of
https://github.com/fly-apps/live_beats.git
synced 2024-05-28 22:18:07 +00:00
Merge 57f5c0f142
into 712204ca17
This commit is contained in:
commit
2450a9dbfd
10
Dockerfile
10
Dockerfile
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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: []
|
||||
|
|
43
fly.toml
43
fly.toml
|
@ -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"
|
|
@ -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
17
lib/live_beats/audio.ex
Normal 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
|
|
@ -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
|
||||
|
|
|
@ -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})
|
||||
|
|
|
@ -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 ->
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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}
|
||||
|
|
9
mix.exs
9
mix.exs
|
@ -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
|
||||
|
||||
|
|
22
mix.lock
22
mix.lock
|
@ -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"},
|
||||
}
|
||||
|
|
|
@ -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
|
Loading…
Reference in a new issue