tictacsync 0.97a0__py3-none-any.whl → 0.98a0__py3-none-any.whl
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.
Potentially problematic release.
This version of tictacsync might be problematic. Click here for more details.
- tictacsync/device_scanner.py +199 -107
- tictacsync/entry.py +177 -75
- tictacsync/remrgmx.py +87 -5
- tictacsync/timeline.py +195 -103
- tictacsync/yaltc.py +17 -20
- {tictacsync-0.97a0.dist-info → tictacsync-0.98a0.dist-info}/METADATA +4 -3
- tictacsync-0.98a0.dist-info/RECORD +16 -0
- tictacsync-0.97a0.dist-info/RECORD +0 -16
- {tictacsync-0.97a0.dist-info → tictacsync-0.98a0.dist-info}/LICENSE +0 -0
- {tictacsync-0.97a0.dist-info → tictacsync-0.98a0.dist-info}/WHEEL +0 -0
- {tictacsync-0.97a0.dist-info → tictacsync-0.98a0.dist-info}/entry_points.txt +0 -0
- {tictacsync-0.97a0.dist-info → tictacsync-0.98a0.dist-info}/top_level.txt +0 -0
tictacsync/entry.py
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
print('Loading modules...', end='')
|
|
2
1
|
|
|
3
2
|
# I know, the following is ugly, but I need those try's to
|
|
4
3
|
# run the command in my dev setting AND from
|
|
@@ -16,18 +15,18 @@ except:
|
|
|
16
15
|
import timeline
|
|
17
16
|
import multi2polywav
|
|
18
17
|
|
|
19
|
-
import argparse, tempfile
|
|
18
|
+
import argparse, tempfile, configparser
|
|
20
19
|
from loguru import logger
|
|
21
20
|
from pathlib import Path
|
|
22
21
|
# import os, sys
|
|
23
|
-
import os, sys, sox
|
|
22
|
+
import os, sys, sox, platformdirs
|
|
24
23
|
from rich.progress import track
|
|
25
24
|
# from pprint import pprint
|
|
26
25
|
from rich.console import Console
|
|
27
26
|
# from rich.text import Text
|
|
28
27
|
from rich.table import Table
|
|
29
28
|
from rich import print
|
|
30
|
-
from pprint import pprint
|
|
29
|
+
from pprint import pprint, pformat
|
|
31
30
|
import numpy as np
|
|
32
31
|
|
|
33
32
|
DEL_TEMP = False
|
|
@@ -41,37 +40,22 @@ ogg oga mogg opus ra rm raw rf64 sln tta voc vox wav wma wv webm 8svx cda""".spl
|
|
|
41
40
|
|
|
42
41
|
logger.level("DEBUG", color="<yellow>")
|
|
43
42
|
|
|
44
|
-
def process_files_with_progress_bars(medias):
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
def process_files(medias):
|
|
62
|
-
# maps Media objects -> Recording objects
|
|
63
|
-
recordings = []
|
|
64
|
-
rec_with_TTC = []
|
|
65
|
-
times = []
|
|
66
|
-
for m in medias:
|
|
67
|
-
recordings.append(yaltc.Recording(m))
|
|
68
|
-
for r in recordings:
|
|
69
|
-
# print('%s duration %.2fs'%(r.AVpath.name, r.get_duration()))
|
|
70
|
-
if r.seems_to_have_TicTacCode_at_beginning():
|
|
71
|
-
rec_with_TTC.append(r)
|
|
72
|
-
for r in rec_with_TTC:
|
|
73
|
-
times.append(r.get_start_time())
|
|
74
|
-
return recordings, rec_with_TTC, times
|
|
43
|
+
# def process_files_with_progress_bars(medias): # [todo, replace]
|
|
44
|
+
# recordings = []
|
|
45
|
+
# rec_with_TTC = []
|
|
46
|
+
# times = []
|
|
47
|
+
# for m in track(medias,
|
|
48
|
+
# description="1/4 Initializing Recordings:"):
|
|
49
|
+
# # file_alias = 'dummy'
|
|
50
|
+
# recordings.append(yaltc.Recording(m))
|
|
51
|
+
# for r in track(recordings,
|
|
52
|
+
# description="2/4 Looking for TicTacCode:"):
|
|
53
|
+
# if r.seems_to_have_TicTacCode_at_beginning():
|
|
54
|
+
# rec_with_TTC.append(r)
|
|
55
|
+
# for r in track(rec_with_TTC,
|
|
56
|
+
# description="3/4 Finding start times:"):
|
|
57
|
+
# times.append(r.get_start_time())
|
|
58
|
+
# return recordings, rec_with_TTC, times
|
|
75
59
|
|
|
76
60
|
def process_single(file, args):
|
|
77
61
|
# argument is a single file
|
|
@@ -108,7 +92,7 @@ def process_lag_adjustement(media_object):
|
|
|
108
92
|
# returns nothing
|
|
109
93
|
lags = media_object.device.tracks.lag_values
|
|
110
94
|
logger.debug('will process %s lags'%[lags])
|
|
111
|
-
channels = timeline.
|
|
95
|
+
channels = timeline._sox_split_channels(media_object.path)
|
|
112
96
|
# add bk to file on filesystem, but media_object.path is unchanged (?)
|
|
113
97
|
backup_name = str(media_object.path) + 'bk'
|
|
114
98
|
if Path(backup_name).exists():
|
|
@@ -138,12 +122,67 @@ def process_lag_adjustement(media_object):
|
|
|
138
122
|
logger.debug('trimmed_multichanfile %s'%timeline._pathname(trimmed_multichanfile))
|
|
139
123
|
Path(timeline._pathname(trimmed_multichanfile)).replace(media_object.path)
|
|
140
124
|
|
|
125
|
+
def start_proj(folders):
|
|
126
|
+
# if existing values are found,
|
|
127
|
+
# confirm new values with user.
|
|
128
|
+
# returns (source_RAW, destination_synced)
|
|
129
|
+
# either old (and confirmed) or new ones
|
|
130
|
+
def _write_cfg():
|
|
131
|
+
conf_dir = platformdirs.user_config_dir('tictacsync', 'plutz',
|
|
132
|
+
ensure_exists=True)
|
|
133
|
+
logger.debug('will start project with folders %s'%folders)
|
|
134
|
+
conf_file = Path(conf_dir)/'mirrored.cfg'
|
|
135
|
+
logger.debug('writing config in %s'%conf_file)
|
|
136
|
+
conf_prs = configparser.ConfigParser()
|
|
137
|
+
conf_prs['MIRRORED'] = {'source_RAW': folders[0],
|
|
138
|
+
'destination_synced': folders[1]}
|
|
139
|
+
with open(conf_file, 'w') as configfile:
|
|
140
|
+
conf_prs.write(configfile)
|
|
141
|
+
known_values = get_proj()
|
|
142
|
+
if known_values != ():
|
|
143
|
+
source_RAW, destination_synced = known_values
|
|
144
|
+
print('Warning: there is a current project')
|
|
145
|
+
print('with source (RAW) folder: %s\nand destination (synced) folder: %s'%
|
|
146
|
+
(source_RAW, destination_synced))
|
|
147
|
+
answer = input("\nDo you want to change values? [YES|NO]")
|
|
148
|
+
if answer.upper()[0] in ["Y", "YES"]:
|
|
149
|
+
_write_cfg()
|
|
150
|
+
return folders
|
|
151
|
+
elif answer.upper()[0] in ["N", "NO"]:
|
|
152
|
+
print('Ok, will keep old ones')
|
|
153
|
+
return source_RAW, destination_synced
|
|
154
|
+
else:
|
|
155
|
+
_write_cfg()
|
|
156
|
+
return folders
|
|
157
|
+
|
|
158
|
+
sys.exit(0)
|
|
159
|
+
|
|
160
|
+
def get_proj():
|
|
161
|
+
# check if user started a project before.
|
|
162
|
+
# stored in platformdirs.user_config_dir
|
|
163
|
+
# returns (source_RAW, destination_synced) if any
|
|
164
|
+
# () otherwise
|
|
165
|
+
conf_dir = platformdirs.user_config_dir('tictacsync', 'plutz')
|
|
166
|
+
conf_file = Path(conf_dir)/'mirrored.cfg'
|
|
167
|
+
if conf_file.exists():
|
|
168
|
+
logger.debug('reading config in %s'%conf_file)
|
|
169
|
+
conf_prs = configparser.ConfigParser()
|
|
170
|
+
conf_prs.read(conf_file)
|
|
171
|
+
source_RAW = conf_prs.get('MIRRORED', 'source_RAW')
|
|
172
|
+
destination_synced = conf_prs.get('MIRRORED', 'destination_synced')
|
|
173
|
+
logger.debug('read source_RAW: %s and destination_synced: %s'%
|
|
174
|
+
(source_RAW, destination_synced))
|
|
175
|
+
return (source_RAW, destination_synced)
|
|
176
|
+
else:
|
|
177
|
+
logger.debug('no config file found')
|
|
178
|
+
return ()
|
|
179
|
+
|
|
141
180
|
def main():
|
|
142
181
|
parser = argparse.ArgumentParser()
|
|
143
182
|
parser.add_argument(
|
|
144
183
|
"path",
|
|
145
184
|
type=str,
|
|
146
|
-
nargs=
|
|
185
|
+
nargs='*',
|
|
147
186
|
help="directory_name or media_file"
|
|
148
187
|
)
|
|
149
188
|
# parser.add_argument("directory", nargs="?", help="path of media directory")
|
|
@@ -152,8 +191,15 @@ def main():
|
|
|
152
191
|
action='store_true', #ie default False
|
|
153
192
|
dest='verbose_output',
|
|
154
193
|
help='Set verbose ouput')
|
|
155
|
-
parser.add_argument('
|
|
156
|
-
|
|
194
|
+
parser.add_argument('--stop_mirroring',
|
|
195
|
+
action='store_true', #ie default False
|
|
196
|
+
dest='stop_mirroring',
|
|
197
|
+
help='Stop mirroring mode, will write synced files alongside originals.')
|
|
198
|
+
parser.add_argument('--start-project', '-s' ,
|
|
199
|
+
nargs=2,
|
|
200
|
+
dest='proj_folders',
|
|
201
|
+
default = [],
|
|
202
|
+
help='start mirrored tree output mode and specifies 2 folders: source (RAW) and destination (synced).')
|
|
157
203
|
parser.add_argument('-t','--timelineoffset',
|
|
158
204
|
nargs=1,
|
|
159
205
|
default=['00:00:00:00'],
|
|
@@ -163,6 +209,14 @@ def main():
|
|
|
163
209
|
action='store_true',
|
|
164
210
|
dest='plotting',
|
|
165
211
|
help='Produce plots')
|
|
212
|
+
parser.add_argument('-d',
|
|
213
|
+
action='store_true',
|
|
214
|
+
dest='dont_write_cam_folder',
|
|
215
|
+
help="Even if originals are inside CAM folders, don't put synced clips inside a CAM identified folder")
|
|
216
|
+
# parser.add_argument('-m',
|
|
217
|
+
# action='store_true',
|
|
218
|
+
# dest='multicam',
|
|
219
|
+
# help='Outputs multicam structure for NLE program (based on Davinci Resolve input processing)')
|
|
166
220
|
parser.add_argument('--isos',
|
|
167
221
|
action='store_true',
|
|
168
222
|
dest='write_ISOs',
|
|
@@ -182,12 +236,39 @@ def main():
|
|
|
182
236
|
quit()
|
|
183
237
|
if args.verbose_output:
|
|
184
238
|
logger.add(sys.stderr, level="DEBUG")
|
|
239
|
+
# logger.add(sys.stdout, filter=lambda r: r["function"] == "move_multicam_to_dir")
|
|
240
|
+
# logger.add(sys.stdout, filter=lambda r: r["function"] == "_merge_audio_and_video")
|
|
241
|
+
#
|
|
185
242
|
# logger.add(sys.stdout, filter="__main__")
|
|
186
243
|
# logger.add(sys.stdout, filter="yaltc")
|
|
187
|
-
|
|
188
|
-
# logger.
|
|
189
|
-
# logger.add(sys.stdout, filter=lambda r: r["function"] == "
|
|
190
|
-
# logger.add(sys.stdout, filter=lambda r: r["function"] == "
|
|
244
|
+
|
|
245
|
+
# logger.add(sys.stdout, filter=lambda r: r["function"] == "start_proj")
|
|
246
|
+
# logger.add(sys.stdout, filter=lambda r: r["function"] == "_dev_type_for_name")
|
|
247
|
+
# logger.add(sys.stdout, filter=lambda r: r["function"] == "scan_media_and_build_devices_UID")
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
if args.proj_folders != []:
|
|
251
|
+
print('\bSorry, mirrored output mode not implemented yet: synced clips will')
|
|
252
|
+
print('be written alongside originals in a SyncedMedia folder. Bye.')
|
|
253
|
+
sys.exit(0)
|
|
254
|
+
|
|
255
|
+
if not len(args.proj_folders) in [0, 2]:
|
|
256
|
+
print('Error, -s option requires two folders')
|
|
257
|
+
sys.exit(0)
|
|
258
|
+
if len(args.proj_folders) == 2:
|
|
259
|
+
source_RAW, destination_synced = start_proj(args.proj_folders)
|
|
260
|
+
logger.debug('source_RAW: %s destination_synced: %s'%(source_RAW,
|
|
261
|
+
destination_synced))
|
|
262
|
+
proj_folders = get_proj()
|
|
263
|
+
if proj_folders != ():
|
|
264
|
+
# mirrored mode
|
|
265
|
+
source_RAW, destination_synced = proj_folders
|
|
266
|
+
logger.debug('Mirrored mode ON, with %s '%str(proj_folders))
|
|
267
|
+
else:
|
|
268
|
+
source_RAW, destination_synced = None, None
|
|
269
|
+
logger.debug('Mirrored mode OFF, ')
|
|
270
|
+
if source_RAW or destination_synced:
|
|
271
|
+
print('Mirrored not implemented yet, ignoring.')
|
|
191
272
|
top_dir = args.path[0]
|
|
192
273
|
if os.path.isfile(top_dir):
|
|
193
274
|
file = top_dir
|
|
@@ -197,13 +278,13 @@ def main():
|
|
|
197
278
|
sys.exit(1)
|
|
198
279
|
# logger.debug('args.o %s'%args.o)
|
|
199
280
|
# print('args.o %s'%args.o)
|
|
200
|
-
if args.
|
|
201
|
-
|
|
202
|
-
else:
|
|
203
|
-
|
|
204
|
-
if args.
|
|
205
|
-
|
|
206
|
-
|
|
281
|
+
# if args.anchor:
|
|
282
|
+
# anchor_dir = args.anchor[0]
|
|
283
|
+
# else:
|
|
284
|
+
# anchor_dir = None
|
|
285
|
+
# if args.anchor and not os.path.isdir(anchor_dir):
|
|
286
|
+
# print('%s is not a directory or doesnt exist.'%anchor_dir)
|
|
287
|
+
# sys.exit(1)
|
|
207
288
|
multi2polywav.poly_all(top_dir)
|
|
208
289
|
scanner = device_scanner.Scanner(top_dir, stay_silent=args.terse)
|
|
209
290
|
scanner.scan_media_and_build_devices_UID()
|
|
@@ -212,12 +293,12 @@ def main():
|
|
|
212
293
|
if not all([lv == None for lv in m.device.tracks.lag_values]):
|
|
213
294
|
logger.debug('%s has lag_values %s'%(
|
|
214
295
|
m.path, m.device.tracks.lag_values))
|
|
296
|
+
# any lag for a channel is specified by user in tracks.txt
|
|
215
297
|
process_lag_adjustement(m)
|
|
216
298
|
audio_REC_only = all([m.device.dev_type == 'REC' for m
|
|
217
299
|
in scanner.found_media_files])
|
|
218
|
-
|
|
219
300
|
if audio_REC_only:
|
|
220
|
-
if scanner.input_structure != '
|
|
301
|
+
if scanner.input_structure != 'ordered':
|
|
221
302
|
print('For merging audio only, use a directory per device, quitting')
|
|
222
303
|
sys.exit(1)
|
|
223
304
|
print('\n\n\nOnly audio recordings are present')
|
|
@@ -242,12 +323,12 @@ def main():
|
|
|
242
323
|
# ref_device = list(devices)[3 - 1]
|
|
243
324
|
print('When only audio recordings are present, ISOs files will be cut and written.')
|
|
244
325
|
if not args.terse:
|
|
245
|
-
if scanner.input_structure == '
|
|
246
|
-
print('\nDetected structured folders'
|
|
247
|
-
if scanner.top_dir_has_multicam:
|
|
248
|
-
|
|
249
|
-
else:
|
|
250
|
-
|
|
326
|
+
if scanner.input_structure == 'ordered':
|
|
327
|
+
print('\nDetected structured folders')
|
|
328
|
+
# if scanner.top_dir_has_multicam:
|
|
329
|
+
# print(', multicam')
|
|
330
|
+
# else:
|
|
331
|
+
# print()
|
|
251
332
|
else:
|
|
252
333
|
print('\nDetected loose structure')
|
|
253
334
|
if scanner.CAM_numbers() > 1:
|
|
@@ -269,18 +350,19 @@ def main():
|
|
|
269
350
|
# check if all audio recorders have same sampling freq
|
|
270
351
|
freqs = [dev.sampling_freq for dev in all_devices if dev.dev_type == 'REC']
|
|
271
352
|
same = np.isclose(np.std(freqs),0)
|
|
272
|
-
logger.debug('sampling freqs from audio
|
|
353
|
+
logger.debug('sampling freqs from audio recorders %s, same:%s'%(freqs, same))
|
|
273
354
|
if not same:
|
|
274
355
|
print('some audio recorders have different sampling frequencies:')
|
|
275
356
|
print(freqs)
|
|
276
357
|
print('resulting in undefined results: quitting...')
|
|
277
358
|
quit()
|
|
278
359
|
print()
|
|
279
|
-
recordings, rec_with_TTC
|
|
280
|
-
|
|
360
|
+
# recordings, rec_with_TTC = process_files(scanner.found_media_files, args)
|
|
361
|
+
recordings = [yaltc.Recording(m, do_plots=args.plotting) for m
|
|
362
|
+
in scanner.found_media_files]
|
|
281
363
|
recordings_with_time = [
|
|
282
364
|
rec
|
|
283
|
-
for rec in
|
|
365
|
+
for rec in recordings
|
|
284
366
|
if rec.get_start_time()
|
|
285
367
|
]
|
|
286
368
|
if audio_REC_only:
|
|
@@ -299,9 +381,8 @@ def main():
|
|
|
299
381
|
table.add_column("Date\n", justify="center", style='gold1')
|
|
300
382
|
rec_WO_time = [
|
|
301
383
|
rec.AVpath.name
|
|
302
|
-
for rec in
|
|
303
|
-
if not
|
|
304
|
-
]
|
|
384
|
+
for rec in recordings
|
|
385
|
+
if rec not in recordings_with_time]
|
|
305
386
|
if rec_WO_time:
|
|
306
387
|
print('No time found for: ',end='')
|
|
307
388
|
[print(rec, end=' ') for rec in rec_WO_time]
|
|
@@ -325,7 +406,13 @@ def main():
|
|
|
325
406
|
console.print(table)
|
|
326
407
|
print()
|
|
327
408
|
n_devices = scanner.get_devices_number()
|
|
328
|
-
OUT_struct_for_mcam =
|
|
409
|
+
# OUT_struct_for_mcam = scanner.top_dir_has_multicam and \
|
|
410
|
+
# scanner.input_structure != 'loose'
|
|
411
|
+
# OUT_struct_for_mcam = args.multicam
|
|
412
|
+
# if OUT_struct_for_mcam and scanner.input_structure == 'loose':
|
|
413
|
+
# print("\nSorry, can't output multicam structure if input is not structured:")
|
|
414
|
+
# print("each camera must have its own folder with its clips stored inside, quitting.")
|
|
415
|
+
# sys.exit(0)
|
|
329
416
|
if len(recordings_with_time) < 2:
|
|
330
417
|
if not args.terse:
|
|
331
418
|
print('\nNothing to sync, exiting.\n')
|
|
@@ -337,30 +424,45 @@ def main():
|
|
|
337
424
|
print('\nNothing to sync, bye.\n')
|
|
338
425
|
sys.exit(1)
|
|
339
426
|
asked_ISOs = args.write_ISOs
|
|
340
|
-
if asked_ISOs and scanner.input_structure != '
|
|
427
|
+
if asked_ISOs and scanner.input_structure != 'ordered':
|
|
341
428
|
print('Warning, can\'t write ISOs without structured folders: [gold1]--isos[/gold1] option ignored.\n')
|
|
342
429
|
asked_ISOs = False
|
|
343
|
-
output_dir = args.o
|
|
430
|
+
# output_dir = args.o
|
|
344
431
|
# if args.verbose_output or args.terse: # verbose, so no progress bars
|
|
345
432
|
for merger in matcher.mergers:
|
|
346
|
-
merger.build_audio_and_write_merged_media(top_dir,
|
|
347
|
-
|
|
433
|
+
merger.build_audio_and_write_merged_media(top_dir,
|
|
434
|
+
args.dont_write_cam_folder,
|
|
348
435
|
asked_ISOs,
|
|
349
436
|
audio_REC_only)
|
|
350
437
|
if not args.terse:
|
|
351
438
|
print("\n")
|
|
352
|
-
# find out where files were
|
|
353
|
-
a_merger = matcher.mergers[0]
|
|
354
|
-
print('\nWrote output in folder [gold1]%s[/gold1]'%(
|
|
355
|
-
a_merger.synced_clip_dir))
|
|
439
|
+
# find out where files were written
|
|
440
|
+
# a_merger = matcher.mergers[0]
|
|
356
441
|
for merger in matcher.mergers:
|
|
357
442
|
print('[gold1]%s[/gold1]'%merger.videoclip.AVpath.name, end='')
|
|
358
443
|
for audio in merger.get_matched_audio_recs():
|
|
359
444
|
print(' + [gold1]%s[/gold1]'%audio.AVpath.name, end='')
|
|
360
445
|
new_file = merger.videoclip.final_synced_file.parts
|
|
361
|
-
|
|
446
|
+
final_p = merger.videoclip.final_synced_file
|
|
447
|
+
nameAnd2Parents = Path('').joinpath(*final_p.parts[-2:])
|
|
448
|
+
print(' became [gold1]%s[/gold1]'%nameAnd2Parents)
|
|
362
449
|
# matcher._build_otio_tracks_for_cam()
|
|
450
|
+
matcher.set_up_clusters() # multicam
|
|
363
451
|
matcher.shrink_gaps_between_takes(args.timelineoffset)
|
|
452
|
+
logger.debug('matcher.multicam_clips_clusters %s'%
|
|
453
|
+
pformat(matcher.multicam_clips_clusters))
|
|
454
|
+
# clusters is list of {'end': t1, 'start': t2, 'vids': [r1,r3]}
|
|
455
|
+
# really_clusters is True if one of them has len() > 1
|
|
456
|
+
really_clusters = any([len(cl['vids']) > 1 for cl
|
|
457
|
+
in matcher.multicam_clips_clusters])
|
|
458
|
+
if really_clusters:
|
|
459
|
+
if scanner.input_structure == 'loose':
|
|
460
|
+
print('\nThere are synced multicam clips but without structured folders')
|
|
461
|
+
print('they were not grouped together under the same folder.')
|
|
462
|
+
else:
|
|
463
|
+
matcher.move_multicam_to_dir()
|
|
464
|
+
else:
|
|
465
|
+
logger.debug('not really a multicam cluster, nothing to move')
|
|
364
466
|
sys.exit(0)
|
|
365
467
|
|
|
366
468
|
if __name__ == '__main__':
|
tictacsync/remrgmx.py
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
|
-
import os, itertools
|
|
1
|
+
import os, itertools, argparse
|
|
2
2
|
from pathlib import Path
|
|
3
|
+
from loguru import logger
|
|
4
|
+
import sys
|
|
5
|
+
from pprint import pformat
|
|
6
|
+
|
|
7
|
+
logger.level("DEBUG", color="<yellow>")
|
|
8
|
+
|
|
3
9
|
|
|
4
10
|
video_extensions = \
|
|
5
11
|
"""webm mkv flv flv vob ogv ogg drc gif gifv mng avi mov
|
|
@@ -14,8 +20,11 @@ def is_video(f):
|
|
|
14
20
|
name, ext = name_ext
|
|
15
21
|
return ext.lower() in video_extensions
|
|
16
22
|
|
|
17
|
-
def
|
|
18
|
-
#
|
|
23
|
+
def find_ISO_vids_pairs_in_dir(top):
|
|
24
|
+
# look for matching video name and ISO dir name
|
|
25
|
+
# eg: IMG04.mp4 and IMG04_ISO
|
|
26
|
+
# returns list of matches
|
|
27
|
+
# recursively search from 'top' argument
|
|
19
28
|
vids = []
|
|
20
29
|
ISOs = []
|
|
21
30
|
for (root,dirs,files) in os.walk(top, topdown=True):
|
|
@@ -24,7 +33,8 @@ def find_ISO_vids_pairs(top):
|
|
|
24
33
|
ISOs.append(Path(root)/d)
|
|
25
34
|
for f in files:
|
|
26
35
|
if is_video(f):
|
|
27
|
-
vids.append(Path(root)/f)
|
|
36
|
+
vids.append(Path(root)/f)
|
|
37
|
+
logger.debug('vids %s ISOs %s'%(vids, ISOs))
|
|
28
38
|
matches = []
|
|
29
39
|
for pair in list(itertools.product(vids, ISOs)):
|
|
30
40
|
# print(pair)
|
|
@@ -33,6 +43,78 @@ def find_ISO_vids_pairs(top):
|
|
|
33
43
|
if vidname == ISO.name[:-4]:
|
|
34
44
|
matches.append(pair)
|
|
35
45
|
# print(vidname, ISO.name[:-4])
|
|
46
|
+
logger.debug('matches: %s'%pformat(matches))
|
|
36
47
|
return matches
|
|
37
48
|
|
|
38
|
-
[print( vid, ISO) for vid, ISO in find_ISO_vids_pairs('.')]
|
|
49
|
+
# [print( vid, ISO) for vid, ISO in find_ISO_vids_pairs('.')]
|
|
50
|
+
|
|
51
|
+
def parse_and_check_arguments():
|
|
52
|
+
# parses directories from command arguments
|
|
53
|
+
# check for consistencies and warn user and exits,
|
|
54
|
+
# if returns, gives:
|
|
55
|
+
# proxies_dir, originals_dir, audio_dir, both_audio_vid, scan_only
|
|
56
|
+
parser = argparse.ArgumentParser()
|
|
57
|
+
parser.add_argument('-p',
|
|
58
|
+
nargs=1,
|
|
59
|
+
dest='proxies_dir',
|
|
60
|
+
help='Where proxy clips are stored')
|
|
61
|
+
parser.add_argument('-o',
|
|
62
|
+
nargs=1,
|
|
63
|
+
dest='originals_dir',
|
|
64
|
+
help='Original (non-proxy) clips directory')
|
|
65
|
+
parser.add_argument('-a',
|
|
66
|
+
nargs=1,
|
|
67
|
+
dest='audio_dir',
|
|
68
|
+
help='Contains newly changed mix files')
|
|
69
|
+
parser.add_argument('-b',
|
|
70
|
+
nargs=1,
|
|
71
|
+
dest='both_audio_vid',
|
|
72
|
+
help='Directory scanned for both audio and video')
|
|
73
|
+
parser.add_argument('--dry',
|
|
74
|
+
action='store_true',
|
|
75
|
+
dest='scan_only',
|
|
76
|
+
help="Just display changed audio, don't merge")
|
|
77
|
+
args = parser.parse_args()
|
|
78
|
+
logger.debug('args %s'%args)
|
|
79
|
+
# ok cases:
|
|
80
|
+
# -p -o -a + no -b
|
|
81
|
+
# -o -a + no -b
|
|
82
|
+
args_set = [args.proxies_dir != None,
|
|
83
|
+
args.originals_dir != None,
|
|
84
|
+
args.audio_dir != None,
|
|
85
|
+
args.both_audio_vid != None,
|
|
86
|
+
]
|
|
87
|
+
p, o, a, b = args_set
|
|
88
|
+
# check that argument -b (both_audio_vid) is used alone
|
|
89
|
+
if b and any([o, a, p]):
|
|
90
|
+
print("\nDon't specify other argument than -b if both audio and video searched in the same directory.\n")
|
|
91
|
+
parser.print_help(sys.stderr)
|
|
92
|
+
sys.exit(0)
|
|
93
|
+
# check that if proxies (-p) are specified, orginals too (-o)
|
|
94
|
+
if p and not o:
|
|
95
|
+
print("\nIf proxies directory is specified, so should originals directory.\n")
|
|
96
|
+
parser.print_help(sys.stderr)
|
|
97
|
+
sys.exit(0)
|
|
98
|
+
# check that -o and -a are used together
|
|
99
|
+
if not b and not (o and a):
|
|
100
|
+
print("\nAt least originals and audio directories must be given (-o and -a) when audio and video are in different dir.\n")
|
|
101
|
+
parser.print_help(sys.stderr)
|
|
102
|
+
sys.exit(0)
|
|
103
|
+
# work in progress (aug 2025), so limit to -b:
|
|
104
|
+
if not b :
|
|
105
|
+
print("\nFor now, only -b argument is supported (a directory scanned for both audio and video) .\n")
|
|
106
|
+
parser.print_help(sys.stderr)
|
|
107
|
+
sys.exit(0)
|
|
108
|
+
arg_dict = vars(args)
|
|
109
|
+
# list of singletons, so flatten. Keep None and False as is
|
|
110
|
+
return [e[0] if isinstance(e, list) else e for e in arg_dict.values() ]
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def main():
|
|
115
|
+
proxies_dir, originals_dir, audio_dir, both_audio_vid, scan_only = \
|
|
116
|
+
parse_and_check_arguments()
|
|
117
|
+
m = find_ISO_vids_pairs_in_dir(both_audio_vid)
|
|
118
|
+
|
|
119
|
+
if __name__ == '__main__':
|
|
120
|
+
main()
|