Hooked up NFO generation to the source worker
This commit is contained in:
parent
faf4f2e7e4
commit
c9e576a2d6
7 changed files with 165 additions and 13 deletions
|
|
@ -21,6 +21,20 @@ defmodule Pinchflat.Metadata.NfoBuilder do
|
|||
filepath
|
||||
end
|
||||
|
||||
@doc """
|
||||
Builds an NFO file for a souce and stores it at the specified location.
|
||||
Technically works for playlists, but it's really made for channels.
|
||||
|
||||
Returns the filepath of the NFO file.
|
||||
"""
|
||||
def build_and_store_for_source(filepath, metadata) do
|
||||
nfo = build_for_source(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.
|
||||
|
|
@ -39,4 +53,16 @@ defmodule Pinchflat.Metadata.NfoBuilder do
|
|||
</episodedetails>
|
||||
"""
|
||||
end
|
||||
|
||||
defp build_for_source(metadata) do
|
||||
"""
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
||||
<tvshow>
|
||||
<title>#{metadata["title"]}</title>
|
||||
<plot>#{metadata["description"]}</plot>
|
||||
<uniqueid type="youtube" default="true">#{metadata["id"]}</uniqueid>
|
||||
<genre>YouTube</genre>
|
||||
</tvshow>
|
||||
"""
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ defmodule Pinchflat.Metadata.SourceMetadataStorageWorker do
|
|||
use Oban.Worker,
|
||||
queue: :remote_metadata,
|
||||
tags: ["media_source", "source_metadata", "remote_metadata"],
|
||||
max_attempts: 1,
|
||||
max_attempts: 3,
|
||||
# This is the only thing stopping this job from calling itself
|
||||
# in an infinite loop. Time is in seconds
|
||||
unique: [period: 120]
|
||||
|
|
@ -15,6 +15,7 @@ defmodule Pinchflat.Metadata.SourceMetadataStorageWorker do
|
|||
alias Pinchflat.Repo
|
||||
alias Pinchflat.Tasks
|
||||
alias Pinchflat.Sources
|
||||
alias Pinchflat.Metadata.NfoBuilder
|
||||
alias Pinchflat.YtDlp.MediaCollection
|
||||
alias Pinchflat.Metadata.MetadataFileHelpers
|
||||
alias Pinchflat.Downloading.DownloadOptionBuilder
|
||||
|
|
@ -41,6 +42,7 @@ defmodule Pinchflat.Metadata.SourceMetadataStorageWorker do
|
|||
@impl Oban.Worker
|
||||
def perform(%Oban.Job{args: %{"id" => source_id}}) do
|
||||
source = Repo.preload(Sources.get_source!(source_id), [:metadata, :media_profile])
|
||||
source_metadata = fetch_source_metadata(source)
|
||||
series_directory = determine_series_directory(source)
|
||||
|
||||
# Since updating a source kicks this job off again, we enforce job uniqueness (above)
|
||||
|
|
@ -48,8 +50,9 @@ defmodule Pinchflat.Metadata.SourceMetadataStorageWorker do
|
|||
# in an infinite loop.
|
||||
Sources.update_source(source, %{
|
||||
series_directory: series_directory,
|
||||
nfo_filepath: store_source_nfo(source, series_directory, source_metadata),
|
||||
metadata: %{
|
||||
metadata_filepath: store_source_metadata(source)
|
||||
metadata_filepath: store_source_metadata(source, source_metadata)
|
||||
}
|
||||
})
|
||||
|
||||
|
|
@ -59,9 +62,13 @@ defmodule Pinchflat.Metadata.SourceMetadataStorageWorker do
|
|||
Ecto.StaleEntryError -> Logger.info("#{__MODULE__} discarded: source #{source_id} stale")
|
||||
end
|
||||
|
||||
defp store_source_metadata(source) do
|
||||
defp fetch_source_metadata(source) do
|
||||
{:ok, metadata} = MediaCollection.get_source_metadata(source.original_url)
|
||||
|
||||
metadata
|
||||
end
|
||||
|
||||
defp store_source_metadata(source, metadata) do
|
||||
MetadataFileHelpers.compress_and_store_metadata_for(source, metadata)
|
||||
end
|
||||
|
||||
|
|
@ -74,4 +81,12 @@ defmodule Pinchflat.Metadata.SourceMetadataStorageWorker do
|
|||
{:error, _} -> nil
|
||||
end
|
||||
end
|
||||
|
||||
defp store_source_nfo(source, series_directory, metadata) do
|
||||
if source.download_nfo && series_directory do
|
||||
nfo_filepath = Path.join(series_directory, "tvshow.nfo")
|
||||
|
||||
NfoBuilder.build_and_store_for_source(nfo_filepath, metadata)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -106,4 +106,9 @@ defmodule Pinchflat.Sources.Source do
|
|||
# minutes
|
||||
15
|
||||
end
|
||||
|
||||
@doc false
|
||||
def filepath_attributes do
|
||||
~w(nfo_filepath)a
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -102,7 +102,6 @@ defmodule Pinchflat.Sources do
|
|||
"""
|
||||
def delete_source(%Source{} = source, opts \\ []) do
|
||||
delete_files = Keyword.get(opts, :delete_files, false)
|
||||
|
||||
Tasks.delete_tasks_for(source)
|
||||
|
||||
source
|
||||
|
|
@ -111,7 +110,11 @@ defmodule Pinchflat.Sources do
|
|||
Media.delete_media_item(media_item, delete_files: delete_files)
|
||||
end)
|
||||
|
||||
delete_source_metadata_files(source)
|
||||
if delete_files do
|
||||
delete_source_files(source)
|
||||
end
|
||||
|
||||
delete_internal_metadata_files(source)
|
||||
Repo.delete(source)
|
||||
end
|
||||
|
||||
|
|
@ -134,16 +137,23 @@ defmodule Pinchflat.Sources do
|
|||
end
|
||||
end
|
||||
|
||||
defp delete_source_metadata_files(source) do
|
||||
defp delete_source_files(source) do
|
||||
mapped_struct = Map.from_struct(source)
|
||||
|
||||
Source.filepath_attributes()
|
||||
|> Enum.map(fn field -> mapped_struct[field] end)
|
||||
|> Enum.filter(&is_binary/1)
|
||||
|> Enum.each(&FilesystemHelpers.delete_file_and_remove_empty_directories/1)
|
||||
end
|
||||
|
||||
defp delete_internal_metadata_files(source) do
|
||||
metadata = Repo.preload(source, :metadata).metadata || %SourceMetadata{}
|
||||
mapped_struct = Map.from_struct(metadata)
|
||||
|
||||
filepaths =
|
||||
SourceMetadata.filepath_attributes()
|
||||
|> Enum.map(fn field -> mapped_struct[field] end)
|
||||
|> Enum.filter(&is_binary/1)
|
||||
|
||||
Enum.each(filepaths, &FilesystemHelpers.delete_file_and_remove_empty_directories/1)
|
||||
SourceMetadata.filepath_attributes()
|
||||
|> Enum.map(fn field -> mapped_struct[field] end)
|
||||
|> Enum.filter(&is_binary/1)
|
||||
|> Enum.each(&FilesystemHelpers.delete_file_and_remove_empty_directories/1)
|
||||
end
|
||||
|
||||
defp add_source_details_to_changeset(source, changeset) do
|
||||
|
|
|
|||
|
|
@ -31,4 +31,20 @@ defmodule Pinchflat.Metadata.NfoBuilderTest do
|
|||
assert String.contains?(nfo, "<title>#{metadata["title"]}</title>")
|
||||
end
|
||||
end
|
||||
|
||||
describe "build_and_store_for_source/2" do
|
||||
test "returns the filepath", %{metadata: metadata, filepath: filepath} do
|
||||
result = NfoBuilder.build_and_store_for_source(filepath, metadata)
|
||||
|
||||
assert File.exists?(result)
|
||||
end
|
||||
|
||||
test "builds an NFO file", %{metadata: metadata, filepath: filepath} do
|
||||
result = NfoBuilder.build_and_store_for_source(filepath, 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>")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -138,4 +138,64 @@ defmodule Pinchflat.Metadata.SourceMetadataStorageWorkerTest do
|
|||
refute source.series_directory
|
||||
end
|
||||
end
|
||||
|
||||
describe "perform/1 when storing the series NFO" do
|
||||
test "stores the NFO if specified" do
|
||||
stub(YtDlpRunnerMock, :run, fn
|
||||
_url, _opts, ot when ot == @source_details_ot ->
|
||||
filename = Path.join([Application.get_env(:pinchflat, :media_directory), "Season 1", "bar.mp4"])
|
||||
|
||||
{:ok, source_details_return_fixture(%{filename: filename})}
|
||||
|
||||
_url, _opts, ot when ot == @metadata_ot ->
|
||||
{:ok, "{}"}
|
||||
end)
|
||||
|
||||
source = source_fixture(%{download_nfo: true, nfo_filepath: nil})
|
||||
perform_job(SourceMetadataStorageWorker, %{id: source.id})
|
||||
source = Repo.reload(source)
|
||||
|
||||
assert source.nfo_filepath
|
||||
assert source.nfo_filepath == Path.join([source.series_directory, "tvshow.nfo"])
|
||||
assert File.exists?(source.nfo_filepath)
|
||||
|
||||
File.rm!(source.nfo_filepath)
|
||||
end
|
||||
|
||||
test "does not store the NFO if not specified" do
|
||||
stub(YtDlpRunnerMock, :run, fn
|
||||
_url, _opts, ot when ot == @source_details_ot ->
|
||||
filename = Path.join([Application.get_env(:pinchflat, :media_directory), "Season 1", "bar.mp4"])
|
||||
|
||||
{:ok, source_details_return_fixture(%{filename: filename})}
|
||||
|
||||
_url, _opts, ot when ot == @metadata_ot ->
|
||||
{:ok, "{}"}
|
||||
end)
|
||||
|
||||
source = source_fixture(%{download_nfo: false, nfo_filepath: nil})
|
||||
perform_job(SourceMetadataStorageWorker, %{id: source.id})
|
||||
source = Repo.reload(source)
|
||||
|
||||
refute source.nfo_filepath
|
||||
end
|
||||
|
||||
test "does not store the NFO if the series directory cannot be determined" do
|
||||
stub(YtDlpRunnerMock, :run, fn
|
||||
_url, _opts, ot when ot == @source_details_ot ->
|
||||
filename = Path.join([Application.get_env(:pinchflat, :media_directory), "foo", "bar.mp4"])
|
||||
|
||||
{:ok, source_details_return_fixture(%{filename: filename})}
|
||||
|
||||
_url, _opts, ot when ot == @metadata_ot ->
|
||||
{:ok, "{}"}
|
||||
end)
|
||||
|
||||
source = source_fixture(%{download_nfo: true, nfo_filepath: nil})
|
||||
perform_job(SourceMetadataStorageWorker, %{id: source.id})
|
||||
source = Repo.reload(source)
|
||||
|
||||
refute source.nfo_filepath
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ defmodule Pinchflat.SourcesTest do
|
|||
|
||||
alias Pinchflat.Sources
|
||||
alias Pinchflat.Sources.Source
|
||||
alias Pinchflat.Filesystem.FilesystemHelpers
|
||||
alias Pinchflat.Metadata.MetadataFileHelpers
|
||||
alias Pinchflat.Downloading.DownloadingHelpers
|
||||
alias Pinchflat.FastIndexing.FastIndexingWorker
|
||||
|
|
@ -474,9 +475,19 @@ defmodule Pinchflat.SourcesTest do
|
|||
|
||||
{:ok, updated_source} = Sources.update_source(source, update_attrs)
|
||||
|
||||
assert {:ok, _} = Sources.delete_source(updated_source, delete_files: true)
|
||||
assert {:ok, _} = Sources.delete_source(updated_source)
|
||||
refute File.exists?(updated_source.metadata.metadata_filepath)
|
||||
end
|
||||
|
||||
test "does not delete the source's non-metadata files" do
|
||||
filepath = FilesystemHelpers.generate_metadata_tmpfile(:nfo)
|
||||
source = source_fixture(%{nfo_filepath: filepath})
|
||||
|
||||
assert {:ok, _} = Sources.delete_source(source)
|
||||
assert File.exists?(filepath)
|
||||
|
||||
File.rm!(filepath)
|
||||
end
|
||||
end
|
||||
|
||||
describe "delete_source/2 when deleting files" do
|
||||
|
|
@ -498,6 +509,15 @@ defmodule Pinchflat.SourcesTest do
|
|||
|
||||
refute File.exists?(media_item.media_filepath)
|
||||
end
|
||||
|
||||
test "deletes the source's non-metadata files" do
|
||||
filepath = FilesystemHelpers.generate_metadata_tmpfile(:nfo)
|
||||
source = source_fixture(%{nfo_filepath: filepath})
|
||||
|
||||
assert {:ok, _} = Sources.delete_source(source, delete_files: true)
|
||||
|
||||
refute File.exists?(filepath)
|
||||
end
|
||||
end
|
||||
|
||||
describe "change_source/3" do
|
||||
|
|
|
|||
Loading…
Reference in a new issue