Started adding youtube API for fast indexing

This commit is contained in:
Kieran Eglin 2024-06-04 15:01:39 -07:00
parent af86ca1e0e
commit 2fc5e20077
No known key found for this signature in database
GPG key ID: 193984967FCF432D
7 changed files with 88 additions and 11 deletions

View file

@ -12,3 +12,5 @@ services:
- ./docker-run.dev.sh
stdin_open: true
tty: true
env_file:
- .env

View file

@ -26,7 +26,7 @@ defmodule Pinchflat.FastIndexing.FastIndexingHelpers do
downloaded_.
"""
def kickoff_download_tasks_from_youtube_rss_feed(%Source{} = source) do
{:ok, media_ids} = YoutubeRss.get_recent_media_ids_from_rss(source)
{:ok, media_ids} = YoutubeRss.get_recent_media_ids(source)
existing_media_items = list_media_items_by_media_id_for(source, media_ids)
new_media_ids = media_ids -- Enum.map(existing_media_items, & &1.media_id)

View file

@ -0,0 +1,66 @@
defmodule Pinchflat.FastIndexing.YoutubeApi do
@moduledoc """
TODO
"""
require Logger
alias Pinchflat.Sources.Source
alias Pinchflat.Utils.FunctionUtils
# TODO: test
# TODO: make this a behaviour
def get_recent_media_ids(%Source{} = source) do
api_response =
source
|> determine_playlist_id()
|> do_api_request()
case api_response do
{:ok, parsed_json} -> get_media_ids_from_response(parsed_json)
{:error, reason} -> {:error, reason}
end
end
# The UC prefix is for channels which won't work with this API endpoint. Swapping
# the prefix to UU will get us the playlist that represents the channel's uploads
defp determine_playlist_id(%{collection_id: c_id}) do
String.replace_prefix(c_id, "UC", "UU")
end
defp do_api_request(playlist_id) do
Logger.debug("Fetching recent media IDs from YouTube API for playlist: #{playlist_id}")
api_base = "https://youtube.googleapis.com/youtube/v3/playlistItems"
request_url = "#{api_base}?part=contentDetails&maxResults=50&playlistId=#{playlist_id}&key=#{api_key()}"
case http_client().get(request_url, accept: "application/json") do
{:ok, response} ->
Phoenix.json_library().decode(response)
{:error, reason} ->
{:error, reason}
end
end
defp get_media_ids_from_response(parsed_json) do
parsed_json
|> Map.get("items", [])
|> Enum.map(fn item ->
item
|> Map.get("contentDetails", %{})
|> Map.get("videoId", nil)
end)
|> Enum.reject(&is_nil/1)
|> FunctionUtils.wrap_ok()
end
# TODO: replace this with a user setting
defp api_key do
System.get_env("YOUTUBE_API_KEY")
end
defp http_client do
Application.get_env(:pinchflat, :http_client, Pinchflat.HTTP.HTTPClient)
end
end

View file

@ -0,0 +1,3 @@
defmodule Pinchflat.FastIndexing.YoutubeBehaviour do
# TODO
end

View file

@ -12,7 +12,7 @@ defmodule Pinchflat.FastIndexing.YoutubeRss do
Returns {:ok, [binary()]} | {:error, binary()}
"""
def get_recent_media_ids_from_rss(%Source{} = source) do
def get_recent_media_ids(%Source{} = source) do
Logger.debug("Fetching recent media IDs from YouTube RSS feed for source: #{source.collection_id}")
case http_client().get(rss_url_for_source(source)) do

View file

