tictacsync 0.1a14__py3-none-any.whl → 1.4.4b0__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/entry.py CHANGED
@@ -1,42 +1,35 @@
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
5
4
  # a deployment set-up... surely I'm setting
6
- # things wrong [TODO] find why and clean up this mess
5
+ # things wrong [TODO]: find why and clean up this mess
6
+
7
7
  try:
8
8
  from . import yaltc
9
- except:
10
- import yaltc
11
- try:
12
9
  from . import device_scanner
13
- except:
14
- import device_scanner
15
- try:
16
10
  from . import timeline
17
- except:
18
- import timeline
19
- try:
20
11
  from . import multi2polywav
21
12
  except:
13
+ import yaltc
14
+ import device_scanner
15
+ import timeline
22
16
  import multi2polywav
23
17
 
24
- import argparse
18
+ import argparse, tempfile, configparser
25
19
  from loguru import logger
26
20
  from pathlib import Path
27
21
  # import os, sys
28
- import os, sys
22
+ import os, sys, sox, platformdirs
29
23
  from rich.progress import track
30
24
  # from pprint import pprint
31
25
  from rich.console import Console
32
26
  # from rich.text import Text
33
27
  from rich.table import Table
34
28
  from rich import print
35
- # import itertools
36
- # from datetime import timedelta
37
-
38
- # print(' done')
29
+ from pprint import pprint, pformat
30
+ import numpy as np
39
31
 
32
+ DEL_TEMP = False
40
33
 
41
34
  av_file_extensions = \
42
35
  """MOV webm mkv flv flv vob ogv ogg drc gif gifv mng avi MTS M2TS TS mov qt
@@ -47,146 +40,237 @@ ogg oga mogg opus ra rm raw rf64 sln tta voc vox wav wma wv webm 8svx cda""".spl
47
40
 
48
41
  logger.level("DEBUG", color="<yellow>")
49
42
 
50
- def process_files_with_progress_bars(medias):
51
- recordings = []
52
- rec_with_yaltc = []
53
- times = []
54
- for m in track(medias,
55
- description="1/4 Initializing Recording objects:"):
56
- # file_alias = 'dummy'
57
- recordings.append(yaltc.Recording(m))
58
- for r in track(recordings,
59
- description="2/4 Checking if files have YaLTC:"):
60
- if r.seems_to_have_YaLTC_at_beginning():
61
- rec_with_yaltc.append(r)
62
- for r in track(rec_with_yaltc,
63
- description="3/4 Finding start times:"):
64
- times.append(r.get_start_time())
65
- return recordings, rec_with_yaltc, times
66
-
67
- def process_files(medias):
68
- recordings = []
69
- rec_with_yaltc = []
70
- times = []
71
- for m in medias:
72
- recordings.append(yaltc.Recording(m))
73
- for r in recordings:
74
- # print('%s duration %.2fs'%(r.AVpath.name, r.get_duration()))
75
- if r.seems_to_have_YaLTC_at_beginning():
76
- rec_with_yaltc.append(r)
77
- for r in rec_with_yaltc:
78
- times.append(r.get_start_time())
79
- return recordings, rec_with_yaltc, times
80
-
81
43
  def process_single(file, args):
82
44
  # argument is a single file
83
- m = device_scanner.media_dict_from_path(Path(file))
84
- a_rec = yaltc.Recording(m)
85
- time = a_rec.get_start_time(plots=args.plotting)
45
+ m = device_scanner.media_at_path(None, Path(file))
46
+ if args.plotting:
47
+ print('\nPlots can be zoomed and panned...')
48
+ print('Close window for next one.')
49
+ a_rec = yaltc.Recording(m, do_plots=args.plotting)
50
+ time = a_rec.get_start_time()
51
+ # time = a_rec.get_start_time(plots=args.plotting)
86
52
  if time != None:
87
53
  frac_time = int(time.microsecond / 1e2)
88
54
  d = '%s.%s'%(time.strftime("%Y-%m-%d %H:%M:%S"),frac_time)
