Started adding youtube API for fast indexing
This commit is contained in:
parent
af86ca1e0e
commit
2fc5e20077
7 changed files with 88 additions and 11 deletions
|
|
@ -12,3 +12,5 @@ services:
|
||||||
- ./docker-run.dev.sh
|
- ./docker-run.dev.sh
|
||||||
stdin_open: true
|
stdin_open: true
|
||||||
tty: true
|
tty: true
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ defmodule Pinchflat.FastIndexing.FastIndexingHelpers do
|
||||||
downloaded_.
|
downloaded_.
|
||||||
"""
|
"""
|
||||||
def kickoff_download_tasks_from_youtube_rss_feed(%Source{} = source) do
|
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)
|
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)
|
new_media_ids = media_ids -- Enum.map(existing_media_items, & &1.media_id)
|
||||||
|
|
||||||
|
|
|
||||||
66
lib/pinchflat/fast_indexing/youtube_api.ex
Normal file
66
lib/pinchflat/fast_indexing/youtube_api.ex
Normal 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
|
||||||
3
lib/pinchflat/fast_indexing/youtube_behaviour.ex
Normal file
3
lib/pinchflat/fast_indexing/youtube_behaviour.ex
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
defmodule Pinchflat.FastIndexing.YoutubeBehaviour do
|
||||||
|
# TODO
|
||||||
|
end
|
||||||
|
|
@ -12,7 +12,7 @@ defmodule Pinchflat.FastIndexing.YoutubeRss do
|
||||||
|
|
||||||
Returns {:ok, [binary()]} | {:error, binary()}
|
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}")
|
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
|
case http_client().get(rss_url_for_source(source)) do
|
||||||
|
|
|
||||||
|
|
@ -21,9 +21,11 @@ defmodule Pinchflat.HTTP.HTTPClient do
|
||||||
"""
|
"""
|
||||||
@impl HTTPBehaviour
|
@impl HTTPBehaviour
|
||||||
def get(url, headers \\ [], opts \\ []) do
|
def get(url, headers \\ [], opts \\ []) do
|
||||||
|
headers = parse_headers(headers)
|
||||||
|
|
||||||
case :httpc.request(:get, {url, headers}, [], opts) do
|
case :httpc.request(:get, {url, headers}, [], opts) do
|
||||||
{:ok, {{_version, 200, _reason_phrase}, _headers, body}} ->
|
{:ok, {{_version, 200, _reason_phrase}, _headers, body}} ->
|
||||||
{:ok, body}
|
{:ok, to_string(body)}
|
||||||
|
|
||||||
{:ok, {{_version, status_code, reason_phrase}, _headers, _body}} ->
|
{:ok, {{_version, status_code, reason_phrase}, _headers, _body}} ->
|
||||||
{:error, "HTTP request failed with status code #{status_code}: #{reason_phrase}"}
|
{: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}"}
|
{:error, "HTTP request failed: #{reason}"}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp parse_headers(headers) do
|
||||||
|
Enum.map(headers, fn {k, v} -> {to_charlist(k), to_charlist(v)} end)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ defmodule Pinchflat.FastIndexing.YoutubeRssTest do
|
||||||
{:ok, source: source}
|
{:ok, source: source}
|
||||||
end
|
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
|
test "calls the expected URL for channel sources" do
|
||||||
source = source_fixture(collection_type: :channel, collection_id: "channel_id")
|
source = source_fixture(collection_type: :channel, collection_id: "channel_id")
|
||||||
|
|
||||||
|
|
@ -21,7 +21,7 @@ defmodule Pinchflat.FastIndexing.YoutubeRssTest do
|
||||||
{:ok, ""}
|
{:ok, ""}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
assert {:ok, _} = YoutubeRss.get_recent_media_ids_from_rss(source)
|
assert {:ok, _} = YoutubeRss.get_recent_media_ids(source)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "calls the expected URL for playlist sources" do
|
test "calls the expected URL for playlist sources" do
|
||||||
|
|
@ -33,13 +33,13 @@ defmodule Pinchflat.FastIndexing.YoutubeRssTest do
|
||||||
{:ok, ""}
|
{:ok, ""}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
assert {:ok, _} = YoutubeRss.get_recent_media_ids_from_rss(source)
|
assert {:ok, _} = YoutubeRss.get_recent_media_ids(source)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "returns an error if the HTTP request fails", %{source: source} do
|
test "returns an error if the HTTP request fails", %{source: source} do
|
||||||
expect(HTTPClientMock, :get, fn _url -> {:error, ""} end)
|
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
|
end
|
||||||
|
|
||||||
test "returns the media IDs from the RSS feed", %{source: source} do
|
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>"}
|
{:ok, "<yt:videoId>test_1</yt:videoId><yt:videoId>test_2</yt:videoId>"}
|
||||||
end)
|
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
|
end
|
||||||
|
|
||||||
test "strips whitespace from media IDs", %{source: source} do
|
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>"}
|
{:ok, "<yt:videoId> test_1 </yt:videoId><yt:videoId> test_2 </yt:videoId>"}
|
||||||
end)
|
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
|
end
|
||||||
|
|
||||||
test "removes empty media IDs", %{source: source} do
|
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>"}
|
{:ok, "<yt:videoId>test_1</yt:videoId><yt:videoId></yt:videoId>"}
|
||||||
end)
|
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
|
||||||
|
|
||||||
test "removes duplicate media IDs", %{source: source} do
|
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>"}
|
{:ok, "<yt:videoId>test_1</yt:videoId><yt:videoId>test_1</yt:videoId>"}
|
||||||
end)
|
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
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue