tictacsync 0.98a0__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.

@@ -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
- TRACKSFN = 'tracks.txt'
6
+ TRACKSFILE = 'tracks.txt'
7
7
  SILENT_TRACK_TOKENS = '-0n'
8
8
 
9
9
  av_file_extensions = \
@@ -270,12 +270,16 @@ class Scanner:
270
270
 
271
271
  """
272
272
  files = Path(self.top_directory).rglob('*.*')
273
+ def _last4letters(part):
274
+ return
275
+
273
276
  paths = [
274
277
  p
275
278
  for p in files
276
279
  if p.suffix[1:] in av_file_extensions
277
280
  and SYNCEDFOLDER not in p.parts # SyncedMedia
278
281
  and MCCDIR not in p.parts # SyncedMulticamClips
282
+ and '_ISO' not in [part[-4:] for part in p.parts] # exclude ISO wav files
279
283
  ]
280
284
  logger.debug('found media files %s'%paths)
281
285
  parents = [p.parent for p in paths]
@@ -409,19 +413,19 @@ class Scanner:
409
413
 
410
414
  def _get_tracks_from_file(self, device) -> Tracks:
411
415
  """
412
- Look for eventual track names in TRACKSFN file, stored inside the
416
+ Look for eventual track names in TRACKSFILE file, stored inside the
413
417
  recorder folder alongside the audio files. If there, returns a Tracks
414
418
  object, if not returns None.
415
419
  """
416
420
  source_audio_folder = device.folder
417
- tracks_file = source_audio_folder/TRACKSFN
421
+ tracks_file = source_audio_folder/TRACKSFILE
418
422
  track_names = False
419
423
  a_recording = [m for m in self.found_media_files
420
424
  if m.device == device][0]
421
425
  logger.debug('a_recording for device %s : %s'%(device, a_recording))
422
426
  nchan = sox.file_info.channels(str(a_recording.path))
423
427
  if os.path.isfile(tracks_file):
424
- logger.debug('found file: %s'%(TRACKSFN))
428
+ logger.debug('found file: %s'%(TRACKSFILE))
425
429
  tracks = self._parse_track_values(tracks_file)
426
430
  if tracks.error_msg:
427
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/remrgmx.py CHANGED
@@ -32,9 +32,9 @@ def find_ISO_vids_pairs_in_dir(top):
32
32
  if d[-4:] == '_ISO':
33
33
  ISOs.append(Path(root)/d)
34
34
  for f in files:
35
- if is_video(f):
35
+ if is_video(f): # add being in SyncedMedia or SyncedMulticamClips folder
36
36
  vids.append(Path(root)/f)
37
- logger.debug('vids %s ISOs %s'%(vids, ISOs))
37
+ logger.debug('vids %s ISOs %s'%(pformat(vids), pformat(ISOs)))
38
38
  matches = []
39
39
  for pair in list(itertools.product(vids, ISOs)):
40
40
  # print(pair)
@@ -54,14 +54,10 @@ def parse_and_check_arguments():
54
54
  # if returns, gives:
55
55
  # proxies_dir, originals_dir, audio_dir, both_audio_vid, scan_only
56
56
  parser = argparse.ArgumentParser()
57
- parser.add_argument('-p',
58
- nargs=1,
59
- dest='proxies_dir',
60
- help='Where proxy clips are stored')
61
- parser.add_argument('-o',
62
- nargs=1,
63
- dest='originals_dir',
64
- help='Original (non-proxy) clips directory')
57
+ parser.add_argument('-v',
58
+ nargs=*,
59
+ dest='video_dirs',
60
+ help='Where proxy clips and/or originals are stored')
65
61
  parser.add_argument('-a',
66
62
  nargs=1,
67
63
  dest='audio_dir',
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.
@@ -38,7 +39,6 @@ def remove_write_permissions(path):
38
39
  NO_GROUP_WRITING = ~stat.S_IWGRP
39
40
  NO_OTHER_WRITING = ~stat.S_IWOTH
40
41
  NO_WRITING = NO_USER_WRITING & NO_GROUP_WRITING & NO_OTHER_WRITING
41
-
42
42
  current_permissions = stat.S_IMODE(os.lstat(path).st_mode)
43
43
  os.chmod(path, current_permissions & NO_WRITING)
44
44
 
@@ -202,7 +202,7 @@ def _sox_multi2stereo(multichan_tmpfl, stereo_trxs) -> tempfile.NamedTemporaryFi
202
202
  stereo_tempfile = tempfile.NamedTemporaryFile(suffix='.wav',
203
203
  delete=DEL_TEMP)
204
204
  tfm = sox.Transformer()
205
- tfm.channels(1)
205
+ tfm.channels(1) # why ? https://pysox.readthedocs.io/en/latest/api.html?highlight=channels#sox.transform.Transformer.channels
206
206
  status = tfm.build(_pathname(multichan_tmpfl),_pathname(stereo_tempfile))
207
207
  logger.debug('n chan ouput: %s'%
208
208
  sox.file_info.channels(_pathname(stereo_tempfile)))
@@ -656,7 +656,7 @@ class AudioStitcherVideoMerger:
656
656
  """
657
657
  Writes isolated audio files that were synced to synced_clip_file,
658
658
  each track will have its dedicated monofile, named sequentially or with
659
- the name find in TRACKSFN if any, see Scanner._get_tracks_from_file()
659
+ the name find in TRACKSFILE if any, see Scanner._get_tracks_from_file()
660
660
 
661
661
  edited_audio_all_devices:
662
662
  a list of (name, mono_tempfile)
