tictacsync 0.1a13__py3-none-any.whl → 0.1a15__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.

@@ -17,6 +17,8 @@ AMR APE AU AWB DSS DVF FLAC GSM IKLAX IVS M4A M4B M4P MMF MP3 MPC MSV NMF
17
17
  OGG OGA MOGG OPUS RA RM RAW RF64 SLN TTA VOC VOX WAV WMA WV WEBM 8SVX CDA MOV AVI BWF""".split()
18
18
 
19
19
  import ffmpeg
20
+ from os import listdir
21
+ from os.path import isfile, join, isdir
20
22
  from collections import namedtuple
21
23
  from pathlib import Path
22
24
  from pprint import pformat
@@ -121,24 +123,28 @@ def get_device_ffprobe_UID(file):
121
123
  class Scanner:
122
124
  """
123
125
  Class that encapsulates scanning of the directory given as CLI argument.
124
- Depending on the IO structure choosen (loose|foldercam), enforce some directory
126
+ Depending on the IO structure choosen (loose|folder_is_device), enforce some directory
125
127
  structure (or not). Build a list of media files found and a try to
126
128
  indentify uniquely the device used to record each media file.
127
129
 
128
130
  Attributes:
129
131
 
130
- IO_structure: string
132
+ input_structure: string
131
133
 
132
134
  Any of
133
- 'loose' [DEFAULT]
134
- all files audio + video in the same folder
135
- 'foldercam'
135
+ 'loose'
136
+ all files audio + video are in top folder
137
+ 'folder_is_device'
136
138
  eg for multicam on Davinci Resolve
137
139
 
138
140
  top_directory : string
139
141
 
140
142
  String of path of where to start searching for media files.
141
143
 
144
+ top_dir_has_multicam : bool
145
+
146
+ If top dir is folder structures AND more than on cam
147
+
142
148
  devices_names : dict of str
143
149
 
144
150
  more evocative names for each device, keys are same as
@@ -148,14 +154,13 @@ class Scanner:
148
154
  {
149
155
  'path' : as is ,
150
156
  'sample length' : as is
151
- 'dev' : Device named tuple
157
+ 'dev' : Device namedtuple
152
158
  }
153
159
  """
154
160
 
155
161
  def __init__(
156
162
  self,
157
163
  top_directory,
158
- IOstruct,
159
164
  stay_silent=False,
160
165
  ):
161
166
  """
@@ -164,9 +169,9 @@ class Scanner:
164
169
  """
165
170
  self.top_directory = top_directory
166
171
  self.found_media_files = []
167
- self.IOstruct = IOstruct
168
172
  self.stay_silent = stay_silent
169
173
 
174
+
170
175
  def get_devices_number(self):
171
176
  # how many devices have been found
172
177
  return len(set([m['dev'].UID for m in self.found_media_files]))
@@ -185,12 +190,33 @@ class Scanner:
185
190
 
186
191
  Populates Scanner.found_media_files, a list of dict as
187
192
  {
188
- 'path' : # as is,
189
- 'sample length' : # in samples
190
- 'dev UID' : dev_UID or None
193
+ 'path' : as is ,
194
+ 'sample length' : as is
195
+ 'dev' : Device namedtuple
191
196
  }
192
197
 
198
+ Sets input_structure = 'loose'|'folder_is_device'
199
+
193
200
  """
201
+ visible = [f for f in listdir(self.top_directory) if f[0] != '.']
202
+ logger.debug('visible: %s'%visible)
203
+ are_dir = all([isdir(join(self.top_directory, f)) for f in visible])
204
+ are_files = all([isfile(join(self.top_directory, f)) for f in visible])
205
+ # onlyfiles = [f for f in listdir(self.top_directory) if isfile()]
206
+
207
+ logger.debug('dir: %s'%[isdir(join(self.top_directory, f)) for f in visible])
208
+ if are_dir:
209
+ print('\nAssuming those are device folders: ',end='')
210
+ [print(f, end=', ') for f in visible[:-1]]
211
+ print('%s.\n'%visible[-1])
212
+ self.input_structure = 'folder_is_device'
213
+ else: # are_files
214
+ self.input_structure = 'loose'
215
+ if not are_dir and not are_files:
216
+ print('\nInput structure mixed up, files AND folders at top level')
217
+ print(' %s, quitting.\n'%self.top_directory)
218
+ quit()
219
+ # quit()
194
220
  files = Path(self.top_directory).rglob('*.*')
