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/device_scanner.py +5 -24
- tictacsync/entry.py +35 -38
- tictacsync/mamconf.py +175 -0
- tictacsync/mamsync.py +99 -172
- tictacsync/multi2polywav.py +3 -1
- tictacsync/newmix.py +217 -57
- tictacsync/timeline.py +137 -84
- tictacsync/yaltc.py +2 -2
- {tictacsync-1.0.2a0.dist-info → tictacsync-1.2.0b0.dist-info}/METADATA +4 -4
- tictacsync-1.2.0b0.dist-info/RECORD +19 -0
- {tictacsync-1.0.2a0.dist-info → tictacsync-1.2.0b0.dist-info}/entry_points.txt +2 -0
- tictacsync-1.0.2a0.dist-info/RECORD +0 -18
- {tictacsync-1.0.2a0.dist-info → tictacsync-1.2.0b0.dist-info}/LICENSE +0 -0
- {tictacsync-1.0.2a0.dist-info → tictacsync-1.2.0b0.dist-info}/WHEEL +0 -0
- {tictacsync-1.0.2a0.dist-info → tictacsync-1.2.0b0.dist-info}/top_level.txt +0 -0
tictacsync/device_scanner.py
CHANGED
|
@@ -18,6 +18,8 @@ M4V SVI 3GP 3G2 MXF ROQ NSV FLV F4V F4P F4A F4B 3GP AA AAC AAX ACT AIFF ALAC
|
|
|
18
18
|
AMR APE AU AWB DSS DVF FLAC GSM IKLAX IVS M4A M4B M4P MMF MP3 MPC MSV NMF
|
|
19
19
|
OGG OGA MOGG OPUS RA RM RAW RF64 SLN TTA VOC VOX WAV WMA WV WEBM 8SVX CDA MOV AVI BWF""".split()
|
|
20
20
|
|
|
21
|
+
audio_ext = 'aiff wav mp3'.split()
|
|
22
|
+
|
|
21
23
|
from dataclasses import dataclass
|
|
22
24
|
import ffmpeg, os, sys, shutil
|
|
23
25
|
from os import listdir
|
|
@@ -37,9 +39,11 @@ from itertools import groupby
|
|
|
37
39
|
try:
|
|
38
40
|
from . import multi2polywav
|
|
39
41
|
from . import mamsync
|
|
42
|
+
from . import mamconf
|
|
40
43
|
except:
|
|
41
44
|
import multi2polywav
|
|
42
45
|
import mamsync
|
|
46
|
+
import mamconf
|
|
43
47
|
|
|
44
48
|
MCCDIR = 'SyncedMulticamClips'
|
|
45
49
|
SYNCEDFOLDER = 'SyncedMedia'
|
|
@@ -264,7 +268,7 @@ class Scanner:
|
|
|
264
268
|
"""
|
|
265
269
|
logger.debug(f'on entry synced_root: {synced_root}')
|
|
266
270
|
if synced_root != None: # mam mode
|
|
267
|
-
p = Path(platformdirs.user_data_dir('mamsync', 'plutz'))/
|
|
271
|
+
p = Path(platformdirs.user_data_dir('mamsync', 'plutz'))/mamconf.LOG_FILE
|
|
268
272
|
with open(p, 'r') as fh:
|
|
269
273
|
done = set(fh.read().split()) # sets of strings of abs path
|
|
270
274
|
logger.debug(f'done clips: {pformat(done)}')
|
|
@@ -282,29 +286,6 @@ class Scanner:
|
|
|
282
286
|
continue
|
|
283
287
|
else:
|
|
284
288
|
clip_paths.append(raw_path)
|
|
285
|
-
############################################################ MOVE
|
|
286
|
-
elif synced_root != None: # MAM mode
|
|
287
|
-
#non AV files and directories
|
|
288
|
-
synced_path = Path(synced_root)/str(raw_path)[1:] # cant join abs. paths
|
|
289
|
-
if raw_path.is_dir():
|
|
290
|
-
synced_path.mkdir(parents=True, exist_ok=True)
|
|
291
|
-
continue
|
|
292
|
-
# if here, it's a file
|
|
293
|
-
if not synced_path.exists():
|
|
294
|
-
print(f'will mirror non AV file at {synced_path}')
|
|
295
|
-
logger.debug(f'will mirror non AV file at {synced_path}')
|
|
296
|
-
shutil.copy2(raw_path, synced_path)
|
|
297
|
-
continue
|
|
298
|
-
# file exists, check if same
|
|
299
|
-
same = filecmp.cmp(raw_path, synced_path, shallow=False)
|
|
300
|
-
logger.debug(f'copy exists of:\n{raw_path}\n{synced_path}')
|
|
301
|
-
if not same:
|
|
302
|
-
print(f'file changed, copying again\n{raw_path}')
|
|
303
|
-
shutil.copy2(raw_path, synced_path)
|
|
304
|
-
else:
|
|
305
|
-
logger.debug('same content, next')
|
|
306
|
-
continue # next raw_path in loop
|
|
307
|
-
###################################################################
|
|
308
289
|
if some_done:
|
|
309
290
|
print('Somme media files were already synced...')
|
|
310
291
|
logger.debug('found media files %s'%clip_paths)
|
tictacsync/entry.py
CHANGED
|
@@ -164,13 +164,14 @@ def main():
|
|
|
164
164
|
quit()
|
|
165
165
|
if args.verbose_output:
|
|
166
166
|
logger.add(sys.stderr, level="DEBUG")
|
|
167
|
-
|
|
168
|
-
# logger.add(sys.stdout, filter=lambda r: r["function"] == "
|
|
167
|
+
|
|
168
|
+
# logger.add(sys.stdout, filter=lambda r: r["function"] == "_write_ISOs")
|
|
169
|
+
logger.add(sys.stdout, filter=lambda r: r["function"] == "_build_and_write_audio")
|
|
169
170
|
#
|
|
170
171
|
# logger.add(sys.stdout, filter="__main__")
|
|
171
172
|
# logger.add(sys.stdout, filter="yaltc")
|
|
172
173
|
|
|
173
|
-
logger.add(sys.stdout, filter=lambda r: r["function"] == "
|
|
174
|
+
# logger.add(sys.stdout, filter=lambda r: r["function"] == "move_multicam_to_dir")
|
|
174
175
|
# logger.add(sys.stdout, filter=lambda r: r["function"] == "_dev_type_for_name")
|
|
175
176
|
# logger.add(sys.stdout, filter=lambda r: r["function"] == "scan_media_and_build_devices_UID")
|
|
176
177
|
|
|
@@ -203,20 +204,7 @@ def main():
|
|
|
203
204
|
maxch = len(devices)
|
|
204
205
|
for i, d in enumerate(devices):
|
|
205
206
|
print('\t%i - %s'%(i+1, d.name))
|
|
206
|
-
|
|
207
|
-
print('\nEnter your choice:', end='')
|
|
208
|
-
choice = input()
|
|
209
|
-
try:
|
|
210
|
-
choice = int(choice)
|
|
211
|
-
except:
|
|
212
|
-
print('Please use numeric digits.')
|
|
213
|
-
continue
|
|
214
|
-
if choice not in list(range(1, maxch + 1)):
|
|
215
|
-
print('Please enter a number in [1..%i]'%maxch)
|
|
216
|
-
continue
|
|
217
|
-
break
|
|
218
|
-
ref_device = list(devices)[choice - 1]
|
|
219
|
-
# ref_device = list(devices)[3 - 1]
|
|
207
|
+
ref_device = list(devices)[3 - 1]
|
|
220
208
|
print('When only audio recordings are present, ISOs files will be cut and written.')
|
|
221
209
|
if not args.terse:
|
|
222
210
|
if scanner.input_structure == 'ordered':
|
|
@@ -265,7 +253,7 @@ def main():
|
|
|
265
253
|
for rec in recordings:
|
|
266
254
|
# print(rec, rec.device == ref_device)
|
|
267
255
|
if rec.device == ref_device:
|
|
268
|
-
rec.
|
|
256
|
+
rec.is_audio_reference = True
|
|
269
257
|
if not args.terse:
|
|
270
258
|
table = Table(title="tictacsync results")
|
|
271
259
|
table.add_column("Recording\n", justify="center", style='gold1')
|
|
@@ -319,11 +307,19 @@ def main():
|
|
|
319
307
|
# output_dir = args.o
|
|
320
308
|
# if args.verbose_output or args.terse: # verbose, so no progress bars
|
|
321
309
|
print('Merging...')
|
|
310
|
+
# for merger in matcher.mergers:
|
|
311
|
+
# merger.build_audio_and_write_merged_media(top_dir,
|
|
312
|
+
# args.dont_write_cam_folder,
|
|
313
|
+
# asked_ISOs,
|
|
314
|
+
# audio_REC_only)
|
|
322
315
|
for merger in matcher.mergers:
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
316
|
+
if audio_REC_only:
|
|
317
|
+
# rare
|
|
318
|
+
merger._build_and_write_audio(top_dir)
|
|
319
|
+
else:
|
|
320
|
+
# almost always syncing audio to video clips
|
|
321
|
+
merger._build_audio_and_write_video(top_dir,
|
|
322
|
+
args.dont_write_cam_folder, asked_ISOs)
|
|
327
323
|
if not args.terse:
|
|
328
324
|
print("\n")
|
|
329
325
|
# find out where files were written
|
|
@@ -337,23 +333,24 @@ def main():
|
|
|
337
333
|
nameAnd2Parents = Path('').joinpath(*final_p.parts[-2:])
|
|
338
334
|
print(' became [gold1]%s[/gold1]'%nameAnd2Parents)
|
|
339
335
|
# matcher._build_otio_tracks_for_cam()
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
if
|
|
350
|
-
|
|
351
|
-
|
|
336
|
+
if not audio_REC_only:
|
|
337
|
+
matcher.set_up_clusters() # multicam
|
|
338
|
+
matcher.shrink_gaps_between_takes(args.timelineoffset)
|
|
339
|
+
logger.debug('matcher.multicam_clips_clusters %s'%
|
|
340
|
+
pformat(matcher.multicam_clips_clusters))
|
|
341
|
+
# clusters is list of {'end': t1, 'start': t2, 'vids': [r1,r3]}
|
|
342
|
+
# really_clusters is True if one of them has len() > 1
|
|
343
|
+
really_clusters = any([len(cl['vids']) > 1 for cl
|
|
344
|
+
in matcher.multicam_clips_clusters])
|
|
345
|
+
if really_clusters:
|
|
346
|
+
if scanner.input_structure == 'loose':
|
|
347
|
+
print('\nThere are synced multicam clips but without structured folders')
|
|
348
|
+
print('they were not grouped together under the same folder.')
|
|
349
|
+
else:
|
|
350
|
+
matcher.move_multicam_to_dir()
|
|
352
351
|
else:
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
logger.debug('not really a multicam cluster, nothing to move')
|
|
356
|
-
sys.exit(0)
|
|
352
|
+
logger.debug('not really a multicam cluster, nothing to move')
|
|
353
|
+
sys.exit(0)
|
|
357
354
|
|
|
358
355
|
if __name__ == '__main__':
|
|
359
356
|
main()
|
tictacsync/mamconf.py
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import argparse, platformdirs, configparser, sys
|
|
2
|
+
from loguru import logger
|
|
3
|
+
from pprint import pprint, pformat
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
CONF_FILE = 'mamsync.cfg'
|
|
8
|
+
LOG_FILE = 'mamdone.txt'
|
|
9
|
+
|
|
10
|
+
logger.remove()
|
|
11
|
+
# logger.add(sys.stdout, level="DEBUG")
|
|
12
|
+
# logger.add(sys.stdout, filter=lambda r: r["function"] == "write_conf")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def print_out_conf(raw_root, synced_root, snd_root, proxies=''):
|
|
16
|
+
print(f'RAWROOT (source with TC): "{raw_root}"')
|
|
17
|
+
print(f'SYNCEDROOT (destination of synced folder): "{synced_root}"')
|
|
18
|
+
print(f'SNDROOT (destination of ISOs sound files): "{snd_root}"')
|
|
19
|
+
if proxies != '':
|
|
20
|
+
print(f'PROXIES (NLE proxy clips folder): "{proxies}"')
|
|
21
|
+
|
|
22
|
+
def write_conf(conf_key, conf_val):
|
|
23
|
+
# args are pahtlib.Paths.
|
|
24
|
+
# RAWROOT: files with TC (and ROLL folders), as is from cameras
|
|
25
|
+
# SYNCEDROOT: synced and no more TC (ROLL flattened)
|
|
26
|
+
# Writes configuration on filesystem for later retrieval
|
|
27
|
+
# Clears log of already synced clips.
|
|
28
|
+
conf_dir = platformdirs.user_config_dir('mamsync', 'plutz', ensure_exists=True)
|
|
29
|
+
current_values = dict(zip(['RAWROOT', 'SYNCEDROOT', 'SNDROOT', 'PROXIES'],
|
|
30
|
+
get_proj()))
|
|
31
|
+
logger.debug(f'old values {current_values}')
|
|
32
|
+
current_values[conf_key] = conf_val
|
|
33
|
+
logger.debug(f'updated values {current_values}')
|
|
34
|
+
conf_file = Path(conf_dir)/CONF_FILE
|
|
35
|
+
logger.debug('writing config in %s'%conf_file)
|
|
36
|
+
# print(f'\nWriting folders paths in configuration file "{conf_file}"')
|
|
37
|
+
# print_out_conf(raw_root, synced_root, snd_root)
|
|
38
|
+
conf_prs = configparser.ConfigParser()
|
|
39
|
+
conf_prs['SECTION1'] = current_values
|
|
40
|
+
with open(conf_file, 'w') as configfile_handle:
|
|
41
|
+
conf_prs.write(configfile_handle)
|
|
42
|
+
with open(conf_file, 'r') as configfile_handle:
|
|
43
|
+
logger.debug(f'config file content: \n{configfile_handle.read()}')
|
|
44
|
+
|
|
45
|
+
def get_proj(print_conf_stdout=False):
|
|
46
|
+
# check if user started a project before.
|
|
47
|
+
# stored in platformdirs.user_config_dir
|
|
48
|
+
# returns a tuple of strings (RAWROOT, SYNCEDROOTS, SNDROOT, PROXIES)
|
|
49
|
+
# if any, or a tuple of 4 empty strings '' otherwise.
|
|
50
|
+
# print location of conf file if print_conf_stdout
|
|
51
|
+
conf_dir = platformdirs.user_config_dir('mamsync', 'plutz')
|
|
52
|
+
conf_file = Path(conf_dir)/CONF_FILE
|
|
53
|
+
logger.debug('try reading config in %s'%conf_file)
|
|
54
|
+
if print_conf_stdout:
|
|
55
|
+
print(f'\nTrying to read configuration from file {conf_file}')
|
|
56
|
+
if conf_file.exists():
|
|
57
|
+
conf_prs = configparser.ConfigParser()
|
|
58
|
+
conf_prs.read(conf_file)
|
|
59
|
+
try:
|
|
60
|
+
RAWROOT = conf_prs.get('SECTION1', 'RAWROOT')
|
|
61
|
+
except configparser.NoOptionError:
|
|
62
|
+
RAWROOT = ''
|
|
63
|
+
try:
|
|
64
|
+
SYNCEDROOT = conf_prs.get('SECTION1', 'SYNCEDROOT')
|
|
65
|
+
except configparser.NoOptionError:
|
|
66
|
+
SYNCEDROOT = ''
|
|
67
|
+
try:
|
|
68
|
+
PROXIES = conf_prs.get('SECTION1', 'PROXIES')
|
|
69
|
+
except configparser.NoOptionError:
|
|
70
|
+
PROXIES = ''
|
|
71
|
+
try:
|
|
72
|
+
SNDROOT = conf_prs.get('SECTION1', 'SNDROOT')
|
|
73
|
+
except configparser.NoOptionError:
|
|
74
|
+
SNDROOT = ''
|
|
75
|
+
logger.debug('read from conf: RAWROOT= %s SYNCEDROOT= %s SNDROOT=%s PROXIES=%s'%
|
|
76
|
+
(RAWROOT, SYNCEDROOT, SNDROOT, PROXIES))
|
|
77
|
+
return RAWROOT, SYNCEDROOT, SNDROOT, PROXIES
|
|
78
|
+
else:
|
|
79
|
+
logger.debug(f'no config file found at {conf_file}')
|
|
80
|
+
print('No configuration found.')
|
|
81
|
+
return '', '', '', ''
|
|
82
|
+
|
|
83
|
+
def new_parser():
|
|
84
|
+
parser = argparse.ArgumentParser()
|
|
85
|
+
parser.add_argument('--rawroot',
|
|
86
|
+
nargs = 1,
|
|
87
|
+
dest='rawroot',
|
|
88
|
+
help='Sets new value for raw root folder (i.e.: clips with TC)')
|
|
89
|
+
parser.add_argument('--syncedroot',
|
|
90
|
+
nargs = 1,
|
|
91
|
+
dest='syncedroot',
|
|
92
|
+
help="""Sets where the synced files will be written, to be used by the NLE. Will contain a mirror copy of RAWROOT """)
|
|
93
|
+
parser.add_argument('--proxies',
|
|
94
|
+
nargs = 1,
|
|
95
|
+
dest='proxies',
|
|
96
|
+
help='Sets where the proxy files are stored by the NLE')
|
|
97
|
+
parser.add_argument('--sndfolder',
|
|
98
|
+
nargs = 1,
|
|
99
|
+
dest='sndfolder',
|
|
100
|
+
help='Sets new value for sound folder (where ISOs sound files will be stored)')
|
|
101
|
+
parser.add_argument('--clearconf',
|
|
102
|
+
action='store_true',
|
|
103
|
+
dest='clearconf',
|
|
104
|
+
help='Clear configured values.')
|
|
105
|
+
parser.add_argument('--showconf',
|
|
106
|
+
action='store_true',
|
|
107
|
+
dest='showconf',
|
|
108
|
+
help='Show current configured values.')
|
|
109
|
+
return parser
|
|
110
|
+
|
|
111
|
+
def main():
|
|
112
|
+
parser = new_parser()
|
|
113
|
+
args = parser.parse_args()
|
|
114
|
+
logger.debug(f'arguments from argparse {args}')
|
|
115
|
+
if args.rawroot:
|
|
116
|
+
val = args.rawroot[0]
|
|
117
|
+
write_conf('RAWROOT', val)
|
|
118
|
+
print(f'Set source folder of unsynced clips (rawroot) to:\n{val}')
|
|
119
|
+
sys.exit(0)
|
|
120
|
+
if args.syncedroot:
|
|
121
|
+
val = args.syncedroot[0]
|
|
122
|
+
write_conf('SYNCEDROOT', args.syncedroot[0])
|
|
123
|
+
print(f'Set destination folder of synced clips (syncedroot) to:\n{val}')
|
|
124
|
+
sys.exit(0)
|
|
125
|
+
if args.proxies:
|
|
126
|
+
val = args.proxies[0]
|
|
127
|
+
write_conf('PROXIES', args.proxies[0])
|
|
128
|
+
print(f'Set proxies folder to:\n{val}')
|
|
129
|
+
sys.exit(0)
|
|
130
|
+
if args.sndfolder:
|
|
131
|
+
val = args.sndfolder[0]
|
|
132
|
+
write_conf('SNDROOT', args.sndfolder[0])
|
|
133
|
+
print(f'Set destination folder of ISOs sound files (sndfolder) to:\n{val}')
|
|
134
|
+
sys.exit(0)
|
|
135
|
+
if args.clearconf:
|
|
136
|
+
write_conf('RAWROOT', '')
|
|
137
|
+
write_conf('SYNCEDROOT', '')
|
|
138
|
+
write_conf('SNDROOT', '')
|
|
139
|
+
write_conf('PROXIES', '')
|
|
140
|
+
print_out_conf('','','','')
|
|
141
|
+
sys.exit(0)
|
|
142
|
+
if args.showconf:
|
|
143
|
+
get_proj()
|
|
144
|
+
print_out_conf(*get_proj(True))
|
|
145
|
+
sys.exit(0)
|
|
146
|
+
# roots = get_proj(False)
|
|
147
|
+
# if any([r == '' for r in roots]):
|
|
148
|
+
# print("Can't sync if some folders are not set:")
|
|
149
|
+
# print_out_conf(*get_proj())
|
|
150
|
+
# print('Bye.')
|
|
151
|
+
# sys.exit(0)
|
|
152
|
+
# for r in roots:
|
|
153
|
+
# if not r.is_absolute():
|
|
154
|
+
# print(f'\rError: folder {r} must be an absolute path. Bye')
|
|
155
|
+
# sys.exit(0)
|
|
156
|
+
# if not r.exists():
|
|
157
|
+
# print(f'\rError: folder {r} does not exist. Bye')
|
|
158
|
+
# sys.exit(0)
|
|
159
|
+
# if not r.is_dir():
|
|
160
|
+
# print(f'\rError: path {r} is not a folder. Bye')
|
|
161
|
+
# sys.exit(0)
|
|
162
|
+
# raw_root, synced_root, snd_root = roots
|
|
163
|
+
# if args.sub_dir != None:
|
|
164
|
+
# top_dir = args.sub_dir
|
|
165
|
+
# logger.debug(f'sub _dir: {args.sub_dir}')
|
|
166
|
+
# if not Path(top_dir).exists():
|
|
167
|
+
# print(f"\rError: folder {top_dir} doesn't exist, bye.")
|
|
168
|
+
# sys.exit(0)
|
|
169
|
+
# else:
|
|
170
|
+
# top_dir = raw_root
|
|
171
|
+
# if args.resync:
|
|
172
|
+
# clear_log()
|
|
173
|
+
|
|
174
|
+
if __name__ == '__main__':
|
|
175
|
+
main()
|