]>
jfr.im git - yt-dlp.git/blob - yt_dlp/postprocessor/metadataparser.py
4 from .common
import PostProcessor
7 class MetadataParserPP(PostProcessor
):
9 INTERPRET
= 'interpretter'
12 def __init__(self
, downloader
, actions
):
13 PostProcessor
.__init
__(self
, downloader
)
17 assert isinstance(action
, self
.Actions
)
18 self
._actions
.append(getattr(self
, action
.value
)(*f
[1:]))
21 def validate_action(cls
, action
, *data
):
22 ''' Each action can be:
23 (Actions.INTERPRET, from, to) OR
24 (Actions.REPLACE, field, search, replace)
26 if not isinstance(action
, cls
.Actions
):
27 raise ValueError(f
'{action!r} is not a valid action')
28 getattr(cls
, action
.value
)(cls
, *data
) # So this can raise error to validate
31 def field_to_template(tmpl
):
32 if re
.match(r
'[a-zA-Z_]+$', tmpl
):
35 from ..YoutubeDL
import YoutubeDL
36 err
= YoutubeDL
.validate_outtmpl(tmpl
)
42 def format_to_regex(fmt
):
44 Converts a string like
45 '%(title)s - %(artist)s'
47 '(?P<title>.+)\ \-\ (?P<artist>.+)'
49 if not re
.search(r
'%\(\w+\)s', fmt
):
53 # replace %(..)s with regex group and escape other string parts
54 for match
in re
.finditer(r
'%\((\w+)\)s', fmt
):
55 regex
+= re
.escape(fmt
[lastpos
:match
.start()])
56 regex
+= rf
'(?P<{match.group(1)}>.+)'
58 if lastpos
< len(fmt
):
59 regex
+= re
.escape(fmt
[lastpos
:])
63 for f
in self
._actions
:
67 def interpretter(self
, inp
, out
):
69 data_to_parse
= self
._downloader
.evaluate_outtmpl(template
, info
)
70 self
.write_debug(f
'Searching for {out_re.pattern!r} in {template!r}')
71 match
= out_re
.search(data_to_parse
)
73 self
.to_screen(f
'Could not interpret {inp!r} as {out!r}')
75 for attribute
, value
in match
.groupdict().items():
76 info
[attribute
] = value
77 self
.to_screen('Parsed %s from %r: %r' % (attribute
, template
, value
if value
is not None else 'NA'))
79 template
= self
.field_to_template(inp
)
80 out_re
= re
.compile(self
.format_to_regex(out
))
83 def replacer(self
, field
, search
, replace
):
87 self
.to_screen(f
'Video does not have a {field}')
89 elif not isinstance(val
, str):
90 self
.report_warning(f
'Cannot replace in field {field} since it is a {type(val).__name__}')
92 self
.write_debug(f
'Replacing all {search!r} in {field} with {replace!r}')
93 info
[field
], n
= search_re
.subn(replace
, val
)
95 self
.to_screen(f
'Changed {field} to: {info[field]}')
97 self
.to_screen(f
'Did not find {search!r} in {field}')
99 search_re
= re
.compile(search
)
103 class MetadataFromFieldPP(MetadataParserPP
):
105 def to_action(cls
, f
):
106 match
= re
.match(r
'(?s)(?P<in>.*?)(?<!\\):(?P<out>.+)$', f
)
108 raise ValueError(f
'it should be FROM:TO, not {f!r}')
110 cls
.Actions
.INTERPRET
,
111 match
.group('in').replace('\\:', ':'),
115 def __init__(self
, downloader
, formats
):
116 super().__init
__(downloader
, [self
.to_action(f
) for f
in formats
])
120 class MetadataFromTitlePP(MetadataParserPP
):
121 def __init__(self
, downloader
, titleformat
):
122 super().__init
__(downloader
, [(self
.Actions
.INTERPRET
, 'title', titleformat
)])
123 self
.deprecation_warning(
124 'yt_dlp.postprocessor.MetadataFromTitlePP is deprecated '
125 'and may be removed in a future version. Use yt_dlp.postprocessor.MetadataFromFieldPP instead')