89
55
  if args.terse:
90
56
  print('%s UTC:%s pulse: %i in chan %i'%(file, d, a_rec.sync_position,
91
- a_rec.YaLTC_channel))
57
+ a_rec.TicTacCode_channel))
92
58
  else:
93
59
  print('\nRecording started at [gold1]%s[/gold1] UTC'%d)
94
60
  print('true sample rate: [gold1]%.3f Hz[/gold1]'%a_rec.true_samplerate)
95
61
  print('first sync at [gold1]%i[/gold1] samples in channel %i'%(a_rec.sync_position,
96
- a_rec.YaLTC_channel))
62
+ a_rec.TicTacCode_channel))
97
63
  print('N.B.: all results are precise to the displayed digits!\n')
98
64
  else:
99
65
  if args.terse:
100
66
  print('%s UTC: None'%(file))
101
67
  else:
102
68
  print('Start time couldnt be determined')
103
- quit()
69
+ sys.exit(1)
70
+
71
+ def process_lag_adjustement(media_object):
72
+ # trim channels that are lagging (as stated in tracks.txt)
73
+ # replace the old file, and rename the old one with .wavbk
74
+ # if .wavbk exist, process was done already, so dont process
75
+ # returns nothing
76
+ lags = media_object.device.tracks.lag_values
77
+ logger.debug('will process %s lags'%[lags])
78
+ channels = timeline._sox_split_channels(media_object.path)
79
+ # add bk to file on filesystem, but media_object.path is unchanged (?)
80
+ backup_name = str(media_object.path) + 'bk'
81
+ if Path(backup_name).exists():
82
+ logger.debug('%s exists, so return now.'%backup_name)
83
+ return
84
+ media_object.path.replace(backup_name)
85
+ logger.debug('channels %s'%channels)
86
+ def _trim(lag, chan_file):
87
+ # counter intuitive I know. if a file lags, there's too
88
+ # much samples at the start:
89
+ # ..................|.........
90
+ # .......................|....
91
+ # ^ play head ->
92
+ if lag == None:
93
+ return chan_file
94
+ else:
95
+ logger.debug('process %s for lag of %s'%(chan_file, lag))
96
+ sox_transform = sox.Transformer()
97
+ sox_transform.trim(float(lag)*1e-3)
98
+ output_fh = tempfile.NamedTemporaryFile(suffix='.wav', delete=DEL_TEMP)
99
+ out_file = timeline._pathname(output_fh)
100
+ input_file = timeline._pathname(chan_file)
101
+ logger.debug('sox in and out files: %s %s'%(input_file, out_file))
102
+ logger.debug('calling sox_transform.build()')
103
+ status = sox_transform.build(input_file, out_file, return_output=True )
104
+ logger.debug('sox.build exit code %s'%str(status))
105
+ return output_fh
106
+ new_channels = [_trim(*e) for e in zip(lags, channels)]
107
+ logger.debug('new_channels %s'%new_channels)
108
+ trimmed_multichanfile = timeline._sox_combine(new_channels)
109
+ logger.debug('trimmed_multichanfile %s'%timeline._pathname(trimmed_multichanfile))
110
+ Path(timeline._pathname(trimmed_multichanfile)).replace(media_object.path)
104
111
 
105
112
  def main():
106
113
  parser = argparse.ArgumentParser()
107
114
  parser.add_argument(
108
- "directory",
115
+ "path",
109
116
  type=str,
110
117
  nargs=1,
111
- help="path of media directory"
118
+ help="directory_name or media_file"
112
119
  )
113
120
  # parser.add_argument("directory", nargs="?", help="path of media directory")
114
121
  # parser.add_argument('-v', action='store_true')
