Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
18 changes: 17 additions & 1 deletion lib/ambry/media.ex
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,23 @@ defmodule Ambry.Media do
Gets a media and the book with all its details.
"""
def get_media_with_book_details!(id) do
id
|> media_with_book_details_query()
|> Repo.get!(id)
end

@doc """
Gets a media and the book with all its details.

Returns `{:ok, media}` on success or `{:error, :not_found}`.
"""
def fetch_media_with_book_details(id) do
id
|> media_with_book_details_query()
|> Repo.fetch(id)
end

defp media_with_book_details_query(id) do
media_query =
from m in Media, where: m.status == :ready and m.id != ^id, order_by: {:desc, :published}

Expand All @@ -128,7 +145,6 @@ defmodule Ambry.Media do
media: ^{media_query, [:narrators, book: [:authors, series_books: :series]]}
]
])
|> Repo.get!(id)
end

@doc """
Expand Down
7 changes: 7 additions & 0 deletions lib/ambry_web/components/layouts/root.html.heex
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@
<link phx-track-static rel="stylesheet" href={~p"/assets/app.css"} />
<script defer phx-track-static type="text/javascript" src={~p"/assets/app.js"}>
</script>
<script :if={assigns[:global_id]} id="deep-link-script" data-global-id={@global_id}>
// Attempt to open the Ambry mobile app via deep link
(function() {
var globalId = document.getElementById('deep-link-script').dataset.globalId;
window.location = 'ambry://media/' + globalId;
})();
</script>
<script type="text/javascript">
// fix mobile browser viewport height shenanigans.
// https://www.markusantonwolf.com/blog/solution-to-the-mobile-viewport-height-issue-with-tailwind-css/
Expand Down
14 changes: 12 additions & 2 deletions lib/ambry_web/components/preview/layouts/root.html.heex
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,23 @@
<link rel="icon" type="image/png" href={~p"/favicon.png"} sizes="16x16" />
<link rel="icon" type="image/png" href={~p"/favicon-32x32.png"} sizes="32x32" />
<link rel="icon" type="image/png" href={~p"/favicon-96x96.png"} sizes="96x96" />
<title>{@page_title} • Ambry</title>
<title>{assigns[:page_title] || "Personal Audiobook Streaming"} • Ambry</title>
<link phx-track-static rel="stylesheet" href={~p"/assets/app.css"} />

<meta property="og:site_name" content="Ambry • Personal Audiobook Streaming" />
<meta :for={{key, value} <- assigns[:og]} property={"og:#{key}"} content={value} />
<%= if assigns[:og] do %>
<meta :for={{key, value} <- assigns[:og]} property={"og:#{key}"} content={value} />
<% end %>
<meta name="theme-color" content="#a3e635" />
<meta name="twitter:card" content="summary_large_image" />

<script :if={assigns[:global_id]} id="deep-link-script" data-global-id={@global_id}>
// Attempt to open the Ambry mobile app via deep link
(function() {
var globalId = document.getElementById('deep-link-script').dataset.globalId;
window.location = 'ambry://media/' + globalId;
})();
</script>
</head>
<body class="bg-white text-zinc-800 antialiased selection:bg-brand selection:text-white dark:bg-black dark:text-zinc-200 dark:selection:bg-brand-dark dark:selection:text-black">
{@inner_content}
Expand Down
44 changes: 28 additions & 16 deletions lib/ambry_web/controllers/preview/audiobook_controller.ex
Original file line number Diff line number Diff line change
@@ -1,25 +1,37 @@
defmodule AmbryWeb.Preview.AudiobookController do
use AmbryWeb, :controller

alias Ambry.Media
import Absinthe.Relay.Node, only: [to_global_id: 3]
import AmbryWeb.Helpers.IdHelpers

def show(conn, %{"id" => media_id}) do
media = Media.get_media_with_book_details!(media_id)
alias Ambry.Media

