videopython 0.25.2__tar.gz → 0.25.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {videopython-0.25.2 → videopython-0.25.4}/.gitignore +3 -0
- {videopython-0.25.2 → videopython-0.25.4}/PKG-INFO +4 -3
- {videopython-0.25.2 → videopython-0.25.4}/README.md +1 -1
- {videopython-0.25.2 → videopython-0.25.4}/pyproject.toml +3 -2
- {videopython-0.25.2 → videopython-0.25.4}/src/videopython/editing/__init__.py +2 -0
- videopython-0.25.4/src/videopython/editing/premiere_xml.py +313 -0
- {videopython-0.25.2 → videopython-0.25.4}/LICENSE +0 -0
- {videopython-0.25.2 → videopython-0.25.4}/src/videopython/__init__.py +0 -0
- {videopython-0.25.2 → videopython-0.25.4}/src/videopython/ai/__init__.py +0 -0
- {videopython-0.25.2 → videopython-0.25.4}/src/videopython/ai/_device.py +0 -0
- {videopython-0.25.2 → videopython-0.25.4}/src/videopython/ai/dubbing/__init__.py +0 -0
- {videopython-0.25.2 → videopython-0.25.4}/src/videopython/ai/dubbing/dubber.py +0 -0
- {videopython-0.25.2 → videopython-0.25.4}/src/videopython/ai/dubbing/models.py +0 -0
- {videopython-0.25.2 → videopython-0.25.4}/src/videopython/ai/dubbing/pipeline.py +0 -0
- {videopython-0.25.2 → videopython-0.25.4}/src/videopython/ai/dubbing/timing.py +0 -0
- {videopython-0.25.2 → videopython-0.25.4}/src/videopython/ai/generation/__init__.py +0 -0
- {videopython-0.25.2 → videopython-0.25.4}/src/videopython/ai/generation/audio.py +0 -0
- {videopython-0.25.2 → videopython-0.25.4}/src/videopython/ai/generation/image.py +0 -0
- {videopython-0.25.2 → videopython-0.25.4}/src/videopython/ai/generation/translation.py +0 -0
- {videopython-0.25.2 → videopython-0.25.4}/src/videopython/ai/generation/video.py +0 -0
- {videopython-0.25.2 → videopython-0.25.4}/src/videopython/ai/registry.py +0 -0
- {videopython-0.25.2 → videopython-0.25.4}/src/videopython/ai/swapping/__init__.py +0 -0
- {videopython-0.25.2 → videopython-0.25.4}/src/videopython/ai/swapping/inpainter.py +0 -0
- {videopython-0.25.2 → videopython-0.25.4}/src/videopython/ai/swapping/models.py +0 -0
- {videopython-0.25.2 → videopython-0.25.4}/src/videopython/ai/swapping/segmenter.py +0 -0
- {videopython-0.25.2 → videopython-0.25.4}/src/videopython/ai/swapping/swapper.py +0 -0
- {videopython-0.25.2 → videopython-0.25.4}/src/videopython/ai/transforms.py +0 -0
- {videopython-0.25.2 → videopython-0.25.4}/src/videopython/ai/understanding/__init__.py +0 -0
- {videopython-0.25.2 → videopython-0.25.4}/src/videopython/ai/understanding/audio.py +0 -0
- {videopython-0.25.2 → videopython-0.25.4}/src/videopython/ai/understanding/image.py +0 -0
- {videopython-0.25.2 → videopython-0.25.4}/src/videopython/ai/understanding/separation.py +0 -0
- {videopython-0.25.2 → videopython-0.25.4}/src/videopython/ai/understanding/temporal.py +0 -0
- {videopython-0.25.2 → videopython-0.25.4}/src/videopython/ai/video_analysis.py +0 -0
- {videopython-0.25.2 → videopython-0.25.4}/src/videopython/base/__init__.py +0 -0
- {videopython-0.25.2 → videopython-0.25.4}/src/videopython/base/audio/__init__.py +0 -0
- {videopython-0.25.2 → videopython-0.25.4}/src/videopython/base/audio/analysis.py +0 -0
- {videopython-0.25.2 → videopython-0.25.4}/src/videopython/base/audio/audio.py +0 -0
- {videopython-0.25.2 → videopython-0.25.4}/src/videopython/base/combine.py +0 -0
- {videopython-0.25.2 → videopython-0.25.4}/src/videopython/base/description.py +0 -0
- {videopython-0.25.2 → videopython-0.25.4}/src/videopython/base/effects.py +0 -0
- {videopython-0.25.2 → videopython-0.25.4}/src/videopython/base/exceptions.py +0 -0
- {videopython-0.25.2 → videopython-0.25.4}/src/videopython/base/progress.py +0 -0
- {videopython-0.25.2 → videopython-0.25.4}/src/videopython/base/registry.py +0 -0
- {videopython-0.25.2 → videopython-0.25.4}/src/videopython/base/scene.py +0 -0
- {videopython-0.25.2 → videopython-0.25.4}/src/videopython/base/text/__init__.py +0 -0
- {videopython-0.25.2 → videopython-0.25.4}/src/videopython/base/text/overlay.py +0 -0
- {videopython-0.25.2 → videopython-0.25.4}/src/videopython/base/text/transcription.py +0 -0
- {videopython-0.25.2 → videopython-0.25.4}/src/videopython/base/transforms.py +0 -0
- {videopython-0.25.2 → videopython-0.25.4}/src/videopython/base/transitions.py +0 -0
- {videopython-0.25.2 → videopython-0.25.4}/src/videopython/base/utils.py +0 -0
- {videopython-0.25.2 → videopython-0.25.4}/src/videopython/base/video.py +0 -0
- {videopython-0.25.2 → videopython-0.25.4}/src/videopython/editing/multicam.py +0 -0
- {videopython-0.25.2 → videopython-0.25.4}/src/videopython/editing/video_edit.py +0 -0
- {videopython-0.25.2 → videopython-0.25.4}/src/videopython/py.typed +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: videopython
|
|
3
|
-
Version: 0.25.
|
|
3
|
+
Version: 0.25.4
|
|
4
4
|
Summary: Minimal video generation and processing library.
|
|
5
5
|
Project-URL: Homepage, https://videopython.com
|
|
6
6
|
Project-URL: Repository, https://github.com/bartwojtowicz/videopython/
|
|
@@ -15,7 +15,8 @@ Classifier: Programming Language :: Python :: 3
|
|
|
15
15
|
Classifier: Programming Language :: Python :: 3.10
|
|
16
16
|
Classifier: Programming Language :: Python :: 3.11
|
|
17
17
|
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
-
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Requires-Python: <3.14,>=3.10
|
|
19
20
|
Requires-Dist: numpy>=1.25.2
|
|
20
21
|
Requires-Dist: opencv-python-headless>=4.9.0.80
|
|
21
22
|
Requires-Dist: pillow>=12.1.1
|
|
@@ -81,7 +82,7 @@ pip install videopython # core video/audio editing
|
|
|
81
82
|
pip install "videopython[ai]" # + local AI features (GPU recommended)
|
|
82
83
|
```
|
|
83
84
|
|
|
84
|
-
Python `>=3.10, <3.
|
|
85
|
+
Python `>=3.10, <3.14`. AI features run locally - no cloud API keys required, but model weights are downloaded on first use.
|
|
85
86
|
|
|
86
87
|
## Quick Start
|
|
87
88
|
|
|
@@ -30,7 +30,7 @@ pip install videopython # core video/audio editing
|
|
|
30
30
|
pip install "videopython[ai]" # + local AI features (GPU recommended)
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
-
Python `>=3.10, <3.
|
|
33
|
+
Python `>=3.10, <3.14`. AI features run locally - no cloud API keys required, but model weights are downloaded on first use.
|
|
34
34
|
|
|
35
35
|
## Quick Start
|
|
36
36
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "videopython"
|
|
3
|
-
version = "0.25.
|
|
3
|
+
version = "0.25.4"
|
|
4
4
|
description = "Minimal video generation and processing library."
|
|
5
5
|
authors = [
|
|
6
6
|
{ name = "Bartosz Wójtowicz", email = "bartoszwojtowicz@outlook.com" },
|
|
@@ -9,7 +9,7 @@ authors = [
|
|
|
9
9
|
]
|
|
10
10
|
license = { text = "Apache-2.0" }
|
|
11
11
|
readme = "README.md"
|
|
12
|
-
requires-python = ">=3.10, <3.
|
|
12
|
+
requires-python = ">=3.10, <3.14"
|
|
13
13
|
keywords = [
|
|
14
14
|
"python",
|
|
15
15
|
"videopython",
|
|
@@ -27,6 +27,7 @@ classifiers = [
|
|
|
27
27
|
"Programming Language :: Python :: 3.10",
|
|
28
28
|
"Programming Language :: Python :: 3.11",
|
|
29
29
|
"Programming Language :: Python :: 3.12",
|
|
30
|
+
"Programming Language :: Python :: 3.13",
|
|
30
31
|
"Operating System :: OS Independent",
|
|
31
32
|
]
|
|
32
33
|
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
"""Export MultiCamEdit plans to FCP7 XML (xmeml) for Adobe Premiere Pro.
|
|
2
|
+
|
|
3
|
+
Generates xmeml version 5 with a flat sequence of clipitems on one video
|
|
4
|
+
track and stereo audio tracks. Each cut becomes a clipitem that directly
|
|
5
|
+
references its source file.
|
|
6
|
+
|
|
7
|
+
Known limitations:
|
|
8
|
+
- BlurTransition has no xmeml equivalent and is exported as a hard cut.
|
|
9
|
+
- Source file paths are absolute file://localhost/ URLs; the xmeml is not
|
|
10
|
+
portable across machines without relinking media in Premiere.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
from typing import TYPE_CHECKING
|
|
17
|
+
from urllib.parse import quote
|
|
18
|
+
from xml.dom.minidom import parseString
|
|
19
|
+
from xml.etree.ElementTree import Element, SubElement, tostring
|
|
20
|
+
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
from videopython.editing.multicam import MultiCamEdit
|
|
23
|
+
|
|
24
|
+
__all__ = ["to_premiere_xml"]
|
|
25
|
+
|
|
26
|
+
_XMEML_VERSION = "5"
|
|
27
|
+
|
|
28
|
+
# Integer timebase for common NTSC rates (xmeml uses <timebase>/<ntsc>).
|
|
29
|
+
_NTSC_TIMEBASES: dict[float, int] = {
|
|
30
|
+
23.976: 24,
|
|
31
|
+
23.98: 24,
|
|
32
|
+
29.97: 30,
|
|
33
|
+
59.94: 60,
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
# ---------------------------------------------------------------------------
|
|
38
|
+
# Time and path helpers
|
|
39
|
+
# ---------------------------------------------------------------------------
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _fps_to_rate_info(fps: float) -> tuple[int, bool]:
|
|
43
|
+
"""Return ``(timebase, ntsc)`` for a frame rate."""
|
|
44
|
+
rounded = round(fps, 3)
|
|
45
|
+
if rounded in _NTSC_TIMEBASES:
|
|
46
|
+
return _NTSC_TIMEBASES[rounded], True
|
|
47
|
+
return round(fps), False
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _seconds_to_frames(seconds: float, fps: float) -> int:
|
|
51
|
+
"""Convert seconds to an integer frame count."""
|
|
52
|
+
timebase, ntsc = _fps_to_rate_info(fps)
|
|
53
|
+
actual_fps = timebase / 1.001 if ntsc else timebase
|
|
54
|
+
return round(seconds * actual_fps)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _path_to_url(path: Path) -> str:
|
|
58
|
+
"""Convert a local path to a ``file://localhost/...`` URL."""
|
|
59
|
+
absolute = str(path.resolve())
|
|
60
|
+
if not absolute.startswith("/"):
|
|
61
|
+
absolute = "/" + absolute
|
|
62
|
+
return "file://localhost" + quote(absolute)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _serialize(root: Element, doctype: str) -> str:
|
|
66
|
+
"""Pretty-print an ElementTree *root* with XML declaration and DOCTYPE."""
|
|
67
|
+
rough = tostring(root, encoding="unicode", xml_declaration=False)
|
|
68
|
+
dom = parseString(rough)
|
|
69
|
+
lines = dom.toprettyxml(indent=" ", encoding="utf-8").decode("utf-8").splitlines(True)
|
|
70
|
+
return lines[0] + doctype + "\n" + "".join(lines[1:])
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
# ---------------------------------------------------------------------------
|
|
74
|
+
# Element builders
|
|
75
|
+
# ---------------------------------------------------------------------------
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _add_rate(parent: Element, fps: float) -> Element:
|
|
79
|
+
timebase, ntsc = _fps_to_rate_info(fps)
|
|
80
|
+
rate = SubElement(parent, "rate")
|
|
81
|
+
SubElement(rate, "timebase").text = str(timebase)
|
|
82
|
+
SubElement(rate, "ntsc").text = "TRUE" if ntsc else "FALSE"
|
|
83
|
+
return rate
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _add_text(parent: Element, tag: str, text: str) -> Element:
|
|
87
|
+
el = SubElement(parent, tag)
|
|
88
|
+
el.text = text
|
|
89
|
+
return el
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _build_file_element(
|
|
93
|
+
parent: Element,
|
|
94
|
+
file_id: str,
|
|
95
|
+
name: str,
|
|
96
|
+
path: Path,
|
|
97
|
+
fps: float,
|
|
98
|
+
duration_frames: int,
|
|
99
|
+
width: int | None = None,
|
|
100
|
+
height: int | None = None,
|
|
101
|
+
has_video: bool = True,
|
|
102
|
+
has_audio: bool = True,
|
|
103
|
+
) -> Element:
|
|
104
|
+
"""Build a full ``<file>`` definition (first occurrence)."""
|
|
105
|
+
f = SubElement(parent, "file", id=file_id)
|
|
106
|
+
_add_text(f, "name", name)
|
|
107
|
+
_add_text(f, "pathurl", _path_to_url(path))
|
|
108
|
+
_add_rate(f, fps)
|
|
109
|
+
_add_text(f, "duration", str(duration_frames))
|
|
110
|
+
|
|
111
|
+
tc = SubElement(f, "timecode")
|
|
112
|
+
_add_text(tc, "string", "00:00:00:00")
|
|
113
|
+
_add_text(tc, "frame", "0")
|
|
114
|
+
_add_text(tc, "displayformat", "NDF")
|
|
115
|
+
_add_rate(tc, fps)
|
|
116
|
+
|
|
117
|
+
media = SubElement(f, "media")
|
|
118
|
+
if has_video:
|
|
119
|
+
vid = SubElement(media, "video")
|
|
120
|
+
_add_text(vid, "duration", str(duration_frames))
|
|
121
|
+
sc = SubElement(vid, "samplecharacteristics")
|
|
122
|
+
_add_text(sc, "width", str(width))
|
|
123
|
+
_add_text(sc, "height", str(height))
|
|
124
|
+
if has_audio:
|
|
125
|
+
aud = SubElement(media, "audio")
|
|
126
|
+
asc = SubElement(aud, "samplecharacteristics")
|
|
127
|
+
_add_text(asc, "depth", "16")
|
|
128
|
+
_add_text(asc, "samplerate", "48000")
|
|
129
|
+
_add_text(aud, "channelcount", "2")
|
|
130
|
+
|
|
131
|
+
return f
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def _build_video_transition(parent: Element, fps: float, start_frame: int, end_frame: int) -> Element:
|
|
135
|
+
ti = SubElement(parent, "transitionitem")
|
|
136
|
+
_add_rate(ti, fps)
|
|
137
|
+
_add_text(ti, "start", str(start_frame))
|
|
138
|
+
_add_text(ti, "end", str(end_frame))
|
|
139
|
+
_add_text(ti, "alignment", "center")
|
|
140
|
+
|
|
141
|
+
effect = SubElement(ti, "effect")
|
|
142
|
+
_add_text(effect, "name", "Cross Dissolve")
|
|
143
|
+
_add_text(effect, "effectid", "Cross Dissolve")
|
|
144
|
+
_add_text(effect, "effectcategory", "Dissolve")
|
|
145
|
+
_add_text(effect, "effecttype", "transition")
|
|
146
|
+
_add_text(effect, "mediatype", "video")
|
|
147
|
+
_add_text(effect, "wipecode", "0")
|
|
148
|
+
_add_text(effect, "wipeaccuracy", "100")
|
|
149
|
+
_add_text(effect, "startratio", "0")
|
|
150
|
+
_add_text(effect, "endratio", "1")
|
|
151
|
+
_add_text(effect, "reverse", "FALSE")
|
|
152
|
+
return ti
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
# ---------------------------------------------------------------------------
|
|
156
|
+
# Main export function
|
|
157
|
+
# ---------------------------------------------------------------------------
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def to_premiere_xml(edit: MultiCamEdit) -> str:
|
|
161
|
+
"""Export a *MultiCamEdit* plan to FCP7 XML (xmeml) for Premiere Pro.
|
|
162
|
+
|
|
163
|
+
Generates a flat sequence with one video track containing clipitems
|
|
164
|
+
that directly reference source files, plus stereo audio tracks for
|
|
165
|
+
the external audio source.
|
|
166
|
+
|
|
167
|
+
Args:
|
|
168
|
+
edit: A validated ``MultiCamEdit`` instance.
|
|
169
|
+
|
|
170
|
+
Returns:
|
|
171
|
+
A UTF-8 XML string with ``<?xml ...?>`` declaration and
|
|
172
|
+
``<!DOCTYPE xmeml>``.
|
|
173
|
+
"""
|
|
174
|
+
from videopython.base.transitions import FadeTransition
|
|
175
|
+
|
|
176
|
+
meta = edit._source_meta
|
|
177
|
+
fps = meta.fps
|
|
178
|
+
source_duration = edit._source_duration
|
|
179
|
+
total_frames = _seconds_to_frames(source_duration, fps)
|
|
180
|
+
|
|
181
|
+
def frames(s: float) -> int:
|
|
182
|
+
return _seconds_to_frames(s, fps)
|
|
183
|
+
|
|
184
|
+
root = Element("xmeml", version=_XMEML_VERSION)
|
|
185
|
+
|
|
186
|
+
# -- sequence --------------------------------------------------------------
|
|
187
|
+
seq = SubElement(root, "sequence", id="MultiCamEdit")
|
|
188
|
+
_add_text(seq, "name", "MultiCamEdit")
|
|
189
|
+
_add_text(seq, "duration", str(total_frames))
|
|
190
|
+
_add_rate(seq, fps)
|
|
191
|
+
|
|
192
|
+
tc = SubElement(seq, "timecode")
|
|
193
|
+
_add_text(tc, "string", "00:00:00:00")
|
|
194
|
+
_add_text(tc, "frame", "0")
|
|
195
|
+
_add_text(tc, "displayformat", "NDF")
|
|
196
|
+
_add_rate(tc, fps)
|
|
197
|
+
|
|
198
|
+
media = SubElement(seq, "media")
|
|
199
|
+
|
|
200
|
+
# -- video -----------------------------------------------------------------
|
|
201
|
+
video = SubElement(media, "video")
|
|
202
|
+
fmt = SubElement(video, "format")
|
|
203
|
+
sc = SubElement(fmt, "samplecharacteristics")
|
|
204
|
+
_add_text(sc, "width", str(meta.width))
|
|
205
|
+
_add_text(sc, "height", str(meta.height))
|
|
206
|
+
_add_text(sc, "anamorphic", "FALSE")
|
|
207
|
+
_add_text(sc, "pixelaspectratio", "square")
|
|
208
|
+
_add_text(sc, "fielddominance", "none")
|
|
209
|
+
_add_rate(sc, fps)
|
|
210
|
+
_add_text(sc, "colordepth", "24")
|
|
211
|
+
|
|
212
|
+
v_track = SubElement(video, "track")
|
|
213
|
+
defined_file_ids: set[str] = set()
|
|
214
|
+
cuts = edit.cuts
|
|
215
|
+
|
|
216
|
+
for i, cut in enumerate(cuts):
|
|
217
|
+
start_time = cut.time
|
|
218
|
+
end_time = cuts[i + 1].time if i + 1 < len(cuts) else source_duration
|
|
219
|
+
start_frame = frames(start_time)
|
|
220
|
+
end_frame = frames(end_time)
|
|
221
|
+
|
|
222
|
+
camera = cut.camera
|
|
223
|
+
offset = edit.source_offsets.get(camera, 0.0)
|
|
224
|
+
in_frame = frames(start_time - offset)
|
|
225
|
+
out_frame = frames(end_time - offset)
|
|
226
|
+
|
|
227
|
+
# Transition
|
|
228
|
+
if i > 0:
|
|
229
|
+
transition = cut.transition or edit.default_transition
|
|
230
|
+
effect_time = getattr(transition, "effect_time_seconds", 0.0)
|
|
231
|
+
if isinstance(transition, FadeTransition) and effect_time > 0:
|
|
232
|
+
t_half = effect_time / 2
|
|
233
|
+
_build_video_transition(v_track, fps, frames(start_time - t_half), frames(start_time + t_half))
|
|
234
|
+
|
|
235
|
+
ci = SubElement(v_track, "clipitem", id=f"clipitem-v-{i + 1}")
|
|
236
|
+
_add_text(ci, "name", camera)
|
|
237
|
+
_add_text(ci, "duration", str(total_frames))
|
|
238
|
+
_add_rate(ci, fps)
|
|
239
|
+
_add_text(ci, "in", str(in_frame))
|
|
240
|
+
_add_text(ci, "out", str(out_frame))
|
|
241
|
+
_add_text(ci, "start", str(start_frame))
|
|
242
|
+
_add_text(ci, "end", str(end_frame))
|
|
243
|
+
_add_text(ci, "enabled", "TRUE")
|
|
244
|
+
|
|
245
|
+
file_id = f"file-{camera}"
|
|
246
|
+
if file_id not in defined_file_ids:
|
|
247
|
+
src_meta = edit._source_metas[camera]
|
|
248
|
+
src_dur_frames = _seconds_to_frames(src_meta.total_seconds, fps)
|
|
249
|
+
_build_file_element(
|
|
250
|
+
ci,
|
|
251
|
+
file_id,
|
|
252
|
+
edit.sources[camera].name,
|
|
253
|
+
edit.sources[camera],
|
|
254
|
+
fps,
|
|
255
|
+
src_dur_frames,
|
|
256
|
+
width=src_meta.width,
|
|
257
|
+
height=src_meta.height,
|
|
258
|
+
)
|
|
259
|
+
defined_file_ids.add(file_id)
|
|
260
|
+
else:
|
|
261
|
+
SubElement(ci, "file", id=file_id)
|
|
262
|
+
|
|
263
|
+
# -- audio -----------------------------------------------------------------
|
|
264
|
+
if edit.audio_source:
|
|
265
|
+
audio = SubElement(media, "audio")
|
|
266
|
+
afmt = SubElement(audio, "format")
|
|
267
|
+
asc = SubElement(afmt, "samplecharacteristics")
|
|
268
|
+
_add_text(asc, "depth", "16")
|
|
269
|
+
_add_text(asc, "samplerate", "48000")
|
|
270
|
+
|
|
271
|
+
outputs = SubElement(audio, "outputs")
|
|
272
|
+
group = SubElement(outputs, "group")
|
|
273
|
+
_add_text(group, "index", "1")
|
|
274
|
+
_add_text(group, "numchannels", "2")
|
|
275
|
+
_add_text(group, "downmix", "0")
|
|
276
|
+
for ch in (1, 2):
|
|
277
|
+
channel = SubElement(group, "channel")
|
|
278
|
+
_add_text(channel, "index", str(ch))
|
|
279
|
+
|
|
280
|
+
audio_file_id = "file-audio"
|
|
281
|
+
|
|
282
|
+
for track_idx in (1, 2):
|
|
283
|
+
a_track = SubElement(audio, "track")
|
|
284
|
+
|
|
285
|
+
aci = SubElement(a_track, "clipitem", id=f"clipitem-a{track_idx}")
|
|
286
|
+
_add_text(aci, "name", "Audio")
|
|
287
|
+
_add_text(aci, "duration", str(total_frames))
|
|
288
|
+
_add_rate(aci, fps)
|
|
289
|
+
_add_text(aci, "in", "0")
|
|
290
|
+
_add_text(aci, "out", str(total_frames))
|
|
291
|
+
_add_text(aci, "start", "0")
|
|
292
|
+
_add_text(aci, "end", str(total_frames))
|
|
293
|
+
_add_text(aci, "enabled", "TRUE")
|
|
294
|
+
|
|
295
|
+
if audio_file_id not in defined_file_ids:
|
|
296
|
+
_build_file_element(
|
|
297
|
+
aci,
|
|
298
|
+
audio_file_id,
|
|
299
|
+
edit.audio_source.name,
|
|
300
|
+
edit.audio_source,
|
|
301
|
+
fps,
|
|
302
|
+
total_frames,
|
|
303
|
+
has_video=False,
|
|
304
|
+
)
|
|
305
|
+
defined_file_ids.add(audio_file_id)
|
|
306
|
+
else:
|
|
307
|
+
SubElement(aci, "file", id=audio_file_id)
|
|
308
|
+
|
|
309
|
+
st = SubElement(aci, "sourcetrack")
|
|
310
|
+
_add_text(st, "mediatype", "audio")
|
|
311
|
+
_add_text(st, "trackindex", str(track_idx))
|
|
312
|
+
|
|
313
|
+
return _serialize(root, "<!DOCTYPE xmeml>")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|