Many tests
This commit is contained in:
parent
7879e7202f
commit
b7e7992f4d
12 changed files with 410 additions and 50 deletions
|
|
@ -14,8 +14,6 @@ defmodule Pinchflat.Boot.PreJobStartupTasks do
|
|||
|
||||
alias Pinchflat.Repo
|
||||
alias Pinchflat.Settings
|
||||
alias Pinchflat.Sources.Source
|
||||
alias Pinchflat.Media.MediaItem
|
||||
alias Pinchflat.Filesystem.FilesystemHelpers
|
||||
|
||||
def start_link(opts \\ []) do
|
||||
|
|
|
|||
|
|
@ -1,8 +1,27 @@
|
|||
defmodule Pinchflat.Podcasts.PodcastHelpers do
|
||||
@moduledoc """
|
||||
Methods for fetching postcast-related data from a source
|
||||
or its media items
|
||||
"""
|
||||
|
||||
alias Pinchflat.Repo
|
||||
alias Pinchflat.Media
|
||||
alias Pinchflat.Metadata.MediaMetadata
|
||||
alias Pinchflat.Metadata.SourceMetadata
|
||||
|
||||
# TODO: test
|
||||
@doc """
|
||||
Returns a list of media items that have been downloaded to disk
|
||||
and have been proven to still exist there.
|
||||
|
||||
Useful for podcasts since we don't want to serve media that
|
||||
has been deleted or moved, but it's also fairly generally useful
|
||||
so I could see this being moved in the future.
|
||||
|
||||
Options:
|
||||
- limit: integer - the maximum number of media items to return
|
||||
|
||||
Returns: [%MediaItem{}]
|
||||
"""
|
||||
def persisted_media_items_for(source, opts \\ []) do
|
||||
limit = Keyword.get(opts, :limit, 500)
|
||||
|
||||
|
|
@ -11,8 +30,19 @@ defmodule Pinchflat.Podcasts.PodcastHelpers do
|
|||
|> Enum.filter(fn media_item -> File.exists?(media_item.media_filepath) end)
|
||||
end
|
||||
|
||||
# TODO: test
|
||||
# Returns string or nil
|
||||
@doc """
|
||||
Selects a cover image for a source based on the source's metadata
|
||||
and the metadata of the media items associated with the source. Also
|
||||
ensures images exist on disk.
|
||||
|
||||
Only one media item should need to be returned since this is using the
|
||||
internal metadata which, so long as the media_item was _downloaded_, should
|
||||
be guaranteed to exist.
|
||||
|
||||
Prefers the source's poster, then fanart, then the media item's thumbnail.
|
||||
|
||||
Returns: {:ok, filepath} | {:error, :no_suitable_image}
|
||||
"""
|
||||
def select_cover_image(source, media_items) do
|
||||
source_with_preloads = Repo.preload(source, :metadata)
|
||||
|
||||
|
|
@ -20,20 +50,24 @@ defmodule Pinchflat.Podcasts.PodcastHelpers do
|
|||
|> get_images_by_preference(media_items)
|
||||
|> Enum.reject(&is_nil(&1))
|
||||
|> Enum.find(&File.exists?/1)
|
||||
|> case do
|
||||
nil -> {:error, :no_suitable_image}
|
||||
filepath -> {:ok, filepath}
|
||||
end
|
||||
end
|
||||
|
||||
def get_images_by_preference(source_with_preloads, []) do
|
||||
source_metadata = source_with_preloads.metadata
|
||||
defp get_images_by_preference(source_with_preloads, []) do
|
||||
source_metadata = source_with_preloads.metadata || %SourceMetadata{}
|
||||
|
||||
[
|
||||
source_metadata.poster_filepath,
|
||||
source_metadata.banner_filepath
|
||||
source_metadata.fanart_filepath
|
||||
]
|
||||
end
|
||||
|
||||
def get_images_by_preference(source_with_preloads, [media_item | _]) do
|
||||
defp get_images_by_preference(source_with_preloads, [media_item | _]) do
|
||||
media_item_with_preloads = Repo.preload(media_item, :metadata)
|
||||
media_item_metadata = media_item_with_preloads.metadata
|
||||
media_item_metadata = media_item_with_preloads.metadata || %MediaMetadata{}
|
||||
source_images = get_images_by_preference(source_with_preloads, [])
|
||||
|
||||
source_images ++ [media_item_metadata.thumbnail_filepath]
|
||||
|
|
|
|||
|
|
@ -1,17 +1,34 @@
|
|||
defmodule Pinchflat.Podcasts.RssFeedBuilder do
|
||||
@moduledoc """
|
||||
Methods for building an RSS feed for a source and its media items.
|
||||
"""
|
||||
|
||||
@datetime_format "%a, %d %b %Y %H:%M:%S %z"
|
||||
|
||||
alias Pinchflat.Utils.DatetimeUtils
|
||||
alias Pinchflat.Podcasts.PodcastHelpers
|
||||
alias PinchflatWeb.Router.Helpers, as: Routes
|
||||
|
||||
# TODO: test
|
||||
# TODO: only MIs that are confirmed to exist on-disk should be provided
|
||||
def build(source, media_items) do
|
||||
@doc """
|
||||
Builds an RSS feed for a given source and its media items.
|
||||
Only MediaItems that have been persisted will be included in the feed.
|
||||
|
||||
## Options:
|
||||
- `:limit` - The maximum number of media items to include in the feed. Defaults to 300.
|
||||
|
||||
Returns an XML document as a string.
|
||||
"""
|
||||
def build(source, opts \\ []) do
|
||||
limit = Keyword.get(opts, :limit, 300)
|
||||
|
||||
media_items = PodcastHelpers.persisted_media_items_for(source, limit: limit)
|
||||
build_source_xml(source, media_items)
|
||||
end
|
||||
|
||||
defp build_source_xml(source, media_items) do
|
||||
media_item_xml = Enum.map(media_items, &build_media_item_xml(source, &1))
|
||||
# "caching" the image path since it requires some DB calls and is used twice
|
||||
feed_image_path = feed_image_path(source, media_items)
|
||||
|
||||
# Useful: resources:
|
||||
# - https://validator.w3.org/feed/#validate_by_input
|
||||
|
|
@ -30,20 +47,20 @@ defmodule Pinchflat.Podcasts.RssFeedBuilder do
|
|||
<category>TV & Film</category>
|
||||
<generator>Generated by Pinchflat</generator>
|
||||
<language>en-us</language>
|
||||
<lastBuildDate>#{Calendar.strftime(DateTime.utc_now(), @datetime_format)}</lastBuildDate>
|
||||
<lastBuildDate>#{Calendar.strftime(source.updated_at, @datetime_format)}</lastBuildDate>
|
||||
<pubDate>#{Calendar.strftime(source.inserted_at, @datetime_format)}</pubDate>
|
||||
<atom:link href="#{generate_self_link(source)}" rel="self" type="application/rss+xml" />
|
||||
<podcast:locked>yes</podcast:locked>
|
||||
<podcast:guid>#{source.uuid}</podcast:guid>
|
||||
<image>
|
||||
<url>#{feed_image_path(source, media_items)}</url>
|
||||
<url>#{feed_image_path}</url>
|
||||
<title>#{source.custom_name}</title>
|
||||
<link>#{source.original_url}</link>
|
||||
</image>
|
||||
<itunes:author>#{source.custom_name}</itunes:author>
|
||||
<itunes:subtitle>#{source.custom_name}</itunes:subtitle>
|
||||
<itunes:block>yes</itunes:block>
|
||||
<itunes:image href="#{feed_image_path(source, media_items)}"></itunes:image>
|
||||
<itunes:image href="#{feed_image_path}"></itunes:image>
|
||||
<itunes:explicit>false</itunes:explicit>
|
||||
<itunes:category text="TV & Film"></itunes:category>
|
||||
|
||||
|
|
@ -65,8 +82,8 @@ defmodule Pinchflat.Podcasts.RssFeedBuilder do
|
|||
<enclosure
|
||||
url="#{media_stream_path(media_item)}"
|
||||
length="#{media_item.media_size_bytes}"
|
||||
type="#{determine_content_type(media_item)}">
|
||||
</enclosure>
|
||||
type="#{MIME.from_path(media_item.media_filepath)}"
|
||||
/>
|
||||
<itunes:author>#{source.custom_name}</itunes:author>
|
||||
<itunes:subtitle>#{media_item.title}</itunes:subtitle>
|
||||
<itunes:summary><![CDATA[#{media_item.description}]]></itunes:summary>
|
||||
|
|
@ -86,30 +103,22 @@ defmodule Pinchflat.Podcasts.RssFeedBuilder do
|
|||
end
|
||||
|
||||
defp feed_image_path(source, media_items) do
|
||||
image_path_on_disk = PodcastHelpers.select_cover_image(source, media_items)
|
||||
|
||||
case image_path_on_disk do
|
||||
nil ->
|
||||
case PodcastHelpers.select_cover_image(source, media_items) do
|
||||
{:error, _} ->
|
||||
""
|
||||
|
||||
_ ->
|
||||
extension = Path.extname(image_path_on_disk)
|
||||
{:ok, filepath} ->
|
||||
extension = Path.extname(filepath)
|
||||
Path.join(url_base(), "#{podcast_route(:feed_image, source.uuid)}#{extension}")
|
||||
end
|
||||
end
|
||||
|
||||
defp generate_upload_date(media_item) do
|
||||
media_item.upload_date
|
||||
|> Date.to_gregorian_days()
|
||||
|> Kernel.*(86400)
|
||||
|> DateTime.from_gregorian_seconds()
|
||||
|> DatetimeUtils.date_to_datetime()
|
||||
|> Calendar.strftime(@datetime_format)
|
||||
end
|
||||
|
||||
defp determine_content_type(media_item) do
|
||||
MIME.from_path(media_item.media_filepath)
|
||||
end
|
||||
|
||||
defp podcast_route(action, params) do
|
||||
Routes.podcast_path(PinchflatWeb.Endpoint, action, params)
|
||||
end
|
||||
|
|
|
|||
17
lib/pinchflat/utils/datetime_utils.ex
Normal file
17
lib/pinchflat/utils/datetime_utils.ex
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
defmodule Pinchflat.Utils.DatetimeUtils do
|
||||
@moduledoc """
|
||||
Utility methods for working with dates and datetimes
|
||||
"""
|
||||
|
||||
@doc """
|
||||
Converts a Date to a DateTime
|
||||
|
||||
Returns %DateTime{}
|
||||
"""
|
||||
def date_to_datetime(date) do
|
||||
date
|
||||
|> Date.to_gregorian_days()
|
||||
|> Kernel.*(86_400)
|
||||
|> DateTime.from_gregorian_seconds()
|
||||
end
|
||||
end
|
||||
|
|
@ -3,15 +3,13 @@ defmodule PinchflatWeb.Podcasts.PodcastController do
|
|||
|
||||
alias Pinchflat.Repo
|
||||
alias Pinchflat.Media
|
||||
alias Pinchflat.Sources.Source
|
||||
alias Pinchflat.Podcasts.RssFeedBuilder
|
||||
alias Pinchflat.Podcasts.PodcastHelpers
|
||||
|
||||
# TODO: test
|
||||
def rss_feed(conn, %{"uuid" => uuid}) do
|
||||
# TODO: change this to UUID
|
||||
source = Repo.get_by!(Source, id: uuid)
|
||||
media_items = PodcastHelpers.persisted_media_items_for(source)
|
||||
xml = RssFeedBuilder.build(source, media_items)
|
||||
source = Repo.get_by!(Source, uuid: uuid)
|
||||
xml = RssFeedBuilder.build(source, limit: 300)
|
||||
|
||||
conn
|
||||
|> put_resp_content_type("application/rss+xml")
|
||||
|
|
@ -19,21 +17,21 @@ defmodule PinchflatWeb.Podcasts.PodcastController do
|
|||
|> send_resp(200, xml)
|
||||
end
|
||||
|
||||
# TODO: test
|
||||
def feed_image(conn, %{"uuid" => uuid}) do
|
||||
source = Repo.get_by!(Source, uuid: uuid)
|
||||
# This provides a fallback image if the source has none.
|
||||
# We only need one since we're using the internal metadata image which
|
||||
# we know exists.
|
||||
media_items = Media.list_downloaded_media_items_for(source, limit: 1)
|
||||
filepath = PodcastHelpers.select_cover_image(source, media_items)
|
||||
|
||||
if filepath && File.exists?(filepath) do
|
||||
conn
|
||||
|> put_resp_content_type(MIME.from_path(filepath))
|
||||
|> send_file(200, filepath)
|
||||
else
|
||||
send_resp(conn, 404, "File not found")
|
||||
case PodcastHelpers.select_cover_image(source, media_items) do
|
||||
{:error, _} ->
|
||||
send_resp(conn, 404, "Image not found")
|
||||
|
||||
{:ok, filepath} ->
|
||||
conn
|
||||
|> put_resp_content_type(MIME.from_path(filepath))
|
||||
|> send_file(200, filepath)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ defmodule Pinchflat.Metadata.SourceMetadataStorageWorkerTest do
|
|||
_url, _opts, ot when ot == @metadata_ot -> {:ok, render_metadata(:channel_source_metadata)}
|
||||
end)
|
||||
|
||||
source = source_fixture()
|
||||
source = source_fixture(%{description: nil})
|
||||
|
||||
refute source.description
|
||||
perform_job(SourceMetadataStorageWorker, %{id: source.id})
|
||||
|
|
|
|||
64
test/pinchflat/podcasts/podcast_helpers_test.exs
Normal file
64
test/pinchflat/podcasts/podcast_helpers_test.exs
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
defmodule Pinchflat.Podcasts.PodcastHelpersTest do
|
||||
use Pinchflat.DataCase
|
||||
|
||||
import Pinchflat.SourcesFixtures
|
||||
import Pinchflat.MediaFixtures
|
||||
|
||||
alias Pinchflat.Podcasts.PodcastHelpers
|
||||
|
||||
describe "persisted_media_items_for/2" do
|
||||
test "returns media items with files that exist on-disk" do
|
||||
source = source_fixture()
|
||||
good_media = media_item_with_attachments(%{source_id: source.id})
|
||||
_bad_media = media_item_fixture(%{source_id: source.id, media_filepath: "/tmp/existing_file.mp3"})
|
||||
|
||||
assert [persisted_media] = PodcastHelpers.persisted_media_items_for(source)
|
||||
assert persisted_media.id == good_media.id
|
||||
end
|
||||
|
||||
test "lets you specify a limit" do
|
||||
source = source_fixture()
|
||||
_good_media = media_item_with_attachments(%{source_id: source.id})
|
||||
|
||||
assert [] = PodcastHelpers.persisted_media_items_for(source, limit: 0)
|
||||
end
|
||||
end
|
||||
|
||||
describe "select_cover_image/2" do
|
||||
test "returns a source's poster, if present" do
|
||||
source = source_with_metadata_attachments()
|
||||
|
||||
{:ok, res} = PodcastHelpers.select_cover_image(source, [])
|
||||
|
||||
assert res == source.metadata.poster_filepath
|
||||
end
|
||||
|
||||
test "falls back to a source's fanart, if present" do
|
||||
source = source_with_metadata_attachments()
|
||||
|
||||
File.rm(source.metadata.poster_filepath)
|
||||
|
||||
{:ok, res} = PodcastHelpers.select_cover_image(source, [])
|
||||
|
||||
assert res == source.metadata.fanart_filepath
|
||||
end
|
||||
|
||||
test "falls back to a media item's thumbnail, if present" do
|
||||
source = source_with_metadata_attachments()
|
||||
media_item = media_item_with_metadata_attachments(%{source_id: source.id})
|
||||
|
||||
File.rm(source.metadata.poster_filepath)
|
||||
File.rm(source.metadata.fanart_filepath)
|
||||
|
||||
{:ok, res} = PodcastHelpers.select_cover_image(source, [media_item])
|
||||
|
||||
assert res == media_item.metadata.thumbnail_filepath
|
||||
end
|
||||
|
||||
test "returns error if no artwork can be found" do
|
||||
source = source_fixture()
|
||||
|
||||
assert PodcastHelpers.select_cover_image(source, []) == {:error, :no_suitable_image}
|
||||
end
|
||||
end
|
||||
end
|
||||
132
test/pinchflat/podcasts/rss_feed_builder_test.exs
Normal file
132
test/pinchflat/podcasts/rss_feed_builder_test.exs
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
defmodule Pinchflat.Podcasts.RssFeedBuilderTest do
|
||||
use Pinchflat.DataCase
|
||||
|
||||
import Pinchflat.MediaFixtures
|
||||
import Pinchflat.SourcesFixtures
|
||||
|
||||
alias Pinchflat.Podcasts.RssFeedBuilder
|
||||
|
||||
@datetime_format "%a, %d %b %Y %H:%M:%S %z"
|
||||
|
||||
setup do
|
||||
source = source_fixture()
|
||||
|
||||
{:ok, source: source}
|
||||
end
|
||||
|
||||
describe "build/2" do
|
||||
test "returns an XML document", %{source: source} do
|
||||
res = RssFeedBuilder.build(source)
|
||||
|
||||
assert String.contains?(res, ~s(<?xml version="1.0" encoding="UTF-8"?>))
|
||||
end
|
||||
|
||||
test "can optionally apply a limit to media items", %{source: source} do
|
||||
good_media = media_item_with_attachments(%{source_id: source.id})
|
||||
|
||||
res = RssFeedBuilder.build(source, limit: 0)
|
||||
|
||||
refute String.contains?(res, ~s(<title>#{good_media.title}</title>))
|
||||
end
|
||||
end
|
||||
|
||||
describe "build/2 when testing source XML" do
|
||||
test "returns XML for static source attributes", %{source: source} do
|
||||
res = RssFeedBuilder.build(source)
|
||||
|
||||
assert String.contains?(res, ~s(<title>#{source.custom_name}</title>))
|
||||
assert String.contains?(res, ~s(<link>#{source.original_url}</link>))
|
||||
assert String.contains?(res, ~s(<description>#{source.description}</description>))
|
||||
assert String.contains?(res, ~s(<itunes:author>#{source.custom_name}</itunes:author>))
|
||||
assert String.contains?(res, ~s(<itunes:subtitle>#{source.custom_name}</itunes:subtitle>))
|
||||
assert String.contains?(res, ~s(<description>#{source.description}</description>))
|
||||
assert String.contains?(res, ~s(<podcast:guid>#{source.uuid}</podcast:guid>))
|
||||
end
|
||||
|
||||
test "returns the lastBuildDate and pubDate based off the source's timestamps", %{source: source} do
|
||||
res = RssFeedBuilder.build(source)
|
||||
|
||||
assert String.contains?(res, ~s(<lastBuildDate>#{format_date(source.updated_at)}</lastBuildDate>))
|
||||
assert String.contains?(res, ~s(<pubDate>#{format_date(source.inserted_at)}</pubDate>))
|
||||
end
|
||||
|
||||
test "returns a self-link", %{source: source} do
|
||||
res = RssFeedBuilder.build(source)
|
||||
|
||||
assert String.contains?(
|
||||
res,
|
||||
~s(<atom:link href="http://localhost:4008/sources/#{source.uuid}/feed.xml" rel="self" type="application/rss+xml" />)
|
||||
)
|
||||
end
|
||||
|
||||
test "returns a link to the feed image" do
|
||||
source = source_with_metadata_attachments()
|
||||
|
||||
res = RssFeedBuilder.build(source)
|
||||
[_before, image_block, _after] = String.split(res, ~r(</?image>))
|
||||
|
||||
assert String.contains?(image_block, ~s(<url>http://localhost:4008/sources/#{source.uuid}/feed_image.jpg</url>))
|
||||
assert String.contains?(image_block, ~s(<title>#{source.custom_name}</title>))
|
||||
assert String.contains?(image_block, ~s(<link>#{source.original_url}</link>))
|
||||
|
||||
assert String.contains?(
|
||||
res,
|
||||
~s(<itunes:image href="http://localhost:4008/sources/#{source.uuid}/feed_image.jpg"></itunes:image>)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
describe "build/2 when testing media XML" do
|
||||
test "only includes media persisted to disk", %{source: source} do
|
||||
good_media = media_item_with_attachments(%{source_id: source.id})
|
||||
bad_media = media_item_fixture(%{source_id: source.id, media_filepath: "/tmp/existing_file.mp3"})
|
||||
pending_media = media_item_fixture(%{source_id: source.id, media_filepath: nil})
|
||||
|
||||
res = RssFeedBuilder.build(source)
|
||||
|
||||
assert String.contains?(res, ~s(<title>#{good_media.title}</title>))
|
||||
refute String.contains?(res, ~s(<title>#{bad_media.title}</title>))
|
||||
refute String.contains?(res, ~s(<title>#{pending_media.title}</title>))
|
||||
end
|
||||
|
||||
test "returns XML for static media attributes", %{source: source} do
|
||||
media_item = media_item_with_attachments(%{source_id: source.id})
|
||||
|
||||
res = RssFeedBuilder.build(source)
|
||||
[_before, item_xml, _after] = String.split(res, ~r(</?item>))
|
||||
|
||||
assert String.contains?(item_xml, ~s(<guid isPermaLink="false">#{media_item.uuid}</guid>))
|
||||
assert String.contains?(item_xml, ~s(<title>#{media_item.title}</title>))
|
||||
assert String.contains?(item_xml, ~s(<link>#{media_item.original_url}</link>))
|
||||
assert String.contains?(item_xml, ~s(<description>#{media_item.description}</description>))
|
||||
assert String.contains?(item_xml, ~s(<itunes:author>#{source.custom_name}</itunes:author>))
|
||||
assert String.contains?(item_xml, ~s(<itunes:subtitle>#{media_item.title}</itunes:subtitle>))
|
||||
assert String.contains?(item_xml, ~s(<itunes:summary><![CDATA[#{media_item.description}]]></itunes:summary>))
|
||||
end
|
||||
|
||||
test "returns pubDate based off the media's upload_date", %{source: source} do
|
||||
media_item_with_attachments(%{source_id: source.id, upload_date: ~D[2020-01-01]})
|
||||
|
||||
res = RssFeedBuilder.build(source)
|
||||
[_before, item_xml, _after] = String.split(res, ~r(</?item>))
|
||||
|
||||
assert String.contains?(item_xml, ~s(<pubDate>Wed, 01 Jan 2020 00:00:00 +0000</pubDate>))
|
||||
end
|
||||
|
||||
test "returns an enclosure tag with the media's stream URL", %{source: source} do
|
||||
media_item = media_item_with_attachments(%{source_id: source.id, media_size_bytes: 1234})
|
||||
|
||||
res = RssFeedBuilder.build(source)
|
||||
[_before, item_xml, _after] = String.split(res, ~r(</?item>))
|
||||
|
||||
assert String.contains?(item_xml, ~s(<enclosure))
|
||||
assert String.contains?(item_xml, ~s(url="http://localhost:4008/media/#{media_item.uuid}/stream.mp4"))
|
||||
assert String.contains?(item_xml, ~s(length="1234"))
|
||||
assert String.contains?(item_xml, ~s(type="video/mp4"))
|
||||
end
|
||||
end
|
||||
|
||||
defp format_date(date) do
|
||||
Calendar.strftime(date, @datetime_format)
|
||||
end
|
||||
end
|
||||
14
test/pinchflat/utils/datetime_utils_test.exs
Normal file
14
test/pinchflat/utils/datetime_utils_test.exs
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
defmodule Pinchflat.Utils.DatetimeUtilsTest do
|
||||
use ExUnit.Case, async: true
|
||||
|
||||
alias Pinchflat.Utils.DatetimeUtils
|
||||
|
||||
describe "date_to_datetime/1" do
|
||||
test "converts a Date to a DateTime" do
|
||||
date = ~D[2022-01-01]
|
||||
datetime = DatetimeUtils.date_to_datetime(date)
|
||||
|
||||
assert datetime == ~U[2022-01-01 00:00:00Z]
|
||||
end
|
||||
end
|
||||
end
|
||||
38
test/pinchflat_web/controllers/podcast_controller_test.exs
Normal file
38
test/pinchflat_web/controllers/podcast_controller_test.exs
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
defmodule PinchflatWeb.PodcastControllerTest do
|
||||
use PinchflatWeb.ConnCase
|
||||
|
||||
import Pinchflat.SourcesFixtures
|
||||
|
||||
describe "rss_feed" do
|
||||
test "renders the XML document", %{conn: conn} do
|
||||
source = source_fixture()
|
||||
|
||||
conn = get(conn, ~p"/sources/#{source.uuid}/feed" <> ".xml")
|
||||
|
||||
assert conn.status == 200
|
||||
assert {"content-type", "application/rss+xml; charset=utf-8"} in conn.resp_headers
|
||||
assert {"content-disposition", "inline"} in conn.resp_headers
|
||||
end
|
||||
end
|
||||
|
||||
describe "feed_image" do
|
||||
test "returns a feed image if one can be found", %{conn: conn} do
|
||||
source = source_with_metadata_attachments()
|
||||
|
||||
conn = get(conn, ~p"/sources/#{source.uuid}/feed_image" <> ".jpg")
|
||||
|
||||
assert conn.status == 200
|
||||
assert {"content-type", "image/jpeg; charset=utf-8"} in conn.resp_headers
|
||||
assert conn.resp_body == File.read!(source.metadata.poster_filepath)
|
||||
end
|
||||
|
||||
test "returns 404 if an image cannot be found", %{conn: conn} do
|
||||
source = source_fixture()
|
||||
|
||||
conn = get(conn, ~p"/sources/#{source.uuid}/feed_image" <> ".jpg")
|
||||
|
||||
assert conn.status == 404
|
||||
assert conn.resp_body == "Image not found"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -5,6 +5,7 @@ defmodule Pinchflat.MediaFixtures do
|
|||
"""
|
||||
|
||||
alias Pinchflat.SourcesFixtures
|
||||
alias Pinchflat.Filesystem.FilesystemHelpers
|
||||
|
||||
@doc """
|
||||
Generate a media_item.
|
||||
|
|
@ -44,6 +45,27 @@ defmodule Pinchflat.MediaFixtures do
|
|||
media_item_fixture(merged_attrs)
|
||||
end
|
||||
|
||||
def media_item_with_metadata_attachments(attrs \\ %{}) do
|
||||
metadata_dir =
|
||||
Path.join(Application.get_env(:pinchflat, :metadata_directory), "#{:rand.uniform(1_000_000)}")
|
||||
|
||||
json_gz_filepath = Path.join(metadata_dir, "metadata.json.gz")
|
||||
thumbnail_filepath = Path.join(metadata_dir, "thumbnail.jpg")
|
||||
|
||||
FilesystemHelpers.cp_p!(media_metadata_filepath_fixture(), json_gz_filepath)
|
||||
FilesystemHelpers.cp_p!(thumbnail_filepath_fixture(), thumbnail_filepath)
|
||||
|
||||
merged_attrs =
|
||||
Map.merge(attrs, %{
|
||||
metadata: %{
|
||||
metadata_filepath: json_gz_filepath,
|
||||
thumbnail_filepath: thumbnail_filepath
|
||||
}
|
||||
})
|
||||
|
||||
media_item_with_attachments(merged_attrs)
|
||||
end
|
||||
|
||||
def media_item_with_attachments(attrs \\ %{}) do
|
||||
stored_media_filepath =
|
||||
Path.join([
|
||||
|
|
@ -52,10 +74,7 @@ defmodule Pinchflat.MediaFixtures do
|
|||
"#{:rand.uniform(1_000_000)}_media.mp4"
|
||||
])
|
||||
|
||||
fixture_media_filepath = media_filepath_fixture()
|
||||
|
||||
:ok = File.mkdir_p(Path.dirname(stored_media_filepath))
|
||||
:ok = File.cp(fixture_media_filepath, stored_media_filepath)
|
||||
FilesystemHelpers.cp_p!(media_filepath_fixture(), stored_media_filepath)
|
||||
|
||||
merged_attrs = Map.merge(attrs, %{media_filepath: stored_media_filepath})
|
||||
media_item_fixture(merged_attrs)
|
||||
|
|
@ -105,4 +124,14 @@ defmodule Pinchflat.MediaFixtures do
|
|||
"example.info.json"
|
||||
])
|
||||
end
|
||||
|
||||
def media_metadata_filepath_fixture do
|
||||
Path.join([
|
||||
File.cwd!(),
|
||||
"test",
|
||||
"support",
|
||||
"files",
|
||||
"media_metadata.json"
|
||||
])
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -5,8 +5,10 @@ defmodule Pinchflat.SourcesFixtures do
|
|||
"""
|
||||
|
||||
alias Pinchflat.Repo
|
||||
alias Pinchflat.ProfilesFixtures
|
||||
alias Pinchflat.MediaFixtures
|
||||
alias Pinchflat.Sources.Source
|
||||
alias Pinchflat.ProfilesFixtures
|
||||
alias Pinchflat.Filesystem.FilesystemHelpers
|
||||
|
||||
@doc """
|
||||
Generate a source.
|
||||
|
|
@ -22,6 +24,7 @@ defmodule Pinchflat.SourcesFixtures do
|
|||
collection_id: Base.encode16(:crypto.hash(:md5, "#{:rand.uniform(1_000_000)}")),
|
||||
collection_type: "channel",
|
||||
custom_name: "Cool and good internal name!",
|
||||
description: "This is a description",
|
||||
original_url: "https://www.youtube.com/channel/#{Faker.String.base64(12)}",
|
||||
media_profile_id: ProfilesFixtures.media_profile_fixture().id,
|
||||
index_frequency_minutes: 60
|
||||
|
|
@ -48,6 +51,30 @@ defmodule Pinchflat.SourcesFixtures do
|
|||
source_fixture(merged_attrs)
|
||||
end
|
||||
|
||||
def source_with_metadata_attachments(attrs \\ %{}) do
|
||||
metadata_dir =
|
||||
Path.join(Application.get_env(:pinchflat, :metadata_directory), "#{:rand.uniform(1_000_000)}")
|
||||
|
||||
json_gz_filepath = Path.join(metadata_dir, "metadata.json.gz")
|
||||
poster_filepath = Path.join(metadata_dir, "poster.jpg")
|
||||
fanart_filepath = Path.join(metadata_dir, "fanart.jpg")
|
||||
|
||||
FilesystemHelpers.cp_p!(MediaFixtures.media_metadata_filepath_fixture(), json_gz_filepath)
|
||||
FilesystemHelpers.cp_p!(MediaFixtures.thumbnail_filepath_fixture(), poster_filepath)
|
||||
FilesystemHelpers.cp_p!(MediaFixtures.thumbnail_filepath_fixture(), fanart_filepath)
|
||||
|
||||
merged_attrs =
|
||||
Map.merge(attrs, %{
|
||||
metadata: %{
|
||||
metadata_filepath: json_gz_filepath,
|
||||
poster_filepath: poster_filepath,
|
||||
fanart_filepath: fanart_filepath
|
||||
}
|
||||
})
|
||||
|
||||
source_fixture(merged_attrs)
|
||||
end
|
||||
|
||||
def source_attributes_return_fixture do
|
||||
source_attributes = [
|
||||
%{
|
||||
|
|
|
|||
Loading…
Reference in a new issue