tictacsync 1.0.2a0__py3-none-any.whl → 1.2.0b0__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/mamsync.py CHANGED
@@ -9,17 +9,19 @@ try:
9
9
  from . import device_scanner
10
10
  from . import timeline
11
11
  from . import multi2polywav
12
+ from . import mamconf
12
13
  except:
13
14
  import yaltc
14
15
  import device_scanner
15
16
  import timeline
16
17
  import multi2polywav
18
+ import mamconf
17
19
 
18
20
  import argparse, tempfile, configparser
19
21
  from loguru import logger
20
22
  from pathlib import Path
21
23
  # import os, sys
22
- import os, sys, sox, platformdirs
24
+ import os, sys, sox, platformdirs, shutil, filecmp
23
25
  from rich.progress import track
24
26
  # from pprint import pprint
25
27
  from rich.console import Console
@@ -30,8 +32,8 @@ from pprint import pprint, pformat
30
32
  import numpy as np
31
33
 
32
34
  DEL_TEMP = False
33
- CONF_FILE = 'mamsync.cfg'
34
- LOG_FILE = 'mamdone.txt'
35
+ # CONF_FILE = 'mamsync.cfg'
36
+ # LOG_FILE = 'mamdone.txt'
35
37
 
36
38
  av_file_extensions = \
37
39
  """MOV webm mkv flv flv vob ogv ogg drc gif gifv mng avi MTS M2TS TS mov qt
@@ -40,12 +42,10 @@ m4v svi 3gp 3g2 mxf roq nsv flv f4v f4p f4a f4b 3gp aa aac aax act aiff alac
40
42
  amr ape au awb dss dvf flac gsm iklax ivs m4a m4b m4p mmf mp3 mpc msv nmf
41
43
  ogg oga mogg opus ra rm raw rf64 sln tta voc vox wav wma wv webm 8svx cda""".split()
42
44
 
45
+ logger.remove()
43
46
  # logger.add(sys.stdout, level="DEBUG")
44
-
45
-
46
- # logger.add(sys.stdout, filter=lambda r: r["function"] == "scan_media_and_build_devices_UID")
47
47
  # logger.add(sys.stdout, filter=lambda r: r["function"] == "main")
48
-
48
+ # logger.add(sys.stdout, filter=lambda r: r["function"] == "_write_ISOs")
49
49
 
50
50
  def process_single(file, args):
51
51
  # argument is a single file
@@ -112,102 +112,53 @@ def process_lag_adjustement(media_object):
112
112
  logger.debug('trimmed_multichanfile %s'%timeline._pathname(trimmed_multichanfile))
113
113
  Path(timeline._pathname(trimmed_multichanfile)).replace(media_object.path)
114
114
 
