From 0c73914848dac1ee3a076c76bd1fa07e114eac0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20F=C3=A4hrmann?= Date: Tue, 19 Jul 2022 12:24:26 +0200 Subject: [PATCH] [postprocessor:metadata] implement 'mode: modify' (#2640) --- docs/configuration.rst | 33 +++++++++++++++++++++------- gallery_dl/postprocessor/metadata.py | 20 +++++++++++++++++ test/test_postprocessor.py | 26 +++++++++++++++++++++- 3 files changed, 70 insertions(+), 9 deletions(-) diff --git a/docs/configuration.rst b/docs/configuration.rst index b49fcfd2..d95dc3f7 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -3312,14 +3312,15 @@ Type Default ``"json"`` Description - Select how to write metadata. + Selects how to process metadata. - * ``"json"``: all metadata using `json.dump() + * ``"json"``: write metadata using `json.dump() `_ - * ``"tags"``: ``tags`` separated by newlines - * ``"custom"``: result of applying `metadata.content-format`_ + * ``"tags"``: write ``tags`` separated by newlines + * ``"custom"``: write the result of applying `metadata.content-format`_ to a file's metadata dictionary - * ``"delete"``: remove metadata entries specified in `metadata.fields`_ + * ``"modify"``: add or modify metadata entries + * ``"delete"``: remove metadata entries metadata.filename @@ -3411,11 +3412,27 @@ Description metadata.fields --------------- Type - ``list`` of ``strings`` + * ``list`` of ``strings`` + * ``object`` (`field name` -> `format string`_) Example - ``["blocked", "watching", "status[creator][name]"]`` + * .. code:: json + + ["blocked", "watching", "status[creator][name]"] + + * .. code:: json + + { + "blocked" : "***", + "watching" : "\fE 'yes' if watching else 'no'", + "status[username]": "{status[creator][name]!l}" + } + Description - List of metadata field names to remove for ``"mode": "delete"``. + * ``"mode": "delete"``: + A list of metadata field names to remove. + * ``"mode": "modify"``: + An object with metadata field names mapping to a `format string`_ + whose result is assigned to said field name. metadata.content-format diff --git a/gallery_dl/postprocessor/metadata.py b/gallery_dl/postprocessor/metadata.py index 61ee4527..a242c941 100644 --- a/gallery_dl/postprocessor/metadata.py +++ b/gallery_dl/postprocessor/metadata.py @@ -30,6 +30,13 @@ class MetadataPP(PostProcessor): elif mode == "tags": self.write = self._write_tags ext = "txt" + elif mode == "modify": + self.run = self._run_modify + self.fields = { + name: formatter.parse(value, None, util.identity).format_map + for name, value in options.get("fields").items() + } + ext = None elif mode == "delete": self.run = self._run_delete self.fields = options.get("fields") @@ -118,6 +125,19 @@ class MetadataPP(PostProcessor): def _run_stdout(self, pathfmt): self.write(sys.stdout, pathfmt.kwdict) + def _run_modify(self, pathfmt): + kwdict = pathfmt.kwdict + for key, func in self.fields.items(): + obj = kwdict + try: + while "[" in key: + name, _, key = key.partition("[") + obj = obj[name] + key = key.rstrip("]") + obj[key] = func(kwdict) + except Exception: + pass + def _run_delete(self, pathfmt): kwdict = pathfmt.kwdict for key in self.fields: diff --git a/test/test_postprocessor.py b/test/test_postprocessor.py index 7f948490..42babd30 100644 --- a/test/test_postprocessor.py +++ b/test/test_postprocessor.py @@ -339,15 +339,39 @@ class MetadataTest(BasePostprocessorTest): {"category": "test", "extension": "ext", "filename": "file"} """) + def test_metadata_modify(self): + kwdict = {"foo": 0, "bar": {"bax": 1, "bay": 2, "baz": 3}} + self._create({ + "mode": "modify", + "fields": { + "foo" : "{filename}-{foo!s}", + "foo2" : "\fE bar['bax'] + 122", + "bar[baz]": "{_now}", + "bar[ba2]": "test", + }, + }, kwdict) + pdict = self.pathfmt.kwdict + + self.assertIsNot(kwdict, pdict) + self.assertEqual(pdict["foo"], kwdict["foo"]) + self.assertEqual(pdict["bar"], kwdict["bar"]) + + self._trigger() + + self.assertEqual(pdict["foo"] , "file-0") + self.assertEqual(pdict["foo2"] , 123) + self.assertEqual(pdict["bar"]["ba2"], "test") + self.assertIsInstance(pdict["bar"]["baz"], datetime) + def test_metadata_delete(self): kwdict = {"foo": 0, "bar": {"bax": 1, "bay": 2, "baz": 3}} self._create({"mode": "delete", "fields": ["foo", "bar[baz]"]}, kwdict) pdict = self.pathfmt.kwdict + self.assertIsNot(kwdict, pdict) self.assertEqual(pdict["foo"], kwdict["foo"]) self.assertEqual(pdict["bar"], kwdict["bar"]) - self.assertIsNot(kwdict, pdict) del kwdict["foo"] del kwdict["bar"]["baz"]