diff --git a/src/ytdl_sub/plugins/nfo_tags.py b/src/ytdl_sub/plugins/nfo_tags.py
index 7e13509d..6e08124a 100644
--- a/src/ytdl_sub/plugins/nfo_tags.py
+++ b/src/ytdl_sub/plugins/nfo_tags.py
@@ -102,12 +102,18 @@ class SharedNfoTagsPlugin(Plugin[SharedNfoTagsOptions], ABC):
return nfo_tags
- def _create_nfo(self, entry: Entry, save_to_entry: bool = True) -> None:
+ def _create_nfo(
+ self,
+ entry: Entry,
+ nfo_tags: Optional[Dict[str, List[XmlElement]]] = None,
+ save_to_entry: bool = True,
+ ) -> None:
# Write the nfo tags to XML with the nfo_root
nfo_root = self.overrides.apply_formatter(
formatter=self.plugin_options.nfo_root, entry=entry
)
- nfo_tags = self._get_xml_element_dict(entry=entry)
+ if nfo_tags is None:
+ nfo_tags = self._get_xml_element_dict(entry=entry)
if self.plugin_options.kodi_safe:
nfo_root = to_max_3_byte_utf8_string(nfo_root)
diff --git a/src/ytdl_sub/plugins/output_directory_nfo_tags.py b/src/ytdl_sub/plugins/output_directory_nfo_tags.py
index a63f9684..1dcc37cf 100644
--- a/src/ytdl_sub/plugins/output_directory_nfo_tags.py
+++ b/src/ytdl_sub/plugins/output_directory_nfo_tags.py
@@ -1,3 +1,5 @@
+from typing import Dict
+from typing import List
from typing import Optional
from ytdl_sub.config.preset_options import Overrides
@@ -5,6 +7,7 @@ from ytdl_sub.entries.entry import Entry
from ytdl_sub.plugins.nfo_tags import NfoTagsValidator
from ytdl_sub.plugins.nfo_tags import SharedNfoTagsOptions
from ytdl_sub.plugins.nfo_tags import SharedNfoTagsPlugin
+from ytdl_sub.utils.xml import XmlElement
from ytdl_sub.validators.string_formatter_validators import StringFormatterValidator
from ytdl_sub.ytdl_additions.enhanced_download_archive import EnhancedDownloadArchive
@@ -12,8 +15,8 @@ from ytdl_sub.ytdl_additions.enhanced_download_archive import EnhancedDownloadAr
class OutputDirectoryNfoTagsOptions(SharedNfoTagsOptions):
"""
Adds a single NFO file in the output directory. An NFO file is simply an XML file with a
- ``.nfo`` extension. It uses the last entry's source variables which can change per download
- invocation. Be cautious of which variables you use.
+ ``.nfo`` extension. It builds the tags using each entry's source variables, and merges all the
+ unique values into the final set of tags. Be cautious of which variables you use.
Usage:
@@ -47,38 +50,29 @@ class OutputDirectoryNfoTagsOptions(SharedNfoTagsOptions):
@property
def tags(self) -> NfoTagsValidator:
"""
- Tags within the nfo_root tag. In the usage above, it would look like
-
- .. code-block:: xml
-
-
-
- Sweet youtube TV show
-
-
- Also supports xml attributes and duplicate keys:
+ Tags within the nfo_root tag. Also supports xml attributes and duplicate keys:
.. code-block:: yaml
tags:
- named_season:
- - tag: "{source_title}"
+ title: "Sweet youtube TV show"
+ namedseason:
+ - tag: "{season_name}"
attributes:
- number: "{collection_index}"
- behavior: "merge"
+ number: "{season_index}"
genre:
- - tag: "Comedy"
- behavior: "overwrite"
- "Comedy"
- "Drama"
- Which translates to
+ With merged tags, it could translate to
.. code-block:: xml
-
Sweet youtube TV show
+ Sweet youtube TV show
Comedy
Drama
+ Some Playlist
+ Another Playlist
"""
return self._tags
@@ -93,17 +87,31 @@ class OutputDirectoryNfoTagsPlugin(SharedNfoTagsPlugin):
enhanced_download_archive: EnhancedDownloadArchive,
):
super().__init__(plugin_options, overrides, enhanced_download_archive)
+ self._nfo_tags: Dict[str, List[XmlElement]] = {}
self._last_entry: Optional[Entry] = None
def post_process_entry(self, entry: Entry) -> None:
"""
- Tracks the last entry processed
+ Merges the nfo_tags dict of all entries. Tracks the last entry processed
"""
+ nfo_tags = self._get_xml_element_dict(entry=entry)
+ for key, xml_elements in nfo_tags.items():
+ # Key not in nfo_tags, add and continue
+ if key not in self._nfo_tags:
+ self._nfo_tags[key] = xml_elements
+ continue
+
+ # Is in nfo_tags, only add new ones
+ for xml_element in xml_elements:
+ if xml_element not in self._nfo_tags[key]:
+ self._nfo_tags[key].append(xml_element)
+
self._last_entry = entry
def post_process_subscription(self):
"""
- Creates an NFO file in the root of the output directory using the last entry
+ Creates an NFO file in the root of the output directory using merged nfo tags and last entry
+ for the NFO root
"""
if self._last_entry:
- self._create_nfo(entry=self._last_entry, save_to_entry=False)
+ self._create_nfo(entry=self._last_entry, nfo_tags=self._nfo_tags, save_to_entry=False)
diff --git a/tests/e2e/plugins/test_nfo_tags.py b/tests/e2e/plugins/test_nfo_tags.py
index 4c95122c..16f9dbbd 100644
--- a/tests/e2e/plugins/test_nfo_tags.py
+++ b/tests/e2e/plugins/test_nfo_tags.py
@@ -2,7 +2,6 @@ import pytest
from e2e.expected_transaction_log import assert_transaction_log_matches
from ytdl_sub.subscriptions.subscription import Subscription
-from ytdl_sub.utils.exceptions import ValidationException
@pytest.fixture
@@ -61,6 +60,33 @@ def subscription_dict(output_directory):
}
+@pytest.fixture
+def merged_output_nfo_preset_dict(output_directory):
+ return {
+ "preset": "yt_music_video_playlist",
+ "youtube": {"playlist_url": "https://youtube.com/playlist?list=PL5BC0FC26BECA5A35"},
+ # override the output directory with our fixture-generated dir
+ "output_options": {"output_directory": output_directory},
+ # download the worst format so it is fast
+ "ytdl_options": {
+ "format": "worst[ext=mp4]",
+ },
+ "output_directory_nfo_tags": {
+ "nfo_name": "tvshow.nfo",
+ "nfo_root": "tvshow",
+ "tags": {
+ "title": "Test Title",
+ "namedseason": [{"tag": "{title}", "attributes": {"number": "{playlist_index}"}}],
+ "genre": [
+ "Comedy",
+ "Drama",
+ ],
+ },
+ },
+ "overrides": {"artist": "JMC"},
+ }
+
+
class TestNfoTagsPlugins:
@pytest.mark.parametrize("kodi_safe", [True, False])
def test_nfo_tags(self, subscription_dict, music_video_config, output_directory, kodi_safe):
@@ -83,3 +109,21 @@ class TestNfoTagsPlugins:
transaction_log=transaction_log,
transaction_log_summary_file_name=f"plugins/nfo_tags/{transaction_log_file_name}",
)
+
+ def test_merged_output_nfo_tags(
+ self, merged_output_nfo_preset_dict, music_video_config, output_directory
+ ):
+ subscription = Subscription.from_dict(
+ config=music_video_config,
+ preset_name="merged_output_nfo_tags",
+ preset_dict=merged_output_nfo_preset_dict,
+ )
+
+ # Only dry run is needed to see if NFO values are kodi safe
+ transaction_log = subscription.download(dry_run=True)
+ assert_transaction_log_matches(
+ output_directory=output_directory,
+ transaction_log=transaction_log,
+ transaction_log_summary_file_name=f"plugins/nfo_tags/merged_output_nfo_tags.txt",
+ regenerate_transaction_log=True,
+ )
diff --git a/tests/e2e/resources/transaction_log_summaries/plugins/nfo_tags/merged_output_nfo_tags.txt b/tests/e2e/resources/transaction_log_summaries/plugins/nfo_tags/merged_output_nfo_tags.txt
new file mode 100644
index 00000000..813ef613
--- /dev/null
+++ b/tests/e2e/resources/transaction_log_summaries/plugins/nfo_tags/merged_output_nfo_tags.txt
@@ -0,0 +1,53 @@
+Files created in '{output_directory}'
+----------------------------------------
+.ytdl-sub-merged_output_nfo_tags-download-archive.json
+JMC - Jesse's Minecraft Server [Trailer - Feb.1]-thumb.jpg
+JMC - Jesse's Minecraft Server [Trailer - Feb.1].info.json
+JMC - Jesse's Minecraft Server [Trailer - Feb.1].mp4
+JMC - Jesse's Minecraft Server [Trailer - Feb.1].nfo
+ NFO tags:
+ musicvideo:
+ album: Music Videos
+ artist: JMC
+ title: Jesse's Minecraft Server [Trailer - Feb.1]
+ year: 2011
+JMC - Jesse's Minecraft Server [Trailer - Feb.27]-thumb.jpg
+JMC - Jesse's Minecraft Server [Trailer - Feb.27].info.json
+JMC - Jesse's Minecraft Server [Trailer - Feb.27].mp4
+JMC - Jesse's Minecraft Server [Trailer - Feb.27].nfo
+ NFO tags:
+ musicvideo:
+ album: Music Videos
+ artist: JMC
+ title: Jesse's Minecraft Server [Trailer - Feb.27]
+ year: 2011
+JMC - Jesse's Minecraft Server [Trailer - Mar.21]-thumb.jpg
+JMC - Jesse's Minecraft Server [Trailer - Mar.21].info.json
+JMC - Jesse's Minecraft Server [Trailer - Mar.21].mp4
+JMC - Jesse's Minecraft Server [Trailer - Mar.21].nfo
+ NFO tags:
+ musicvideo:
+ album: Music Videos
+ artist: JMC
+ title: Jesse's Minecraft Server [Trailer - Mar.21]
+ year: 2011
+tvshow.nfo
+ NFO tags:
+ tvshow:
+ genre:
+ - Comedy
+ - Drama
+ namedseason:
+ -
+ attributes:
+ number: 3
+ tag: Jesse's Minecraft Server [Trailer - Feb.1]
+ -
+ attributes:
+ number: 2
+ tag: Jesse's Minecraft Server [Trailer - Feb.27]
+ -
+ attributes:
+ number: 1
+ tag: Jesse's Minecraft Server [Trailer - Mar.21]
+ title: Test Title
\ No newline at end of file