115
- def print_out_conf(raw_root, synced_root, snd_root):
116
- print(f'RAWROOT (source with TC): "{raw_root}"')
117
- print(f'SYNCEDROOT (destination of synced clips): "{synced_root}"')
118
- print(f'SNDROOT (destination of ISOs files): "{snd_root}"')
119
-
120
- def clear_log():
121
- # clear the file logging clips already synced
122
- data_dir = platformdirs.user_data_dir('mamsync', 'plutz', ensure_exists=True)
123
- log_file = Path(data_dir)/LOG_FILE
124
- print('Clearing log file "%s"'%log_file)
125
- with open(log_file, 'w') as fh:
126
- fh.write('done:\n')
127
-
128
- def write_conf(raw_root, synced_root, snd_root):
129
- # args are pahtlib.Paths.
130
- # RAWROOT: files with TC (and ROLL folders), as is from cameras
131
- # SYNCEDROOT: synced and no more TC (ROLL flattened)
132
- # Writes configuration on filesystem for later retrieval
133
- # Clears log of already synced clips.
134
- conf_dir = platformdirs.user_config_dir('mamsync', 'plutz', ensure_exists=True)
135
- logger.debug(f'will start project with raw_root:{raw_root}, synced_root:{synced_root}')
136
- conf_file = Path(conf_dir)/CONF_FILE
137
- logger.debug('writing config in %s'%conf_file)
138
- print(f'\nWriting folders paths in configuration file "{conf_file}"')
139
- print_out_conf(raw_root, synced_root, snd_root)
140
- conf_prs = configparser.ConfigParser()
141
- conf_prs['SECTION1'] = {'RAWROOT': raw_root,
142
- 'SYNCEDROOT': synced_root,
143
- 'SNDROOT': snd_root}
144
- with open(conf_file, 'w') as configfile_handle:
145
- conf_prs.write(configfile_handle)
146
- with open(conf_file, 'r') as configfile_handle:
147
- logger.debug(f'config file content: \n{configfile_handle.read()}')
148
- clear_log()
115
+ def copy_to_syncedroot(raw_root, synced_root):
116
+ # args are str
117
+ # copy dirs and non AV files
118
+ logger.debug(f'raw_root {raw_root}')
119
+ logger.debug(f'synced_root {synced_root}')
120
+ for raw_path in Path(raw_root).rglob('*'):
121
+ ext = raw_path.suffix[1:]
122
+ is_DS_Store = raw_path.name == '.DS_Store'# mac os
123
+ if ext not in av_file_extensions and not is_DS_Store:
124
+ logger.debug(f'raw_path: {raw_path}')
125
+ # dont copy WAVs either, they will be in ISOs
126
+ # synced_path = Path(synced_root)/str(raw_path)[1:] # cant join abs. paths
127
+ rel = raw_path.relative_to(raw_root)
128
+ logger.debug(f'relative path {rel}')
129
+ synced_path = Path(synced_root)/Path(raw_root).name/rel
130
+ logger.debug(f'synced_path: {synced_path}')
131
+ if raw_path.is_dir():
132
+ synced_path.mkdir(parents=True, exist_ok=True)
133
+ continue
134
+ # if here, it's a file
135
+ if not synced_path.exists():
136
+ print(f'will mirror non AV file {synced_path}')
137
+ logger.debug(f'will mirror non AV file at {synced_path}')
138
+ shutil.copy2(raw_path, synced_path)
139
+ continue
140
+ # file exists, check if same
141
+ same = filecmp.cmp(raw_path, synced_path, shallow=False)
142
+ logger.debug(f'copy exists of:\n{raw_path}\n{synced_path}')
143
+ if not same:
144
+ print(f'file changed, copying again\n{raw_path}')
145
+ shutil.copy2(raw_path, synced_path)
146
+ else:
147
+ logger.debug('same content, next')
148
+ continue # next raw_path in loop
149
149
 
150
+ def copy_raw_root_tree_to_sndroot(raw_root, snd_root):
151
+ # args are str
152
+ # copy only tree structure, no files
153
+ for raw_path in Path(raw_root).rglob('*'):
154
+ synced_path = Path(snd_root)/str(raw_path)[1:] # cant join abs. paths
155
+ if raw_path.is_dir():
156
+ synced_path.mkdir(parents=True, exist_ok=True)
150
157
 
151
158
 
152
- # if known_values != ():
153
- # RAWROOT, SYNCEDROOTS = known_values
154
- # print('Warning: there is a current project')
155
- # print('with source (RAW) folder: %s\nand destination (synced) folder: %s'%
156
- # (RAWROOT, SYNCEDROOTS))
157
- # answer = input("\nDo you want to change values? [YES|NO]")
158
- # if answer.upper()[0] in ["Y", "YES"]:
159
- # _write_cfg()
160
- # return folders
161
- # elif answer.upper()[0] in ["N", "NO"]:
162
- # print('Ok, will keep old ones')
163
- # return RAWROOT, SYNCEDROOTS
164
- # else:
165
- # _write_cfg()
166
- # return folders
167
- # sys.exit(0)
168
159
 
