struct

DubbedEpisode

Inherits JSON::Serializable < Struct < Value < Object

Constants

DUBBED_RECENT_SELECT = "SELECT\n e.id AS episode_id,\n e.feed_id, f.title AS feed_title, f.image_url AS feed_image,\n e.title AS ep_title, e.image_url AS ep_image, e.duration_sec,\n e.original_language AS source_lang,\n de.language AS target_lang,\n de.completed_at,\n EXISTS (\n SELECT 1 FROM user_feeds uf\n WHERE uf.user_id = $1 AND uf.feed_id = e.feed_id\n ) AS subscribed,\n (de.completed_at > NOW() - INTERVAL '24 hours') AS is_new\nFROM dubbed_episodes de\nJOIN episodes e ON e.id = de.episode_id\nJOIN feeds f ON f.id = e.feed_id\nWHERE de.status = 'done' AND de.completed_at IS NOT NULL\n AND ($3::text[] IS NULL OR de.language = ANY($3))"

Shared column list + JOINs for the DubbedRecent projection. $1 = user_id (drives the subscribed flag), $3 = optional text[] language filter.

Constructors

new(id : Int64, episode_id : Int64, language : String, status : String, step : String, r2_url : Nil | String, translation : Nil | String, error : Nil | String, expires_at : Time | Nil, created_at : Time, requester_telegram_id : Int64 | Nil)
Source
new(*, __pull_for_json_serializable pull : JSON::PullParser)
Source

Class methods

all_for_user(user_id : Int64, limit : Int32 = 100, langs : Array(String) | Nil = nil) : Array(DubbedRecent)

Full dubbed history for the dedicated Dubbed page: pure recency, optional langs filter. Same DubbedRecent shape as the inbox widget.

Source
distinct_done_languages

Distinct target languages across all done dubs whose episode + feed still exist — drives the Dubbed page's filter chips (unfiltered, so the chip row is stable regardless of the current selection).

Source
find(episode_id : Int64, language : String) : DubbedEpisode | Nil
Source
find_by_id(id : Int64) : DubbedEpisode | Nil
Source
recent_for_inbox(user_id : Int64, limit : Int32 = 12, langs : Array(String) | Nil = nil) : Array(DubbedRecent)

Inbox "Latest dubbed" widget: subscribed-first, then most recent. langs (when present) restricts to those target languages before the limit, so a selected language always surfaces its latest dub.

Source
set_complete(id : Int64, r2_url : String | Nil, speaker_samples : String | Nil = nil)
Source
set_failed(id : Int64, error : String)
Source
set_processing(id : Int64)
Source
set_step(id : Int64, step : String)
Source
statuses_for_episode(episode_id : Int64)

Returns a map of language → {status, step, r2_url?, translation?} for all dubs of an episode.

Source
upsert_pending(episode_id : Int64, language : String, requester_telegram_id : Int64) : Int64
Source

Instance methods

created_at
Source
created_at=(created_at : Time)
Source
effective_status
Source
episode_id
Source
episode_id=(episode_id : Int64)
Source
error
Source
error=(error : String | Nil)
Source
expired?
Source
expires_at
Source
expires_at=(expires_at : Time | Nil)
Source
id=(id : Int64)
Source
language
Source
language=(language : String)
Source
r2_url
Source
r2_url=(r2_url : String | Nil)
Source
requester_telegram_id
Source
requester_telegram_id=(requester_telegram_id : Int64 | Nil)
Source
status
Source
status=(status : String)
Source
step
Source
step=(step : String)
Source
translation
Source
translation=(translation : String | Nil)
Source

Nested types