Added nfo builder for 'episodes'
This commit is contained in:
parent
cf59bf99cd
commit
b8add0201e
7 changed files with 158 additions and 13 deletions
|
|
@ -15,12 +15,25 @@ defmodule Pinchflat.Filesystem.FilesystemHelpers do
|
|||
tmpfile_directory = Application.get_env(:pinchflat, :tmpfile_directory)
|
||||
filepath = Path.join([tmpfile_directory, "#{StringUtils.random_string(64)}.#{type}"])
|
||||
|
||||
:ok = File.mkdir_p!(Path.dirname(filepath))
|
||||
:ok = File.write(filepath, "")
|
||||
:ok = write_p!(filepath, "")
|
||||
|
||||
filepath
|
||||
end
|
||||
|
||||
@doc """
|
||||
Writes content to a file, creating directories as needed.
|
||||
Takes the same args as File.write!/3.
|
||||
|
||||
Returns :ok | raises on error
|
||||
"""
|
||||
def write_p!(filepath, content, modes \\ []) do
|
||||
filepath
|
||||
|> Path.dirname()
|
||||
|> File.mkdir_p!()
|
||||
|
||||
File.write!(filepath, content, modes)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Fetches the file size of a media item and saves it to the database.
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ defmodule Pinchflat.Metadata.MetadataFileHelpers do
|
|||
needed
|
||||
"""
|
||||
|
||||
alias Pinchflat.Filesystem.FilesystemHelpers
|
||||
|
||||
@doc """
|
||||
Compresses and stores metadata for a media item, returning the filepath.
|
||||
|
||||
|
|
@ -18,8 +20,7 @@ defmodule Pinchflat.Metadata.MetadataFileHelpers do
|
|||
filepath = generate_filepath_for(database_record, "metadata.json.gz")
|
||||
{:ok, json} = Phoenix.json_library().encode(metadata_map)
|
||||
|
||||
File.mkdir_p!(Path.dirname(filepath))
|
||||
:ok = File.write(filepath, json, [:compressed])
|
||||
:ok = FilesystemHelpers.write_p!(filepath, json, [:compressed])
|
||||
|
||||
filepath
|
||||
end
|
||||
|
|
@ -45,12 +46,23 @@ defmodule Pinchflat.Metadata.MetadataFileHelpers do
|
|||
filepath = generate_filepath_for(database_record, Path.basename(thumbnail_url))
|
||||
thumbnail_blob = fetch_thumbnail_from_url(thumbnail_url)
|
||||
|
||||
File.mkdir_p!(Path.dirname(filepath))
|
||||
:ok = File.write(filepath, thumbnail_blob)
|
||||
:ok = FilesystemHelpers.write_p!(filepath, thumbnail_blob)
|
||||
|
||||
filepath
|
||||
end
|
||||
|
||||
@doc """
|
||||
Parses an upload date from the YYYYMMDD string returned in yt-dlp metadata
|
||||
and returns a Date struct.
|
||||
|
||||
Returns Date.t()
|
||||
"""
|
||||
def parse_upload_date(upload_date) do
|
||||
<<year::binary-size(4)>> <> <<month::binary-size(2)>> <> <<day::binary-size(2)>> = upload_date
|
||||
|
||||
Date.from_iso8601!("#{year}-#{month}-#{day}")
|
||||
end
|
||||
|
||||
defp fetch_thumbnail_from_url(url) do
|
||||
http_client = Application.get_env(:pinchflat, :http_client, Pinchflat.HTTP.HTTPClient)
|
||||
{:ok, body} = http_client.get(url, [], body_format: :binary)
|
||||
|
|
|
|||
44
lib/pinchflat/metadata/nfo_builder.ex
Normal file
44
lib/pinchflat/metadata/nfo_builder.ex
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
defmodule Pinchflat.Metadata.NfoBuilder do
|
||||
@moduledoc """
|
||||
Provides methods for building and storing NFO files for
|
||||
use by Kodi/Jellyfin and other media center software.
|
||||
"""
|
||||
|
||||
alias Pinchflat.Metadata.MetadataFileHelpers
|
||||
alias Pinchflat.Filesystem.FilesystemHelpers
|
||||
|
||||
@doc """
|
||||
Builds an NFO file for a media item (read: single "episode") and
|
||||
stores it in the same directory as the media file. Has the same name
|
||||
as the media file, but with a .nfo extension.
|
||||
|
||||
Returns the filepath of the NFO file.
|
||||
"""
|
||||
def build_and_store_for_media_item(metadata) do
|
||||
filepath = Path.rootname(metadata["filepath"]) <> ".nfo"
|
||||
nfo = build_for_media_item(metadata)
|
||||
|
||||
FilesystemHelpers.write_p!(filepath, nfo)
|
||||
|
||||
filepath
|
||||
end
|
||||
|
||||
defp build_for_media_item(metadata) do
|
||||
upload_date = MetadataFileHelpers.parse_upload_date(metadata["upload_date"])
|
||||
# Cribbed from a combination of the Kodi wiki, ytdl-nfo, and ytdl-sub.
|
||||
# WHO NEEDS A FANCY XML PARSER ANYWAY?!
|
||||
"""
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
||||
<episodedetails>
|
||||
<title>#{metadata["title"]}</title>
|
||||
<showtitle>#{metadata["uploader"]}</showtitle>
|
||||
<uniqueid type="youtube" default="true">#{metadata["id"]}</uniqueid>
|
||||
<plot>#{metadata["description"]}</plot>
|
||||
<premiered>#{upload_date}</premiered>
|
||||
<season>#{upload_date.year}</season>
|
||||
<episode>#{Calendar.strftime(upload_date, "%m%d")}</episode>
|
||||
<genre>YouTube</genre>
|
||||
</episodedetails>
|
||||
"""
|
||||
end
|
||||
end
|
||||
|
|
@ -25,6 +25,7 @@ defmodule Pinchflat.YtDlp.Media do
|
|||
|
||||
alias __MODULE__
|
||||
alias Pinchflat.Utils.FunctionUtils
|
||||
alias Pinchflat.Metadata.MetadataFileHelpers
|
||||
|
||||
@doc """
|
||||
Downloads a single piece of media (and possibly its metadata) directly to its
|
||||
|
|
@ -86,7 +87,7 @@ defmodule Pinchflat.YtDlp.Media do
|
|||
original_url: response["webpage_url"],
|
||||
livestream: response["was_live"],
|
||||
short_form_content: response["webpage_url"] && short_form_content?(response),
|
||||
upload_date: response["upload_date"] && parse_upload_date(response["upload_date"])
|
||||
upload_date: response["upload_date"] && MetadataFileHelpers.parse_upload_date(response["upload_date"])
|
||||
}
|
||||
end
|
||||
|
||||
|
|
@ -106,12 +107,6 @@ defmodule Pinchflat.YtDlp.Media do
|
|||
end
|
||||
end
|
||||
|
||||
defp parse_upload_date(upload_date) do
|
||||
<<year::binary-size(4)>> <> <<month::binary-size(2)>> <> <<day::binary-size(2)>> = upload_date
|
||||
|
||||
Date.from_iso8601!("#{year}-#{month}-#{day}")
|
||||
end
|
||||
|
||||
defp backend_runner do
|
||||
# This approach lets us mock the command for testing
|
||||
Application.get_env(:pinchflat, :yt_dlp_runner)
|
||||
|
|
|
|||
|
|
@ -33,4 +33,27 @@ defmodule Pinchflat.Filesystem.FilesystemHelpersTest do
|
|||
assert {:error, _} = FilesystemHelpers.compute_and_save_media_filesize(media_item)
|
||||
end
|
||||
end
|
||||
|
||||
describe "write_p!/3" do
|
||||
test "writes content to a file" do
|
||||
filepath = FilesystemHelpers.generate_metadata_tmpfile(:json)
|
||||
content = "{}"
|
||||
|
||||
assert :ok = FilesystemHelpers.write_p!(filepath, content)
|
||||
assert File.read!(filepath) == content
|
||||
|
||||
File.rm!(filepath)
|
||||
end
|
||||
|
||||
test "creates directories as needed" do
|
||||
tmpfile_directory = Application.get_env(:pinchflat, :tmpfile_directory)
|
||||
filepath = Path.join([tmpfile_directory, "foo", "bar", "file.json"])
|
||||
content = "{}"
|
||||
|
||||
assert :ok = FilesystemHelpers.write_p!(filepath, content)
|
||||
assert File.read!(filepath) == content
|
||||
|
||||
File.rm!(filepath)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -84,4 +84,12 @@ defmodule Pinchflat.Metadata.MetadataFileHelpersTest do
|
|||
assert Path.basename(filepath) == "maxres.webp"
|
||||
end
|
||||
end
|
||||
|
||||
describe "parse_upload_date/1" do
|
||||
test "returns a date from the given metadata upload date" do
|
||||
upload_date = "20210101"
|
||||
|
||||
assert Helpers.parse_upload_date(upload_date) == ~D[2021-01-01]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
50
test/pinchflat/metadata/nfo_builder_test.exs
Normal file
50
test/pinchflat/metadata/nfo_builder_test.exs
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
defmodule Pinchflat.Metadata.NfoBuilderTest do
|
||||
use Pinchflat.DataCase
|
||||
|
||||
alias Pinchflat.Metadata.NfoBuilder
|
||||
|
||||
setup do
|
||||
json_filepath =
|
||||
Path.join([
|
||||
File.cwd!(),
|
||||
"test",
|
||||
"support",
|
||||
"files",
|
||||
"media_metadata.json"
|
||||
])
|
||||
|
||||
{:ok, file_body} = File.read(json_filepath)
|
||||
{:ok, parsed_json} = Phoenix.json_library().decode(file_body)
|
||||
|
||||
{:ok, %{metadata: parsed_json}}
|
||||
end
|
||||
|
||||
describe "build_and_store_for_media_item/1" do
|
||||
test "returns the filepath", %{metadata: metadata} do
|
||||
result = NfoBuilder.build_and_store_for_media_item(metadata)
|
||||
|
||||
assert File.exists?(result)
|
||||
|
||||
File.rm!(result)
|
||||
end
|
||||
|
||||
test "builds filepath based on media location", %{metadata: metadata} do
|
||||
result = NfoBuilder.build_and_store_for_media_item(metadata)
|
||||
|
||||
assert String.contains?(result, Path.rootname(metadata["filepath"]))
|
||||
assert String.ends_with?(result, ".nfo")
|
||||
|
||||
File.rm!(result)
|
||||
end
|
||||
|
||||
test "builds an NFO file", %{metadata: metadata} do
|
||||
result = NfoBuilder.build_and_store_for_media_item(metadata)
|
||||
nfo = File.read!(result)
|
||||
|
||||
assert String.contains?(nfo, ~S(<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>))
|
||||
assert String.contains?(nfo, "<title>#{metadata["title"]}</title>")
|
||||
|
||||
File.rm!(result)
|
||||
end
|
||||
end
|
||||
end
|
||||
Loading…
Reference in a new issue