169
- def get_proj(print_conf_stdout=False):
170
- # check if user started a project before.
171
- # stored in platformdirs.user_config_dir
172
- # returns (RAWROOT, SYNCEDROOTS) if any, () otherwise.
173
- # print location of conf file if print_conf_stdout
174
- conf_dir = platformdirs.user_config_dir('mamsync', 'plutz')
175
- conf_file = Path(conf_dir)/CONF_FILE
176
- logger.debug('try reading config in %s'%conf_file)
177
- if print_conf_stdout:
178
- print(f'\nReading configuration from file {conf_file}')
179
- if conf_file.exists():
180
- conf_prs = configparser.ConfigParser()
181
- conf_prs.read(conf_file)
182
- RAWROOT = conf_prs.get('SECTION1', 'RAWROOT')
183
- SYNCEDROOT = conf_prs.get('SECTION1', 'SYNCEDROOT')
184
- SNDROOT = conf_prs.get('SECTION1', 'SNDROOT')
185
- logger.debug('read from conf: RAWROOT= %s SYNCEDROOT= %s SNDROOT=%s'%
186
- (RAWROOT, SYNCEDROOT, SNDROOT))
187
- return RAWROOT, SYNCEDROOT, SNDROOT
188
- else:
189
- logger.debug(f'no config file found at {conf_file}')
190
- return ()
191
-
192
-
193
- def main():
160
+ def new_parser():
194
161
  parser = argparse.ArgumentParser()
195
- parser.add_argument('--rawfolder',
196
- nargs = 1,
197
- dest='rawfolder',
198
- help='Sets new value for raw root folder (i.e.: clips with TC)')
199
- parser.add_argument('--syncedfolder',
200
- nargs = 1,
201
- dest='syncedfolder',
202
- help='Sets new value for synced root folder (i.e.: synced clips without TC)')
203
- parser.add_argument('--sndfolder',
204
- nargs = 1,
205
- dest='sndfolder',
206
- help='Sets new value for sound folder (where ISOs will be stored)')
207
- parser.add_argument('--showconf',
208
- action='store_true',
209
- dest='showconf',
210
- help='Show current configured values.')
211
162
  parser.add_argument('--resync',
212
163
  action='store_true',
213
164
  dest='resync',
@@ -222,47 +173,51 @@ def main():
222
173
  action='store_true',
223
174
  dest='terse',
224
175
  help='Terse output')
225
- parser.add_argument('--isos',
226
- action='store_true',
227
- dest='write_ISOs',
228
- help='Cut ISO sound files')
176
+ # parser.add_argument('--isos', # default True in mamsync
177
+ # action='store_true',
178
+ # dest='write_ISOs',
179
+ # help='Cut ISO sound files')
229
180
  parser.add_argument('-t','--timelineoffset',
230
181
  nargs=1,
231
182
  default=['00:00:00:00'],
232
183
  dest='timelineoffset',
233
184
  help='When processing multicam, where to place clips on NLE timeline (HH:MM:SS:FF)')
185
+ return parser
186
+
187
+ def clear_log():
188
+ # clear the file logging clips already synced
189
+ data_dir = platformdirs.user_data_dir('mamsync', 'plutz', ensure_exists=True)
190
+ log_file = Path(data_dir)/mamconf.LOG_FILE
191
+ print('Clearing log file of synced clips: "%s"'%log_file)
192
+ with open(log_file, 'w') as fh:
193
+ fh.write('done:\n')
194
+
195
+ def main():
196
+ parser = new_parser()
234
197
  args = parser.parse_args()
235
198
  logger.debug(f'arguments from argparse {args}')
236
- # check --rawfolder, --syncedfolder and --sndfolder are used together
237
- at_least_one = args.rawfolder != None or args.syncedfolder != None or args.sndfolder != None
238
- tous = args.rawfolder != None and args.syncedfolder != None and args.sndfolder != None
239
- if at_least_one:
240
- if not tous:
241
- print('Error: all --rawfolder, --syncedfolder and --sndfolder must be specified.')
199
+ roots_strings = mamconf.get_proj(False)
200
+ roots_pathlibPaths = [Path(s) for s in mamconf.get_proj(False)]
201
+ logger.debug(f'roots_strings from mamconf.get_proj {roots_strings}')
202
+ logger.debug(f'roots_pathlibPaths from mamconf.get_proj {roots_pathlibPaths}')
203
+ # check all have values, except for PROXIES, the last one
204
+ if any([r == '' for r in roots_strings][:-1]):
205
+ print("Can't sync if some folders are not set:")
206
+ mamconf.print_out_conf(*mamconf.get_proj())
207
+ print('Bye.')
208
+ sys.exit(0)
209
+ # because optional PROXIES folder '' yields a '.' path, exclude it
210
+ for r in [rp for rp in roots_pathlibPaths if rp != Path('.')]:
211
+ if not r.is_absolute():
212
+ print(f'\rError: folder {r} must be an absolute path. Bye')
242
213
  sys.exit(0)
