Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New bout page #730

Draft
wants to merge 21 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
37eb44a
Add blank new bout page, non-null category on bout
skanderm Nov 14, 2024
0e4d10d
Merge branch 'main' into skander/new-bout
skanderm Nov 21, 2024
a29780d
Rename ReportsLayout to SimpleLayout
skanderm Nov 21, 2024
e7bab08
Add loading spinner, start of audio_images gql queries
skanderm Nov 25, 2024
6dcd702
Merge branch 'main' into skander/new-bout
skanderm Nov 26, 2024
b735f88
Add skeleton for new bout page, scrollable spectrogram element, feed_…
skanderm Nov 29, 2024
3f35467
Add feedStreams query to frontend, update feed_streams gql client wit…
skanderm Dec 3, 2024
3ccb700
Update feed_streams client query call in GlobalSetup
skanderm Dec 3, 2024
5a09253
Merge branch 'main' into skander/new-bout
skanderm Dec 3, 2024
ad66cd7
Update stream/segment populate script and fix create actions
skanderm Dec 6, 2024
58defa5
Update populate script. Add target time for bout player
skanderm Dec 11, 2024
79f4fd5
Add spectrogram layers, tick marks, update feed stream queries
skanderm Dec 13, 2024
60b6f07
Only show spectrograms for the visible window
skanderm Dec 17, 2024
31169c3
Add more buffer for loading spectrogram images, update background and…
skanderm Dec 18, 2024
60a4b41
Attempt to forward playerTime ref
skanderm Dec 21, 2024
7e5530f
Add working PlayHeadLayer with time updates using a ref
skanderm Dec 31, 2024
60e1aae
Set window scroll based on player time
skanderm Dec 31, 2024
61eb2a5
Add new separate ticker layer
skanderm Jan 6, 2025
0ba36c3
Remove mouse wheel zoom. Replace playhead with sticky element in the …
skanderm Jan 6, 2025
871e66d
Refactor layer components into their own files
skanderm Jan 6, 2025
f8b212f
Add timeline markers, set bout start/end
skanderm Jan 7, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion server/config/config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ config :orcasite, Oban,
repo: Orcasite.Repo,
# 7 day job retention
plugins: [{Oban.Plugins.Pruner, max_age: 7 * 24 * 60 * 60}],
queues: [default: 10, email: 10, feeds: 10]
queues: [default: 10, email: 10, feeds: 10, audio_images: 5]

config :spark, :formatter,
remove_parens?: true,
Expand Down
2 changes: 2 additions & 0 deletions server/config/dev.exs
Original file line number Diff line number Diff line change
Expand Up @@ -113,3 +113,5 @@ config :ex_aws,
config :orcasite,
audio_image_bucket: System.get_env("ORCASITE_AUDIO_IMAGE_BUCKET", "dev-audio-viz"),
audio_image_bucket_region: System.get_env("ORCASITE_AUDIO_IMAGE_BUCKET_REGION", "us-west-2")

config :orcasite, :env, :dev
2 changes: 2 additions & 0 deletions server/config/prod.exs
Original file line number Diff line number Diff line change
Expand Up @@ -80,3 +80,5 @@ else
config :hammer,
backend: {Hammer.Backend.ETS, [expiry_ms: 60_000 * 60 * 4, cleanup_interval_ms: 60_000 * 10]}
end

config :orcasite, :env, :prod
4 changes: 3 additions & 1 deletion server/config/runtime.exs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ if System.get_env("PHX_SERVER") do
config :orcasite, OrcasiteWeb.Endpoint, server: true
end

config :orcasite, :prod_host, System.get_env("HOST_URL", "live.orcasound.net")