description = (media |> Media.get_media_description() |> String.slice(0..100)) <> "..."
def show(conn, %{"id" => id_param}) do
with {:ok, media_id} <- parse_id(id_param, :media),
{:ok, media} <- Media.fetch_media_with_book_details(media_id) do
description = (media |> Media.get_media_description() |> String.slice(0..100)) <> "..."
global_id = to_global_id("Media", media.id, AmbrySchema)

conn
|> put_session(:user_return_to, current_path(conn))
|> render(:show, %{
media: media,
page_title: description,
og: %{
title: description,
image: media.thumbnails && unverified_url(conn, media.thumbnails.extra_large),
description: media.description && truncate_markdown(media.description),
url: url(conn, ~p"/audiobooks/#{media.id}")
}
})
conn
|> put_session(:user_return_to, current_path(conn))
|> render(:show, %{
media: media,
global_id: global_id,
page_title: description,
og: %{
title: description,
image: media.thumbnails && unverified_url(conn, media.thumbnails.extra_large),
description: media.description && truncate_markdown(media.description),
url: url(conn, ~p"/audiobooks/#{media.id}")
}
})
else
_ ->
conn
|> put_status(:not_found)
|> put_view(AmbryWeb.ErrorHTML)
|> render(:"404")
end
end

defp truncate_markdown(markdown) do
Expand Down
48 changes: 48 additions & 0 deletions lib/ambry_web/helpers/id_helpers.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
defmodule AmbryWeb.Helpers.IdHelpers do
@moduledoc """
Helpers for parsing IDs that may be either integer IDs or Relay global IDs.
"""

import Absinthe.Relay.Node, only: [from_global_id: 2]

@doc """
Parses an ID that may be either an integer string or a Relay global ID.
Returns {:ok, integer_id} or {:error, reason}.

## Examples

iex> parse_id("123")
{:ok, 123}

iex> parse_id("TWVkaWE6MTIz") # base64 for "Media:123"
{:ok, 123}

iex> parse_id("TWVkaWE6MTIz", :media)
{:ok, 123}

iex> parse_id("TWVkaWE6MTIz", :book)
{:error, :type_mismatch}

"""
def parse_id(id_string, expected_type \\ nil) do
# First try as plain integer
case Integer.parse(id_string) do
{int_id, ""} ->
{:ok, int_id}

_ ->
# Try as Relay global ID
case from_global_id(id_string, AmbrySchema) do
{:ok, %{id: id_str, type: type}} ->
if expected_type && type != expected_type do
{:error, :type_mismatch}
else
{:ok, String.to_integer(id_str)}
end

_ ->
{:error, :invalid_id}
end
end
end
end
30 changes: 19 additions & 11 deletions lib/ambry_web/live/audiobook_live.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ defmodule AmbryWeb.AudiobookLive do

use AmbryWeb, :live_view

import Absinthe.Relay.Node, only: [to_global_id: 3]
import AmbryWeb.Helpers.IdHelpers
import AmbryWeb.TimeUtils, only: [duration_display: 1]

alias Ambry.Books
Expand Down Expand Up @@ -119,18 +121,24 @@ defmodule AmbryWeb.AudiobookLive do
end

@impl Phoenix.LiveView
def mount(%{"id" => media_id}, _session, socket) do
media = Media.get_media_with_book_details!(media_id)

if connected?(socket) do
Player.subscribe!(socket.assigns.player)
def mount(%{"id" => id_param}, _session, socket) do
with {:ok, media_id} <- parse_id(id_param, :media),
{:ok, media} <- Media.fetch_media_with_book_details(media_id) do
if connected?(socket) do
Player.subscribe!(socket.assigns.player)
end

global_id = to_global_id("Media", media.id, AmbrySchema)

{:ok,
assign(socket,
page_title: Books.get_book_description(media.book),
media: media,
global_id: global_id
)}
else
_ -> {:ok, redirect(socket, to: ~p"/")}
end

{:ok,
assign(socket,
page_title: Books.get_book_description(media.book),
media: media
)}
end

@impl Phoenix.LiveView
Expand Down