243
- # check the 3 paths are ok
244
- roots = [Path(e) for e in [args.rawfolder[0],
245
- args.syncedfolder[0], args.sndfolder[0]]]
246
- for r in roots:
247
- if not r.is_absolute():
248
- print(f'\rError: folder {r} must be an absolute path. Bye')
249
- sys.exit(0)
250
- if not r.exists():
251
- print(f'\rError: folder {r} does not exist. Bye')
252
- sys.exit(0)
253
- if not r.is_dir():
254
- print(f'\rError: path {r} is not a folder. Bye')
255
- sys.exit(0)
256
- # if still here, everything in 3 folders is ok
257
- write_conf(*roots)
214
+ if not r.exists():
215
+ print(f'\rError: folder {r} does not exist. Bye')
258
216
  sys.exit(0)
259
- if args.showconf:
260
- raw_root, synced_root = get_proj(True)
261
- print_out_conf(raw_root, synced_root)
262
- sys.exit(0)
263
- # if still here, no rawfolder, --syncedfolder --sndfolder or --showconf
264
- # so go for a scan and sync, maybe with a sub_dir
265
- raw_root, synced_root, snd_root = get_proj(False)
217
+ if not r.is_dir():
218
+ print(f'\rError: path {r} is not a folder. Bye')
219
+ sys.exit(0)
220
+ raw_root, synced_root, snd_root, _ = roots_pathlibPaths
266
221
  if args.sub_dir != None:
267
222
  top_dir = args.sub_dir
268
223
  logger.debug(f'sub _dir: {args.sub_dir}')
@@ -273,6 +228,9 @@ def main():
273
228
  top_dir = raw_root
274
229
  if args.resync:
275
230
  clear_log()
231
+ # go, mamsync!
232
+ copy_to_syncedroot(raw_root, synced_root)
233
+ # copy_raw_root_tree_to_sndroot(raw_root, snd_root) # why?
276
234
  multi2polywav.poly_all(top_dir)
277
235
  scanner = device_scanner.Scanner(top_dir, stay_silent=args.terse)
278
236
  scanner.scan_media_and_build_devices_UID(synced_root=synced_root)
@@ -285,31 +243,6 @@ def main():
285
243
  process_lag_adjustement(m)
286
244
  audio_REC_only = all([m.device.dev_type == 'REC' for m
287
245
  in scanner.found_media_files])
288
- # if audio_REC_only:
289
- # if scanner.input_structure != 'ordered':
290
- # print('For merging audio only, use a directory per device, quitting')
291
- # sys.exit(1)
292
- # print('\n\n\nOnly audio recordings are present')
293
- # print('Which device should be the reference?\n')
294
- # devices = scanner.get_devices()
295
- # maxch = len(devices)
296
- # for i, d in enumerate(devices):
297
- # print('\t%i - %s'%(i+1, d.name))
298
- # while True:
299
- # print('\nEnter your choice:', end='')
300
- # choice = input()
301
- # try:
302
- # choice = int(choice)
303
- # except:
304
- # print('Please use numeric digits.')
305
- # continue
306
- # if choice not in list(range(1, maxch + 1)):
307
- # print('Please enter a number in [1..%i]'%maxch)
308
- # continue
309
- # break
310
- # ref_device = list(devices)[choice - 1]
311
- # # ref_device = list(devices)[3 - 1]
312
- # print('When only audio recordings are present, ISOs files will be cut and written.')
313
246
  if not args.terse:
314
247
  if scanner.input_structure == 'ordered':
315
248
  print('\nDetected structured folders')
@@ -345,7 +278,6 @@ def main():
345
278
  print('resulting in undefined results: quitting...')
346
279
  quit()
347
280
  print()
348
- # recordings, rec_with_TTC = process_files(scanner.found_media_files, args)
349
281
  recordings = [yaltc.Recording(m, do_plots=False) for m
350
282
  in scanner.found_media_files]
