[Bugfix] Determine NFO season and episode from filepath (#320)
* Updated NFO generator to determine S/E from filepath * Tests
This commit is contained in:
parent
4822127e3f
commit
4c7a63c970
4 changed files with 97 additions and 16 deletions
|
|
@ -124,6 +124,21 @@ defmodule Pinchflat.Metadata.MetadataFileHelpers do
|
|||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Attempts to determine the season and episode number from a media filepath.
|
||||
|
||||
Returns {:ok, {binary(), binary()}} | {:error, :indeterminable}
|
||||
"""
|
||||
def season_and_episode_from_media_filepath(media_filepath) do
|
||||
# matches s + 1 or more digits + e + 1 or more digits (case-insensitive)
|
||||
season_episode_regex = ~r/s(\d+)e(\d+)/i
|
||||
|
||||
case Regex.scan(season_episode_regex, media_filepath) do
|
||||
[[_, season, episode] | _] -> {:ok, {season, episode}}
|
||||
_ -> {:error, :indeterminable}
|
||||
end
|
||||
end
|
||||
|
||||
defp generate_filepath_for(database_record, filename) do
|
||||
Path.join([
|
||||
metadata_directory_for(database_record),
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@ defmodule Pinchflat.Metadata.NfoBuilder do
|
|||
|
||||
import Pinchflat.Utils.XmlUtils, only: [safe: 1]
|
||||
|
||||
alias Pinchflat.Metadata.MetadataFileHelpers
|
||||
alias Pinchflat.Utils.FilesystemUtils
|
||||
alias Pinchflat.Metadata.MetadataFileHelpers
|
||||
|
||||
@doc """
|
||||
Builds an NFO file for a media item (read: single "episode") and
|
||||
|
|
@ -15,12 +15,12 @@ defmodule Pinchflat.Metadata.NfoBuilder do
|
|||
|
||||
Returns the filepath of the NFO file.
|
||||
"""
|
||||
def build_and_store_for_media_item(filepath, metadata) do
|
||||
nfo = build_for_media_item(metadata)
|
||||
def build_and_store_for_media_item(nfo_filepath, metadata) do
|
||||
nfo = build_for_media_item(nfo_filepath, metadata)
|
||||
|
||||
FilesystemUtils.write_p!(filepath, nfo)
|
||||
FilesystemUtils.write_p!(nfo_filepath, nfo)
|
||||
|
||||
filepath
|
||||
nfo_filepath
|
||||
end
|
||||
|
||||
@doc """
|
||||
|
|
@ -37,10 +37,15 @@ defmodule Pinchflat.Metadata.NfoBuilder do
|
|||
filepath
|
||||
end
|
||||
|
||||
defp build_for_media_item(metadata) do
|
||||
defp build_for_media_item(nfo_filepath, metadata) do
|
||||
upload_date = MetadataFileHelpers.parse_upload_date(metadata["upload_date"])
|
||||
# NOTE: the filepath here isn't the path of the media item, it's the path that
|
||||
# the NFO should be saved to. This works because the NFO's path is the same as
|
||||
# the media's path, just with a different extension. If this ever changes I'll
|
||||
# need to pass in the media item's path as well.
|
||||
{season, episode} = determine_season_and_episode_number(nfo_filepath, 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>
|
||||
|
|
@ -49,8 +54,8 @@ defmodule Pinchflat.Metadata.NfoBuilder do
|
|||
<uniqueid type="youtube" default="true">#{safe(metadata["id"])}</uniqueid>
|
||||
<plot>#{safe(metadata["description"])}</plot>
|
||||
<aired>#{safe(upload_date)}</aired>
|
||||
<season>#{safe(upload_date.year)}</season>
|
||||
<episode>#{Calendar.strftime(upload_date, "%m%d")}</episode>
|
||||
<season>#{safe(season)}</season>
|
||||
<episode>#{episode}</episode>
|
||||
<genre>YouTube</genre>
|
||||
</episodedetails>
|
||||
"""
|
||||
|
|
@ -67,4 +72,11 @@ defmodule Pinchflat.Metadata.NfoBuilder do
|
|||
</tvshow>
|
||||
"""
|
||||
end
|
||||
|
||||
defp determine_season_and_episode_number(filepath, upload_date) do
|
||||
case MetadataFileHelpers.season_and_episode_from_media_filepath(filepath) do
|
||||
{:ok, {season, episode}} -> {season, episode}
|
||||
{:error, _} -> {upload_date.year, Calendar.strftime(upload_date, "%m%d")}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -11,6 +11,16 @@ defmodule Pinchflat.Metadata.MetadataFileHelpersTest do
|
|||
{:ok, %{media_item: media_item}}
|
||||
end
|
||||
|
||||
describe "metadata_directory_for/1" do
|
||||
test "returns the metadata directory for the given record", %{media_item: media_item} do
|
||||
base_metadata_directory = Application.get_env(:pinchflat, :metadata_directory)
|
||||
|
||||
metadata_directory = Helpers.metadata_directory_for(media_item)
|
||||
|
||||
assert metadata_directory == Path.join([base_metadata_directory, "media_items", "#{media_item.id}"])
|
||||
end
|
||||
end
|
||||
|
||||
describe "compress_and_store_metadata_for/2" do
|
||||
test "returns the filepath", %{media_item: media_item} do
|
||||
metadata_map = %{"foo" => "bar"}
|
||||
|
|
@ -142,13 +152,20 @@ defmodule Pinchflat.Metadata.MetadataFileHelpersTest do
|
|||
end
|
||||
end
|
||||
|
||||
describe "metadata_directory_for/1" do
|
||||
test "returns the metadata directory for the given record", %{media_item: media_item} do
|
||||
base_metadata_directory = Application.get_env(:pinchflat, :metadata_directory)
|
||||
describe "season_and_episode_from_media_filepath/1" do
|
||||
test "returns a season and episode if one can be determined" do
|
||||
assert {:ok, {"1", "2"}} = Helpers.season_and_episode_from_media_filepath("/foo/s1e2 - test.mp4")
|
||||
assert {:ok, {"1", "2"}} = Helpers.season_and_episode_from_media_filepath("/foo/S1E2 - test.mp4")
|
||||
assert {:ok, {"001", "002"}} = Helpers.season_and_episode_from_media_filepath("/foo/s001e002 - test.mp4")
|
||||
assert {:ok, {"1", "2"}} = Helpers.season_and_episode_from_media_filepath("/foo/s1e2bar - test.mp4")
|
||||
assert {:ok, {"1", "2"}} = Helpers.season_and_episode_from_media_filepath("/foo/bar s1e2 - test.mp4")
|
||||
end
|
||||
|
||||
metadata_directory = Helpers.metadata_directory_for(media_item)
|
||||
|
||||
assert metadata_directory == Path.join([base_metadata_directory, "media_items", "#{media_item.id}"])
|
||||
test "returns an error if a season and episode can't be determined" do
|
||||
assert {:error, :indeterminable} = Helpers.season_and_episode_from_media_filepath("/foo/test.mp4")
|
||||
assert {:error, :indeterminable} = Helpers.season_and_episode_from_media_filepath("/foo/s1 - test.mp4")
|
||||
assert {:error, :indeterminable} = Helpers.season_and_episode_from_media_filepath("/foo/s1e - test.mp4")
|
||||
assert {:error, :indeterminable} = Helpers.season_and_episode_from_media_filepath("/foo/s1etest.mp4")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ defmodule Pinchflat.Metadata.NfoBuilderTest do
|
|||
alias Pinchflat.Utils.FilesystemUtils
|
||||
|
||||
setup do
|
||||
filepath = FilesystemUtils.generate_metadata_tmpfile(:json)
|
||||
filepath = FilesystemUtils.generate_metadata_tmpfile(:nfo)
|
||||
|
||||
on_exit(fn -> File.rm!(filepath) end)
|
||||
|
||||
|
|
@ -45,6 +45,43 @@ defmodule Pinchflat.Metadata.NfoBuilderTest do
|
|||
|
||||
assert String.contains?(nfo, "hello' & <world>")
|
||||
end
|
||||
|
||||
test "uses the season and episode number from the filepath if it can be determined" do
|
||||
metadata = %{
|
||||
"title" => "title",
|
||||
"uploader" => "uploader",
|
||||
"id" => "id",
|
||||
"description" => "description",
|
||||
"upload_date" => "20210101"
|
||||
}
|
||||
|
||||
tmpfile_directory = Application.get_env(:pinchflat, :tmpfile_directory)
|
||||
filepath = Path.join([tmpfile_directory, "foo/s0123e456.nfo"])
|
||||
|
||||
result = NfoBuilder.build_and_store_for_media_item(filepath, metadata)
|
||||
nfo = File.read!(result)
|
||||
|
||||
assert String.contains?(nfo, "<season>0123</season>")
|
||||
assert String.contains?(nfo, "<episode>456</episode>")
|
||||
|
||||
File.rm!(filepath)
|
||||
end
|
||||
|
||||
test "uses the upload date if the season and episode number can't be determined", %{filepath: filepath} do
|
||||
metadata = %{
|
||||
"title" => "title",
|
||||
"uploader" => "uploader",
|
||||
"id" => "id",
|
||||
"description" => "description",
|
||||
"upload_date" => "20210101"
|
||||
}
|
||||
|
||||
result = NfoBuilder.build_and_store_for_media_item(filepath, metadata)
|
||||
nfo = File.read!(result)
|
||||
|
||||
assert String.contains?(nfo, "<season>2021</season>")
|
||||
assert String.contains?(nfo, "<episode>0101</episode>")
|
||||
end
|
||||
end
|
||||
|
||||
describe "build_and_store_for_source/2" do
|
||||
|
|
|
|||
Loading…
Reference in a new issue