Removed Channel yt-dlp module, instead throwing things in the VideoCollection module
This commit is contained in:
parent
2250695a3e
commit
6a31d333ad
5 changed files with 80 additions and 107 deletions
|
|
@ -1,32 +0,0 @@
|
||||||
defmodule Pinchflat.MediaClient.Backends.YtDlp.Channel do
|
|
||||||
@moduledoc """
|
|
||||||
Contains utilities for working with a channel's videos
|
|
||||||
"""
|
|
||||||
|
|
||||||
use Pinchflat.MediaClient.Backends.YtDlp.VideoCollection
|
|
||||||
alias Pinchflat.MediaClient.SourceDetails
|
|
||||||
|
|
||||||
@doc """
|
|
||||||
Gets a channel's ID and name from its URL.
|
|
||||||
|
|
||||||
yt-dlp does not _really_ have channel-specific functions, so
|
|
||||||
instead we're fetching just the first video (using playlist_end: 1)
|
|
||||||
and parsing the channel ID and name from _its_ metadata
|
|
||||||
|
|
||||||
Returns {:ok, %SourceDetails{}} | {:error, any, ...}.
|
|
||||||
"""
|
|
||||||
def get_source_details(channel_url) do
|
|
||||||
opts = [:skip_download, playlist_end: 1]
|
|
||||||
|
|
||||||
with {:ok, output} <- backend_runner().run(channel_url, opts, "%(.{channel,channel_id})j"),
|
|
||||||
{:ok, parsed_json} <- Phoenix.json_library().decode(output) do
|
|
||||||
{:ok, SourceDetails.new(parsed_json["channel_id"], parsed_json["channel"])}
|
|
||||||
else
|
|
||||||
err -> err
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp backend_runner do
|
|
||||||
Application.get_env(:pinchflat, :yt_dlp_runner)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,28 +1,47 @@
|
||||||
defmodule Pinchflat.MediaClient.Backends.YtDlp.VideoCollection do
|
defmodule Pinchflat.MediaClient.Backends.YtDlp.VideoCollection do
|
||||||
@moduledoc """
|
@moduledoc """
|
||||||
Contains utilities for working with collections of videos (ie: channels, playlists).
|
Contains utilities for working with collections of
|
||||||
|
videos (aka: a source [ie: channels, playlists]).
|
||||||
Meant to be included in other modules but can be used on its own. Channels and playlists
|
|
||||||
will have many of their own methods, but also share a lot of methods. This module is for
|
|
||||||
those shared methods.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
defmacro __using__(_) do
|
alias Pinchflat.MediaClient.SourceDetails
|
||||||
quote do
|
|
||||||
@doc """
|
|
||||||
Returns a list of strings representing the video ids in the collection.
|
|
||||||
|
|
||||||
Returns {:ok, [binary()]} | {:error, any, ...}.
|
@doc """
|
||||||
"""
|
Returns a list of strings representing the video ids in the collection.
|
||||||
def get_video_ids(url, command_opts \\ []) do
|
|
||||||
runner = Application.get_env(:pinchflat, :yt_dlp_runner)
|
|
||||||
opts = command_opts ++ [:simulate, :skip_download]
|
|
||||||
|
|
||||||
case runner.run(url, opts, "%(id)s") do
|
Returns {:ok, [binary()]} | {:error, any, ...}.
|
||||||
{:ok, output} -> {:ok, String.split(output, "\n", trim: true)}
|
"""
|
||||||
res -> res
|
def get_video_ids(url, command_opts \\ []) do
|
||||||
end
|
runner = Application.get_env(:pinchflat, :yt_dlp_runner)
|
||||||
end
|
opts = command_opts ++ [:simulate, :skip_download]
|
||||||
|
|
||||||
|
case runner.run(url, opts, "%(id)s") do
|
||||||
|
{:ok, output} -> {:ok, String.split(output, "\n", trim: true)}
|
||||||
|
res -> res
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Gets a source's ID and name from its URL.
|
||||||
|
|
||||||
|
yt-dlp does not _really_ have source-specific functions, so
|
||||||
|
instead we're fetching just the first video (using playlist_end: 1)
|
||||||
|
and parsing the source ID and name from _its_ metadata
|
||||||
|
|
||||||
|
Returns {:ok, %SourceDetails{}} | {:error, any, ...}.
|
||||||
|
"""
|
||||||
|
def get_source_details(source_url) do
|
||||||
|
opts = [:skip_download, playlist_end: 1]
|
||||||
|
|
||||||
|
with {:ok, output} <- backend_runner().run(source_url, opts, "%(.{channel,channel_id})j"),
|
||||||
|
{:ok, parsed_json} <- Phoenix.json_library().decode(output) do
|
||||||
|
{:ok, SourceDetails.new(parsed_json["channel_id"], parsed_json["channel"])}
|
||||||
|
else
|
||||||
|
err -> err
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp backend_runner do
|
||||||
|
Application.get_env(:pinchflat, :yt_dlp_runner)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ defmodule Pinchflat.MediaClient.SourceDetails do
|
||||||
@enforce_keys [:id, :name]
|
@enforce_keys [:id, :name]
|
||||||
defstruct [:id, :name]
|
defstruct [:id, :name]
|
||||||
|
|
||||||
alias Pinchflat.MediaClient.Backends.YtDlp.Channel, as: YtDlpChannel
|
alias Pinchflat.MediaClient.Backends.YtDlp.VideoCollection, as: YtDlpSource
|
||||||
|
|
||||||
@doc false
|
@doc false
|
||||||
def new(id, name) do
|
def new(id, name) do
|
||||||
|
|
@ -35,7 +35,7 @@ defmodule Pinchflat.MediaClient.SourceDetails do
|
||||||
|
|
||||||
defp source_module(backend) do
|
defp source_module(backend) do
|
||||||
case backend do
|
case backend do
|
||||||
:yt_dlp -> YtDlpChannel
|
:yt_dlp -> YtDlpSource
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,45 +0,0 @@
|
||||||
defmodule Pinchflat.MediaClient.Backends.YtDlp.ChannelTest do
|
|
||||||
use ExUnit.Case, async: true
|
|
||||||
import Mox
|
|
||||||
|
|
||||||
alias Pinchflat.MediaClient.SourceDetails
|
|
||||||
alias Pinchflat.MediaClient.Backends.YtDlp.Channel
|
|
||||||
|
|
||||||
@channel_url "https://www.youtube.com/c/TheUselessTrials"
|
|
||||||
|
|
||||||
setup :verify_on_exit!
|
|
||||||
|
|
||||||
describe "get_source_details/1" do
|
|
||||||
test "it returns a %SourceDetails{} with data on success" do
|
|
||||||
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot ->
|
|
||||||
{:ok, "{\"channel\": \"TheUselessTrials\", \"channel_id\": \"UCQH2\"}"}
|
|
||||||
end)
|
|
||||||
|
|
||||||
assert {:ok, res} = Channel.get_source_details(@channel_url)
|
|
||||||
assert %SourceDetails{id: "UCQH2", name: "TheUselessTrials"} = res
|
|
||||||
end
|
|
||||||
|
|
||||||
test "it passes the expected args to the backend runner" do
|
|
||||||
expect(YtDlpRunnerMock, :run, fn @channel_url, opts, ot ->
|
|
||||||
assert opts == [:skip_download, playlist_end: 1]
|
|
||||||
assert ot == "%(.{channel,channel_id})j"
|
|
||||||
|
|
||||||
{:ok, "{}"}
|
|
||||||
end)
|
|
||||||
|
|
||||||
assert {:ok, _} = Channel.get_source_details(@channel_url)
|
|
||||||
end
|
|
||||||
|
|
||||||
test "it returns an error if the runner returns an error" do
|
|
||||||
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot -> {:error, "Big issue", 1} end)
|
|
||||||
|
|
||||||
assert {:error, "Big issue", 1} = Channel.get_source_details(@channel_url)
|
|
||||||
end
|
|
||||||
|
|
||||||
test "it returns an error if the output is not JSON" do
|
|
||||||
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot -> {:ok, "Not JSON"} end)
|
|
||||||
|
|
||||||
assert {:error, %Jason.DecodeError{}} = Channel.get_source_details(@channel_url)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -2,13 +2,10 @@ defmodule Pinchflat.MediaClient.Backends.YtDlp.VideoCollectionTest do
|
||||||
use ExUnit.Case, async: true
|
use ExUnit.Case, async: true
|
||||||
import Mox
|
import Mox
|
||||||
|
|
||||||
|
alias Pinchflat.MediaClient.SourceDetails
|
||||||
alias Pinchflat.MediaClient.Backends.YtDlp.VideoCollection
|
alias Pinchflat.MediaClient.Backends.YtDlp.VideoCollection
|
||||||
|
|
||||||
@channel_url "https://www.youtube.com/@TheUselessTrials"
|
@channel_url "https://www.youtube.com/c/TheUselessTrials"
|
||||||
|
|
||||||
defmodule VideoCollectionUser do
|
|
||||||
use VideoCollection
|
|
||||||
end
|
|
||||||
|
|
||||||
setup :verify_on_exit!
|
setup :verify_on_exit!
|
||||||
|
|
||||||
|
|
@ -16,7 +13,7 @@ defmodule Pinchflat.MediaClient.Backends.YtDlp.VideoCollectionTest do
|
||||||
test "returns a list of video ids with no blank elements" do
|
test "returns a list of video ids with no blank elements" do
|
||||||
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot -> {:ok, "id1\nid2\n\nid3\n"} end)
|
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot -> {:ok, "id1\nid2\n\nid3\n"} end)
|
||||||
|
|
||||||
assert {:ok, ["id1", "id2", "id3"]} = VideoCollectionUser.get_video_ids(@channel_url)
|
assert {:ok, ["id1", "id2", "id3"]} = VideoCollection.get_video_ids(@channel_url)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it passes the expected default args" do
|
test "it passes the expected default args" do
|
||||||
|
|
@ -27,7 +24,7 @@ defmodule Pinchflat.MediaClient.Backends.YtDlp.VideoCollectionTest do
|
||||||
{:ok, ""}
|
{:ok, ""}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
assert {:ok, _} = VideoCollectionUser.get_video_ids(@channel_url)
|
assert {:ok, _} = VideoCollection.get_video_ids(@channel_url)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it passes the expected custom args" do
|
test "it passes the expected custom args" do
|
||||||
|
|
@ -37,13 +34,47 @@ defmodule Pinchflat.MediaClient.Backends.YtDlp.VideoCollectionTest do
|
||||||
{:ok, ""}
|
{:ok, ""}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
assert {:ok, _} = VideoCollectionUser.get_video_ids(@channel_url, [:custom_arg])
|
assert {:ok, _} = VideoCollection.get_video_ids(@channel_url, [:custom_arg])
|
||||||
end
|
end
|
||||||
|
|
||||||
test "returns the error straight through when the command fails" do
|
test "returns the error straight through when the command fails" do
|
||||||
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot -> {:error, "Big issue", 1} end)
|
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot -> {:error, "Big issue", 1} end)
|
||||||
|
|
||||||
assert {:error, "Big issue", 1} = VideoCollectionUser.get_video_ids(@channel_url)
|
assert {:error, "Big issue", 1} = VideoCollection.get_video_ids(@channel_url)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "get_source_details/1" do
|
||||||
|
test "it returns a %SourceDetails{} with data on success" do
|
||||||
|
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot ->
|
||||||
|
{:ok, "{\"channel\": \"TheUselessTrials\", \"channel_id\": \"UCQH2\"}"}
|
||||||
|
end)
|
||||||
|
|
||||||
|
assert {:ok, res} = VideoCollection.get_source_details(@channel_url)
|
||||||
|
assert %SourceDetails{id: "UCQH2", name: "TheUselessTrials"} = res
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it passes the expected args to the backend runner" do
|
||||||
|
expect(YtDlpRunnerMock, :run, fn @channel_url, opts, ot ->
|
||||||
|
assert opts == [:skip_download, playlist_end: 1]
|
||||||
|
assert ot == "%(.{channel,channel_id})j"
|
||||||
|
|
||||||
|
{:ok, "{}"}
|
||||||
|
end)
|
||||||
|
|
||||||
|
assert {:ok, _} = VideoCollection.get_source_details(@channel_url)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it returns an error if the runner returns an error" do
|
||||||
|
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot -> {:error, "Big issue", 1} end)
|
||||||
|
|
||||||
|
assert {:error, "Big issue", 1} = VideoCollection.get_source_details(@channel_url)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it returns an error if the output is not JSON" do
|
||||||
|
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot -> {:ok, "Not JSON"} end)
|
||||||
|
|
||||||
|
assert {:error, %Jason.DecodeError{}} = VideoCollection.get_source_details(@channel_url)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue