RSS feed for sources (#110)
* Add media streaming (#108) * [WIP] set up streaming route * Added UUID to sources and media items * Added media preview to MI show page * Added plug to strip file extensions * [VERY WIP] basic podcast RSS setup * [WIP] got basic podcast RSS working * [WIP] more expanding on RSS * Comment * [WIP] Working on refactoring feed * Added UUID backfill to a migration * [WIP] Moar refactoring * [WIP] Adding UI for getting RSS feed * Many tests * Added conditional routing for feed URLs * Removed the need for url_base to be set * Updated preset name * Rendered certain fields HTML-safe; Added logging to confirm range request support * Fixed incorrect scheme issue * Updated env var * Updated other UI to use dropdown * removed commented code * Generate rss feeds (#123) * Added plug to strip file extensions * [VERY WIP] basic podcast RSS setup * [WIP] got basic podcast RSS working * [WIP] more expanding on RSS * [WIP] Working on refactoring feed * Added UUID backfill to a migration * [WIP] Moar refactoring * [WIP] Adding UI for getting RSS feed * Many tests * Added conditional routing for feed URLs * Removed the need for url_base to be set * Updated preset name * Rendered certain fields HTML-safe; Added logging to confirm range request support * Fixed incorrect scheme issue * Updated env var * Updated other UI to use dropdown * removed commented code * docs * Added unique index to UUID fields
This commit is contained in:
@@ -66,6 +66,147 @@ defmodule PinchflatWeb.MediaItemControllerTest do
|
||||
end
|
||||
end
|
||||
|
||||
describe "streaming media" do
|
||||
test "returns 404 if the media isn't found", %{conn: conn} do
|
||||
media_item = media_item_fixture()
|
||||
conn = get(conn, ~p"/media/#{media_item.uuid}/stream")
|
||||
|
||||
assert conn.status == 404
|
||||
end
|
||||
|
||||
test "automatically sets the content type", %{conn: conn} do
|
||||
media_item = media_item_with_attachments()
|
||||
conn = get(conn, ~p"/media/#{media_item.uuid}/stream")
|
||||
|
||||
assert {"content-type", "video/mp4; charset=utf-8"} in conn.resp_headers
|
||||
end
|
||||
|
||||
test "sets the content length", %{conn: conn} do
|
||||
media_item = media_item_with_attachments()
|
||||
filesize = File.stat!(media_item.media_filepath).size
|
||||
|
||||
conn = get(conn, ~p"/media/#{media_item.uuid}/stream")
|
||||
|
||||
assert {"content-length", to_string(filesize)} in conn.resp_headers
|
||||
end
|
||||
end
|
||||
|
||||
describe "streaming media when range is valid" do
|
||||
setup do
|
||||
media_item = media_item_with_attachments()
|
||||
|
||||
%{media_item: media_item}
|
||||
end
|
||||
|
||||
test "sets the correct status and headers", %{conn: conn, media_item: media_item} do
|
||||
filesize = File.stat!(media_item.media_filepath).size
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> put_req_header("range", "bytes=0-100")
|
||||
|> get(~p"/media/#{media_item.uuid}/stream")
|
||||
|
||||
assert conn.status == 206
|
||||
assert {"content-range", "bytes 0-100/#{filesize}"} in conn.resp_headers
|
||||
assert {"content-length", "101"} in conn.resp_headers
|
||||
end
|
||||
|
||||
test "streams the specified range", %{conn: conn, media_item: media_item} do
|
||||
conn =
|
||||
conn
|
||||
|> put_req_header("range", "bytes=0-100")
|
||||
|> get(~p"/media/#{media_item.uuid}/stream")
|
||||
|
||||
assert byte_size(conn.resp_body) == 101
|
||||
end
|
||||
|
||||
test "supports range offsets", %{conn: conn, media_item: media_item} do
|
||||
contents = File.read!(media_item.media_filepath)
|
||||
expected = String.slice(contents, 100..200)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> put_req_header("range", "bytes=100-200")
|
||||
|> get(~p"/media/#{media_item.uuid}/stream")
|
||||
|
||||
assert conn.resp_body == expected
|
||||
end
|
||||
|
||||
test "returns as expected if the requested range is larger than the file", %{conn: conn, media_item: media_item} do
|
||||
contents = File.read!(media_item.media_filepath)
|
||||
filesize = File.stat!(media_item.media_filepath).size
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> put_req_header("range", "bytes=0-#{filesize * 10}")
|
||||
|> get(~p"/media/#{media_item.uuid}/stream")
|
||||
|
||||
assert conn.resp_body == contents
|
||||
assert {"content-range", "bytes 0-#{filesize - 1}/#{filesize}"} in conn.resp_headers
|
||||
assert {"content-length", to_string(filesize)} in conn.resp_headers
|
||||
end
|
||||
|
||||
test "supports endless ranges", %{conn: conn, media_item: media_item} do
|
||||
contents = File.read!(media_item.media_filepath)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> put_req_header("range", "bytes=0-")
|
||||
|> get(~p"/media/#{media_item.uuid}/stream")
|
||||
|
||||
assert conn.resp_body == contents
|
||||
end
|
||||
|
||||
test "supports endless ranges with offsets", %{conn: conn, media_item: media_item} do
|
||||
contents = File.read!(media_item.media_filepath)
|
||||
{_, expected} = String.split_at(contents, 100)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> put_req_header("range", "bytes=100-")
|
||||
|> get(~p"/media/#{media_item.uuid}/stream")
|
||||
|
||||
assert conn.resp_body == expected
|
||||
end
|
||||
end
|
||||
|
||||
describe "streaming media when range is invalid or not present" do
|
||||
setup do
|
||||
media_item = media_item_with_attachments()
|
||||
|
||||
%{media_item: media_item}
|
||||
end
|
||||
|
||||
test "sets the correct status and headers", %{conn: conn, media_item: media_item} do
|
||||
filesize = File.stat!(media_item.media_filepath).size
|
||||
|
||||
conn = get(conn, ~p"/media/#{media_item.uuid}/stream")
|
||||
|
||||
assert conn.status == 200
|
||||
assert {"content-length", to_string(filesize)} in conn.resp_headers
|
||||
end
|
||||
|
||||
test "streams the entire file", %{conn: conn, media_item: media_item} do
|
||||
contents = File.read!(media_item.media_filepath)
|
||||
|
||||
conn = get(conn, ~p"/media/#{media_item.uuid}/stream")
|
||||
|
||||
assert conn.resp_body == contents
|
||||
end
|
||||
|
||||
test "doesn't blow up if the range header is invalid", %{conn: conn, media_item: media_item} do
|
||||
contents = File.read!(media_item.media_filepath)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> put_req_header("range", "bytes=-")
|
||||
|> get(~p"/media/#{media_item.uuid}/stream")
|
||||
|
||||
assert conn.status == 200
|
||||
assert conn.resp_body == contents
|
||||
end
|
||||
end
|
||||
|
||||
defp create_media_item(_) do
|
||||
media_item = media_item_fixture()
|
||||
%{media_item: media_item}
|
||||
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user