diff --git a/lib/pinchflat/metadata/metadata_file_helpers.ex b/lib/pinchflat/metadata/metadata_file_helpers.ex index c4320fa..b728f47 100644 --- a/lib/pinchflat/metadata/metadata_file_helpers.ex +++ b/lib/pinchflat/metadata/metadata_file_helpers.ex @@ -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), diff --git a/lib/pinchflat/metadata/nfo_builder.ex b/lib/pinchflat/metadata/nfo_builder.ex index 3db237f..38a26dd 100644 --- a/lib/pinchflat/metadata/nfo_builder.ex +++ b/lib/pinchflat/metadata/nfo_builder.ex @@ -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?! """ @@ -49,8 +54,8 @@ defmodule Pinchflat.Metadata.NfoBuilder do #{safe(metadata["id"])} #{safe(metadata["description"])} #{safe(upload_date)} - #{safe(upload_date.year)} - #{Calendar.strftime(upload_date, "%m%d")} + #{safe(season)} + #{episode} YouTube """ @@ -67,4 +72,11 @@ defmodule Pinchflat.Metadata.NfoBuilder do """ 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 diff --git a/test/pinchflat/metadata/metadata_file_helpers_test.exs b/test/pinchflat/metadata/metadata_file_helpers_test.exs index 36db3cd..b1b8b2b 100644 --- a/test/pinchflat/metadata/metadata_file_helpers_test.exs +++ b/test/pinchflat/metadata/metadata_file_helpers_test.exs @@ -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 diff --git a/test/pinchflat/metadata/nfo_builder_test.exs b/test/pinchflat/metadata/nfo_builder_test.exs index c8b032a..47217c7 100644 --- a/test/pinchflat/metadata/nfo_builder_test.exs +++ b/test/pinchflat/metadata/nfo_builder_test.exs @@ -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, "0123") + assert String.contains?(nfo, "456") + + 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, "2021") + assert String.contains?(nfo, "0101") + end end describe "build_and_store_for_source/2" do