[Enhancement] Download image when using playlists with media center apps (#313)
* [WIP] started adding calls for downloading posters for playlists * Updated source image parser to work with playlists
This commit is contained in:
@@ -20,29 +20,49 @@ defmodule Pinchflat.Metadata.SourceImageParser do
|
||||
def store_source_images(base_directory, source_metadata) do
|
||||
(source_metadata["thumbnails"] || [])
|
||||
|> Enum.filter(&(&1["filepath"] != nil))
|
||||
|> select_useful_images()
|
||||
|> select_useful_images(source_metadata)
|
||||
|> Enum.map(&move_image(&1, base_directory))
|
||||
|> Enum.into(%{})
|
||||
end
|
||||
|
||||
defp select_useful_images(images) do
|
||||
defp select_useful_images(images, source_metadata) do
|
||||
labelled_images =
|
||||
Enum.reduce(images, [], fn image_map, acc ->
|
||||
Enum.reduce(images, %{}, fn image_map, acc ->
|
||||
case image_map do
|
||||
%{"id" => "avatar_uncropped"} ->
|
||||
acc ++ [{:poster, :poster_filepath, image_map["filepath"]}]
|
||||
|
||||
%{"id" => "banner_uncropped"} ->
|
||||
acc ++ [{:fanart, :fanart_filepath, image_map["filepath"]}]
|
||||
|
||||
_ ->
|
||||
acc
|
||||
%{"id" => "avatar_uncropped"} -> put_image_key(acc, :poster, image_map["filepath"])
|
||||
%{"id" => "banner_uncropped"} -> put_image_key(acc, :fanart, image_map["filepath"])
|
||||
_ -> acc
|
||||
end
|
||||
end)
|
||||
|
||||
labelled_images
|
||||
|> Enum.concat([{:banner, :banner_filepath, determine_best_banner(images)}])
|
||||
|> Enum.filter(fn {_, _, tmp_filepath} -> tmp_filepath end)
|
||||
|> add_fallback_poster(source_metadata)
|
||||
|> put_image_key(:banner, determine_best_banner(images))
|
||||
|> Enum.filter(fn {_key, attrs} -> attrs.current_filepath end)
|
||||
end
|
||||
|
||||
# If a poster is set, short-circuit and return the images as-is
|
||||
defp add_fallback_poster(%{poster: _} = images, _), do: images
|
||||
|
||||
# If a poster is NOT set, see if we can find a suitable image to use as a fallback
|
||||
defp add_fallback_poster(images, source_metadata) do
|
||||
case source_metadata["entries"] do
|
||||
nil -> images
|
||||
[] -> images
|
||||
[first_entry | _] -> add_poster_from_entry_thumbnail(images, first_entry)
|
||||
end
|
||||
end
|
||||
|
||||
defp add_poster_from_entry_thumbnail(images, entry) do
|
||||
thumbnail =
|
||||
(entry["thumbnails"] || [])
|
||||
|> Enum.reverse()
|
||||
|> Enum.find(& &1["filepath"])
|
||||
|
||||
case thumbnail do
|
||||
nil -> images
|
||||
_ -> put_image_key(images, :poster, thumbnail["filepath"])
|
||||
end
|
||||
end
|
||||
|
||||
defp determine_best_banner(images) do
|
||||
@@ -58,12 +78,22 @@ defmodule Pinchflat.Metadata.SourceImageParser do
|
||||
Map.get(best_candidate || %{}, "filepath")
|
||||
end
|
||||
|
||||
defp move_image({filename, source_attr_name, tmp_filepath}, base_directory) do
|
||||
extension = Path.extname(tmp_filepath)
|
||||
final_filepath = Path.join([base_directory, "#{filename}#{extension}"])
|
||||
defp move_image({_key, attrs}, base_directory) do
|
||||
extension = Path.extname(attrs.current_filepath)
|
||||
final_filepath = Path.join([base_directory, "#{attrs.final_filename}#{extension}"])
|
||||
|
||||
FilesystemUtils.cp_p!(tmp_filepath, final_filepath)
|
||||
FilesystemUtils.cp_p!(attrs.current_filepath, final_filepath)
|
||||
|
||||
{source_attr_name, final_filepath}
|
||||
{attrs.attribute_name, final_filepath}
|
||||
end
|
||||
|
||||
defp put_image_key(map, key, image) do
|
||||
attribute_atom = String.to_existing_atom("#{key}_filepath")
|
||||
|
||||
Map.put(map, key, %{
|
||||
attribute_name: attribute_atom,
|
||||
final_filename: to_string(key),
|
||||
current_filepath: image
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
@@ -77,10 +77,8 @@ defmodule Pinchflat.Metadata.SourceMetadataStorageWorker do
|
||||
|
||||
defp fetch_source_metadata_and_images(series_directory, source) do
|
||||
metadata_directory = MetadataFileHelpers.metadata_directory_for(source)
|
||||
tmp_output_path = "#{tmp_directory()}/#{StringUtils.random_string(16)}/source_image.%(ext)S"
|
||||
opts = [:write_all_thumbnails, convert_thumbnails: "jpg", output: tmp_output_path]
|
||||
|
||||
{:ok, metadata} = MediaCollection.get_source_metadata(source.original_url, opts)
|
||||
{:ok, metadata} = fetch_metadata_for_source(source)
|
||||
metadata_image_attrs = SourceImageParser.store_source_images(metadata_directory, metadata)
|
||||
|
||||
if source.media_profile.download_source_images && series_directory do
|
||||
@@ -110,6 +108,20 @@ defmodule Pinchflat.Metadata.SourceMetadataStorageWorker do
|
||||
end
|
||||
end
|
||||
|
||||
defp fetch_metadata_for_source(source) do
|
||||
tmp_output_path = "#{tmp_directory()}/#{StringUtils.random_string(16)}/source_image.%(ext)S"
|
||||
base_opts = [convert_thumbnails: "jpg", output: tmp_output_path]
|
||||
|
||||
opts =
|
||||
if source.collection_type == :channel do
|
||||
base_opts ++ [:write_all_thumbnails, playlist_items: 0]
|
||||
else
|
||||
base_opts ++ [:write_thumbnail, playlist_items: 1]
|
||||
end
|
||||
|
||||
MediaCollection.get_source_metadata(source.original_url, opts)
|
||||
end
|
||||
|
||||
defp tmp_directory do
|
||||
Application.get_env(:pinchflat, :tmpfile_directory)
|
||||
end
|
||||
|
||||
@@ -99,10 +99,21 @@ defmodule Pinchflat.YtDlp.MediaCollection do
|
||||
as a compressed blob for possible future use. That's why it's not getting formatted like
|
||||
`get_source_details/1`
|
||||
|
||||
! IMPORTANT ! - you'll always want to set `playlist_items: int` in `addl_opts.
|
||||
This is great if you want to also return details about the videos in the playlists,
|
||||
but it should be set in all cases to not over-fetch data.
|
||||
For channels you should usually set this to 0 since channels return all the
|
||||
metadata we need without needing to fetch the videos. On the other hand, playlists
|
||||
don't return very useful images so you can set this to 1 to get the first video's
|
||||
images, for instance.
|
||||
|
||||
Returns {:ok, map()} | {:error, any, ...}.
|
||||
"""
|
||||
def get_source_metadata(source_url, addl_opts \\ []) do
|
||||
opts = [playlist_items: 0] ++ addl_opts
|
||||
def get_source_metadata(source_url, addl_opts \\ [playlist_items: 0]) do
|
||||
# This only validates that the `playlist_items` key is present. It's otherwise unused
|
||||
_playlist_items = Keyword.fetch!(addl_opts, :playlist_items)
|
||||
|
||||
opts = [:skip_download] ++ addl_opts
|
||||
output_template = "playlist:%()j"
|
||||
|
||||
with {:ok, output} <- backend_runner().run(source_url, opts, output_template),
|
||||
|
||||
Reference in New Issue
Block a user