@ -21,9 +21,11 @@ defmodule Pinchflat.HTTP.HTTPClient do
"""
@impl HTTPBehaviour
def get(url, headers \\ [], opts \\ []) do
headers = parse_headers(headers)
case :httpc.request(:get, {url, headers}, [], opts) do
{:ok, {{_version, 200, _reason_phrase}, _headers, body}} ->
{:ok, body}
{:ok, to_string(body)}
{:ok, {{_version, status_code, reason_phrase}, _headers, _body}} ->
{:error, "HTTP request failed with status code #{status_code}: #{reason_phrase}"}
@ -32,4 +34,8 @@ defmodule Pinchflat.HTTP.HTTPClient do
{:error, "HTTP request failed: #{reason}"}
end
end
defp parse_headers(headers) do
Enum.map(headers, fn {k, v} -> {to_charlist(k), to_charlist(v)} end)
end
end

View file

@ -11,7 +11,7 @@ defmodule Pinchflat.FastIndexing.YoutubeRssTest do
{:ok, source: source}
end
describe "get_recent_media_ids_from_rss/1" do
describe "get_recent_media_ids/1" do
test "calls the expected URL for channel sources" do
source = source_fixture(collection_type: :channel, collection_id: "channel_id")
@ -21,7 +21,7 @@ defmodule Pinchflat.FastIndexing.YoutubeRssTest do
{:ok, ""}
end)
assert {:ok, _} = YoutubeRss.get_recent_media_ids_from_rss(source)
assert {:ok, _} = YoutubeRss.get_recent_media_ids(source)
end
test "calls the expected URL for playlist sources" do
@ -33,13 +33,13 @@ defmodule Pinchflat.FastIndexing.YoutubeRssTest do
{:ok, ""}
end)
assert {:ok, _} = YoutubeRss.get_recent_media_ids_from_rss(source)
assert {:ok, _} = YoutubeRss.get_recent_media_ids(source)
end
test "returns an error if the HTTP request fails", %{source: source} do
expect(HTTPClientMock, :get, fn _url -> {:error, ""} end)
assert {:error, "Failed to fetch RSS feed"} = YoutubeRss.get_recent_media_ids_from_rss(source)
assert {:error, "Failed to fetch RSS feed"} = YoutubeRss.get_recent_media_ids(source)
end
test "returns the media IDs from the RSS feed", %{source: source} do
@ -47,7 +47,7 @@ defmodule Pinchflat.FastIndexing.YoutubeRssTest do
{:ok, "<yt:videoId>test_1</yt:videoId><yt:videoId>test_2</yt:videoId>"}
end)
assert {:ok, ["test_1", "test_2"]} = YoutubeRss.get_recent_media_ids_from_rss(source)
assert {:ok, ["test_1", "test_2"]} = YoutubeRss.get_recent_media_ids(source)
end
test "strips whitespace from media IDs", %{source: source} do
@ -55,7 +55,7 @@ defmodule Pinchflat.FastIndexing.YoutubeRssTest do
{:ok, "<yt:videoId> test_1 </yt:videoId><yt:videoId> test_2 </yt:videoId>"}
end)
assert {:ok, ["test_1", "test_2"]} = YoutubeRss.get_recent_media_ids_from_rss(source)
assert {:ok, ["test_1", "test_2"]} = YoutubeRss.get_recent_media_ids(source)
end
test "removes empty media IDs", %{source: source} do
@ -63,7 +63,7 @@ defmodule Pinchflat.FastIndexing.YoutubeRssTest do
{:ok, "<yt:videoId>test_1</yt:videoId><yt:videoId></yt:videoId>"}
end)
assert {:ok, ["test_1"]} = YoutubeRss.get_recent_media_ids_from_rss(source)
assert {:ok, ["test_1"]} = YoutubeRss.get_recent_media_ids(source)
end
test "removes duplicate media IDs", %{source: source} do
@ -71,7 +71,7 @@ defmodule Pinchflat.FastIndexing.YoutubeRssTest do
{:ok, "<yt:videoId>test_1</yt:videoId><yt:videoId>test_1</yt:videoId>"}
end)
assert {:ok, ["test_1"]} = YoutubeRss.get_recent_media_ids_from_rss(source)
assert {:ok, ["test_1"]} = YoutubeRss.get_recent_media_ids(source)
end
end
end