renumSeq 1.3.2__py3-none-any.whl → 1.4.1__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.
@@ -1,22 +1,21 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: renumSeq
3
- Version: 1.3.2
3
+ Version: 1.4.1
4
4
  Summary: Tool to renumber image sequences.
5
5
  Home-page: https://github.com/jrowellfx/renumSeq
6
6
  Author: James Philip Rowell
7
7
  Author-email: james@alpha-eleven.com
8
- License: UNKNOWN
9
- Platform: UNKNOWN
10
8
  Classifier: Programming Language :: Python :: 3
11
9
  Classifier: License :: OSI Approved :: BSD License
12
10
  Classifier: Operating System :: POSIX
13
11
  Classifier: Operating System :: Unix
14
12
  Classifier: Operating System :: MacOS
15
13
  Classifier: Development Status :: 5 - Production/Stable
16
- Requires-Python: >=3.6, <4
14
+ Requires-Python: >=3.7, <4
17
15
  Description-Content-Type: text/markdown
18
16
  License-File: LICENSE
19
- Requires-Dist: seqLister
17
+ Requires-Dist: seqLister >=1.2.0
18
+ Requires-Dist: lsseq >=2.5.0
20
19
 
21
20
  # About renumseq
22
21
 
@@ -24,23 +23,24 @@ Requires-Dist: seqLister
24
23
  which are most
25
24
  typically used in CG post-production.
26
25
 
27
- `renumseq` allows you to renumber sequences with an offset, or give them a new 'start' frame.
26
+ `renumseq` allows you to renumber sequences with an offset or give them a new `start` frame.
28
27
  It also allows you adjust the padding of the frame numbers.
29
28
 
