tictacsync 0.99a0__py3-none-any.whl → 1.0.0a0__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 +4 -4
- tictacsync/newmix.py +320 -0
- tictacsync/timeline.py +6 -4
- {tictacsync-0.99a0.dist-info → tictacsync-1.0.0a0.dist-info}/METADATA +1 -1
- tictacsync-1.0.0a0.dist-info/RECORD +17 -0
- {tictacsync-0.99a0.dist-info → tictacsync-1.0.0a0.dist-info}/entry_points.txt +1 -1
- tictacsync-0.99a0.dist-info/RECORD +0 -16
- {tictacsync-0.99a0.dist-info → tictacsync-1.0.0a0.dist-info}/LICENSE +0 -0
- {tictacsync-0.99a0.dist-info → tictacsync-1.0.0a0.dist-info}/WHEEL +0 -0
- {tictacsync-0.99a0.dist-info → tictacsync-1.0.0a0.dist-info}/top_level.txt +0 -0
tictacsync/device_scanner.py
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
# while inotifywait --recursive -e close_write . ; do python entry.py tests/multi2/; done
|
|
4
4
|
# above for linux
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
TRACKSFILE = 'tracks.txt'
|
|
7
7
|
SILENT_TRACK_TOKENS = '-0n'
|
|
8
8
|
|
|
9
9
|
av_file_extensions = \
|
|
@@ -413,19 +413,19 @@ class Scanner:
|
|
|
413
413
|
|
|
414
414
|
def _get_tracks_from_file(self, device) -> Tracks:
|
|
415
415
|
"""
|
|
416
|
-
Look for eventual track names in
|
|
416
|
+
Look for eventual track names in TRACKSFILE file, stored inside the
|
|
417
417
|
recorder folder alongside the audio files. If there, returns a Tracks
|
|
418
418
|
object, if not returns None.
|
|
419
419
|
"""
|
|
420
420
|
source_audio_folder = device.folder
|
|
421
|
-
tracks_file = source_audio_folder/
|
|
421
|
+
tracks_file = source_audio_folder/TRACKSFILE
|
|
422
422
|
track_names = False
|
|
423
423
|
a_recording = [m for m in self.found_media_files
|
|
424
424
|
if m.device == device][0]
|
|
425
425
|
logger.debug('a_recording for device %s : %s'%(device, a_recording))
|
|
426
426
|
nchan = sox.file_info.channels(str(a_recording.path))
|
|
427
427
|
if os.path.isfile(tracks_file):
|
|
428
|
-
logger.debug('found file: %s'%(
|
|
428
|
+
logger.debug('found file: %s'%(TRACKSFILE))
|
|
429
429
|
tracks = self._parse_track_values(tracks_file)
|
|
430
430
|
if tracks.error_msg:
|
|
431
431
|
print('\nError parsing [gold1]%s[/gold1] file: %s, quitting.\n'%
|
tictacsync/newmix.py
ADDED
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
import os, itertools, argparse, ffmpeg, tempfile
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from loguru import logger
|
|
4
|
+
import shutil, sys, re, sox
|
|
5
|
+
from pprint import pformat
|
|
6
|
+
from rich import print
|
|
7
|
+
|
|
8
|
+
OUT_DIR_DEFAULT = 'SyncedMedia'
|
|
9
|
+
MCCDIR = 'SyncedMulticamClips'
|
|
10
|
+
SEC_DELAY_CHANGED_SND = 10 #sec, SND_DIR changed if diff time is bigger
|
|
11
|
+
DEL_TEMP = True
|
|
12
|
+
|
|
13
|
+
logger.level("DEBUG", color="<yellow>")
|
|
14
|
+
logger.remove()
|
|
15
|
+
# logger.add(sys.stdout, filter=lambda r: r["function"] == "_change_audio4video")
|
|
16
|
+
# logger.add(sys.stdout, filter=lambda r: r["function"] == "find_SND_vids_pairs_in_dir")
|
|
17
|
+
|
|
18
|
+
video_extensions = \
|
|
19
|
+
"""webm mkv flv flv vob ogv ogg drc gif gifv mng avi mov
|
|
20
|
+
qt wmv yuv rm rmvb viv asf mp4 m4p m4v mpg mp2 mpeg mpe
|
|
21
|
+
mpv mpg mpeg m2v m4v svi 3gp 3g2 mxf roq nsv""".split() # from wikipedia
|
|
22
|
+
|
|
23
|
+
def _pathname(tempfile_or_path) -> str:
|
|
24
|
+
# utility for obtaining a str from different filesystem objects
|
|
25
|
+
if isinstance(tempfile_or_path, str):
|
|
26
|
+
return tempfile_or_path
|
|
27
|
+
if isinstance(tempfile_or_path, Path):
|
|
28
|
+
return str(tempfile_or_path)
|
|
29
|
+
if isinstance(tempfile_or_path, tempfile._TemporaryFileWrapper):
|
|
30
|
+
return tempfile_or_path.name
|
|
31
|
+
else:
|
|
32
|
+
raise Exception('%s should be Path or tempfile...'%tempfile_or_path)
|
|
33
|
+
|
|
34
|
+
def is_synced_video(f):
|
|
35
|
+
# True if name as video extension
|
|
36
|
+
# and is under SyncedMedia or SyncedMulticamClips folders
|
|
37
|
+
# f is a Path
|
|
38
|
+
ext = f.suffix[1:] # removing leading '.'
|
|
39
|
+
ok_ext = ext.lower() in video_extensions
|
|
40
|
+
f_parts = f.parts
|
|
41
|
+
ok_folders = OUT_DIR_DEFAULT in f_parts or MCCDIR in f_parts
|
|
42
|
+
# logger.debug('ok_ext: %s ok_folders: %s'%(ok_ext, ok_folders))
|
|
43
|
+
return ok_ext and ok_folders
|
|
44
|
+
|
|
45
|
+
def find_SND_vids_pairs_in_dir(top):
|
|
46
|
+
# look for matching video name and SND dir name
|
|
47
|
+
# eg: IMG04.mp4 and IMG04_SND
|
|
48
|
+
# maybe IMG04v2.mp4 if audio changed before (than it will be IMG04v3.mp4)
|
|
49
|
+
# returns list of matches
|
|
50
|
+
# recursively search from 'top' argument
|
|
51
|
+
vids = []
|
|
52
|
+
SNDs = []
|
|
53
|
+
for (root,dirs,files) in os.walk(top):
|
|
54
|
+
for d in dirs:
|
|
55
|
+
if d[-4:] == '_SND':
|
|
56
|
+
SNDs.append(Path(root)/d)
|
|
57
|
+
for f in files:
|
|
58
|
+
if is_synced_video(Path(root)/f): # add being in SyncedMedia or SyncedMulticamClips folder
|
|
59
|
+
vids.append(Path(root)/f)
|
|
60
|
+
logger.debug('vids %s SNDs %s'%(pformat(vids), pformat(SNDs)))
|
|
61
|
+
matches = []
|
|
62
|
+
def _names_match(vidname, SND_name):
|
|
63
|
+
# vidname is a str and has no extension
|
|
64
|
+
# vidname could have vNN suffix as in DSC_8064v31 so matches DSC_8064
|
|
65
|
+
if vidname == SND_name: # no suffix presents
|
|
66
|
+
return True
|
|
67
|
+
m = re.match(SND_name + r'v(\d+)', vidname)
|
|
68
|
+
if m != None:
|
|
69
|
+
logger.debug('its a natch and N= %s'%m.groups()[0])
|
|
70
|
+
return m != None
|
|
71
|
+
for pair in list(itertools.product(SNDs, vids)):
|
|
72
|
+
# print(pair)
|
|
73
|
+
SND, vid = pair # Paths
|
|
74
|
+
vidname, ext = vid.name.split('.') # string
|
|
75
|
+
if _names_match(vidname, SND.name[:-4]):
|
|
76
|
+
logger.debug('SND %s matches video %s'%(
|
|
77
|
+
Path('').joinpath(*SND.parts[-2:]),
|
|
78
|
+
Path('').joinpath(*vid.parts[-3:])))
|
|
79
|
+
matches.append(pair) # list of Paths
|
|
80
|
+
logger.debug('matches: %s'%pformat(matches))
|
|
81
|
+
return matches
|
|
82
|
+
|
|
83
|
+
def parse_and_check_arguments():
|
|
84
|
+
# parses directories from command arguments
|
|
85
|
+
# check for consistencies and warn user and exits,
|
|
86
|
+
# if returns, gives:
|
|
87
|
+
# proxies_dir, originals_dir, audio_dir, both_audio_vid, scan_only
|
|
88
|
+
parser = argparse.ArgumentParser()
|
|
89
|
+
# parser.add_argument('-v',
|
|
90
|
+
# nargs=1,
|
|
91
|
+
# dest='video_dirs',
|
|
92
|
+
# help='Where proxy clips and/or originals are stored')
|
|
93
|
+
# parser.add_argument('-a',
|
|
94
|
+
# nargs=1,
|
|
95
|
+
# dest='audio_dir',
|
|
96
|
+
# help='Contains newly changed mix files')
|
|
97
|
+
parser.add_argument('-b',
|
|
98
|
+
nargs=1,
|
|
99
|
+
dest='both_audio_vid',
|
|
100
|
+
help='Directory scanned for both audio and video')
|
|
101
|
+
parser.add_argument('--dry',
|
|
102
|
+
action='store_true',
|
|
103
|
+
dest='scan_only',
|
|
104
|
+
help="Just display changed audio, don't merge")
|
|
105
|
+
args = parser.parse_args()
|
|
106
|
+
logger.debug('args %s'%args)
|
|
107
|
+
# ok cases:
|
|
108
|
+
# -p -o -a + no -b
|
|
109
|
+
# -o -a + no -b
|
|
110
|
+
# args_set = [args.originals_dir != None,
|
|
111
|
+
# args.audio_dir != None,
|
|
112
|
+
# args.both_audio_vid != None,
|
|
113
|
+
# ]
|
|
114
|
+
# p, o, a, b = args_set
|
|
115
|
+
# check that argument -b (both_audio_vid) is used alone
|
|
116
|
+
# if b and any([o, a, p]):
|
|
117
|
+
# print("\nDon't specify other argument than -b if both audio and video searched in the same directory.\n")
|
|
118
|
+
# parser.print_help(sys.stderr)
|
|
119
|
+
# sys.exit(0)
|
|
120
|
+
# check that if proxies (-p) are specified, orginals too (-o)
|
|
121
|
+
# if p and not o:
|
|
122
|
+
# print("\nIf proxies directory is specified, so should originals directory.\n")
|
|
123
|
+
# parser.print_help(sys.stderr)
|
|
124
|
+
# sys.exit(0)
|
|
125
|
+
# check that -o and -a are used together
|
|
126
|
+
# if not b and not (o and a):
|
|
127
|
+
# print("\nAt least originals and audio directories must be given (-o and -a) when audio and video are in different dir.\n")
|
|
128
|
+
# parser.print_help(sys.stderr)
|
|
129
|
+
# sys.exit(0)
|
|
130
|
+
# # work in progress (aug 2025), so limit to -b:
|
|
131
|
+
# if not b :
|
|
132
|
+
# print("\nFor now, only -b argument is supported (a directory scanned for both audio and video) .\n")
|
|
133
|
+
# parser.print_help(sys.stderr)
|
|
134
|
+
# sys.exit(0)
|
|
135
|
+
# list of singletons, so flatten. Keep None and False as is
|
|
136
|
+
return args
|
|
137
|
+
|
|
138
|
+
def get_recent_mix(SND_dir, vid):
|
|
139
|
+
# check if there are mixl, mixr or mix files in SND_dir
|
|
140
|
+
# and return the paths if they are more recent than vid.
|
|
141
|
+
# returns empty tuple otherwise
|
|
142
|
+
# arguments SND_dir, vid and returned values are of Path type
|
|
143
|
+
wav_files = list(SND_dir.iterdir())
|
|
144
|
+
logger.debug(f'wav_files {wav_files} in {SND_dir}')
|
|
145
|
+
def is_mix(p):
|
|
146
|
+
re_result = re.match(r'mix([lrLR])*', p.name)
|
|
147
|
+
logger.debug(f'for {p.name} re_result {re_result}')
|
|
148
|
+
return re_result is not None
|
|
149
|
+
mix_files = [p for p in wav_files if is_mix(p)]
|
|
150
|
+
if len(mix_files) == 0:
|
|
151
|
+
return ()
|
|
152
|
+
# consistency check, should be 1 or 2 files
|
|
153
|
+
if not len(mix_files) in (1,2):
|
|
154
|
+
print(f'\nError: too many mix files in [bold]{SND_dir}[/bold], bye.')
|
|
155
|
+
sys.exit(0)
|
|
156
|
+
# one file? it must be mix.wav
|
|
157
|
+
if len(mix_files) == 1:
|
|
158
|
+
fn = mix_files[0].name
|
|
159
|
+
if fn.upper() != 'MIX.WAV':
|
|
160
|
+
print(f'\nError in [bold]{SND_dir}[/bold], the only file should be mix.wav, not [bold]{fn}[/bold][/bold]; bye.')
|
|
161
|
+
sys.exit(0)
|
|
162
|
+
# two files? verify they are mixL and mixR and mono each
|
|
163
|
+
if len(mix_files) == 2:
|
|
164
|
+
first3uppercase = [p.name[:4].upper() for p in mix_files]
|
|
165
|
+
first3uppercase.sort()
|
|
166
|
+
first3uppercase = ''.join(first3uppercase)
|
|
167
|
+
if first3uppercase != 'MIXLMIXR':
|
|
168
|
+
print(f'\nError: mix names mismatch in [bold]{SND_dir}[/bold];')
|
|
169
|
+
print(f'names are [bold]{[p.name for p in mix_files]}[/bold], check they are simply mixL.wav and mixR.wav; bye.')
|
|
170
|
+
sys.exit(0)
|
|
171
|
+
def _nch(p):
|
|
172
|
+
return sox.file_info.channels(str(p))
|
|
173
|
+
are_mono = [_nch(p) == 1 for p in mix_files]
|
|
174
|
+
logger.debug('are_mono: %s'%are_mono)
|
|
175
|
+
if not all(are_mono):
|
|
176
|
+
print(f'\nError in [bold]{SND_dir}[/bold], some files are not mono, bye.')
|
|
177
|
+
sys.exit(0)
|
|
178
|
+
logger.debug(f'mix_files: {mix_files}')
|
|
179
|
+
# check dates, if two files, take first
|
|
180
|
+
mix_modification_time = mix_files[0].stat().st_mtime
|
|
181
|
+
vid_mod_time = vid.stat().st_mtime
|
|
182
|
+
# difference of modification time in secs
|
|
183
|
+
mix_more_recent_by = mix_modification_time - vid_mod_time
|
|
184
|
+
logger.debug('mix_more_recent_by: %s'%mix_more_recent_by)
|
|
185
|
+
if mix_more_recent_by > SEC_DELAY_CHANGED_SND:
|
|
186
|
+
if len(mix_files) == 1:
|
|
187
|
+
two_folders_up = mix_files[0]
|
|
188
|
+
# two_folders_up = Path('').joinpath(*mix_files[0].parts[-3:])
|
|
189
|
+
print(f'\nFound new mix: [bold]{two_folders_up}[/bold]')
|
|
190
|
+
return mix_files
|
|
191
|
+
else:
|
|
192
|
+
return ()
|
|
193
|
+
|
|
194
|
+
def _keep_VIDEO_only(video_path):
|
|
195
|
+
# return file handle to a temp video file formed from the video_path
|
|
196
|
+
# stripped of its sound
|
|
197
|
+
in1 = ffmpeg.input(_pathname(video_path))
|
|
198
|
+
video_extension = video_path.suffix
|
|
199
|
+
silenced_opts = ["-loglevel", "quiet", "-nostats", "-hide_banner"]
|
|
200
|
+
file_handle = tempfile.NamedTemporaryFile(suffix=video_extension,
|
|
201
|
+
delete=DEL_TEMP)
|
|
202
|
+
out1 = in1.output(file_handle.name, map='0:v', vcodec='copy')
|
|
203
|
+
ffmpeg.run([out1.global_args(*silenced_opts)], overwrite_output=True)
|
|
204
|
+
return file_handle
|
|
205
|
+
|
|
206
|
+
def _change_audio4video(audio_path: Path, video: Path):
|
|
207
|
+
"""
|
|
208
|
+
Replace audio in video (argument) by the audio contained in
|
|
209
|
+
audio_path (argument) returns nothing
|
|
210
|
+
If name has version number, bump it (DSC_8064v13.MOV -> DSC_8064v14.MOV)
|
|
211
|
+
|
|
212
|
+
"""
|
|
213
|
+
vidname, video_ext = video.name.split('.')
|
|
214
|
+
vid_only_handle = _keep_VIDEO_only(video)
|
|
215
|
+
a_n = _pathname(audio_path)
|
|
216
|
+
v_n = _pathname(vid_only_handle)
|
|
217
|
+
# check v suffix
|
|
218
|
+
m = re.match(f'(.*)v(\\d+)', vidname)
|
|
219
|
+
if m == None:
|
|
220
|
+
# no suffix, add one
|
|
221
|
+
out_path = video.parent / f'{vidname}v2.{video_ext}'
|
|
222
|
+
out_n = _pathname(out_path)
|
|
223
|
+
else:
|
|
224
|
+
base, number_str = m.groups()
|
|
225
|
+
logger.debug(f'base {base}, number_str {number_str}')
|
|
226
|
+
up_tick = 1 + int(number_str)
|
|
227
|
+
out_path = video.parent / f'{base}v{up_tick}.{video_ext}'
|
|
228
|
+
out_n = _pathname(out_path)
|
|
229
|
+
print(f'Video [bold]{video}[/bold] \nhas new sound and is now [bold]{out_path}[/bold]')
|
|
230
|
+
video.unlink()
|
|
231
|
+
# building args for debug purpose only:
|
|
232
|
+
ffmpeg_args = (
|
|
233
|
+
ffmpeg
|
|
234
|
+
.input(v_n)
|
|
235
|
+
.output(out_n, vcodec='copy')
|
|
236
|
+
# .output(out_n, shortest=None, vcodec='copy')
|
|
237
|
+
.global_args('-i', a_n, "-hide_banner")
|
|
238
|
+
.overwrite_output()
|
|
239
|
+
.get_args()
|
|
240
|
+
)
|
|
241
|
+
logger.debug('ffmpeg args: %s'%' '.join(ffmpeg_args))
|
|
242
|
+
try: # for real now
|
|
243
|
+
_, out = (
|
|
244
|
+
ffmpeg
|
|
245
|
+
.input(v_n)
|
|
246
|
+
# .output(out_n, shortest=None, vcodec='copy')
|
|
247
|
+
.output(out_n, vcodec='copy')
|
|
248
|
+
.global_args('-i', a_n, "-hide_banner")
|
|
249
|
+
.overwrite_output()
|
|
250
|
+
.run(capture_stderr=True)
|
|
251
|
+
)
|
|
252
|
+
logger.debug('ffmpeg output')
|
|
253
|
+
for l in out.decode("utf-8").split('\n'):
|
|
254
|
+
logger.debug(l)
|
|
255
|
+
except ffmpeg.Error as e:
|
|
256
|
+
print('ffmpeg.run error merging: \n\t %s + %s = %s\n'%(
|
|
257
|
+
audio_path,
|
|
258
|
+
video_path,
|
|
259
|
+
synced_clip_file
|
|
260
|
+
))
|
|
261
|
+
print(e)
|
|
262
|
+
print(e.stderr.decode('UTF-8'))
|
|
263
|
+
sys.exit(1)
|
|
264
|
+
|
|
265
|
+
def _sox_combine(paths) -> Path:
|
|
266
|
+
"""
|
|
267
|
+
Combines (stacks) files referred by the list of Path into a new temporary
|
|
268
|
+
files passed on return each files are stacked in a different channel, so
|
|
269
|
+
len(paths) == n_channels
|
|
270
|
+
"""
|
|
271
|
+
if len(paths) == 1: # one device only, nothing to stack
|
|
272
|
+
logger.debug('one device only, nothing to stack')
|
|
273
|
+
return paths[0] ########################################################
|
|
274
|
+
out_file_handle = tempfile.NamedTemporaryFile(suffix='.wav',
|
|
275
|
+
delete=DEL_TEMP)
|
|
276
|
+
filenames = [_pathname(p) for p in paths]
|
|
277
|
+
out_file_name = _pathname(out_file_handle)
|
|
278
|
+
logger.debug('combining files: %s into %s'%(
|
|
279
|
+
filenames,
|
|
280
|
+
out_file_name))
|
|
281
|
+
cbn = sox.Combiner()
|
|
282
|
+
cbn.set_input_format(file_type=['wav']*len(paths))
|
|
283
|
+
status = cbn.build(
|
|
284
|
+
filenames,
|
|
285
|
+
out_file_name,
|
|
286
|
+
combine_type='merge')
|
|
287
|
+
logger.debug('sox.build status: %s'%status)
|
|
288
|
+
if status != True:
|
|
289
|
+
print('Error, sox did not merge files in _sox_combine()')
|
|
290
|
+
sys.exit(1)
|
|
291
|
+
merged_duration = sox.file_info.duration(
|
|
292
|
+
_pathname(out_file_handle))
|
|
293
|
+
nchan = sox.file_info.channels(
|
|
294
|
+
_pathname(out_file_handle))
|
|
295
|
+
logger.debug('merged file duration %f s with %i channels '%
|
|
296
|
+
(merged_duration, nchan))
|
|
297
|
+
return out_file_handle
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
def main():
|
|
301
|
+
# proxies_dir, originals_dir, audio_dir, both_audio_vid, scan_only = \
|
|
302
|
+
# parse_and_check_arguments()
|
|
303
|
+
args = parse_and_check_arguments()
|
|
304
|
+
matching_pairs = find_SND_vids_pairs_in_dir(args.both_audio_vid[0])
|
|
305
|
+
for SND_dir, vid in matching_pairs:
|
|
306
|
+
new_mix_files = get_recent_mix(SND_dir, vid)
|
|
307
|
+
# logger.debug('new_mix_files: %s'%str(new_mix_files))
|
|
308
|
+
if new_mix_files != ():
|
|
309
|
+
logger.debug(f'new mixes {new_mix_files} in {SND_dir} for {vid.name}')
|
|
310
|
+
if len(new_mix_files) == 2:
|
|
311
|
+
new_audio_wav = _sox_combine(new_mix_files)
|
|
312
|
+
logger.debug('stereo_wav: %s'%new_audio_wav)
|
|
313
|
+
else: # len == 1, mono wav file
|
|
314
|
+
new_audio_wav = new_mix_files[0]
|
|
315
|
+
_change_audio4video(new_audio_wav, vid)
|
|
316
|
+
# print('\nVideo %s has new audio'%vid)
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
if __name__ == '__main__':
|
|
320
|
+
main()
|
tictacsync/timeline.py
CHANGED
|
@@ -27,6 +27,7 @@ DB_OSX_NORM = -6 #dB
|
|
|
27
27
|
OUT_DIR_DEFAULT = 'SyncedMedia'
|
|
28
28
|
MCCDIR = 'SyncedMulticamClips'
|
|
29
29
|
|
|
30
|
+
|
|
30
31
|
# utility to lock ISO audio files
|
|
31
32
|
def remove_write_permissions(path):
|
|
32
33
|
"""Remove write permissions from this path, while keeping all other permissions intact.
|
|
@@ -201,7 +202,7 @@ def _sox_multi2stereo(multichan_tmpfl, stereo_trxs) -> tempfile.NamedTemporaryFi
|
|
|
201
202
|
stereo_tempfile = tempfile.NamedTemporaryFile(suffix='.wav',
|
|
202
203
|
delete=DEL_TEMP)
|
|
203
204
|
tfm = sox.Transformer()
|
|
204
|
-
tfm.channels(1)
|
|
205
|
+
tfm.channels(1) # why ? https://pysox.readthedocs.io/en/latest/api.html?highlight=channels#sox.transform.Transformer.channels
|
|
205
206
|
status = tfm.build(_pathname(multichan_tmpfl),_pathname(stereo_tempfile))
|
|
206
207
|
logger.debug('n chan ouput: %s'%
|
|
207
208
|
sox.file_info.channels(_pathname(stereo_tempfile)))
|
|
@@ -655,7 +656,7 @@ class AudioStitcherVideoMerger:
|
|
|
655
656
|
"""
|
|
656
657
|
Writes isolated audio files that were synced to synced_clip_file,
|
|
657
658
|
each track will have its dedicated monofile, named sequentially or with
|
|
658
|
-
the name find in
|
|
659
|
+
the name find in TRACKSFILE if any, see Scanner._get_tracks_from_file()
|
|
659
660
|
|
|
660
661
|
edited_audio_all_devices:
|
|
661
662
|
a list of (name, mono_tempfile)
|
|
@@ -711,7 +712,8 @@ class AudioStitcherVideoMerger:
|
|
|
711
712
|
synced_clip_dir = synced_clip_file.parent
|
|
712
713
|
# build ISOs subfolders structure, see comment string below
|
|
713
714
|
video_stem_WO_suffix = synced_clip_file.stem
|
|
714
|
-
ISOdir = synced_clip_dir/(video_stem_WO_suffix + '
|
|
715
|
+
# ISOdir = synced_clip_dir/(video_stem_WO_suffix + 'ISO')
|
|
716
|
+
ISOdir = synced_clip_dir/(video_stem_WO_suffix + '_SND')/'ISOfiles'
|
|
715
717
|
os.makedirs(ISOdir, exist_ok=True)
|
|
716
718
|
logger.debug('edited_audio_all_devices %s'%edited_audio_all_devices)
|
|
717
719
|
logger.debug('ISOdir %s'%ISOdir)
|
|
@@ -794,7 +796,7 @@ class AudioStitcherVideoMerger:
|
|
|
794
796
|
print('Error: TicTacCode channel detected is [gold1]%i[/gold1]'%
|
|
795
797
|
(device.ttc), end=' ')
|
|
796
798
|
print('and [gold1]%s[/gold1] for the device [gold1]%s[/gold1] specifies channel [gold1]%i[/gold1],'%
|
|
797
|
-
(device_scanner.
|
|
799
|
+
(device_scanner.TRACKSFILE,
|
|
798
800
|
device.name, device.tracks.ttc-1))
|
|
799
801
|
print('Please correct the discrepancy and rerun. Quitting.')
|
|
800
802
|
sys.exit(1)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
tictacsync/LTCcheck.py,sha256=IEfpB_ZajWuRTWtqji0H-B2g7GQvWmGVjfT0Icumv7o,15704
|
|
2
|
+
tictacsync/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
+
tictacsync/device_scanner.py,sha256=lNoOyPuAsPzjGs5a6iKJQ-BAVXKKINk8fvTEgXgFeK0,35593
|
|
4
|
+
tictacsync/entry.py,sha256=KOhB8ivgme3GPpWShad2adS1lvIU9v0yMFY0CELwAmM,20673
|
|
5
|
+
tictacsync/multi2polywav.py,sha256=-nX5reZo6QNxFYdhsliHTs8bTfMjPzcONDT8vJbkZUA,7291
|
|
6
|
+
tictacsync/newmix.py,sha256=-_bZrbZHMrLaP3As_tc5Q96Y-pI96sOvnOQiQDeY7x8,13009
|
|
7
|
+
tictacsync/remergemix.py,sha256=bRyi1hyNcyM1rTkHh8DmSsIQjYpwPprxSyyVipnxz30,9909
|
|
8
|
+
tictacsync/remrgmx.py,sha256=FxaAo5qqynpj6O56ekQGD31YP6X2g-kEdwVpHSCoh4Q,4265
|
|
9
|
+
tictacsync/synciso.py,sha256=XmUcdUF9rl4VdCm7XW4PeYWYWM0vgAY9dC2hapoul9g,4821
|
|
10
|
+
tictacsync/timeline.py,sha256=aMu1ntVrpFtH1YfyIgp80k7DT2RFo5B0E-BlMWE8wAs,72723
|
|
11
|
+
tictacsync/yaltc.py,sha256=xbMucI19UJKrEvIzyfpOsi3piSWzqM1gKgooeT9DV8g,53167
|
|
12
|
+
tictacsync-1.0.0a0.dist-info/LICENSE,sha256=ZAOPXLh1zlQAnhHUd7oLslKM01YZ5UiAu3STYjwIxck,1068
|
|
13
|
+
tictacsync-1.0.0a0.dist-info/METADATA,sha256=toUxrfuNZkQNbpNXBQGOSRBbN9WLD8w07_e5Gg_y54U,5697
|
|
14
|
+
tictacsync-1.0.0a0.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
|
|
15
|
+
tictacsync-1.0.0a0.dist-info/entry_points.txt,sha256=bMsk7T_7fwCtAOUbFyvECvHOzCJb2fmWjkUKQTkwbsc,131
|
|
16
|
+
tictacsync-1.0.0a0.dist-info/top_level.txt,sha256=eaCWG-BsYTRR-gLTJbK4RfcaXajr0gjQ6wG97MkGRrg,11
|
|
17
|
+
tictacsync-1.0.0a0.dist-info/RECORD,,
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
tictacsync/LTCcheck.py,sha256=IEfpB_ZajWuRTWtqji0H-B2g7GQvWmGVjfT0Icumv7o,15704
|
|
2
|
-
tictacsync/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
-
tictacsync/device_scanner.py,sha256=A41PmzI6jr4zCKRm3BL5aBQkdJImpN3DH4QIicCZolA,35585
|
|
4
|
-
tictacsync/entry.py,sha256=KOhB8ivgme3GPpWShad2adS1lvIU9v0yMFY0CELwAmM,20673
|
|
5
|
-
tictacsync/multi2polywav.py,sha256=-nX5reZo6QNxFYdhsliHTs8bTfMjPzcONDT8vJbkZUA,7291
|
|
6
|
-
tictacsync/remergemix.py,sha256=bRyi1hyNcyM1rTkHh8DmSsIQjYpwPprxSyyVipnxz30,9909
|
|
7
|
-
tictacsync/remrgmx.py,sha256=FxaAo5qqynpj6O56ekQGD31YP6X2g-kEdwVpHSCoh4Q,4265
|
|
8
|
-
tictacsync/synciso.py,sha256=XmUcdUF9rl4VdCm7XW4PeYWYWM0vgAY9dC2hapoul9g,4821
|
|
9
|
-
tictacsync/timeline.py,sha256=DW_24cddLIxmcAEywy_sU46lBc7bJLhpVSVi7TlWV4E,72531
|
|
10
|
-
tictacsync/yaltc.py,sha256=xbMucI19UJKrEvIzyfpOsi3piSWzqM1gKgooeT9DV8g,53167
|
|
11
|
-
tictacsync-0.99a0.dist-info/LICENSE,sha256=ZAOPXLh1zlQAnhHUd7oLslKM01YZ5UiAu3STYjwIxck,1068
|
|
12
|
-
tictacsync-0.99a0.dist-info/METADATA,sha256=XRzYPb479yzUd7SgUf5YagcDHeQqREeTUTOZnZyXWCE,5696
|
|
13
|
-
tictacsync-0.99a0.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
|
|
14
|
-
tictacsync-0.99a0.dist-info/entry_points.txt,sha256=g3tdFFrVRcrKpuyKOCLUVBMgYfV65q9kpLZUOD_XCKg,139
|
|
15
|
-
tictacsync-0.99a0.dist-info/top_level.txt,sha256=eaCWG-BsYTRR-gLTJbK4RfcaXajr0gjQ6wG97MkGRrg,11
|
|
16
|
-
tictacsync-0.99a0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|