115
- parser.add_argument('-v', action='store_true', default=False,
116
- dest='verbose_output',
117
- help='Set verbose ouput')
118
- parser.add_argument('-p', action='store_true', default=False,
122
+ parser.add_argument('-v',
123
+ action='store_true', #ie default False
124
+ dest='verbose_output',
125
+ help='Set verbose ouput')
126
+ # parser.add_argument('--stop_mirroring',
127
+ # action='store_true', #ie default False
128
+ # dest='stop_mirroring',
129
+ # help='Stop mirroring mode, will write synced files alongside originals.')
130
+ # parser.add_argument('--start-project', '-s' ,
131
+ # nargs=2,
132
+ # dest='proj_folders',
133
+ # default = [],
134
+ # help='start mirrored tree output mode and specifies 2 folders: source (RAW) and destination (synced).')
135
+ parser.add_argument('-t','--timelineoffset',
136
+ nargs=1,
137
+ default=['00:00:00:00'],
138
+ dest='timelineoffset',
139
+ help='When processing multicam, where to place clips on NLE timeline (HH:MM:SS:FF)')
140
+ parser.add_argument('-p',
141
+ action='store_true',
119
142
  dest='plotting',
120
- help='Make plots')
121
- parser.add_argument('--isos', action='store_true', default=False,
143
+ help='Produce plots')
144
+ parser.add_argument('-d',
145
+ action='store_true',
146
+ dest='dont_write_cam_folder',
147
+ help="Even if originals are inside CAM folders, don't put synced clips inside a CAM identified folder")
148
+ # parser.add_argument('-m',
149
+ # action='store_true',
150
+ # dest='multicam',
151
+ # help='Outputs multicam structure for NLE program (based on Davinci Resolve input processing)')
152
+ parser.add_argument('--isos',
153
+ action='store_true',
122
154
  dest='write_ISOs',
123
- help='Write ISO sound files')
124
- parser.add_argument('--nosync', action='store_true',
155
+ help='Cut ISO sound files')
156
+ parser.add_argument('--nosync',
157
+ action='store_true',
125
158
  dest='nosync',
126
- help='just scan and decode')
127
- parser.add_argument('--terse', action='store_true',
159
+ help='Just scan and decode')
160
+ parser.add_argument('--terse',
161
+ action='store_true',
128
162
  dest='terse',
129
- help='terse output')
130
- # parser.add_argument('--io', dest='output_structure', type=str, default='loose',
131
- # help='should be one of: loose [DEFAULT], foldercam')
163
+ help='Terse output')
132
164
  args = parser.parse_args()
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()
165
+ # print(args)
166
+ if len(args.timelineoffset) != 1:
167
+ print('--timelineoffset needs one value, got %s'%args.timelineoffset)
168
+ quit()
136
169
  if args.verbose_output:
137
170
  logger.add(sys.stderr, level="DEBUG")
138
- # logger.add(sys.stdout, filter="__main__")
139
- # logger.add(sys.stdout, filter="device_scanner")
140
- # logger.add(sys.stdout, filter="yaltc")
141
- # logger.add(sys.stdout, filter="timeline")
142
- # logger.add(sys.stdout, filter=lambda r: r["function"] == "_enforce_folder_is_device")
143
- # logger.add(sys.stdout, filter=lambda r: r["function"] == "build_audio_and_write_video")
144
- # logger.add(sys.stdout, filter=lambda r: r["function"] == "_get_BFSK_word_boundaries")
145
- top_dir = args.directory[0]
171
+
172
+ # logger.add(sys.stdout, filter=lambda r: r["function"] == "move_multicam_to_dir")
173
+ # logger.add(sys.stdout, filter=lambda r: r["function"] == "_build_audio_and_write_video")
174
+ # logger.add(sys.stdout, filter=lambda r: r["function"] == "main")
175
+ # logger.add(sys.stdout, filter=lambda r: r["function"] == "_build_from_tracks_txt")
176
+ # logger.add(sys.stdout, filter=lambda r: r["function"] == "scan_media_and_build_devices_UID")
177
+
178
+ top_dir = args.path[0]
146
179
  if os.path.isfile(top_dir):
147
180
  file = top_dir