@@ -712,7 +712,8 @@ class AudioStitcherVideoMerger:
712
712
  synced_clip_dir = synced_clip_file.parent
713
713
  # build ISOs subfolders structure, see comment string below
714
714
  video_stem_WO_suffix = synced_clip_file.stem
715
- ISOdir = synced_clip_dir/(video_stem_WO_suffix + '_ISO')
715
+ # ISOdir = synced_clip_dir/(video_stem_WO_suffix + 'ISO')
716
+ ISOdir = synced_clip_dir/(video_stem_WO_suffix + '_SND')/'ISOfiles'
716
717
  os.makedirs(ISOdir, exist_ok=True)
717
718
  logger.debug('edited_audio_all_devices %s'%edited_audio_all_devices)
718
719
  logger.debug('ISOdir %s'%ISOdir)
@@ -720,7 +721,7 @@ class AudioStitcherVideoMerger:
720
721
  destination = ISOdir/('%s.wav'%name)
721
722
  mono_tmpfl_trimpad = _fit_length(mono_tmpfl)
722
723
  shutil.copy(_pathname(mono_tmpfl_trimpad), destination)
723
- remove_write_permissions(destination)
724
+ # remove_write_permissions(destination)
724
725
  logger.debug('destination:%s'%destination)
725
726
 
726
727
  def _get_device_mix(self, device, multichan_tmpfl) -> tempfile.NamedTemporaryFile:
@@ -795,7 +796,7 @@ class AudioStitcherVideoMerger:
795
796
  print('Error: TicTacCode channel detected is [gold1]%i[/gold1]'%
796
797
  (device.ttc), end=' ')
797
798
  print('and [gold1]%s[/gold1] for the device [gold1]%s[/gold1] specifies channel [gold1]%i[/gold1],'%
798
- (device_scanner.TRACKSFN,
799
+ (device_scanner.TRACKSFILE,
799
800
  device.name, device.tracks.ttc-1))
800
801
  print('Please correct the discrepancy and rerun. Quitting.')
801
802
  sys.exit(1)
@@ -1500,7 +1501,7 @@ class Matcher:
1500
1501
  dest = r.final_synced_file.replace(multicam_dir/cam/clip_name)
1501
1502
  logger.debug('dest: %s'%dest)
1502
1503
  origin_folder = r.final_synced_file.parent
1503
- folder_now_empty = len(list(origin_folder.glob('*.*'))) == 0
1504
+ folder_now_empty = len(list(origin_folder.glob('*'))) == 0
1504
1505
  if folder_now_empty:
1505
1506
  logger.debug('after moving %s, folder is now empty, removing it'%dest)
1506
1507
  origin_folder.rmdir()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: tictacsync
3
- Version: 0.98a0
3
+ Version: 1.0.0a0
4
4
  Summary: command for syncing audio video recordings
5
5
  Home-page: https://tictacsync.org/
6
6
  Author: Raymond Lutz
@@ -68,7 +68,7 @@ Then pip install the syncing program:
68
68
  This should install python dependencies _and_ the `tictacsync` command.
69
69
  ## Usage
70
70
 
71
- Download multiple sample files [here](https://nuage.lutz.quebec/s/NpjzXH5R7DrQEWS/download/dailies.zip) (625 MB, sorry) unzip and run:
71
+ Download multiple sample files [here](https://nuage.lutz.quebec/s/P3gbZR4GgGy8xQp/download/dailies1_1.zip) (625 MB, sorry) unzip and run:
72
72
 
73
73
  > tictacsync dailies/loose
74
74
  The program `tictacsync` will recursively scan the directory given as argument, find all audio that coincide with any video and merge them into a subfolder named `SyncedMedia`. When the argument is an unique media file (not a directory), no syncing will occur but the decoded starting time will be printed to stdout:
@@ -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,4 +1,4 @@
1
1
  [console_scripts]
2
2
  multi2polywav = tictacsync.multi2polywav:main
3
- remergemix = tictacsync.remergemix:main
3
+ newmix = tictacsync.newmix:main
4
4
  tictacsync = tictacsync.entry:main
@@ -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=6XTO4N0ipJ3HNa1I0aSKkTNIgPk_BtCoDDjwCVhOjpI,35446
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=nGuNg55BtXpKTpklwZqunsgVNi-1h-_22OFSnGk7K8k,4340
8
- tictacsync/synciso.py,sha256=XmUcdUF9rl4VdCm7XW4PeYWYWM0vgAY9dC2hapoul9g,4821
9
- tictacsync/timeline.py,sha256=2CkTzMDiazYlBq2F1fhM2w4r6CIgmpQk1L2ZvAYRcnA,72532
10
- tictacsync/yaltc.py,sha256=xbMucI19UJKrEvIzyfpOsi3piSWzqM1gKgooeT9DV8g,53167
11
- tictacsync-0.98a0.dist-info/LICENSE,sha256=ZAOPXLh1zlQAnhHUd7oLslKM01YZ5UiAu3STYjwIxck,1068
12
- tictacsync-0.98a0.dist-info/METADATA,sha256=PuGAcwkwrbbkh8wwudBmw4s5DU0kbgKsXJq27dYSXzY,5693
13
- tictacsync-0.98a0.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
14
- tictacsync-0.98a0.dist-info/entry_points.txt,sha256=g3tdFFrVRcrKpuyKOCLUVBMgYfV65q9kpLZUOD_XCKg,139
15
- tictacsync-0.98a0.dist-info/top_level.txt,sha256=eaCWG-BsYTRR-gLTJbK4RfcaXajr0gjQ6wG97MkGRrg,11
16
- tictacsync-0.98a0.dist-info/RECORD,,