195
221
  paths = [
196
222
  p
@@ -202,15 +228,17 @@ class Scanner:
202
228
  new_media = media_dict_from_path(p) # dev UID set here
203
229
  self.found_media_files.append(new_media)
204
230
  logger.debug('Scanner.found_media_files = %s'%self.found_media_files)
205
- if self.IOstruct == 'foldercam':
206
- self._enforce_folder_structure_alpha()
207
- self._use_folder_as_device_ID()
231
+ if self.input_structure == 'folder_is_device':
232
+ self._enforce_folder_is_device()
233
+ self._use_folder_as_device_name()
234
+ else:
235
+ self.top_dir_has_multicam = False
208
236
  pprint_found_media_files = pformat(self.found_media_files)
209
237
  logger.debug('scanner.found_media_files = %s'%pprint_found_media_files)
210
238
 
211
- def _use_folder_as_device_ID(self):
239
+ def _use_folder_as_device_name(self):
212
240
  """
213
- For each media in self.found_media_files replace existing dev_UID by
241
+ For each media in self.found_media_files replace existing Device.name by
214
242
  folder name.
215
243
 
216
244
  Returns nothing
@@ -229,7 +257,7 @@ class Scanner:
229
257
  # quit()
230
258
  logger.debug(self.found_media_files)
231
259
 
232
- def _enforce_folder_structure_alpha(self):
260
+ def _enforce_folder_is_device(self):
233
261
  """
234
262
 
235
263
  Checks for files in self.found_media_files for structure as following.
@@ -259,6 +287,7 @@ class Scanner:
259
287
  # build lists for multiple reference of iterators
260
288
  media_grouped_by_folder = [ (k, list(iterator)) for k, iterator
261
289
  in groupby(medias, folder_key)]
290
+ logger.debug('media_grouped_by_folder %s'%media_grouped_by_folder)
262
291
  complete_path_folders = [e[0] for e in media_grouped_by_folder]
263
292
  name_of_folders = [p.name for p in complete_path_folders]
264
293
  logger.debug('complete_path_folders with media files %s'%complete_path_folders)
@@ -276,11 +305,15 @@ class Scanner:
276
305
  print('please rename and rerun. Quitting..')
277
306
  quit()
278
307
  # print(media_grouped_by_folder)
308
+ n_CAM_folder = 0
279
309
  for folder, list_of_medias_in_folder in media_grouped_by_folder:
280
310
  # list_of_medias_in_folder = list(media_files_same_folder_iterator)
281
311
  # check all medias are either video or audio recordings in folder
282
312
  # if not, warn user and quit.
283
313
  dev_types = set([m['dev'].type for m in list_of_medias_in_folder])
314
+ logger.debug('dev_types %s'%dev_types)
315
+ if dev_types == {'CAM'}:
316
+ n_CAM_folder += 1
284
317
  if len(dev_types) != 1:
285
318
  print('\nProblem while scanning for media files. In [gold1]%s[/gold1]:'%folder)
286
319
  print('There is a mix of video and audio files:')
@@ -295,7 +328,7 @@ class Scanner:
295
328
  logger.debug('devices in folder %s:'%folder)
296
329
  logger.debug(' media with unknown devices %s'%unidentified)
297
330
  logger.debug(' media with UIDed devices %s'%UIDed)
298
- if False and len(unidentified) != 0 and len(UIDed) != 0:
331
+ if len(unidentified) != 0 and len(UIDed) != 0:
299
332
  print('\nProblem while grouping files in [gold1]%s[/gold1]:'%folder)
300
333
  print('There is a mix of unidentifiable and identified devices.')
301
334
  print('Is this file:')
@@ -328,7 +361,10 @@ class Scanner:
328
361
  # if we are here, the check is done: either
329
362
  # all files in folder are from unidentified device or
330
363
  # all files in folder are from the same identified device
331
- return
364
+ logger.debug('n_CAM_folder %i'%n_CAM_folder)
365
+ self.top_dir_has_multicam = n_CAM_folder > 1
366
+ logger.debug('top_dir_has_multicam: %s'%self.top_dir_has_multicam)
367
+ return
332
368
 
333
369
 
334
370
 
tictacsync/entry.py CHANGED
@@ -118,25 +118,28 @@ def main():
118
118
  parser.add_argument('-p', action='store_true', default=False,
119
119
  dest='plotting',
120
120
  help='Make plots')
121
+ parser.add_argument('--isos', action='store_true', default=False,
122
+ dest='write_ISOs',
123
+ help='Write ISO sound files')
121
124
  parser.add_argument('--nosync', action='store_true',
122
125
  dest='nosync',
123
126
  help='just scan and decode')
124
127
  parser.add_argument('--terse', action='store_true',
125
128
  dest='terse',
126
129
  help='terse output')
127
- parser.add_argument('--io', dest='output_structure', type=str, default='loose',
128
- help='should be one of: loose [DEFAULT], foldercam')
130
+ # parser.add_argument('--io', dest='output_structure', type=str, default='loose',
131
+ # help='should be one of: loose [DEFAULT], foldercam')
129
132
  args = parser.parse_args()
130
- if not args.output_structure in 'loose foldercam reconf'.split():
131
- print('Unknown --io option value "%s", should be one of: loose [DEFAULT] or foldercam'%args.output_structure)
132
- quit()
133
+ # if not args.output_structure in 'loose foldercam reconf'.split():
134
+ # print('Unknown --io option value "%s", should be one of: loose [DEFAULT] or foldercam'%args.output_structure)
135
+ # quit()
133
136
  if args.verbose_output:
134
137
  logger.add(sys.stderr, level="DEBUG")
135
138
  # logger.add(sys.stdout, filter="__main__")
136
139
  # logger.add(sys.stdout, filter="device_scanner")
137
140
  # logger.add(sys.stdout, filter="yaltc")
138
141
  # logger.add(sys.stdout, filter="timeline")
139
- # logger.add(sys.stdout, filter=lambda r: r["function"] == "_parse_track_values")
142
+ # logger.add(sys.stdout, filter=lambda r: r["function"] == "_enforce_folder_is_device")
140
143
  # logger.add(sys.stdout, filter=lambda r: r["function"] == "build_audio_and_write_video")
141
144
  # logger.add(sys.stdout, filter=lambda r: r["function"] == "_get_BFSK_word_boundaries")
142
145
  top_dir = args.directory[0]
@@ -147,8 +150,7 @@ def main():
147
150
  print('%s is not a directory or doesnt exist.'%top_dir)
148
151
  quit()
149
152
  multi2polywav.poly_all(top_dir)
150
- scanner = device_scanner.Scanner(top_dir, args.output_structure,
151
- stay_silent=args.terse)
153
+ scanner = device_scanner.Scanner(top_dir, stay_silent=args.terse)
152
154
  # (Path(top_dir)/Path('tictacsynced')).mkdir(exist_ok=True)
153
155
  # scanner.cluster_media_files_by_name()
154
156
  scanner.scan_media_and_build_devices_UID(recursive=False)
@@ -208,6 +210,7 @@ def main():
208
210
  console.print(table)
209
211
  print('\n')
210
212
  n_devices = scanner.get_devices_number()
213
+ OUT_struct_for_mcam = scanner.top_dir_has_multicam
211
214
  # if n_devices > 2:
212
215
  # print('\nMerging for more than 2 devices is not implemented yet, quitting...')
213
216
  # quit()
@@ -215,7 +218,7 @@ def main():
215
218
  if not args.terse:
216
219
  print('\nNothing to sync, exiting.\n')
217
220
  quit()
218
- matcher = timeline.Matcher(recordings_with_time, args.output_structure)
221
+ matcher = timeline.Matcher(recordings_with_time)
219
222
  matcher.scan_audio_for_each_ref_rec()
220
223
  if not matcher.video_mergers:
221
224
  if not args.terse:
@@ -223,23 +226,25 @@ def main():
223
226
  quit()
224
227
  if args.verbose_output or args.terse: # verbose, so no progress bars
225
228
  for stitcher in matcher.video_mergers:
226
- stitcher.build_audio_and_write_video(top_dir, args.output_structure)
229
+ stitcher.build_audio_and_write_video(top_dir, OUT_struct_for_mcam,
230
+ args.write_ISOs)
227
231
  else:
228
232
  for stitcher in track(matcher.video_mergers,
229
233
  description="4/4 Merging sound to videos:"):
230
- stitcher.build_audio_and_write_video(top_dir, args.output_structure)
234
+ stitcher.build_audio_and_write_video(top_dir, OUT_struct_for_mcam,
235
+ args.write_ISOs)
231
236
  if not args.terse:
232
237
  print("\n")
233
- if args.output_structure == 'foldercam':
234
- print('output written in [gold1]%s[/gold1] folder in [gold1]%s[/gold1]\n'%(
238
+ # if args.output_structure == 'foldercam':
239
+ print('output written in [gold1]%s[/gold1] folder in [gold1]%s[/gold1]\n'%(
235
240
  'SyncedMedia',top_dir))
236
- if args.output_structure == 'loose':
237
- for stitcher in matcher.video_mergers:
238
- print('[gold1]%s[/gold1]'%stitcher.ref_recording.AVpath.name, end='')
239
- for audio in stitcher.get_matched_audio_recs():
240
- print(' + [gold1]%s[/gold1]'%audio.AVpath.name, end='')
241
- new_file = stitcher.ref_recording.final_synced_file.parts
242
- print(' became [gold1]%s[/gold1]'%stitcher.ref_recording.final_synced_file.name)
241
+ # if args.output_structure == 'loose':
242
+ for stitcher in matcher.video_mergers:
243
+ print('[gold1]%s[/gold1]'%stitcher.ref_recording.AVpath.name, end='')
244
+ for audio in stitcher.get_matched_audio_recs():
245
+ print(' + [gold1]%s[/gold1]'%audio.AVpath.name, end='')
246
+ new_file = stitcher.ref_recording.final_synced_file.parts
247
+ print(' became [gold1]%s[/gold1]'%stitcher.ref_recording.final_synced_file.name)
243
248
  # matcher._build_otio_tracks_for_cam()
244
249
  matcher.shrink_gaps_between_takes()
245
250
 
tictacsync/timeline.py CHANGED
@@ -464,7 +464,7 @@ class AudioStitcherVideoMerger:
464
464
  return {'error msg': 'more than one "mix" token'}
465
465
  output_dict['mix'] = [mono_mix_tags[0][1]]
466
466
  else:
467
- output_dict['mix'] = None
467
+ output_dict['mix'] = []
468
468
  [tracks_lines.remove(e) for e in mono_mix_tags]
469
469
  logger.debug('mono mix done, will continue with %s'%tracks_lines)
470
470
  # fourth, look for 'ttc'
@@ -484,18 +484,22 @@ class AudioStitcherVideoMerger:
484
484
  output_dict['0'] = zeroed
485
485
  [tracks_lines.remove(('0',i)) for i in zeroed]
486
486
  else:
487
- output_dict['0'] = None
487
+ output_dict['0'] = []
488
488
  # sixth, check for 'others'
489
489
  logger.debug('0s done, will continue with %s'%tracks_lines)
490
490
  if tracks_lines:
491
491
  output_dict['others'] = tracks_lines
492
492
  else:
493
- output_dict['others'] = None
493
+ output_dict['others'] = []
494
494
  return output_dict
495
495
 
496
- def build_audio_and_write_video(self, top_dir, IO_structure):
496
+ def build_audio_and_write_video(self, top_dir,
497
+ OUT_struct_for_mcam,
498
+ write_ISOs):
497
499
  """
498
- IO_structure is either: loose | foldercam
500
+ OUT_struct_for_mcam: bool flag
501
+
502
+ write_ISOs: bool flag
499
503
 
500
504
  For each audio devices found overlapping self.ref_recording: pad, trim
501
505
  or stretch audio files calling _get_concatenated_audiofile_for(), and
@@ -509,15 +513,14 @@ class AudioStitcherVideoMerger:
509
513
  """
510
514
  logger.debug('device for rec %s: %s'%(self.ref_recording,
511
515
  self.ref_recording.device))
512
- match IO_structure:
513
- case 'foldercam':
514
- D1, D2 = CAMDIR.split('/')
515
- device_name = self.ref_recording.device.name
516
- synced_clip_dir = Path(top_dir)/D1/D2/device_name
517
- os.makedirs(synced_clip_dir, exist_ok=True)
518
- logger.debug('created %s'%synced_clip_dir)
519
- case 'loose':
520
- synced_clip_dir = Path(top_dir)
516
+ D1, D2 = CAMDIR.split('/')
517
+ if OUT_struct_for_mcam:
518
+ device_name = self.ref_recording.device.name
519
+ synced_clip_dir = Path(top_dir)/D1/D2/device_name
520
+ logger.debug('created %s'%synced_clip_dir)
521
+ else:
522
+ synced_clip_dir = Path(top_dir)/D1
523
+ os.makedirs(synced_clip_dir, exist_ok=True)
521
524
  synced_clip_file = synced_clip_dir/Path(self.ref_recording.new_rec_name).name
522
525
  logger.debug('editing files for %s'%synced_clip_file)
523
526
  # looping over devices:
@@ -540,17 +543,34 @@ class AudioStitcherVideoMerger:
540
543
  # look for track names in TRACKSFN file:
541
544
  tracks_file = source_audio_folder/TRACKSFN
542
545
  track_names = False
546
+ nchan = sox.file_info.channels(str(joined_audio))
543
547
  if os.path.isfile(tracks_file):
544
548
  logger.debug('found file: %s'%(TRACKSFN))
545
549
  track_dict = self._parse_track_values(tracks_file)
550
+ if track_dict['error msg']:
551
+ print('\nError parsing %s file: %s, quitting.\n'%(tracks_file,
552
+ track_dict['error msg']))
553
+ quit()
554
+
546
555
  logger.debug('parsed track_dict %s'%track_dict)
556
+ n_IDied_chan = 2*len(track_dict['stereomics'])
557
+ n_IDied_chan += len(track_dict['mix'])
558
+ n_IDied_chan += len(track_dict['0'])
559
+ n_IDied_chan += len(track_dict['others'])
560
+ n_IDied_chan += 1 # for ttc track
561
+ logger.debug(' n chan: %i n tracks file: %i'%(nchan, n_IDied_chan))
562
+ if n_IDied_chan != nchan:
563
+ print('\nError parsing %s content'%tracks_file)
564
+ print('incoherent number of tracks, %i vs %i quitting\n'%
565
+ (nchan, n_IDied_chan))
566
+ # quit()
547
567
  err_msg = track_dict['error msg']
548
568
  if err_msg != None:
549
569
  print('Error, quitting: in file %s, %s'%(tracks_file, err_msg))
550
570
  raise Exception
551
571
  else:
552
572
  logger.debug('no tracks.txt file found')
553
- if IO_structure == 'foldercam':
573
+ if write_ISOs:
554
574
  # build ISOs subfolders structure, see comment string below
555
575
  video_stem_WO_suffix = synced_clip_file.stem.split('.')[0]
556
576
  D1, D2 = ISOsDIR.split('/')
@@ -561,11 +581,6 @@ class AudioStitcherVideoMerger:
561
581
  logger.debug('will split audio to %s'%(ISOdir))
562
582
  shutil.copy(joined_audio, ISO_multi_chan)
563
583
  filepath = str(ISO_multi_chan)
564
- nchan = sox.file_info.channels(str(ISO_multi_chan))
565
- if track_names and nchan != len(track_names):
566
- print('error parsing %s content'%tracks_file)
567
- print('incoherent number of tracks, quitting\n')
568
- quit()
569
584
  for n in range(nchan):
570
585
  if track_names:
571
586
  iso_destination = ISOdir / (track_names[n]+'.wav')
@@ -599,7 +614,6 @@ class AudioStitcherVideoMerger:
599
614
  canon24fps02.MOV
600
615
  """
601
616
 
602
-
603
617
  def _keep_VIDEO_only(self, video_path):
604
618
  # return file handle to a temp video file formed from the video_path
605
619
  # stripped of its sound
@@ -701,7 +715,7 @@ class Matcher:
701
715
 
702
716
  """
703
717
 
704
- def __init__(self, recordings_list, IO_structure):
718
+ def __init__(self, recordings_list):
705
719
  """
706
720
  At this point in the program, all recordings in recordings_list should
707
721
  have a valid Recording.start_time attribute and one of its channels
@@ -711,31 +725,31 @@ class Matcher:
711
725
  """
712
726
  self.recordings = recordings_list
713
727
  self.video_mergers = []
714
- self._rename_all_recs(IO_structure)
728
+ # self._rename_all_recs(IO_structure)
715
729
 
716
730
 
717
- def _rename_all_recs(self, IO_structure):
731
+ def _rename_all_recs(self):
718
732
  """
719
733
  Add _synced to filenames of synced video files. Change stored name only:
720
734
  files have yet to be written to.
721
735
  """
722
- match IO_structure:
723
- case 'foldercam':
724
- for rec in self.recordings:
725
- rec_extension = rec.AVpath.suffix
726
- rel_path_new_name = '%s%s'%(rec.AVpath.stem, rec_extension)
727
- rec.new_rec_name = Path(rel_path_new_name)
728
- logger.debug('for %s new name: %s'%(
729
- _pathname(rec.AVpath),
730
- _pathname(rec.new_rec_name)))
731
- case 'loose':
732
- for rec in self.recordings:
733
- rec_extension = rec.AVpath.suffix
734
- rel_path_new_name = '%s%s%s'%('ttsd_',rec.AVpath.stem, rec_extension)
735
- rec.new_rec_name = Path(rel_path_new_name)
736
- logger.debug('for %s new name: %s'%(
737
- _pathname(rec.AVpath),
738
- _pathname(rec.new_rec_name)))
736
+ # match IO_structure:
737
+ # case 'foldercam':
738
+ for rec in self.recordings:
739
+ rec_extension = rec.AVpath.suffix
740
+ rel_path_new_name = '%s%s'%(rec.AVpath.stem, rec_extension)
741
+ rec.new_rec_name = Path(rel_path_new_name)
742
+ logger.debug('for %s new name: %s'%(
743
+ _pathname(rec.AVpath),
744
+ _pathname(rec.new_rec_name)))
745
+ # case 'loose':
746
+ # for rec in self.recordings:
747
+ # rec_extension = rec.AVpath.suffix
748
+ # rel_path_new_name = '%s%s%s'%('ttsd_',rec.AVpath.stem, rec_extension)
749
+ # rec.new_rec_name = Path(rel_path_new_name)
750
+ # logger.debug('for %s new name: %s'%(
751
+ # _pathname(rec.AVpath),
752
+ # _pathname(rec.new_rec_name)))
739
753
 
740
754
  def scan_audio_for_each_ref_rec(self):
741
755
  """
tictacsync/yaltc.py CHANGED
@@ -68,42 +68,42 @@ N_SYMBOLS_SAMD21 = 35 # including sync pulse
68
68
 
69
69
  BPF_LOW_FRQ, BPF_HIGH_FRQ = (0.5*F1, 2*F2)
70
70
 
71
- try:
72
- layouts, _ = (
73
- ffmpeg.input('').output('')
74
- .global_args("-loglevel", "quiet","-nostats","-hide_banner","-layouts")
75
- .run(capture_stdout=True)
76
- )
77
- except ffmpeg.Error as e:
78
- print('error',e.stderr)
79
-
80
-
81
-
82
- names_str, layouts = [l.split('\n') for l in
83
- layouts.decode("utf-8").split('DECOMPOSITION')]
84
- [names_str.pop(0) for i in range(2)]
85
- [names_str.pop(-1) for i in range(3)]
86
- layouts.pop(0)
87
- layouts.pop(-1)
88
- channel_name_regex = re.compile(r'(\w+)\s+(.+)')
89
- layouts_regex = re.compile(r'(\S+)\s+(.+)')
90
- FFMPEG_CHAN_NAMES = [
91
- channel_name_regex.match(name).group(1)
92
- for name in names_str
93
- ]
94
- FFMPEG_LAYO_NAMES = dict([
95
- layouts_regex.match(lay_out).groups()
96
- for lay_out in layouts
97
- ])
98
-
99
- """
100
- FFMPEG_CHAN_NAMES are the names used by ffmpeg for channels, eg: FL, FR, FC for
101
- front left, front right and front center.
102
- FFMPEG_LAYO_NAMES are the order of channels for each layout, eg: stereo is FL+FR
103
- and 5.1 is FL+FR+FC+LFE+BL+BR. Layout names are the dict keys.
104
- Run ffmpeg -layouts for details.
105
-
106
- """
71
+ # try:
72
+ # layouts, _ = (
73
+ # ffmpeg.input('').output('')
74
+ # .global_args("-loglevel", "quiet","-nostats","-hide_banner","-layouts")
75
+ # .run(capture_stdout=True)
76
+ # )
77
+ # except ffmpeg.Error as e:
78
+ # print('error',e.stderr)
79
+
80
+
81
+
82
+ # names_str, layouts = [l.split('\n') for l in
83
+ # layouts.decode("utf-8").split('DECOMPOSITION')]
84
+ # [names_str.pop(0) for i in range(2)]
85
+ # [names_str.pop(-1) for i in range(3)]
86
+ # layouts.pop(0)
87
+ # layouts.pop(-1)
88
+ # channel_name_regex = re.compile(r'(\w+)\s+(.+)')
89
+ # layouts_regex = re.compile(r'(\S+)\s+(.+)')
90
+ # FFMPEG_CHAN_NAMES = [
91
+ # channel_name_regex.match(name).group(1)
92
+ # for name in names_str
93
+ # ]
94
+ # FFMPEG_LAYO_NAMES = dict([
95
+ # layouts_regex.match(lay_out).groups()
96
+ # for lay_out in layouts
97
+ # ])
98
+
99
+ # """
100
+ # FFMPEG_CHAN_NAMES are the names used by ffmpeg for channels, eg: FL, FR, FC for
101
+ # front left, front right and front center.
102
+ # FFMPEG_LAYO_NAMES are the order of channels for each layout, eg: stereo is FL+FR
103
+ # and 5.1 is FL+FR+FC+LFE+BL+BR. Layout names are the dict keys.
104
+ # Run ffmpeg -layouts for details.
105
+
106
+ # """
107
107
 
108
108
  # utility for accessing pathnames
109
109
  def _pathname(tempfile_or_path):
@@ -620,7 +620,6 @@ class Decoder:
620
620
  format="png")
621
621
  plt.close()
622
622
 
623
-
624
623
  def _detect_sync_pulse_position(self):
625
624
  """
626
625
  Determines noise level during silence period and use it to detect the
@@ -1022,10 +1021,13 @@ class Recording:
1022
1021
  probe : dict
1023
1022
  returned value of ffmpeg.probe(self.AVpath)
1024
1023
 
1025
- YaLTC_channel : tuple of int
1026
- which channel is sync track, 0 for left or mono,
1027
- 1 for right. Value is None if not YaLTC found.
1028
- Set in _read_sound_extract().
1024
+ YaLTC_channel : int
1025
+ which channel is sync track. 0 is first channel,
1026
+ set in _read_sound_find_YaLTC().
1027
+
1028
+ silent_channels : list of ints
1029
+ list of channel which are silent. 1 is first channel,
1030
+ set in _strip_yaltc()
1029
1031
 
1030
1032
  decoder : yaltc.decoder
1031
1033
  associated decoder object, if file is audiovideo
@@ -1461,6 +1463,8 @@ class Recording:
1461
1463
  Returns a tempfile.NamedTemporaryFile of sound file stripped
1462
1464
  of YaLTC channel and of any silent channel. Criteria for
1463
1465
  silence is sox stats "RMS lev db" < DB_RMS_SILENCE_SOX
1466
+
1467
+ Sets self.silent_channels
1464
1468
  """
1465
1469
  input_file = _pathname(self.AVpath)
1466
1470
  # building dict according to pysox.remix format.
@@ -1472,12 +1476,12 @@ class Recording:
1472
1476
  _, _, stat_output = sox.core.sox(args)
1473
1477
  sox_RMS_lev_dB = stat_output.split('\n')[5].split()[4:]
1474
1478
  logger.debug('Sox RMS %s, n_channels %i'%(sox_RMS_lev_dB, n_channels))
1475
- silent_channel = [i + 1 for i,db in enumerate(sox_RMS_lev_dB)
1479
+ self.silent_channels = [i + 1 for i,db in enumerate(sox_RMS_lev_dB)
1476
1480
  if float(db) < DB_RMS_SILENCE_SOX]
1477
- logger.debug('silent chans (1st = #1) %s'%silent_channel)
1481
+ logger.debug('silent chans (1st = #1) %s'%self.silent_channels)
1478
1482
  chan_list_out = range(1, n_channels) # eg [1,2,3]
1479
1483
  all_channels = range(1, n_channels + 1) # eg [1,2,3,4]
1480
- excluded_chans = silent_channel + [YaLTC_channel]
1484
+ excluded_chans = self.silent_channels + [YaLTC_channel]
1481
1485
  logger.debug('excluded chans %s'%excluded_chans)
1482
1486
  kept_chans = [[n] for n in all_channels if n not in excluded_chans]
1483
1487
  # eg [[1], [3], [4]]
@@ -1,8 +1,8 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: tictacsync
3
- Version: 0.1a13
3
+ Version: 0.1a15
4
4
  Summary: command for syncing audio video recordings
5
- Home-page: https://sr.ht/~proflutz/TicTacSync/
5
+ Home-page: https://tictacsync.org/
6
6
  Author: Raymond Lutz
7
7
  Author-email: lutzrayblog@mac.com
8
8
  Classifier: Development Status :: 2 - Pre-Alpha
@@ -0,0 +1,13 @@
1
+ tictacsync/LTCcheck.py,sha256=SUq93zfky5njzvnsnIj-5nC01QYf3hVBegJftPOFeJQ,15708
2
+ tictacsync/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
+ tictacsync/device_scanner.py,sha256=wDkhE_H33Zh5RIjASS33HOmX5-qI-dJ3h9yyvaTrAI0,15177
4
+ tictacsync/entry.py,sha256=ICTDI6ZPaPsDnkZX-c7dahr6qpG09x618j9nPh3x2jM,10279
5
+ tictacsync/multi2polywav.py,sha256=7Qnb986D3HJIAkNJ3IyvPWxIdqqh3KIfwnwFoUBw0pU,7013
6
+ tictacsync/timeline.py,sha256=1NpfbvBv24oDtet8SafPcpvk5ppK8qTdEybs8qz8gkc,39258
7
+ tictacsync/yaltc.py,sha256=Hu827V61yp1VcjZHJ9HMlCL_EEcxlhi_4hesOQy66bc,71391
8
+ tictacsync-0.1a15.dist-info/LICENSE,sha256=ZAOPXLh1zlQAnhHUd7oLslKM01YZ5UiAu3STYjwIxck,1068
9
+ tictacsync-0.1a15.dist-info/METADATA,sha256=oHbCfMD9Ar3utJci8OLQ-Sx-VlUbz9ZtfWYj26KoTs0,4206
10
+ tictacsync-0.1a15.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
11
+ tictacsync-0.1a15.dist-info/entry_points.txt,sha256=bre_DmdWFgHljcC0cyqh_dC9siGANO04MQ_wYsO2sxY,135
12
+ tictacsync-0.1a15.dist-info/top_level.txt,sha256=eaCWG-BsYTRR-gLTJbK4RfcaXajr0gjQ6wG97MkGRrg,11
13
+ tictacsync-0.1a15.dist-info/RECORD,,
@@ -1,13 +0,0 @@
1
- tictacsync/LTCcheck.py,sha256=SUq93zfky5njzvnsnIj-5nC01QYf3hVBegJftPOFeJQ,15708
2
- tictacsync/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
- tictacsync/device_scanner.py,sha256=7fIYyzbrhDj1slBUkO6O1aqD81vUa-8uTaD7pBs178o,13604
4
- tictacsync/entry.py,sha256=TU1n_dBPYP329zOMj-gJ23kRskRK2ZYZLjCBAD1GcVc,9997
5
- tictacsync/multi2polywav.py,sha256=7Qnb986D3HJIAkNJ3IyvPWxIdqqh3KIfwnwFoUBw0pU,7013
6
- tictacsync/timeline.py,sha256=bIyi3vARsGKDGz0m3DfRsp6nw-HAyuj2VY9vkcFDqWQ,38690
7
- tictacsync/yaltc.py,sha256=6YqqdFEnOgP9MkPWgps4HOPb0BI2NFUixqrMkSQFpE0,71203
8
- tictacsync-0.1a13.dist-info/LICENSE,sha256=ZAOPXLh1zlQAnhHUd7oLslKM01YZ5UiAu3STYjwIxck,1068
9
- tictacsync-0.1a13.dist-info/METADATA,sha256=O3LTSHFLvWgsmLX_MDbN_26e6VdkgUnSVbU-tsKaWPE,4218
10
- tictacsync-0.1a13.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
11
- tictacsync-0.1a13.dist-info/entry_points.txt,sha256=bre_DmdWFgHljcC0cyqh_dC9siGANO04MQ_wYsO2sxY,135
12
- tictacsync-0.1a13.dist-info/top_level.txt,sha256=eaCWG-BsYTRR-gLTJbK4RfcaXajr0gjQ6wG97MkGRrg,11
13
- tictacsync-0.1a13.dist-info/RECORD,,