148
181
  process_single(file, args)
149
182
  if not os.path.isdir(top_dir):
150
183
  print('%s is not a directory or doesnt exist.'%top_dir)
151
- quit()
184
+ sys.exit(1)
152
185
  multi2polywav.poly_all(top_dir)
153
186
  scanner = device_scanner.Scanner(top_dir, stay_silent=args.terse)
154
- # (Path(top_dir)/Path('tictacsynced')).mkdir(exist_ok=True)
155
- # scanner.cluster_media_files_by_name()
156
- scanner.scan_media_and_build_devices_UID(recursive=False)
187
+ scanner.scan_media_and_build_devices_UID()
188
+ for m in scanner.found_media_files:
189
+ if m.device.tracks:
190
+ if not all([lv == None for lv in m.device.tracks.lag_values]):
191
+ logger.debug('%s has lag_values %s'%(
192
+ m.path, m.device.tracks.lag_values))
193
+ # any lag for a channel is specified by user in tracks.txt
194
+ process_lag_adjustement(m)
195
+ audio_REC_only = all([m.device.dev_type == 'REC' for m
196
+ in scanner.found_media_files])
197
+ if audio_REC_only:
198
+ if scanner.input_structure != 'ordered':
199
+ print('For merging audio only, use a directory per device, quitting')
200
+ sys.exit(1)
201
+ print('\n\n\nOnly audio recordings are present')
202
+ print('Which device should be the reference?\n')
203
+ devices = scanner.get_devices()
204
+ maxch = len(devices)
205
+ for i, d in enumerate(devices):
206
+ print('\t%i - %s'%(i+1, d.name))
207
+ ref_device = list(devices)[3 - 1]
208
+ print('When only audio recordings are present, ISOs files will be cut and written.')
157
209
  if not args.terse:
158
- print('\n\nFound [gold1]%i[/gold1] media files from [gold1]%i[/gold1] devices'%(
159
- len(scanner.found_media_files),
160
- scanner.get_devices_number()), end='')
161
- print('\nThese recordings will be analysed for timestamps:\n')
162
- for m in (scanner.found_media_files):
163
- print(' ', '[gold1]%s[/gold1]'%m['path'].name)
210
+ if scanner.input_structure == 'ordered':
211
+ print('\nDetected structured folders')
212
+ # if scanner.top_dir_has_multicam:
213
+ # print(', multicam')
214
+ # else:
215
+ # print()
216
+ else:
217
+ print('\nDetected loose structure')
218
+ if scanner.CAM_numbers() > 1:
219
+ print('\nNote: different CAMs are present, will sync audio for each of them but if you want to set their')
220
+ print('respective timecode for NLE timeline alignement you should regroup clips by CAM under their own DIR.')
221
+ print('\nFound [gold1]%i[/gold1] media files '%(
222
+ len(scanner.found_media_files)), end='')
223
+ print('from [gold1]%i[/gold1] devices:\n'%(
224
+ scanner.get_devices_number()))
225
+ all_devices = scanner.get_devices()
226
+ for dev in all_devices:
227
+ dt = 'Camera' if dev.dev_type == 'CAM' else 'Recorder'
228
+ print('%s [gold1]%s[/gold1] with files:'%(dt, dev.name), end = ' ')
229
+ medias = scanner.get_media_for_device(dev)
230
+ for m in medias[:-1]: # last printed out of loop
231
+ print('[gold1]%s[/gold1]'%m.path.name, end=', ')
232
+ print('[gold1]%s[/gold1]'%medias[-1].path.name)
233
+ a_media = medias[0]
234
+ # check if all audio recorders have same sampling freq
235
+ freqs = [dev.sampling_freq for dev in all_devices if dev.dev_type == 'REC']
236
+ same = np.isclose(np.std(freqs),0)
237
+ logger.debug('sampling freqs from audio recorders %s, same:%s'%(freqs, same))
238
+ if not same:
239
+ print('some audio recorders have different sampling frequencies:')
240
+ print(freqs)
241
+ print('resulting in undefined results: quitting...')
242
+ quit()
164
243
  print()