if config_env() == :prod do
database_url =
System.get_env("DATABASE_URL") ||
Expand Down Expand Up @@ -49,7 +51,7 @@ if config_env() == :prod do
You can generate one by calling: mix phx.gen.secret
"""

host = System.get_env("HOST_URL") || "live.orcasite.com"
host = System.get_env("HOST_URL") || "live.orcasound.net"
port = String.to_integer(System.get_env("PORT") || "4000")

if System.get_env("FEED_STREAM_QUEUE_URL", "") != "" do
Expand Down
2 changes: 2 additions & 0 deletions server/config/test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,5 @@ config :ex_aws,
:instance_role
],
region: "us-west-2"

config :orcasite, :env, :test
1 change: 0 additions & 1 deletion server/lib/orcasite/accounts/user.ex
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,6 @@ defmodule Orcasite.Accounts.User do
code_interface do
domain Orcasite.Accounts

define :register_with_password
define :sign_in_with_password
define :by_email, args: [:email]
define :request_password_reset_with_password
Expand Down
56 changes: 56 additions & 0 deletions server/lib/orcasite/global_setup.ex
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ defmodule Orcasite.GlobalSetup do
|> case do
{:ok, %{timestamps: [_ | _] = timestamps}} ->
timestamp = List.last(timestamps)

{:ok, feed_stream} =
Orcasite.Radio.FeedStream
|> Ash.Changeset.for_create(:create, %{feed: feed, playlist_timestamp: timestamp})
Expand All @@ -34,4 +35,59 @@ defmodule Orcasite.GlobalSetup do
:ok
end
end

def populate_latest_feed_streams(feed, minutes_ago \\ 10) do
if Application.get_env(:orcasite, :env) != :prod do
# Get prod feed id for feed
{:ok, feed_resp} = Orcasite.Radio.GraphqlClient.get_feed(feed.slug)

feed_resp
|> get_in(["data", "feed", "id"])
|> case do
nil ->
{:error, :feed_not_found}

feed_id ->
now = DateTime.utc_now()
minutes_ago_datetime = now |> DateTime.add(-minutes_ago, :minute)

# Get stream for the last `minutes` minutes
{:ok, feed_streams_response} =
Orcasite.Radio.GraphqlClient.get_feed_streams_with_segments(
feed_id,
minutes_ago_datetime,
now
)

feed_streams = get_in(feed_streams_response, ["data", "feedStreams", "results"])

feed_streams
|> Recase.Enumerable.convert_keys(&Recase.to_snake/1)
|> Enum.map(fn feed_stream ->
feed_stream
|> Map.drop(["id"])
|> Map.put("feed", feed)
|> Map.update(
"feed_segments",
[],
&Enum.map(&1, fn seg ->
seg
|> Map.drop(["id"])
|> Map.put("feed", feed)
|> Recase.Enumerable.atomize_keys()
end)
)
|> Recase.Enumerable.atomize_keys()
end)
|> Ash.bulk_create(
Orcasite.Radio.FeedStream,
:populate_with_segments,
return_errors?: true,
stop_on_error?: true,
upsert?: true,
upsert_identity: :playlist_m3u8_path
)
end
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ defmodule Orcasite.Notifications.Workers.SendNotificationEmail do
end
|> Enum.filter(&(&1.id != notification_id))

:ok = continue?()
:ok = Orcasite.RateLimiter.continue?("ses_email", 1_000, 14)

%{meta: params}
|> Map.merge(%{
Expand Down Expand Up @@ -95,14 +95,4 @@ defmodule Orcasite.Notifications.Workers.SendNotificationEmail do
end)
end

def continue?() do
case Hammer.check_rate("ses_email", 1_000, 14) do
{:allow, _count} ->
:ok

{:deny, _limit} ->
Process.sleep(250)
continue?()
end
end
end
24 changes: 22 additions & 2 deletions server/lib/orcasite/radio/audio_image.ex
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,18 @@ defmodule Orcasite.Radio.AudioImage do
actions do
defaults [:read, :destroy, create: :*, update: :*]

read :for_feed do
argument :feed_id, :string, allow_nil?: false

pagination do
offset? true
countable true
default_limit 100
end

filter expr(feed_id == ^arg(:feed_id))
end

create :for_feed_segment do
upsert? true
upsert_identity :unique_audio_image
Expand Down Expand Up @@ -201,7 +213,7 @@ defmodule Orcasite.Radio.AudioImage do
{:error, error} ->
image
|> Ash.Changeset.for_update(:update, %{
status: :failed
status: :errored
})
|> Ash.Changeset.force_change_attribute(:last_error, inspect(error))
|> Ash.update(authorize?: false)
Expand All @@ -215,10 +227,18 @@ defmodule Orcasite.Radio.AudioImage do
prepend?: true
)
end

update :set_failed do
change set_attribute(:status, :failed)
end
end

graphql do
type :audio_image
attribute_types [feed_id: :id]
attribute_types feed_id: :id

queries do
list :audio_images, :for_feed
end
end
end
1 change: 1 addition & 0 deletions server/lib/orcasite/radio/bout.ex
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ defmodule Orcasite.Radio.Bout do

attribute :category, Orcasite.Types.AudioCategory do
public? true
allow_nil? false
end

create_timestamp :inserted_at
Expand Down
8 changes: 7 additions & 1 deletion server/lib/orcasite/radio/detection.ex
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,10 @@ defmodule Orcasite.Radio.Detection do

relationships do
belongs_to :candidate, Candidate, public?: true
belongs_to :feed, Feed, public?: true
belongs_to :feed, Feed do
public? true

end

belongs_to :user, Orcasite.Accounts.User
end
Expand Down Expand Up @@ -103,6 +106,9 @@ defmodule Orcasite.Radio.Detection do
default_limit 100
end

argument :feed_id, :string

filter expr(if not is_nil(^arg(:feed_id)), do: feed_id == ^arg(:feed_id), else: true)
prepare build(load: [:uuid], sort: [inserted_at: :desc])
end

Expand Down
2 changes: 1 addition & 1 deletion server/lib/orcasite/radio/feed_segment.ex
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ defmodule Orcasite.Radio.FeedSegment do
]

argument :feed, :map, allow_nil?: false
argument :feed_stream, :map, allow_nil?: false
argument :feed_stream, :map

argument :segment_path, :string do
allow_nil? false
Expand Down
35 changes: 35 additions & 0 deletions server/lib/orcasite/radio/feed_stream.ex
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,41 @@ defmodule Orcasite.Radio.FeedStream do
end
end

create :populate_with_segments do
upsert? true
upsert_identity :playlist_m3u8_path
accept [
:start_time,
:end_time,
:duration,
:bucket,
:bucket_region,
:cloudfront_url,
:playlist_path,
:playlist_timestamp,
:playlist_m3u8_path
]

upsert_fields [
:start_time,
:end_time,
:duration,
:bucket,
:bucket_region,
:cloudfront_url,
:playlist_path,
:playlist_timestamp,
:playlist_m3u8_path,
:updated_at
]

argument :feed_segments, {:array, :map}
argument :feed, :map

change manage_relationship(:feed_segments, type: :create)
change manage_relationship(:feed, type: :append)
end

update :update_segments do
description "Pulls contents of m3u8 file and creates a FeedSegment per new entry"
require_atomic? false
Expand Down
99 changes: 99 additions & 0 deletions server/lib/orcasite/radio/graphql_client.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
defmodule Orcasite.Radio.GraphqlClient do
def get_feed(feed_slug) do
~s|
{
feed(slug: "#{feed_slug}") {
id
slug
}
}
|
|> submit()
end

def get_feed_streams_with_segments(feed_id, from_datetime, to_datetime) do
day_before = from_datetime |> DateTime.add(-1, :day)

~s|
{
feedStreams(
feedId: "#{feed_id}",
filter: {
and: [
{startTime: {lessThanOrEqual: "#{DateTime.to_iso8601(to_datetime)}"}},
{startTime: {greaterThanOrEqual: "#{DateTime.to_iso8601(day_before)}"}}
],
or: [{endTime: {isNil: true}}, {endTime: {greaterThanOrEqual: "#{DateTime.to_iso8601(from_datetime)}"}}]
},
sort: {field: START_TIME, order: DESC},
limit: 2
) {
count
results {
id
startTime
endTime
duration
bucket
bucketRegion
cloudfrontUrl
playlistTimestamp
playlistPath
playlistM3u8Path

feedSegments(
filter: {
and: [
{startTime: {lessThanOrEqual: "#{DateTime.to_iso8601(to_datetime)}"}},
{startTime: {greaterThanOrEqual: "#{DateTime.to_iso8601(day_before)}"}}
],
endTime: {greaterThanOrEqual: "#{DateTime.to_iso8601(from_datetime)}"}
},
sort: {field: START_TIME, order: DESC},
) {
startTime
endTime
duration
bucket
bucketRegion
cloudfrontUrl
fileName
playlistM3u8Path
playlistPath
playlistTimestamp
segmentPath
}
}
}
}
|
|> submit()
end

def submit(query) do
Finch.build(
:post,
gql_url(),
[{"content-type", "application/json"}],
Jason.encode!(%{
query: query
})
)
|> Finch.request(Orcasite.Finch)
|> case do
{:ok, %{body: body}} -> Jason.decode(body)
resp -> resp
end
end

def gql_url() do
Application.get_env(:orcasite, :prod_host)
|> case do
"https://" <> _host = url -> url
"http://" <> _host = url -> url
host -> "https://" <> host
end
|> String.trim_trailing("/")
|> then(&(&1 <> "/graphql"))
end
end
Loading
Loading