Skip to content

Commit

Permalink
✨ allow spotify urls
Browse files Browse the repository at this point in the history
  • Loading branch information
benvp committed Jun 9, 2021
1 parent 6bba9b9 commit d516018
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 21 deletions.
Binary file modified apps/toniefy/assets/static/images/how-to-get-spotify-uri.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
52 changes: 38 additions & 14 deletions apps/toniefy/lib/toniex/clients/spotify.ex
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
defmodule Toniex.Clients.Spotify do
@spotify_uri_regex ~r/^spotify:(?<type>(playlist|album|track)):(?<id>[\w\d]+)$/
@spotify_url_regex ~r/\/(?<type>(playlist|album|track))\/(?<id>[\w\d]+)/

def get_token(refresh_token) do
res =
Expand Down Expand Up @@ -35,7 +36,7 @@ defmodule Toniex.Clients.Spotify do
end

@doc """
Get the total duration of a Spotify URI. Returns
Get the total duration of a Spotify URI or URL. Returns
Returns `{:ok, duration_ms}` or an error tuple `{:error, any()}`.
Expand All @@ -47,8 +48,8 @@ defmodule Toniex.Clients.Spotify do
"""
@spec total_duration(Tesla.Client.t(), String.t()) ::
{:ok, integer()} | {:error, :uri_not_supported | any()}
def total_duration(client, uri) do
%{type: type, id: id} = parse_uri(uri)
def total_duration(client, uri_or_url) do
%{type: type, id: id} = parse_uri(uri_or_url)

case type do
"track" ->
Expand All @@ -58,7 +59,7 @@ defmodule Toniex.Clients.Spotify do
end

"album" ->
case get_all_tracks(client, uri) do
case get_all_tracks(client, uri_or_url) do
{:ok, tracks} ->
duration =
Enum.reduce(tracks, 0, fn track, duration ->
Expand All @@ -72,7 +73,7 @@ defmodule Toniex.Clients.Spotify do
end

"playlist" ->
case get_all_tracks(client, uri) do
case get_all_tracks(client, uri_or_url) do
{:ok, tracks} ->
duration =
Enum.reduce(tracks, 0, fn track, duration ->
Expand Down Expand Up @@ -111,7 +112,7 @@ defmodule Toniex.Clients.Spotify do
{:ok, list()} | {:error, any()}
def get_all_tracks(
client,
uri,
uri_or_url,
tracks \\ [],
page_url \\ ""
)
Expand All @@ -126,11 +127,11 @@ defmodule Toniex.Clients.Spotify do

def get_all_tracks(
client,
uri,
uri_or_url,
tracks,
page_url
) do
%{id: id, type: type} = parse_uri(uri)
%{id: id, type: type} = parse_uri(uri_or_url)

url =
case type do
Expand All @@ -148,14 +149,42 @@ defmodule Toniex.Clients.Spotify do

case result do
{:ok, %{"items" => items, "next" => next}} ->
{:ok, page_tracks} = get_all_tracks(client, uri, items, next)
{:ok, page_tracks} = get_all_tracks(client, uri_or_url, items, next)
{:ok, page_tracks ++ tracks}

{:error, reason} ->
{:error, reason}
end
end

@doc """
Parse a spotify URI or URL.
"""
def parse_uri(uri_or_url) do
cond do
String.match?(uri_or_url, @spotify_uri_regex) ->
%{"id" => id, "type" => type} = Regex.named_captures(@spotify_uri_regex, uri_or_url)
%{id: id, type: type}

String.match?(uri_or_url, @spotify_url_regex) ->
%{"id" => id, "type" => type} = Regex.named_captures(@spotify_url_regex, uri_or_url)
%{id: id, type: type}

true ->
{:error, :invalid_uri_or_url}
end
end

@doc """
Converts a Spotify URL to an URI
"""
def to_uri(url) do
case Regex.named_captures(@spotify_url_regex, url) do
%{"id" => id, "type" => type} -> "spotify:#{type}:#{id}"
_ -> {:error, :invalid_url}
end
end

defp base64_credentials() do
config = Application.fetch_env!(:ueberauth, Ueberauth.Strategy.Spotify.OAuth)

Expand All @@ -165,11 +194,6 @@ defmodule Toniex.Clients.Spotify do
Base.encode64("#{client_id}:#{client_secret}")
end

defp parse_uri(uri) do
%{"id" => id, "type" => type} = Regex.named_captures(@spotify_uri_regex, uri)
%{id: id, type: type}
end

@spec handle_response(Tesla.Env.result()) :: {:ok, any()} | {:error, any()}
defp handle_response(response) do
case response do
Expand Down
13 changes: 8 additions & 5 deletions apps/toniefy/lib/toniex/recorder.ex
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,16 @@ defmodule Toniex.Recorder do
require Logger

@upload_dir Application.fetch_env!(:toniex, :upload_dir)
@spotify_uri_regex ~r/^spotify:(?<type>(playlist|album|track)):(?<id>[\w\d]+)$/

@spec enqueue(Toniex.Accounts.User.t(), binary) ::
{:error, :invalid_uri | :max_duration_exceeded | any} | {:ok, Oban.Job.t()}
def enqueue(user, uri) do
def enqueue(user, uri_or_url) do
client =
Accounts.get_session(user, :spotify)
|> Spotify.client()

with true <- spotify_uri_valid?(uri),
with true <- spotify_uri_valid?(uri_or_url),
uri <- Spotify.to_uri(uri_or_url),
:ok <- spotify_validate_duration(client, uri) do
%{
id: Ecto.UUID.generate(),
Expand Down Expand Up @@ -130,8 +130,11 @@ defmodule Toniex.Recorder do
Repo.delete(session)
end

defp spotify_uri_valid?(uri) do
String.match?(uri, @spotify_uri_regex)
defp spotify_uri_valid?(uri_or_url) do
case Spotify.parse_uri(uri_or_url) do
m when is_map(m) -> true
_ -> false
end
end

defp spotify_validate_duration(client, uri) do
Expand Down
4 changes: 2 additions & 2 deletions apps/toniefy/lib/toniex_web/live/recorder_live.ex
Original file line number Diff line number Diff line change
Expand Up @@ -105,13 +105,13 @@ defmodule ToniexWeb.RecorderLive do
<div class="card">
<h2 class="card__title">Neue Aufnahme starten</h2>
<div class="card__body">
<p>Bitte gib eine Spotify URI in das Textfeld ein. Dies kann der Link zu einem einzelnen Lied, einer Playlist oder einem Album sein.</p>
<p>Bitte gib eine Spotify URL oder URI in das Textfeld ein. Dies kann der Link zu einem einzelnen Lied, einer Playlist oder einem Album sein.</p>
<div class="mt-6">
<%= f = form_for :recorder, "#", [phx_submit: :record] %>
<%= text_input f, :uri, required: true, class: "input w-full text-xl py-3", placeholder: "spotify:awesome-track" %>
<p class="mt-2">
<%= link "Wo finde ich die Spotify URI?", to: Routes.static_path(@socket, "/images/how-to-get-spotify-uri.gif"), target: "_blank", class: "link" %>
<%= link "Wo finde ich die Spotify URL?", to: Routes.static_path(@socket, "/images/how-to-get-spotify-uri.gif"), target: "_blank", class: "link" %>
</p>
<div class="text-right mt-6">
<%= submit "Aufnahme starten", phx_disable_with: "Aufnahme starten...", class: "btn btn-primary" %>
Expand Down

0 comments on commit d516018

Please sign in to comment.