]>
jfr.im git - yt-dlp.git/blob - yt_dlp/postprocessor/metadataparser.py
5 from .common
import PostProcessor
8 class MetadataParserPP(PostProcessor
):
10 INTERPRET
= 'interpretter'
13 def __init__(self
, downloader
, actions
):
14 PostProcessor
.__init
__(self
, downloader
)
18 assert isinstance(action
, self
.Actions
)
19 self
._actions
.append(getattr(self
, action
.value
)(*f
[1:]))
22 def validate_action(cls
, action
, *data
):
23 ''' Each action can be:
24 (Actions.INTERPRET, from, to) OR
25 (Actions.REPLACE, field, search, replace)
27 if not isinstance(action
, cls
.Actions
):
28 raise ValueError(f
'{action!r} is not a valid action')
29 getattr(cls
, action
.value
)(cls
, *data
)
32 def field_to_template(tmpl
):
33 if re
.match(r
'[a-zA-Z_]+$', tmpl
):
38 def format_to_regex(fmt
):
40 Converts a string like
41 '%(title)s - %(artist)s'
43 '(?P<title>.+)\ \-\ (?P<artist>.+)'
45 if not re
.search(r
'%\(\w+\)s', fmt
):
49 # replace %(..)s with regex group and escape other string parts
50 for match
in re
.finditer(r
'%\((\w+)\)s', fmt
):
51 regex
+= re
.escape(fmt
[lastpos
:match
.start()])
52 regex
+= rf
'(?P<{match.group(1)}>.+)'
54 if lastpos
< len(fmt
):
55 regex
+= re
.escape(fmt
[lastpos
:])
59 for f
in self
._actions
:
63 def interpretter(self
, inp
, out
):
65 data_to_parse
= self
._downloader
.evaluate_outtmpl(template
, info
)
66 self
.write_debug(f
'Searching for {out_re.pattern!r} in {template!r}')
67 match
= out_re
.search(data_to_parse
)
69 self
.report_warning(f
'Could not interpret {inp!r} as {out!r}')
71 for attribute
, value
in match
.groupdict().items():
72 info
[attribute
] = value
73 self
.to_screen('Parsed %s from %r: %r' % (attribute
, template
, value
if value
is not None else 'NA'))
75 template
= self
.field_to_template(inp
)
76 out_re
= re
.compile(self
.format_to_regex(out
))
79 def replacer(self
, field
, search
, replace
):
83 self
.report_warning(f
'Video does not have a {field}')
85 elif not isinstance(val
, str):
86 self
.report_warning(f
'Cannot replace in field {field} since it is a {type(val).__name__}')
88 self
.write_debug(f
'Replacing all {search!r} in {field} with {replace!r}')
89 info
[field
], n
= search_re
.subn(replace
, val
)
91 self
.to_screen(f
'Changed {field} to: {info[field]}')
93 self
.to_screen(f
'Did not find {search!r} in {field}')
95 search_re
= re
.compile(search
)
99 class MetadataFromFieldPP(MetadataParserPP
):
101 def to_action(cls
, f
):
102 match
= re
.match(r
'(?s)(?P<in>.*?)(?<!\\):(?P<out>.+)$', f
)
104 raise ValueError(f
'it should be FROM:TO, not {f!r}')
106 cls
.Actions
.INTERPRET
,
107 match
.group('in').replace('\\:', ':'),
111 def __init__(self
, downloader
, formats
):
112 super().__init
__(downloader
, [self
.to_action(f
) for f
in formats
])
116 class MetadataFromTitlePP(MetadataParserPP
):
117 def __init__(self
, downloader
, titleformat
):
118 super().__init
__(downloader
, [(self
.Actions
.INTERPRET
, 'title', titleformat
)])
119 self
.deprecation_warning(
120 'yt_dlp.postprocessor.MetadataFromTitlePP is deprecated '
121 'and may be removed in a future version. Use yt_dlp.postprocessor.MetadataFromFieldPP instead')