351
283
  recordings_with_time = [
@@ -353,11 +285,6 @@ def main():
353
285
  for rec in recordings
354
286
  if rec.get_start_time()
355
287
  ]
356
- # if audio_REC_only:
357
- # for rec in recordings:
358
- # # print(rec, rec.device == ref_device)
359
- # if rec.device == ref_device:
360
- # rec.is_reference = True
361
288
  if not args.terse:
362
289
  table = Table(title="tictacsync results")
363
290
  table.add_column("Recording\n", justify="center", style='gold1')
@@ -404,24 +331,24 @@ def main():
404
331
  if not args.terse:
405
332
  print('\nNothing to sync, bye.\n')
406
333
  sys.exit(1)
407
- asked_ISOs = args.write_ISOs
408
- if asked_ISOs and scanner.input_structure != 'ordered':
409
- print('Warning, can\'t write ISOs without structured folders: [gold1]--isos[/gold1] option ignored.\n')
410
- asked_ISOs = False
411
- # output_dir = args.o
412
- # if args.verbose_output or args.terse: # verbose, so no progress bars
334
+ if scanner.input_structure != 'ordered':
335
+ print('Warning, can\'t run mamsync without structured folders: [gold1]--isos[/gold1] option ignored.\n')
413
336
  print('Merging...')
337
+ asked_ISOs = True # par defaut
338
+ dont_write_cam_folder = False # write them
414
339
  for merger in matcher.mergers:
415
- merger.build_audio_and_write_merged_media(top_dir,
416
- False, # args.dont_write_cam_folder,
340
+ merger._build_audio_and_write_video(top_dir,
341
+ dont_write_cam_folder,
417
342
  asked_ISOs,
418
- audio_REC_only)
343
+ synced_root = synced_root,
344
+ snd_root = snd_root,
345
+ raw_root = raw_root)
419
346
  if not args.terse:
420
347
  print("\n")
421
348
  # find out where files were written
422
349
  # a_merger = matcher.mergers[0]
423
350
  # log file
424
- p = Path(platformdirs.user_data_dir('mamsync', 'plutz'))/LOG_FILE
351
+ p = Path(platformdirs.user_data_dir('mamsync', 'plutz'))/mamconf.LOG_FILE
425
352
  log_filehandle = open(p, 'a')
426
353
  for merger in matcher.mergers:
427
354
  print('[gold1]%s[/gold1]'%merger.videoclip.AVpath.name, end='')
@@ -448,7 +375,7 @@ def main():
448
375
  print('\nThere are synced multicam clips but without structured folders')
449
376
  print('they were not grouped together under the same folder.')
450
377
  else:
451
- matcher.move_multicam_to_dir()
378
+ matcher.move_multicam_to_dir(raw_root=raw_root, synced_root=synced_root)
452
379
  else:
453
380
  logger.debug('not really a multicam cluster, nothing to move')
454
381
  sys.exit(0)
@@ -83,7 +83,9 @@ def build_poly_name(multifiles):
83
83
  def jump_metadata(from_file, to_file):
84
84
  tempfile_for_metadata = tempfile.NamedTemporaryFile(suffix='.wav', delete=True)
85
85
  tempfile_for_metadata = tempfile_for_metadata.name
86
- process_list = ['ffmpeg', '-loglevel', 'quiet', '-nostats', '-hide_banner', '-i', from_file, '-i', to_file, '-map_metadata', '0', '-c', 'copy', tempfile_for_metadata]
86
+ # ffmpeg -i 32ch-44100-bwf.wav -i onechan.wav -map 1 -map_metadata 0 -c copy outmeta.wav
87
+ process_list = ['ffmpeg', '-loglevel', 'quiet', '-nostats', '-hide_banner', '-i', from_file, '-i', to_file, '-map', '1',
88
+ '-map_metadata', '0', '-c', 'copy', tempfile_for_metadata]
87
89
  # ss = shlex.split("ffmpeg -i %s -i %s -map_metadata 0 -c copy %s"%(from_file, to_file, tempfile_for_metadata))
88
90
  # print(ss)
89
91
  # logger.debug('process %s'%process_list)