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/device_scanner.py
CHANGED
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
TRACKSFN = 'tracks.txt'
|
|
7
7
|
SILENT_TRACK_TOKENS = '-0n'
|
|
8
8
|
|
|
9
|
-
|
|
10
9
|
av_file_extensions = \
|
|
11
10
|
"""webm mkv flv flv vob ogv ogg drc gif gifv mng avi MTS M2TS TS mov qt
|
|
12
11
|
wmv yuv rm rmvb viv asf amv mp4 m4p m4v mpg mp2 mpeg mpe mpv mpg mpeg m2v
|
|
@@ -40,6 +39,8 @@ try:
|
|
|
40
39
|
except:
|
|
41
40
|
import multi2polywav
|
|
42
41
|
|
|
42
|
+
MCCDIR = 'SyncedMulticamClips'
|
|
43
|
+
SYNCEDFOLDER = 'SyncedMedia'
|
|
43
44
|
|
|
44
45
|
# utility for accessing pathnames
|
|
45
46
|
def _pathname(tempfile_or_path):
|
|
@@ -73,7 +74,7 @@ class Tracks:
|
|
|
73
74
|
@dataclass
|
|
74
75
|
class Device:
|
|
75
76
|
UID: int
|
|
76
|
-
folder: Path
|
|
77
|
+
folder: Path # media's parent folder
|
|
77
78
|
name: str
|
|
78
79
|
dev_type: str # CAM or REC
|
|
79
80
|
n_chan: int
|
|
@@ -97,7 +98,7 @@ def media_at_path(input_structure, p):
|
|
|
97
98
|
dev_UID, dt, sf = get_device_ffprobe_UID(p)
|
|
98
99
|
dev_name = None
|
|
99
100
|
logger.debug('ffprobe dev_UID:%s dt:%s sf:%s'%(dev_UID, dt,sf))
|
|
100
|
-
if input_structure == '
|
|
101
|
+
if input_structure == 'ordered':
|
|
101
102
|
dev_name = p.parent.name
|
|
102
103
|
if dev_UID is None:
|
|
103
104
|
dev_UID = hash(dev_name)
|
|
@@ -132,7 +133,7 @@ def get_device_ffprobe_UID(file):
|
|
|
132
133
|
"""
|
|
133
134
|
Tries to find an unique hash integer identifying the device that produced
|
|
134
135
|
the file based on the string inside ffprobe metadata without any
|
|
135
|
-
reference to date
|
|
136
|
+
reference to date, location, length or time. Find out with ffprobe the type
|
|
136
137
|
of device: CAM or REC for videocamera or audio recorder.
|
|
137
138
|
|
|
138
139
|
Device UIDs are used later in Montage._get_concatenated_audiofile_for()
|
|
@@ -192,7 +193,7 @@ def get_device_ffprobe_UID(file):
|
|
|
192
193
|
class Scanner:
|
|
193
194
|
"""
|
|
194
195
|
Class that encapsulates scanning of the directory given as CLI argument.
|
|
195
|
-
Depending on the input_structure detected (loose|
|
|
196
|
+
Depending on the input_structure detected (loose|ordered), enforce
|
|
196
197
|
some directory structure (or not). Build a list of media files found and a
|
|
197
198
|
try to indentify uniquely the device used to record each media file.
|
|
198
199
|
|
|
@@ -202,15 +203,15 @@ class Scanner:
|
|
|
202
203
|
Any of:
|
|
203
204
|
'loose'
|
|
204
205
|
all files audio + video are in top folder
|
|
205
|
-
'
|
|
206
|
+
'ordered'
|
|
206
207
|
eg for multicam on Davinci Resolve
|
|
207
208
|
input_structure is set in scan_media_and_build_devices_UID()
|
|
208
209
|
|
|
209
210
|
top_directory : string
|
|
210
211
|
String of path where to start searching for media files.
|
|
211
212
|
|
|
212
|
-
top_dir_has_multicam : bool
|
|
213
|
-
|
|
213
|
+
# top_dir_has_multicam : bool
|
|
214
|
+
# If top dir is folder structures AND more than on cam
|
|
214
215
|
|
|
215
216
|
found_media_files: list of Media objects
|
|
216
217
|
"""
|
|
@@ -243,6 +244,15 @@ class Scanner:
|
|
|
243
244
|
CAMs = [d for d in devices if d.dev_type == 'CAM']
|
|
244
245
|
return len(set(CAMs))
|
|
245
246
|
|
|
247
|
+
def get_device_in_path(self, a_media):
|
|
248
|
+
"""
|
|
249
|
+
Return a device name taken from folders in path of a_media if and only
|
|
250
|
+
if all media files below it are from the same device. Goes up max two
|
|
251
|
+
levels from media; e.g., in /FOLDER2/FOLDER1/DSC00234.MOV will test up
|
|
252
|
+
to FOLDER2 as candidate.
|
|
253
|
+
"""
|
|
254
|
+
|
|
255
|
+
|
|
246
256
|
def scan_media_and_build_devices_UID(self, recursive=True):
|
|
247
257
|
"""
|
|
248
258
|
Scans Scanner.top_directory recursively for files with known audio-video
|
|
@@ -256,7 +266,7 @@ class Scanner:
|
|
|
256
266
|
|
|
257
267
|
Populates Scanner.found_media_files, a list of Media objects
|
|
258
268
|
|
|
259
|
-
Sets Scanner.input_structure = 'loose'|'
|
|
269
|
+
Sets Scanner.input_structure = 'loose'|'ordered'
|
|
260
270
|
|
|
261
271
|
"""
|
|
262
272
|
files = Path(self.top_directory).rglob('*.*')
|
|
@@ -264,81 +274,137 @@ class Scanner:
|
|
|
264
274
|
p
|
|
265
275
|
for p in files
|
|
266
276
|
if p.suffix[1:] in av_file_extensions
|
|
267
|
-
and
|
|
277
|
+
and SYNCEDFOLDER not in p.parts # SyncedMedia
|
|
278
|
+
and MCCDIR not in p.parts # SyncedMulticamClips
|
|
268
279
|
]
|
|
269
280
|
logger.debug('found media files %s'%paths)
|
|
270
281
|
parents = [p.parent for p in paths]
|
|
271
|
-
logger.debug('found parents %s'%parents)
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
if all_parents_are_the_same:
|
|
282
|
+
logger.debug('found parents %s'%pformat(parents))
|
|
283
|
+
# True if all elements are identical
|
|
284
|
+
AV_files_have_same_parent = parents.count(parents[0]) == len(parents)
|
|
285
|
+
logger.debug('AV_files_have_same_parent %s'%AV_files_have_same_parent)
|
|
286
|
+
if AV_files_have_same_parent:
|
|
277
287
|
# all media (video + audio) are in a same folder, so this is loose
|
|
278
288
|
self.input_structure = 'loose'
|
|
279
289
|
# for now (TO DO?) 'loose' == no multi-cam
|
|
280
|
-
self.top_dir_has_multicam = False
|
|
290
|
+
# self.top_dir_has_multicam = False
|
|
281
291
|
else:
|
|
282
|
-
# check later if inside each folder, media have same device
|
|
283
|
-
# for now, we'll guess structure is '
|
|
284
|
-
self.input_structure = '
|
|
292
|
+
# check later if inside each folder, media have same device
|
|
293
|
+
# for now, we'll guess structure is 'ordered'
|
|
294
|
+
self.input_structure = 'ordered'
|
|
285
295
|
for p in paths:
|
|
286
296
|
new_media = media_at_path(self.input_structure, p) # dev UID set here
|
|
287
297
|
self.found_media_files.append(new_media)
|
|
288
|
-
|
|
298
|
+
# for non UIDed try building UID from filenam
|
|
299
|
+
def _try_name_from_files(medias):
|
|
289
300
|
# return common first strings in filename
|
|
301
|
+
def _all_identical(a_list):
|
|
302
|
+
return a_list.count(a_list[0]) == len(a_list)
|
|
290
303
|
names = [m.path.name for m in medias]
|
|
291
304
|
transposed_names = list(map(list, zip(*names)))
|
|
292
|
-
same = list(map(
|
|
305
|
+
same = list(map(_all_identical, transposed_names))
|
|
293
306
|
try:
|
|
294
307
|
first_diff = same.index(False)
|
|
295
308
|
except:
|
|
296
309
|
return names[0].split('.')[0]
|
|
297
310
|
return names[0][:first_diff]
|
|
298
|
-
|
|
311
|
+
no_device_UID_medias = [m for m in self.found_media_files
|
|
299
312
|
if not m.device.UID]
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
313
|
+
logger.debug('those media have no device UID %s'%no_device_UID_medias)
|
|
314
|
+
if no_device_UID_medias:
|
|
315
|
+
# will guess a device name from media filenames
|
|
316
|
+
logger.debug('no_device_UID_medias %s'%no_device_UID_medias)
|
|
317
|
+
start_string = _try_name_from_files(no_device_UID_medias)
|
|
303
318
|
if len(start_string) < 2:
|
|
304
319
|
print('\nError, cant identify the device for those files:')
|
|
305
|
-
[print('%s, '%m.path.name, end='') for m in
|
|
320
|
+
[print('%s, '%m.path.name, end='') for m in no_device_UID_medias]
|
|
306
321
|
print('\n')
|
|
307
322
|
sys.exit(1)
|
|
308
|
-
one_device =
|
|
323
|
+
one_device = no_device_UID_medias[0].device
|
|
309
324
|
one_device.name = start_string
|
|
310
325
|
if not one_device.UID:
|
|
311
326
|
one_device.UID = hash(start_string)
|
|
312
327
|
print('\nWarning, guessing a device ID for those files:')
|
|
313
328
|
[print('[gold1]%s[/gold1], '%m.path.name, end='') for m
|
|
314
|
-
in
|
|
329
|
+
in no_device_UID_medias]
|
|
315
330
|
print('UID: [gold1]%s[/gold1]'%start_string)
|
|
316
|
-
for m in
|
|
331
|
+
for m in no_device_UID_medias:
|
|
317
332
|
m.device = one_device
|
|
318
333
|
logger.debug('new device added %s'%self.found_media_files)
|
|
319
|
-
logger.debug('Scanner.found_media_files = %s'%self.found_media_files)
|
|
320
|
-
if self.input_structure == '
|
|
321
|
-
self.
|
|
334
|
+
logger.debug('Scanner.found_media_files = %s'%pformat(self.found_media_files))
|
|
335
|
+
if self.input_structure == 'ordered':
|
|
336
|
+
self._confirm_folders_have_same_device()
|
|
322
337
|
# self._use_folder_as_device_name()
|
|
323
338
|
devices = set([m.device for m in self.found_media_files])
|
|
324
339
|
audio_devices = [d for d in devices if d.dev_type == 'REC']
|
|
325
340
|
for recorder in audio_devices:
|
|
341
|
+
# process tracks.txt for audio recorders
|
|
326
342
|
recorder.tracks = self._get_tracks_from_file(recorder)
|
|
343
|
+
# logging only:
|
|
327
344
|
if recorder.tracks:
|
|
328
345
|
if not all([lv == None for lv in recorder.tracks.lag_values]):
|
|
329
346
|
logger.debug('%s has lag_values %s'%(
|
|
330
347
|
recorder.name, recorder.tracks.lag_values))
|
|
348
|
+
# check if device is in fact two parents up (and parent = ROLLxx):
|
|
349
|
+
# Group media by folder 2up and verify all media for each
|
|
350
|
+
# group have same device.
|
|
351
|
+
folder2up = lambda m: m.path.parent.parent
|
|
352
|
+
# logger.debug('folder2up: %s'%pformat([folder2up(m) for m
|
|
353
|
+
# in self.found_media_files]))
|
|
354
|
+
medias = sorted(self.found_media_files, key=folder2up)
|
|
355
|
+
# build lists for multiple reference of iterators
|
|
356
|
+
media_grouped_by_folder2up = [ (k, list(iterator)) for k, iterator
|
|
357
|
+
in groupby(medias, folder2up)]
|
|
358
|
+
logger.debug('media_grouped_by_folder2up: %s'%pformat(
|
|
359
|
+
media_grouped_by_folder2up))
|
|
360
|
+
folder_and_UIDs = [(f, [m.device.UID for m in medias])
|
|
361
|
+
for f, medias in media_grouped_by_folder2up]
|
|
362
|
+
logger.debug('devices: %s'%pformat(folder_and_UIDs))
|
|
363
|
+
def _multiple_and_same(a_list):
|
|
364
|
+
same = a_list.count(a_list[0]) == len(a_list)
|
|
365
|
+
return len(a_list) > 1 and same
|
|
366
|
+
folders_with_same_dev = [(f.name, UIDs[0]) for f, UIDs
|
|
367
|
+
in folder_and_UIDs
|
|
368
|
+
if _multiple_and_same(UIDs)]
|
|
369
|
+
logger.debug('folders_with_same_dev: %s'%pformat(folders_with_same_dev))
|
|
370
|
+
for name, UID in folders_with_same_dev:
|
|
371
|
+
for m in self.found_media_files:
|
|
372
|
+
if m.device.UID == UID:
|
|
373
|
+
m.device.name = name
|
|
374
|
+
# logger.debug('renamed device media: %s'%pformat(self.found_media_files))
|
|
331
375
|
no_name_devices = [m.device for m in self.found_media_files
|
|
332
|
-
|
|
376
|
+
if not m.device.name]
|
|
377
|
+
# possible if self.input_structure == 'loose'
|
|
378
|
+
def _try_name_from_metadata(media):
|
|
379
|
+
# search model and make from fprobe
|
|
380
|
+
file = Path(media.path)
|
|
381
|
+
logger.debug('trying to find maker model for %s'%file)
|
|
382
|
+
try:
|
|
383
|
+
probe = ffmpeg.probe(file)
|
|
384
|
+
except ffmpeg.Error as e:
|
|
385
|
+
print('ffmpeg.probe error')
|
|
386
|
+
print(e.stderr, file)
|
|
387
|
+
return None, None #-----------------------------------------------------
|
|
388
|
+
# fall back to folder name
|
|
389
|
+
logger.debug('ffprobe %s'%pformat(probe))
|
|
390
|
+
# [TO BE COMPLETED]
|
|
391
|
+
# could reside in ['format','tags','com.apple.quicktime.model'],
|
|
392
|
+
# or ['format','tags','model'],
|
|
393
|
+
# or ['streams'][0]['tags']['vendor_id']) :-(
|
|
394
|
+
|
|
333
395
|
for anon_dev in no_name_devices:
|
|
334
396
|
medias = self.get_media_for_device(anon_dev)
|
|
335
|
-
guess_name =
|
|
397
|
+
guess_name = _try_name_from_files(medias)
|
|
336
398
|
# print('dev %s has no name, guessing %s'%(anon_dev, guess_name))
|
|
337
399
|
logger.debug('dev %s has no name, guessing %s'%(anon_dev, guess_name))
|
|
338
400
|
anon_dev.name = guess_name
|
|
339
401
|
pprint_found_media_files = pformat(self.found_media_files)
|
|
340
402
|
logger.debug('scanner.found_media_files = %s'%pprint_found_media_files)
|
|
341
403
|
logger.debug('all devices %s'%[m.device for m in self.found_media_files])
|
|
404
|
+
dev_is_REC = [m.device.dev_type == 'REC' for m in self.found_media_files]
|
|
405
|
+
if not any(dev_is_REC): # no audio recordings!
|
|
406
|
+
print('\rNo audio recording found, nothing to sync, bye.')
|
|
407
|
+
sys.exit(0)
|
|
342
408
|
# print('devices 312 %s'%set([m.device for m in self.found_media_files]))
|
|
343
409
|
|
|
344
410
|
def _get_tracks_from_file(self, device) -> Tracks:
|
|
@@ -384,60 +450,69 @@ class Scanner:
|
|
|
384
450
|
logger.debug('no tracks.txt file found')
|
|
385
451
|
return None
|
|
386
452
|
|
|
387
|
-
def
|
|
453
|
+
def _confirm_folders_have_same_device(self):
|
|
388
454
|
"""
|
|
389
|
-
Since input_structure == '
|
|
455
|
+
Since input_structure == 'ordered',
|
|
390
456
|
checks for files in self.found_media_files for structure as following.
|
|
391
457
|
|
|
392
458
|
Warns user and quit program for:
|
|
393
459
|
A- folders with mix of video and audio
|
|
394
460
|
B- folders with mix of uniquely identified devices and unUIDied ones
|
|
395
|
-
C- folders with mixed audio
|
|
461
|
+
C- folders with mixed audio an video files
|
|
396
462
|
|
|
397
463
|
Warns user but proceeds for:
|
|
398
464
|
D- folder with only unUIDied files (overlaps will be check later)
|
|
399
465
|
|
|
466
|
+
Changes self.input_structure to 'loose' if a folder contains files
|
|
467
|
+
from different devices.
|
|
468
|
+
|
|
400
469
|
Proceeds silently if
|
|
401
470
|
E- all files in the folder are from the same device
|
|
402
471
|
|
|
403
472
|
Returns nothing
|
|
404
473
|
"""
|
|
405
|
-
def
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
474
|
+
def _exit_on_folder_name_clash():
|
|
475
|
+
# Check media parent folders are unique
|
|
476
|
+
# returns media_grouped_by_folder
|
|
477
|
+
def _list_duplicates(seq):
|
|
478
|
+
seen = set()
|
|
479
|
+
seen_add = seen.add
|
|
480
|
+
# adds all elements it doesn't know yet to seen and all other to seen_twice
|
|
481
|
+
seen_twice = set( x for x in seq if x in seen or seen_add(x) )
|
|
482
|
+
# turn the set into a list (as requested)
|
|
483
|
+
return list( seen_twice )
|
|
484
|
+
folder_key = lambda m: m.path.parent
|
|
485
|
+
medias = sorted(self.found_media_files, key=folder_key)
|
|
486
|
+
# build lists for multiple reference of iterators
|
|
487
|
+
media_grouped_by_folder = [ (k, list(iterator)) for k, iterator
|
|
488
|
+
in groupby(medias, folder_key)]
|
|
489
|
+
logger.debug('media_grouped_by_folder %s'%pformat(
|
|
490
|
+
media_grouped_by_folder))
|
|
491
|
+
complete_path_folders = [e[0] for e in media_grouped_by_folder]
|
|
492
|
+
name_of_folders = [p.name for p in complete_path_folders]
|
|
493
|
+
logger.debug('complete_path_folders with media files %s'%
|
|
494
|
+
complete_path_folders)
|
|
495
|
+
logger.debug('name_of_folders with media files %s'%name_of_folders)
|
|
496
|
+
# unique_folder_names = set(name_of_folders) [TODO] is this useful ?
|
|
497
|
+
# repeated_folders = _list_duplicates(name_of_folders)
|
|
498
|
+
# logger.debug('repeated_folders %s'%repeated_folders)
|
|
499
|
+
# if repeated_folders:
|
|
500
|
+
# print('There are conflicts for some repeated folder names:')
|
|
501
|
+
# for f in [str(p) for p in repeated_folders]:
|
|
502
|
+
# print(' [gold1]%s[/gold1]'%f)
|
|
503
|
+
# print('Here are the complete paths:')
|
|
504
|
+
# for f in [str(p) for p in complete_path_folders]:
|
|
505
|
+
# print(' [gold1]%s[/gold1]'%f)
|
|
506
|
+
# print('please rename and rerun. Quitting..')
|
|
507
|
+
# sys.exit(1) ####################################################
|
|
508
|
+
return media_grouped_by_folder
|
|
509
|
+
media_grouped_by_folder = _exit_on_folder_name_clash()
|
|
435
510
|
n_CAM_folder = 0
|
|
436
511
|
for folder, list_of_medias_in_folder in media_grouped_by_folder:
|
|
437
512
|
# check all medias are either video or audio recordings in folder
|
|
438
513
|
# if not, warn user and quit.
|
|
439
514
|
dev_types = set([m.device.dev_type for m in list_of_medias_in_folder])
|
|
440
|
-
logger.debug('dev_types %s'%dev_types)
|
|
515
|
+
logger.debug('dev_types for folder%s: %s'%(folder,dev_types))
|
|
441
516
|
if dev_types == {'CAM'}:
|
|
442
517
|
n_CAM_folder += 1
|
|
443
518
|
if len(dev_types) != 1:
|
|
@@ -446,14 +521,14 @@ class Scanner:
|
|
|
446
521
|
[print('[gold1]%s[/gold1]'%m.path.name, end =', ')
|
|
447
522
|
for m in list_of_medias_in_folder]
|
|
448
523
|
print('\nplease move them in exclusive folders and rerun.\n')
|
|
449
|
-
sys.exit(1)
|
|
524
|
+
sys.exit(1) ######################################################
|
|
450
525
|
unidentified = [m for m in list_of_medias_in_folder
|
|
451
526
|
if m.device.UID == None]
|
|
452
527
|
UIDed = [m for m in list_of_medias_in_folder
|
|
453
528
|
if m.device.UID != None]
|
|
454
529
|
logger.debug('devices in folder %s:'%folder)
|
|
455
|
-
logger.debug(' media with unknown devices %s'%unidentified)
|
|
456
|
-
logger.debug(' media with UIDed devices %s'%UIDed)
|
|
530
|
+
logger.debug(' media with unknown devices %s'%pformat(unidentified))
|
|
531
|
+
logger.debug(' media with UIDed devices %s'%pformat(UIDed))
|
|
457
532
|
if len(unidentified) != 0 and len(UIDed) != 0:
|
|
458
533
|
print('\nProblem while grouping files in [gold1]%s[/gold1]:'%folder)
|
|
459
534
|
print('There is a mix of unidentifiable and identified devices.')
|
|
@@ -466,16 +541,38 @@ class Scanner:
|
|
|
466
541
|
elif answer.upper() in ["N", "NO"]:
|
|
467
542
|
# Do action you need
|
|
468
543
|
print('please move the following files in a folder named appropriately:\n')
|
|
469
|
-
sys.exit(1)
|
|
544
|
+
sys.exit(1) ################################################
|
|
470
545
|
# if, in a folder, there's a mix of different identified devices,
|
|
471
546
|
# Warn user and quit.
|
|
547
|
+
UIDs = [m.device.UID for m in UIDed]
|
|
548
|
+
all_same_device = UIDs.count(UIDs[0]) == len(UIDs)
|
|
549
|
+
logger.debug('UIDs in %s: %s. all_same_device %s'%(folder,
|
|
550
|
+
pformat(UIDs), all_same_device))
|
|
551
|
+
if not all_same_device:
|
|
552
|
+
self.input_structure = 'loose'
|
|
553
|
+
# self.top_dir_has_multicam = False
|
|
554
|
+
logger.debug('changed input_structure to loose')
|
|
555
|
+
# device name should be generated (it isn't the folder name...)
|
|
556
|
+
distinct_UIDS = set(UIDs)
|
|
557
|
+
n_UIDs = len(distinct_UIDS)
|
|
558
|
+
logger.debug('There are %i UIDs: %s'%(n_UIDs, distinct_UIDS))
|
|
559
|
+
# Buid CAM01, CAM02 or REC01, REC02.
|
|
560
|
+
# Get dev type from first media in list
|
|
561
|
+
devT = UIDed[0].device.dev_type # 'CAM' or 'REC'
|
|
562
|
+
generic_names = [devT + str(i).zfill(2) for i in range(n_UIDs)]
|
|
563
|
+
devUIDs_names = dict(zip(distinct_UIDS, generic_names))
|
|
564
|
+
logger.debug('devUIDs_names %s'%pformat(devUIDs_names))
|
|
565
|
+
# rename
|
|
566
|
+
for m in UIDed:
|
|
567
|
+
m.device.name = devUIDs_names[m.device.UID]
|
|
568
|
+
logger.debug('new name %s'%m.device.name)
|
|
472
569
|
if len(dev_types) != 1:
|
|
473
570
|
print('\nProblem while scanning for media files. In [gold1]%s[/gold1]:'%folder)
|
|
474
571
|
print('There is a mix of files from different devices:')
|
|
475
572
|
[print('[gold1]%s[/gold1]'%m.path.name, end =', ')
|
|
476
573
|
for m in list_of_medias_in_folder]
|
|
477
574
|
print('\nplease move them in exclusive folders and rerun.\n')
|
|
478
|
-
sys.exit(1)
|
|
575
|
+
sys.exit(1) ####################################################
|
|
479
576
|
if len(unidentified) == len(list_of_medias_in_folder):
|
|
480
577
|
# all unidentified
|
|
481
578
|
if len(unidentified) > 1:
|
|
@@ -487,11 +584,6 @@ class Scanner:
|
|
|
487
584
|
# all files in folder are from unidentified device or
|
|
488
585
|
# all files in folder are from the same identified device
|
|
489
586
|
logger.debug('n_CAM_folder %i'%n_CAM_folder)
|
|
490
|
-
if n_CAM_folder > 1 :
|
|
491
|
-
self.top_dir_has_multicam = True
|
|
492
|
-
else:
|
|
493
|
-
self.top_dir_has_multicam = False
|
|
494
|
-
logger.debug('top_dir_has_multicam: %s'%self.top_dir_has_multicam)
|
|
495
587
|
return
|
|
496
588
|
|
|
497
589
|
def _parse_track_values(self, tracks_file) -> Tracks:
|
|
@@ -499,16 +591,16 @@ class Scanner:
|
|
|
499
591
|
read track names for naming separated ISOs
|
|
500
592
|
from tracks_file.
|
|
501
593
|
|
|
502
|
-
tokens looked for: mix; L mix
|
|
594
|
+
tokens looked for: mix; mix L; mix R; 0 and TC
|
|
503
595
|
|
|
504
596
|
repeating "mic*" pattern signals a stereo track
|
|
505
597
|
and entries will correspondingly panned into
|
|
506
|
-
a stero mix named
|
|
598
|
+
a stero mix named mixL.wav and mixL.wav
|
|
507
599
|
|
|
508
|
-
L
|
|
509
|
-
R
|
|
510
|
-
L
|
|
511
|
-
R
|
|
600
|
+
mic L # spaces are ignored |
|
|
601
|
+
mic R | stereo pair
|
|
602
|
+
micB L
|
|
603
|
+
micB R
|
|
512
604
|
|
|
513
605
|
Returns: a Tracks instance:
|
|
514
606
|
# track numbers start at 1 for first track (as needed by sox)
|
|
@@ -523,22 +615,22 @@ class Scanner:
|
|
|
523
615
|
unused=[],
|
|
524
616
|
stereomics=[('mic', (4, 3)), ('mic2', (6, 5))],
|
|
525
617
|
mix=[], others=[('clics', 1)],
|
|
526
|
-
rawtrx=['clics', 'TC', '
|
|
618
|
+
rawtrx=['clics', 'TC', 'micL', 'micR', 'mic2L;1000', 'mic2R;1000', 'mixL', 'mixR'],
|
|
527
619
|
error_msg=None, lag_values=[None, None, None, None, '1000', '1000', None, None])
|
|
528
620
|
"""
|
|
529
621
|
def _WOspace(chaine):
|
|
530
622
|
ch = [c for c in chaine if c != ' ']
|
|
531
623
|
return ''.join(ch)
|
|
532
|
-
def _WO_LR(chaine):
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
def _seemsStereoMic(tag):
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
624
|
+
# def _WO_LR(chaine):
|
|
625
|
+
# ch = [c for c in chaine if c not in 'LR']
|
|
626
|
+
# return ''.join(ch)
|
|
627
|
+
# def _seemsStereoMic(tag):
|
|
628
|
+
# # is tag likely a stereo pair tag?
|
|
629
|
+
# # should starts with 'mic' and ends with 'l' or 'r'
|
|
630
|
+
# return tag[1:4]=='mic' and tag[0] in 'lr'
|
|
539
631
|
file=open(tracks_file,"r")
|
|
540
632
|
whole_txt = file.read()
|
|
541
|
-
logger.debug('all_lines:\n%s'%whole_txt)
|
|
633
|
+
logger.debug('file %s all_lines:\n%s'%(tracks_file, whole_txt))
|
|
542
634
|
tracks_lines_wspaces = [l.split('#')[0] for l in whole_txt.splitlines()
|
|
543
635
|
if len(l) > 0 ]
|
|
544
636
|
tracks_lines = [_WOspace(l) for l in tracks_lines_wspaces if len(l) > 0 ]
|
|
@@ -552,7 +644,7 @@ class Scanner:
|
|
|
552
644
|
splt += [None]
|
|
553
645
|
if len(splt) != 2:
|
|
554
646
|
# error
|
|
555
|
-
print('
|
|
647
|
+
print('\nText error in %s, line %s has too many ";"'%(
|
|
556
648
|
tracks_file, line))
|
|
557
649
|
return splt
|
|
558
650
|
tracks_lines, lag_values = zip(*[_detach_lag_value(l) for l
|
|
@@ -563,11 +655,10 @@ class Scanner:
|
|
|
563
655
|
logger.debug('tracks_lines lower case: %s'%tracks_lines)
|
|
564
656
|
# print(lag_values)
|
|
565
657
|
logger.debug('lag_values: %s'%lag_values)
|
|
566
|
-
tagsWOl_r = [e[1
|
|
567
|
-
logger.debug('tags WO
|
|
658
|
+
tagsWOl_r = [e[:-1] for e in tracks_lines] # skip last letter
|
|
659
|
+
logger.debug('tags WO LR letter %s'%tagsWOl_r)
|
|
568
660
|
# find idx of start of pairs
|
|
569
|
-
# ['clics', 'TC', '
|
|
570
|
-
# ^ ^ ^
|
|
661
|
+
# ['clics', 'TC', 'micL', 'micR', 'mic2L', 'mic2R', 'mixL', 'mixR']
|
|
571
662
|
def _micOrmix(a,b):
|
|
572
663
|
# test if same and mic mic or mix mix
|
|
573
664
|
if len(a) == 0:
|
|
@@ -577,9 +668,10 @@ class Scanner:
|
|
|
577
668
|
in zip(tagsWOl_r,tagsWOl_r[1:])]) if same]
|
|
578
669
|
logger.debug('pair_idx_start %s'%pair_idx_start)
|
|
579
670
|
def LR_OK(idx):
|
|
580
|
-
# in tracks_lines, check if idx
|
|
581
|
-
|
|
582
|
-
|
|
671
|
+
# in tracks_lines, check if idx ends a LR pair
|
|
672
|
+
# delays, if any, have been removed
|
|
673
|
+
a = tracks_lines[idx][-1]
|
|
674
|
+
b = tracks_lines[idx+1][-1]
|
|
583
675
|
return a+b in ['lr', 'rl']
|
|
584
676
|
LR_OKs = [LR_OK(p) for p in pair_idx_start]
|
|
585
677
|
logger.debug('LR_OKs %s'%LR_OKs)
|
|
@@ -625,11 +717,11 @@ class Scanner:
|
|
|
625
717
|
def _findLR(i_first):
|
|
626
718
|
# returns L R indexes (+1 for sox non zero based indexing)
|
|
627
719
|
i_2nd = i_first + 1
|
|
628
|
-
a = tracks_lines[i_first][
|
|
629
|
-
b = tracks_lines[i_2nd][
|
|
720
|
+
a = tracks_lines[i_first][-1] # l|r at end
|
|
721
|
+
b = tracks_lines[i_2nd][-1] # l|r at end
|
|
630
722
|
if a == 'l':
|
|
631
723
|
if b == 'r':
|
|
632
|
-
# sequence is
|
|
724
|
+
# sequence is mixL mixR
|
|
633
725
|
return i_first+1, i_2nd+1
|
|
634
726
|
else:
|
|
635
727
|
print('\nError in %s'%tracks_file)
|
|
@@ -638,7 +730,7 @@ class Scanner:
|
|
|
638
730
|
quit()
|
|
639
731
|
elif a == 'r':
|
|
640
732
|
if b == 'l':
|
|
641
|
-
# sequence is
|
|
733
|
+
# sequence is mixR mixL
|
|
642
734
|
return i_2nd+1, i_first+1
|
|
643
735
|
else:
|
|
644
736
|
print('\nError in %s'%tracks_file)
|
|
@@ -666,7 +758,7 @@ class Scanner:
|
|
|
666
758
|
# those are stereo pairs
|
|
667
759
|
stereo_pairs = []
|
|
668
760
|
for first_in_pair in pair_idx_start:
|
|
669
|
-
suffix = tracks_lines[first_in_pair][1
|
|
761
|
+
suffix = tracks_lines[first_in_pair][:-1]
|
|
670
762
|
stereo_pairs.append((suffix, _findLR(first_in_pair)))
|
|
671
763
|
logger.debug('stereo_pairs %s'%stereo_pairs)
|
|
672
764
|
output_tracks.stereomics = stereo_pairs
|