165
- if args.verbose_output or args.terse: # verbose or terse, so no progress bars
166
- rez = process_files(scanner.found_media_files)
167
- else:
168
- rez = process_files_with_progress_bars(scanner.found_media_files)
169
- recordings, rec_with_yaltc, times = rez
244
+ recordings = [yaltc.Recording(m, do_plots=args.plotting) for m
245
+ in scanner.found_media_files]
170
246
  recordings_with_time = [
171
247
  rec
172
- for rec in rec_with_yaltc
248
+ for rec in recordings
173
249
  if rec.get_start_time()
174
250
  ]
251
+ [r.load_track_info() for r in recordings_with_time if r.is_audio()]
252
+ for r in recordings:
253
+ # print(f'{r} device: #{id(r.device):x}')
254
+ logger.debug(f'{r} \nDevice instance id: #{id(r.device):x}')
255
+ logger.debug(f'device content: {r.device}')
256
+ if audio_REC_only:
257
+ for rec in recordings:
258
+ # print(rec, rec.device == ref_device)
259
+ if rec.device == ref_device:
260
+ rec.is_audio_reference = True
175
261
  if not args.terse:
176
- print('\n\n')
177
262
  table = Table(title="tictacsync results")
178
263
  table.add_column("Recording\n", justify="center", style='gold1')
179
- table.add_column("YaLTC\nChannel", justify="center", style='gold1')
264
+ table.add_column("TTC chan\n (1st=#0)", justify="center", style='gold1')
180
265
  # table.add_column("Device\n", justify="center", style='gold1')
181
266
  table.add_column("UTC times\nstart:end", justify="center", style='gold1')
182
267
  table.add_column("Clock drift\n(ppm)", justify="right", style='gold1')
183
- table.add_column("SN ratio\n(dB)", justify="center", style='gold1')
268
+ # table.add_column("SN ratio\n(dB)", justify="center", style='gold1')
184
269
  table.add_column("Date\n", justify="center", style='gold1')
185
270
  rec_WO_time = [
186
271
  rec.AVpath.name
187
- for rec in rec_with_yaltc
188
- if not rec.get_start_time()
189
- ]
272
+ for rec in recordings
273
+ if rec not in recordings_with_time]
190
274
  if rec_WO_time:
191
275
  print('No time found for: ',end='')
192
276
  [print(rec, end=' ') for rec in rec_WO_time]
@@ -198,61 +282,82 @@ def main():
198
282
  times_range = start_HHMMSS + ':' + end_MMSS
199
283
  table.add_row(
200
284
  str(r.AVpath.name),
201
- str(r.YaLTC_channel),
285
+ str(r.TicTacCode_channel),
202
286
  # r.device,
203
287
  times_range,
204
288
  # '%.6f'%(r.true_samplerate/1e3),
205
289
  '%2i'%(r.get_samplerate_drift()),
206
- '%.0f'%r.decoder.SN_ratio,
290
+ # '%.0f'%r.decoder.SN_ratio,
207
291
  date
208
292
  )
209
293
  console = Console()
210
294
  console.print(table)
211
- print('\n')
295
+ print()
212
296
  n_devices = scanner.get_devices_number()
213
- OUT_struct_for_mcam = scanner.top_dir_has_multicam
214
- # if n_devices > 2:
215
- # print('\nMerging for more than 2 devices is not implemented yet, quitting...')
216
- # quit()
217
297
  if len(recordings_with_time) < 2:
218
298
  if not args.terse:
219
299
  print('\nNothing to sync, exiting.\n')
220
- quit()
300
+ sys.exit(1)
221
301
  matcher = timeline.Matcher(recordings_with_time)
222
- matcher.scan_audio_for_each_ref_rec()
223
- if not matcher.video_mergers:
302
+ matcher.scan_audio_for_each_videoclip()
303
+ if not matcher.mergers:
224
304
  if not args.terse:
225
305
  print('\nNothing to sync, bye.\n')
226
- quit()
227
- if args.verbose_output or args.terse: # verbose, so no progress bars
228
- for stitcher in matcher.video_mergers:
229
- stitcher.build_audio_and_write_video(top_dir, OUT_struct_for_mcam,
230
- args.write_ISOs)
231
- else:
232
- for stitcher in track(matcher.video_mergers,
233
- description="4/4 Merging sound to videos:"):
234
- stitcher.build_audio_and_write_video(top_dir, OUT_struct_for_mcam,
235
- args.write_ISOs)
306
+ sys.exit(1)
307
+ asked_ISOs = args.write_ISOs
308
+ if asked_ISOs and scanner.input_structure != 'ordered':
309
+ print('Warning, can\'t write ISOs without structured folders: [gold1]--isos[/gold1] option ignored.\n')
310
+ asked_ISOs = False
311
+ # output_dir = args.o
312
+ # if args.verbose_output or args.terse: # verbose, so no progress bars
313
+ print('Merging...')
314
+ # for merger in matcher.mergers:
315
+ # merger.build_audio_and_write_merged_media(top_dir,
316
+ # args.dont_write_cam_folder,
317
+ # asked_ISOs,
318
+ # audio_REC_only)
319
+ for merger in matcher.mergers:
320
+ if audio_REC_only:
321
+ # rare
322
+ merger._build_and_write_audio(top_dir)
323
+ else:
324
+ # almost always syncing audio to video clips
325
+ merger._build_audio_and_write_video(top_dir,
326
+ args.dont_write_cam_folder, asked_ISOs)
236
327
  if not args.terse:
237
328
  print("\n")
238
- # if args.output_structure == 'foldercam':
239
- print('output written in [gold1]%s[/gold1] folder in [gold1]%s[/gold1]\n'%(
240
- 'SyncedMedia',top_dir))
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():
329
+ # find out where files were written
330
+ # a_merger = matcher.mergers[0]
331
+ for merger in matcher.mergers:
332
+ print('[gold1]%s[/gold1]'%merger.videoclip.AVpath.name, end='')
333
+ for audio in merger.get_matched_audio_recs():
245
334
  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)
335
+ new_file = merger.videoclip.final_synced_file.parts
336
+ final_p = merger.videoclip.final_synced_file
337
+ nameAnd2Parents = Path('').joinpath(*final_p.parts[-2:])
338
+ print(' became [gold1]%s[/gold1]'%nameAnd2Parents)
248
339
  # matcher._build_otio_tracks_for_cam()
249
- matcher.shrink_gaps_between_takes()
340
+ if not audio_REC_only:
341
+ matcher.set_up_clusters() # multicam
342
+ matcher.shrink_gaps_between_takes(args.timelineoffset)
343
+ logger.debug('matcher.multicam_clips_clusters %s'%
344
+ pformat(matcher.multicam_clips_clusters))
345
+ # clusters is list of {'end': t1, 'start': t2, 'vids': [r1,r3]}
346
+ # really_clusters is True if one of them has len() > 1
347
+ really_clusters = any([len(cl['vids']) > 1 for cl
348
+ in matcher.multicam_clips_clusters])
349
+ if really_clusters:
350
+ if scanner.input_structure == 'loose':
351
+ print('\nThere are synced multicam clips but without structured folders')
352
+ print('they were not grouped together under the same folder.')
353
+ else:
354
+ matcher.move_multicam_to_dir()
355
+ else:
356
+ logger.debug('not really a multicam cluster, nothing to move')
357
+ sys.exit(0)
250
358
 
251
359
  if __name__ == '__main__':
252
360
  main()
253
361
 
254
362
 
255
363
 