30
- `renumseq` borrows the syntax of the native output of
29
+ `renumseq` uses the syntax of the native output of
31
30
  [`lsseq`](https://github.com/jrowellfx/lsseq) to specify
32
31
  the sequence to be renumbered. Therefore it is recommended to
33
- also install `lsseq` as it makes using `renumseq` easier.
32
+ use `lsseq` as it makes using `renumseq` easier.
34
33
 
35
34
  For example, use `lsseq` to list a sequence, then
36
35
  cut and paste its
37
- output as the arguments to `renumseq` with appropriate optional
38
- arguments for setting the offset etc.
36
+ output as the arguments to `renumseq` with the appropriate
37
+ arguments for setting the offset or new start-frame.
39
38
 
40
39
  `renumseq` was written to be safe in that it won't
41
40
  unintentionally overwrite any existing files
42
- during the renumbering. If
43
- `renumseq` finds that by renumbering a sequence it will write over another frame
41
+ during renumbering.
42
+
43
+ If `renumseq` finds that by renumbering a sequence it will write over another frame
44
44
  outside the range specified then it will skip renumbering that sequence
45
45
  (printing a warning) and go onto the next sequence in the list. Naturally
46
46
  there is an option to force `renumseq` to overwrite those files if desired.
@@ -48,20 +48,12 @@ there is an option to force `renumseq` to overwrite those files if desired.
48
48
  `renumseq` doesn't need to make temporary copies of files during the renumbering
49
49
  (it does a move of the file), so it's fast.
50
50
 
51
- `renumseq` also has a useful option, called `--replaceUnderscore` to change files of this form:
52
-
53
- ```
54
- filename_[n-m].extension
55
- ```
56
-
57
- ...to this,
58
-
51
+ `renumseq` also has a useful option, called `--replaceUnderscore`
52
+ that changes any underscore-separators (separating the filename from the
53
+ frame-number) with dot-separators, like this:
59
54
 
60
- ```
61
- filename.[n-m].extension
62
- ```
55
+ `filename_[n-m].extension` -> `filename.[n-m].extension`
63
56
 
64
- which, as you can see, replaces the underscore-separator with a dot-separator.
65
57
  `Protip`: If all you want to do is switch the separator from an underscore to a dot, then
66
58
  use a zero offset, plus the `--replaceUnderscore` argument.
67
59
 
@@ -114,17 +106,13 @@ aaa.002.tif -> aaa.012.tif
114
106
  aaa.001.tif -> aaa.011.tif
115
107
  ```
116
108
 
117
- Alternatively you can just enclose the argument in quotes like we did above: `'aaa.[001-005].tif'`.
109
+ Alternatively you can just enclose the argument in quotes
110
+ (`'aaa.[001-005].tif'`)
111
+ like we did in the example above.
118
112
 
119
113
  Type this:
120
114
 
121
115
  ```
122
116
  $ renumseq --help
123
117
  ```
124
-
125
118
  ...for much more useful info.
126
-
127
- Please contact j a m e s \<at\> a l p h a - e l e v e n . c o m with any bug
128
- reports, suggestions or praise as the case may be.
129
-
130
-
@@ -0,0 +1,8 @@
1
+ renumseq/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ renumseq/__main__.py,sha256=gKc1Vepf2nhLTfJAZcdC_nswjXkG0vt-qnihfPYEqgw,25868
3
+ renumSeq-1.4.1.dist-info/LICENSE,sha256=R6fdEb8ZKpCYTdOM-ZNPs2bVjqLdDfUwiDcx1xP0dCk,1620
4
+ renumSeq-1.4.1.dist-info/METADATA,sha256=TJrzwZdbW6_om5pg-csNfOkw_AOaaNEC8ZAK3ePjI4c,3511
5
+ renumSeq-1.4.1.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
6
+ renumSeq-1.4.1.dist-info/entry_points.txt,sha256=Vu6IBWCxwfVRG3XOZi4ENlMnpe4igvepbHfKy8NGuOY,52
7
+ renumSeq-1.4.1.dist-info/top_level.txt,sha256=N4wzD8IZmW7curkqRcVgXNstK4X3cqZ4K1tVNYsukDI,9
8
+ renumSeq-1.4.1.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.37.1)
2
+ Generator: bdist_wheel (0.43.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,3 +1,2 @@
1
1
  [console_scripts]
2
2
  renumseq = renumseq.__main__:main
3
-
renumseq/__main__.py CHANGED
@@ -44,24 +44,30 @@ import argparse
44
44
  import os, sys
45
45
  import textwrap
46
46
  import time
47
- from datetime import datetime, timedelta, timezone
47
+ from datetime import datetime
48
48
  import seqLister
49
49
  from enum import Enum
50
+ import subprocess
51
+ import glob
50
52
 
51
53
  # MAJOR version for incompatible API changes
52
54
  # MINOR version for added functionality in a backwards compatible manner
53
55
  # PATCH version for backwards compatible bug fixes
54
56
  #
55
- VERSION = "1.3.2"
57
+ VERSION = "1.4.1"
56
58
 
57
59
  PROG_NAME = "renumseq"
58
60
 
61
+ EXIT_NO_ERROR = 0 # Clean exit.
62
+ EXIT_ERROR = 1 # Internal error other than argparse - currently not used.
63
+ EXIT_ARGPARSE_ERROR = 2 # The default code that argparse exits with if bad option.
64
+
59
65
  # List of date formats accepted to set file times with --touch.
60
66
  # They are same as the formats used by 'lsseq --onlyShow'.
61
67
  #
62
68
  # Note: We MUST list %y before %Y in each case below to make sure
63
69
  # that, for example, "200731" get's interpreted as July 31, 2020
64
- # and not May 1, 2007, as it will if %Y is listed first because
70
+ # and not Mar 1, 2007, as it will if %Y is listed first because
65
71
  # strptime() does not enforce zero padding for month, day, etc.
66
72
  #
67
73
  # Note the ordered pairs below. The second entry is the length
@@ -96,6 +102,7 @@ def main():
96
102
  howToTouch = "" # Set below.
97
103
  specificTime = 0 # Set below.
98
104
  currentTime = time.time()
105
+ seqPath = '' # Used for --rename option, set early, also used later.
99
106
 
100
107
  # Redefine the exception handling routine so that it does NOT
101
108
  # do a trace dump if the user types ^C while renumseq is running.
@@ -109,20 +116,15 @@ def main():
109
116
  sys.excepthook = new_hook
110
117
 
111
118
  p = argparse.ArgumentParser(
119
+ prog=PROG_NAME,
112
120
  formatter_class=argparse.RawDescriptionHelpFormatter,
113
121
  description=textwrap.dedent('''
114
122
  Renumber the frame range of each SEQ listed on the command line.
115
123
  SEQ should be specified using lsseq's native format.
116
124
 
117
- Protip: Turning off globbing, or enclosing SEQ in quotes, or placing
118
- backslashes ahead of '[' and ']' will likely be necessary to turn off the special
125
+ Protip: Enclosing SEQ in quotes will turn off the special
119
126
  treatment of '[' and ']' by the shell.
120
127
 
121
- Caution: The files in the sequence MUST BE correctly padded.
122
- Pay attention to lsseq's --showBadPadding for reports of badly padded frame
123
- numbers and fix them before renumbering a sequence with this utility.
124
- (This is a Rare issue.)
125
-
126
128
  Example usage:
127
129
  $ lsseq
128
130
  aaa.[001-005].tif
@@ -145,10 +147,6 @@ def main():
145
147
  help="offset SEQ by this number of frames (can be negative). \
146
148
  Frame i becomes i + FRAME_OFFSET")
147
149
 
148
- p.add_argument("--dryRun", action="store_true",
149
- dest="dryRun", default=False,
150
- help="Don't renumber SEQ, just display how the \
151
- files would have been renumbered. Forces --verbose" )
152
150
  p.add_argument("--skip", action="store_false",
153
151
  dest="clobber", default=False,
154
152
  help="if renumbering a file in SEQ would result in overwriting \
@@ -170,8 +168,14 @@ def main():
170
168
  help="set the padding of the frame numbers to be PAD digits. \
171
169
  The default action is to leave the padding unchanged. Note, \
172
170
  lsseq's native format output properly lists the sequence \
173
- range with appropriate padding and can also report when there are \
174
- incorrectly padded frame-numbers with --showBadPadding")
171
+ range with appropriate padding.")
172
+ p.add_argument("--rename", type=str, nargs=1,
173
+ dest="newSeqName",
174
+ default=[],
175
+ metavar="NEW_SEQNAME",
176
+ help="Rename the DESCRIPTIVE_NAME part of SEQ from its existing name to NEW_SEQNAME. \
177
+ When using this option then the command will exit with an error unless \
178
+ exactly one SEQ is being renamed and/or renumbered.")
175
179
  p.add_argument("--replaceUnderscore", action="store_true",
176
180
  dest="fixUnderscore", default=False,
177
181
  help="in the case that SEQ uses an underscore ('_') \
@@ -187,16 +191,17 @@ def main():
187
191
  metavar="[CC]YYMMDD[-hh[mm[ss]]]",
188
192
  help="If no date is provided then update the access time of \
189
193
  the files being renumbered to the current time. \
190
- Otherwise, use the date provided to update the \
191
- file's access time. (Default: renumbering a sequence leaves \
192
- the access time unchanged.) \
193
- When specifying the date, the optional CC (century) defaults to the current century. \
194
- The optional '-hh' (hours), 'mm' (minutes) or 'ss' (seconds) \
195
- default to zero if not specified. \
196
- Note: if this is the last argument on the command line and the optional \
197
- date was not specified then \
198
- append '--' before the list of SEQs to delineate the end of the options.")
199
-
194
+ Otherwise, use the date provided to update the file's access time. \
195
+ The optional CC (century) defaults to the current century and \
196
+ '-hh' (hours), 'mm' (minutes) or 'ss' (seconds) \
197
+ default to zero if not specified.\
198
+ Note: the default action is to leave \
199
+ the access time of the SEQ unchanged. ")
200
+
201
+ p.add_argument("--dryRun", "--dryrun", action="store_true",
202
+ dest="dryRun", default=False,
203
+ help="Don't renumber SEQ, just display how the \
204
+ files would have been renumbered. Forces --verbose" )
200
205
  p.add_argument("--silent", "--quiet", "-s", action="store_true",
201
206
  dest="silent", default=False,
202
207
  help="suppress all errors and warnings")
@@ -209,20 +214,128 @@ def main():
209
214
 
210
215
  args = p.parse_args()
211
216
 
212
- if args.files == [] :
213
- sys.exit(0)
217
+ # The following regular expression is created to match lsseq native
218
+ # sequence syntax which means (number below refer to parenthesis
219
+ # groupings (**a**)):
220
+ #
221
+ # 0 - one or more of anything, followed by
222
+ # 1 - a dot or underscore, followed by
223
+ # an open square bracket, followed by
224
+ # 2 - a frame range, followed by
225
+ # a close square bracket then a dot, followed by
226
+ # 3 - one or more letters, optionally one dot,
227
+ # then one or more letters, then one or more letters and numbers
228
+ # and the end of the line.
229
+ #
230
+ lsseqPattern = re.compile(r"(.+)([._])\[(-?[0-9]+-?-?[0-9]+)\]\.([a-zA-Z]+\.?[a-zA-Z]+[a-zA-Z0-9]*$)")
231
+
232
+ # --rename has nargs set to "1", so parse_args() above will catch
233
+ # most invalid cases. Now we need to catch four other invalid cases.
234
+ #
235
+ # 1) renumseq --rename aaa.[1-10].jpg
236
+ # 2) renumseq --rename xxx aaa.[1-10].jpg bbb.[1-10].jpg
237
+ # 3) renumseq --rename aaa.[1-10].jpg bbb.[1-10].jpg
238
+ # 4) see below
239
+ #
240
+ # Case 1) is when the user likely forgot to put the NEW_SEQNAME
241
+ # on the command-line when renaming 'aaa'.
242
+ # Case 2) is when the user is trying to rename TWO sequences to
243
+ # the same name ('xxx') which is obviously undesirable.
244
+ # Case 3) Hard to say what the user is doing here exactly, but
245
+ # they either forgot to add new NEW_SEQNAME *and* to
246
+ # remove one or other of the two SEQ from the command-line.
247
+ # *OR* they didn't want to actually use --rename at all.
248
+ # How to catch this one is if the NEW_SEQNAME
249
+ # looks like an SEQ in lsseq native-format.
250
+ # Case 4) $ lsseq
251
+ # aaa.[1-10].jpg
252
+ # bbb.[0101-0110].jpg
253
+ # $ renumseq --start 20 --rename bbb aaa.[1-10].jpg
254
+ #
255
+ # In this case, regardless of the start frame, or padding differences,
256
+ # seq 'aaa' is attempting to be renamed to an SEQ 'bbb' that already exists.
257
+ #
258
+ if len(args.newSeqName) == 1 :
259
+
260
+ # One other case not mentioned above. --rename is assuming that the "newSeqName" is
261
+ # just the "descriptiveName" part of the sequence, i.e no path (and no ".<framenum>.ext",
262
+ # checked below), so check for a possibly embedded path.
263
+ #
264
+ if len(args.newSeqName[0].split('/')) > 1 :
265
+ if not args.silent :
266
+ print(PROG_NAME, ": error: --rename will rename the sequence in-place, so please omit the path ",
267
+ '/'.join(args.newSeqName[0].split('/')[:-1]),
268
+ file=sys.stderr, sep='')
269
+ sys.exit(EXIT_ARGPARSE_ERROR)
270
+
271
+ match = lsseqPattern.search(args.newSeqName[0])
214
272
 
215
- # The following logic means "do nothing" - so just exit cleanly (**a**)
273
+ if len(args.files) == 0 and match : # If 'not match', command will just exit cleanly below.
274
+ if not args.silent :
275
+ print(PROG_NAME, ": error: NEW_SEQNAME not supplied. Perhaps NEW_SEQNAME was",
276
+ file=sys.stderr, sep='')
277
+ print(" omitted from '--rename ", args.newSeqName[0], "'", " by mistake?",
278
+ file=sys.stderr, sep='')
279
+ sys.exit(EXIT_ARGPARSE_ERROR)
280
+
281
+ elif len(args.files) >= 1 and match : # If len() > 1 then also invalid, but this catches both.
282
+ if not args.silent :
283
+ print(PROG_NAME, ": error: --rename NEW_SEQNAME should only supply the descriptive-name",
284
+ file=sys.stderr, sep='')
285
+ print(" part of the new name. That is, ", args.newSeqName[0],
286
+ file=sys.stderr, sep='')
287
+ print(" appears to be a full lsseq native-format description of a sequence.",
288
+ file=sys.stderr, sep='')
289
+ sys.exit(EXIT_ARGPARSE_ERROR)
290
+
291
+ elif len(args.files) > 1 :
292
+ if not args.silent :
293
+ print(PROG_NAME, ": error: can NOT rename more than one SEQ at a time.",
294
+ file=sys.stderr, sep='')
295
+ sys.exit(EXIT_ARGPARSE_ERROR)
296
+
297
+ # Now check to see if a sequence with NEW_SEQNAME exists already.
298
+ # This code relies on lsseq >= v2.5.0 be installed.
299
+ #
300
+ elif len(args.files) == 1 :
301
+ seqPath = '/'.join(args.files[0].split('/')[:-1])
302
+ if len(seqPath) > 0 :
303
+ seqPath = seqPath + '/'
304
+
305
+ # This next globPattern will put us in the ballpark - lsseq will check more carefully
306
+ # based on this. If NEW_SEQNAME contains a '*', '?', '[' or ']', then this isn't going
307
+ # to work so well, so hopefully the user isn't trying to rename to something with
308
+ # a globbing-wildcard as part of the new name.
309
+ #
310
+ globPattern = seqPath + args.newSeqName[0] + '[._]' + '[0-9]*.*'
311
+
312
+ globResult = glob.glob(globPattern)
313
+
314
+ if len(globResult) > 0 :
315
+ lsseqCmd = ['lsseq', '--looseNumSeparator', '--onlySequences', '--noErrorLists'] + globResult
316
+ lsseqResult = subprocess.run(lsseqCmd, capture_output=True, text=True)
317
+ if len(lsseqResult.stdout) > 0 :
318
+ if not args.silent :
319
+ print(PROG_NAME, ": error: can NOT rename to an existing sequence ",
320
+ lsseqResult.stdout,
321
+ file=sys.stderr, sep='', end='')
322
+ sys.exit(EXIT_ARGPARSE_ERROR)
323
+
324
+ if len(args.files) == 0 :
325
+ sys.exit(EXIT_NO_ERROR)
326
+
327
+ # The following logic means "do nothing" - so just exit cleanly (**b**)
216
328
  #
217
329
  if args.offsetFrames == 0 \
218
330
  and args.pad < 0 \
219
331
  and not args.fixUnderscore \
220
- and args.startFrame == NEVER_START_FRAME :
332
+ and args.startFrame == NEVER_START_FRAME \
333
+ and len(args.newSeqName) == 0 :
221
334
  if not args.silent :
222
335
  print(PROG_NAME,
223
- ": warning: no offset, no padding change etc., nothing to do",
336
+ ": warning: no offset, no rename, no padding change, etc., nothing to do",
224
337
  file=sys.stderr, sep='')
225
- sys.exit(0)
338
+ sys.exit(EXIT_NO_ERROR)
226
339
 
227
340
  # args.touch is either a string, presumably containing a date (so need to
228
341
  # check its validity), the string "0" (meaning --touch was called with NO argument),
@@ -238,7 +351,6 @@ def main():
238
351
  howToTouch = Touch.SPECIFIC_TIME
239
352
 
240
353
  # Loop through list of acceptable formats declared globally.
241
- # Note that the order of the formats listed
242
354
  #
243
355
  matchedDate = False
244
356
  for dateFormat in DATE_FORMAT_LIST :
@@ -273,33 +385,19 @@ def main():
273
385
  print(PROG_NAME,
274
386
  ": error: argument --touch: the time must be of the form [CC]YYMMDD[-hh[mm[ss]]]",
275
387
  file=sys.stderr, sep='')
276
- sys.exit(1)
388
+ sys.exit(EXIT_ARGPARSE_ERROR)
277
389
 
278
390
  specificTime = int(time.mktime(timeData.timetuple())) # Epoch time
279
391
 
280
392
  if args.dryRun : # verbose to show how renumbering would occur.
281
393
  args.verbose = True
282
394
 
283
- # The following regular expression is created to match lsseq native sequence syntax
284
- # which means (number labels refer to parenthesis groupings **):
285
- #
286
- # 0 - one or more of anything, followed by
287
- # 1 - a dot or underscore, followed by
288
- # an open square bracket, followed by
289
- # 2 - a frame range, followed by
290
- # a close square bracket then a dot, followed by
291
- # 3 - one or more letters, optionally one dot,
292
- # then one or more letters, then one or more letters and numbers
293
- # and the end of the line.
294
- #
295
- pattern = re.compile(r"(.+)([._])\[(-?[0-9]+-?-?[0-9]+)\]\.([a-zA-Z]+\.?[a-zA-Z]+[a-zA-Z0-9]*$)")
296
-
297
395
  for arg in args.files :
298
396
  abortSeq = False
299
397
 
300
398
  # Check if 'arg' is a sequence in valid lsseq native format
301
399
  #
302
- match = pattern.search(arg)
400
+ match = lsseqPattern.search(arg)
303
401
  if not match :
304
402
  if not args.silent :
305
403
  print(PROG_NAME, ": warning: ", arg,
@@ -310,7 +408,7 @@ def main():
310
408
  v = match.groups()
311
409
 
312
410
  usesUnderscore = (v[1] == '_')
313
- seq = [v[0], v[2], v[3]] # base filename, range, file-extension. (see above **)
411
+ seq = [v[0], v[2], v[3]] # base filename, range, file-extension. (see above (**a**))
314
412
 
315
413
  # seq might be range with neg numbers. Assume N,M >= 0,
316
414
  # then there are only 5 seq cases that we need to be
@@ -384,7 +482,7 @@ def main():
384
482
 
385
483
  args.offsetFrames = args.startFrame - start
386
484
 
387
- # This duplicates the test above (**a**) because now
485
+ # This duplicates the test above (**b**) because now
388
486
  # we might have a zero offset for this sequence.
389
487
  # Instead of exiting we just skip to the next seq.
390
488
  #
@@ -470,8 +568,14 @@ def main():
470
568
  origFile = seq[0] + currentSeparator + currentFormatStr.format(i) + '.' + seq[2]
471
569
  if os.path.exists(origFile) :
472
570
  origName.append(origFile)
473
- newName.append(seq[0] + newSeparator + newFormatStr.format(i+args.offsetFrames) \
474
- + '.' + seq[2])
571
+ if len(args.newSeqName) == 1 :
572
+ newName.append(seqPath + args.newSeqName[0] + newSeparator + \
573
+ newFormatStr.format(i+args.offsetFrames) \
574
+ + '.' + seq[2])
575
+ else :
576
+ newName.append(seq[0] + newSeparator + \
577
+ newFormatStr.format(i+args.offsetFrames) \
578
+ + '.' + seq[2])
475
579
 
476
580
  if origName == [] :
477
581
  if not args.silent :
@@ -1,8 +0,0 @@
1
- renumseq/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- renumseq/__main__.py,sha256=6L07_jK7G5ZqSPhd9t7nHuha7VVgN8ZtsoFmuQeD8uw,20774
3
- renumSeq-1.3.2.dist-info/LICENSE,sha256=R6fdEb8ZKpCYTdOM-ZNPs2bVjqLdDfUwiDcx1xP0dCk,1620
4
- renumSeq-1.3.2.dist-info/METADATA,sha256=hOHdxCB1ZNma_eZybhbLn4vGsVG70HXQnmq3CKGUaUw,3653
5
- renumSeq-1.3.2.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
6
- renumSeq-1.3.2.dist-info/entry_points.txt,sha256=rGNSv4SOqnQWjgiPUaXHLBE_xiQRX0lgB-Ig-lmB5_Y,53
7
- renumSeq-1.3.2.dist-info/top_level.txt,sha256=N4wzD8IZmW7curkqRcVgXNstK4X3cqZ4K1tVNYsukDI,9
8
- renumSeq-1.3.2.dist-info/RECORD,,