forked from tobbegraf/kdenlive-slideshow-generator
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathksg.py
executable file
·284 lines (225 loc) · 7.77 KB
/
ksg.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
# kdenlive slideshow generator
# 2023 Tobias Philipp
# GPL V3
#
# usage: ksg.py projectfile.kdenlive
# you need a prepared kdenlive project file:
# all images in a timeline with the desired duration
# and transitions
# run the tool with the file as arugmuent and
# then open the created slideshow.kdenlive file in kdenlive
# for rendering or further editing
#
# effectListZoom/effectListMove:
# you can choose which zoom/move effects are used
# or how often if you add them multiple times to the list
import xml.etree.ElementTree as ET
import random
import sys
# corner from/to zomming in/out
# 1 top left
# 2 top center
# 3 top right
# 4 middle left
# 5 middle center
# 6 middle right
# 7 bottom left
# 8 bottom center
# 9 bottom right
effectListZoom = (1, 2, 3, 4, 5, 6, 7, 8, 9)
# 10 top left to right
# 11 middle left to right
# 12 bottom left to right
# 13 top right to left
# 14 middle right to left
# 15 bottom right to left
effectListMove = (10, 11, 12, 13, 14, 15)
# 10% zooming in or out
zoom = 1.1
if(len(sys.argv) < 2 ):
print ("kdenlive slideshow generator")
print ("usage: ksg.py projekt.kdenlive")
sys.exit()
def makeEffect(inTime, outTime, frameW, frameH, imageW, imageH):
#1 zoom in, 2 zoom out, 3 move
effectType = random.randint(1,3)
effectDirection = random.choice(effectListZoom)
match effectType:
case 1:
startW = imageW
startH = imageH
endW = imageW * zoom
endH = imageH * zoom
case 2:
startW = imageW * zoom
startH = imageH * zoom
endW = imageW
endH = imageH
case 3:
startW = imageW * zoom
startH = imageH * zoom
endW = imageW * zoom
endH = imageH * zoom
effectDirection = random.choice(effectListMove)
match effectDirection:
# top left
case 1:
startX = 0
startY = 0
endX = 0
endY = 0
# top center
case 2:
startX = (frameW - startW) / 2
startY = 0
endX = (frameW - endW) / 2
endY = 0
# top right
case 3:
startX = frameW - startW
startY = 0
endX = frameW - endW
endY = 0
#middle left
case 4:
startX = 0
startY = (frameH - startH) / 2
endX = 0
endY = (frameH - endH) / 2
#middle center
case 5:
startX = (frameW - startW) / 2
startY = (frameH - startH) / 2
endX = (frameW - endW) / 2
endY = (frameH - endH) / 2
#middle right
case 6:
startX = frameW - startW
startY = (frameH - startH) / 2
endX = frameW - endW
endY = (frameH - endH) / 2
#bottom left
case 7:
startX = 0
startY = frameH - startH
endX = 0
endY = frameH - endH
#bottom center
case 8:
startX = (frameW - startW) / 2
startY = frameH - startH
endX = (frameW - endW) / 2
endY = frameH - endH
#bottom right
case 9:
startX = frameW - startW
startY = frameH - startH
endX = frameW - endW
endY = frameH - endH
#move top left to right
case 10:
startX = 0
startY = 0
endX = frameW - endW
endY = 0
#move middle left to right
case 11:
startX = 0
startY = (frameH - startH) / 2
endX = frameW - endW
endY = (frameH - endH) / 2
#move bottom left to right
case 12:
startX = 0
startY = frameH - startH
endX = frameW - endW
endY = frameH - endH
#move top right to left
case 13:
startX = 0
startY = frameW - startW
endX = 0
endY = 0
#move middle right to left
case 14:
startX = frameW - startW
startY = (frameH - startH) / 2
endX = 0
endY = (frameH - endH) / 2
#move bottom right to left
case 15:
startX = frameW - startW
startY = frameH - startH
endX = 0
endY = frameH - endH
return (inTime, round(startX), round(startY), round(startW), round(startH), outTime, round(endX), round(endY), round(endW), round(endH))
########### start of main
tree = ET.parse(sys.argv[1])
root = tree.getroot()
#print(tree)
#print(root)
# for child in root:
# print(child.tag, child.attrib)
# root directory for the kdenlive projekt
rootDir = root.attrib['root']
# video profile directory for the kdenlive projekt
profile = root.find('profile')
# get tearget video dimensions
targetWidth = int(profile.attrib['width'])
targetHeight = int(profile.attrib['height'])
filterCounter = 10
for playlist in root.iterfind('playlist'):
# skip the main_bin playlist
if(playlist.get('id') != 'main_bin'):
for entry in playlist.iterfind('entry'):
entryIn = entry.get('in')
entryOut = entry.get('out')
# find producer element
producer = root.find("./producer[@id='%s']" % entry.get('producer'))
if producer is None:
# entry is not a frame
continue
# get data from the producer element
sourceFile = producer.find("./property/[@name='resource']").text
sourceWidth = int(producer.find("./property/[@name='meta.media.width']").text)
sourceHeight = int(producer.find("./property/[@name='meta.media.height']").text)
# skip entry if it already contains a filter
if entry.find('filter') is not None:
print("skipping %s (a filter already exists)" % (sourceFile))
continue
else:
print ("working on %s" % (sourceFile))
# fill the frame with the image
hRatio = targetWidth / sourceWidth
vRatio = targetHeight / sourceHeight
ratio = max(hRatio, vRatio)
calcWidth = sourceWidth * ratio
calcHeight = sourceHeight * ratio
# add the filter entries
filter = ET.SubElement(entry, 'filter', {'id' : str(filterCounter)})
filterCounter += 1
rotate_center = ET.SubElement(filter, 'property', {'name' : 'rotate_center'})
rotate_center.text = '1';
mlt_service = ET.SubElement(filter, 'property', {'name' : 'mlt_service'})
mlt_service.text = 'qtblend';
kdenlive_id = ET.SubElement(filter, 'property', {'name' : 'kdenlive_id'})
kdenlive_id.text = 'qtblend';
effectData = makeEffect(entryIn, entryOut, targetWidth, targetHeight, calcWidth, calcHeight)
# add the animation
rect = ET.SubElement(filter, 'property', {'name' : 'rect'})
rect.text = "%s=%s %s %s %s 1.000000;%s=%s %s %s %s 1.000000" % effectData;
rotation = ET.SubElement(filter, 'property', {'name' : 'rotation'})
rotation.text = "%s=0;%s=0" % (entryIn, entryOut);
compositing = ET.SubElement(filter, 'property', {'name' : 'compositing'})
compositing.text = '0';
distort = ET.SubElement(filter, 'property', {'name' : 'distort'})
distort.text = '0';
kdenlive_collapsed = ET.SubElement(filter, 'property', {'name' : 'kdenlive:collapsed'})
kdenlive_collapsed.text = '0';
with open('slideshow.kdenlive', 'wb') as f:
tree.write(f, encoding='utf-8', xml_declaration=True)
f.close()
print ("slideshow.kdenlive generated")