256
-
257
-
258
-
tictacsync/mamconf.py ADDED
@@ -0,0 +1,157 @@
1
+ import argparse, platformdirs, configparser, sys
2
+ from loguru import logger
3
+ from pprint import pformat
4
+ from pathlib import Path
5
+ from rich import print
6
+
7
+ # [TODO] add in the doc:
8
+ # RAWROOT (sources with TC): "/Users/foobar/movies/MyBigMovie/"
9
+ # SYNCEDROOT (where RAWROOT will be mirrored, but with synced clips): "/Users/foobar/synced"
10
+ # SNDROOT (destination of ISOs sound files): "/Users/foobar/MovieSounds"
11
+ # then
12
+ # "/Users/foobar/synced/MyBigMovie" and "/Users/foobar/MovieSounds/MyBigMovie" will be created
13
+
14
+
15
+ CONF_FILE = 'mamsync.cfg'
16
+ LOG_FILE = 'mamdone.txt'
17
+
18
+ logger.remove()
19
+ # logger.add(sys.stdout, level="DEBUG")
20
+ # logger.add(sys.stdout, filter=lambda r: r["function"] == "write_conf")
21
+
22
+
23
+ def print_out_conf(raw_root, synced_root, snd_root, proxies=''):
24
+ print(f'RAWROOT (sources with TC): "{raw_root}"')
25
+ print(f'SYNCEDROOT (where RAWROOT will be mirrored, but with synced clips): "{synced_root}"')
26
+ print(f'SNDROOT (destination of ISOs sound files): "{snd_root}"')
27
+ if proxies != '':
28
+ print(f'PROXIES (NLE proxy clips folder): "{proxies}"')
29
+
30
+ def write_conf(conf_key, conf_val):
31
+ # args are pahtlib.Paths.
32
+ # RAWROOT: files with TC (and ROLL folders), as is from cameras
33
+ # SYNCEDROOT: synced and no more TC (ROLL flattened)
34
+ # Writes configuration on filesystem for later retrieval
35
+ # Clears log of already synced clips.
36
+ conf_dir = platformdirs.user_config_dir('mamsync', 'plutz', ensure_exists=True)
37
+ current_values = dict(zip(['RAWROOT', 'SYNCEDROOT', 'SNDROOT', 'PROXIES'],
38
+ get_proj()))
39
+ logger.debug(f'old values {current_values}')
40
+ current_values[conf_key] = conf_val
41
+ logger.debug(f'updated values {current_values}')
42
+ conf_file = Path(conf_dir)/CONF_FILE
43
+ logger.debug('writing config in %s'%conf_file)
44
+ # print(f'\nWriting folders paths in configuration file "{conf_file}"')
45
+ # print_out_conf(raw_root, synced_root, snd_root)
46
+ conf_prs = configparser.ConfigParser()
47
+ conf_prs['SECTION1'] = current_values
48
+ with open(conf_file, 'w') as configfile_handle:
49
+ conf_prs.write(configfile_handle)
50
+ with open(conf_file, 'r') as configfile_handle:
51
+ logger.debug(f'config file content: \n{configfile_handle.read()}')
52
+
53
+ def get_proj(print_conf_stdout=False):
54
+ # check if user started a project before.
55
+ # stored in platformdirs.user_config_dir
56
+ # returns a tuple of strings (RAWROOT, SYNCEDROOTS, SNDROOT, PROXIES)
57
+ # if any, or a tuple of 4 empty strings '' otherwise.
58
+ # print location of conf file if print_conf_stdout
59
+ # Note: only raw_root contains the name project
60
+ conf_dir = platformdirs.user_config_dir('mamsync', 'plutz')
61
+ conf_file = Path(conf_dir)/CONF_FILE
62
+ logger.debug('try reading config in %s'%conf_file)
63
+ if print_conf_stdout:
64
+ print(f'Will read configuration from file {conf_file}')
65
+ if conf_file.exists():
66
+ conf_prs = configparser.ConfigParser()
67
+ conf_prs.read(conf_file)
68
+ try:
69
+ RAWROOT = conf_prs.get('SECTION1', 'RAWROOT')
70
+ except configparser.NoOptionError:
71
+ RAWROOT = ''
72
+ try:
73
+ SYNCEDROOT = conf_prs.get('SECTION1', 'SYNCEDROOT')
74
+ except configparser.NoOptionError:
75
+ SYNCEDROOT = ''
76
+ try:
77
+ PROXIES = conf_prs.get('SECTION1', 'PROXIES')
78
+ except configparser.NoOptionError:
79
+ PROXIES = ''
80
+ try:
81
+ SNDROOT = conf_prs.get('SECTION1', 'SNDROOT')
82
+ except configparser.NoOptionError:
83
+ SNDROOT = ''
84
+ logger.debug('read from conf: RAWROOT= %s SYNCEDROOT= %s SNDROOT=%s PROXIES=%s'%
85
+ (RAWROOT, SYNCEDROOT, SNDROOT, PROXIES))
86
+ return RAWROOT, SYNCEDROOT, SNDROOT, PROXIES
87
+ else:
88
+ logger.debug(f'no config file found at {conf_file}')
89
+ print('No configuration found.')
90
+ return '', '', '', ''
91
+
92
+ def new_parser():
93
+ parser = argparse.ArgumentParser()
94
+ parser.add_argument('--rr',
95
+ nargs = 1,
96
+ dest='rawroot',
97
+ help='Sets new value for raw root folder (i.e.: clips with TC)')
98
+ parser.add_argument('--sr',
99
+ nargs = 1,
100
+ dest='syncedroot',
101
+ help="""Sets where the synced files will be written, to be used by the NLE. Will contain a mirror copy of RAWROOT """)
102
+ parser.add_argument('--pr',
103
+ nargs = 1,
104
+ dest='proxies',
105
+ help='Sets where the proxy files are stored by the NLE')
106
+ parser.add_argument('--sf',
107
+ nargs = 1,
108
+ dest='sndfolder',
109
+ help='Sets value for sound folder (will contain a mirror copy of RAWROOT, but with ISO files only)')
110
+ parser.add_argument('--clearconf',
111
+ action='store_true',
112
+ dest='clearconf',
113
+ help='Clear configured values.')
114
+ parser.add_argument('--showconf',
115
+ action='store_true',
116
+ dest='showconf',
117
+ help='Show current configured values.')
118
+ return parser
119
+
120
+ def main():
121
+ parser = new_parser()
122
+ args = parser.parse_args()
123
+ logger.debug(f'arguments from argparse {args}')
124
+ if args.rawroot:
125
+ val = args.rawroot[0]
126
+ write_conf('RAWROOT', val)
127
+ print(f'Set source folder of unsynced clips (rawroot) to:\n{val}')
128
+ sys.exit(0)
129
+ if args.syncedroot:
130
+ val = args.syncedroot[0]
131
+ write_conf('SYNCEDROOT', args.syncedroot[0])
132
+ print(f'Set destination folder of synced clips (syncedroot) to:\n{val}')
133
+ sys.exit(0)
134
+ if args.proxies:
135
+ val = args.proxies[0]
136
+ write_conf('PROXIES', args.proxies[0])
137
+ print(f'Set proxies folder to:\n{val}')
138
+ sys.exit(0)
139
+ if args.sndfolder:
140
+ val = args.sndfolder[0]
141
+ write_conf('SNDROOT', args.sndfolder[0])
142
+ print(f'Set destination folder of ISOs sound files (sndfolder) to:\n{val}')
143
+ sys.exit(0)
144
+ if args.clearconf:
145
+ write_conf('RAWROOT', '')
146
+ write_conf('SYNCEDROOT', '')
147
+ write_conf('SNDROOT', '')
148
+ write_conf('PROXIES', '')
149
+ print_out_conf('','','','')
150
+ sys.exit(0)
151
+ if args.showconf:
152
+ get_proj()
153
+ print_out_conf(*get_proj(True))
154
+ sys.exit(0)
155
+
156
+ if __name__ == '__main__':
157
+ main()