defmodule Algora.Workers.Transcriber do use Oban.Worker, queue: :default, max_attempts: 1, unique: [period: 86_400] alias Algora.Library import Ecto.Query, warn: false require Logger @impl Oban.Worker def perform(%Oban.Job{args: %{"video_id" => video_id}} = job) do video = Library.get_video!(video_id) build_transcriber(job, video) await_transcriber(video) end defp build_transcriber(job, %Library.Video{} = video) do job_pid = self() Task.async(fn -> try do prediction = Library.transcribe_video(video, fn progress -> send(job_pid, {:progress, progress}) end) _output = await_prediction(prediction.id, fn progress -> send(job_pid, {:progress, progress}) end) send(job_pid, {:complete, video}) rescue e -> send(job_pid, {:error, e, job}) reraise e, __STACKTRACE__ end end) end defp await_prediction(id, cb) do case Replicate.Predictions.get(id) do {:ok, %Replicate.Predictions.Prediction{status: "succeeded", output: output}} -> {:ok, resp} = Finch.build(:get, output) |> Finch.request(Algora.Finch) Jason.decode!(resp.body) {:ok, %Replicate.Predictions.Prediction{logs: logs}} -> cb.(%{stage: logs |> String.split("\n") |> Enum.at(-1), done: 1, total: 1}) :timer.sleep(1000) await_prediction(id, cb) error -> error end end defp await_transcriber(video, stage \\ :retrieving, done \\ 0) do receive do {:progress, %{stage: stage_now, done: done_now, total: total}} -> Library.broadcast_processing_progressed!(stage, video, min(1, done / total)) done_total = if(stage == stage_now, do: done, else: 0) await_transcriber(video, stage_now, done_total + done_now) {:complete, video} -> Library.broadcast_processing_progressed!(stage, video, 1) Library.broadcast_processing_completed!(:transcription, video, video.url) {:ok, video.url} {:error, e, %Oban.Job{attempt: attempt, max_attempts: max_attempts}} -> Library.broadcast_processing_failed!(video, attempt, max_attempts) {:error, e} end end end