easyrip 4.15.0__py3-none-any.whl → 4.15.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.
easyrip/__main__.py CHANGED
@@ -107,6 +107,7 @@ def run() -> NoReturn:
107
107
  )
108
108
  for ctv in ctvs
109
109
  for name in ctv.names
110
+ if not ctv.is_no_prompt_child
110
111
  }
111
112
  )
112
113
 
@@ -132,6 +133,7 @@ def run() -> NoReturn:
132
133
  )
133
134
  for ctv in ctvs
134
135
  for name in ctv.names
136
+ if not ctv.is_no_prompt_child
135
137
  }
136
138
 
137
139
  cmd_ctv_tuple = tuple(ct.value for ct in Cmd_type if ct != Cmd_type.Option)
@@ -28,6 +28,12 @@ class Cmd_type_val:
28
28
  _param: str
29
29
  _description: str
30
30
  childs: tuple["Cmd_type_val", ...]
31
+ is_no_prompt_child: bool
32
+ """此项作为父项的子项时,不在自动补全时使用"""
33
+ is_no_doc_child: bool
34
+ """此项作为父项的子项时,不在 doc 时使用"""
35
+ is_all_no_doc_childs: bool
36
+ """此项的所有子项不在 doc 时使用"""
31
37
 
32
38
  @property
33
39
  def param(self) -> str:
@@ -64,11 +70,17 @@ class Cmd_type_val:
64
70
  param: str = "",
65
71
  description: str = "",
66
72
  childs: tuple["Cmd_type_val", ...] = (),
73
+ is_no_prompt_child: bool = False,
74
+ is_no_doc_child: bool = False,
75
+ is_all_no_doc_childs: bool = False,
67
76
  ) -> None:
68
77
  self.names = names
69
78
  self.param = param
70
79
  self.description = description
71
80
  self.childs = childs
81
+ self.is_no_prompt_child = is_no_prompt_child
82
+ self.is_no_doc_child = is_no_doc_child
83
+ self.is_all_no_doc_childs = is_all_no_doc_childs
72
84
 
73
85
  def __eq__(self, other: object) -> bool:
74
86
  if isinstance(other, Cmd_type_val):
@@ -79,7 +91,19 @@ class Cmd_type_val:
79
91
  return hash(self.names)
80
92
 
81
93
  def to_doc(self) -> str:
82
- return f"{' / '.join(self.names)} {self.param}\n{textwrap.indent(self.description, ' ', lambda _: True)}"
94
+ description: str = ((f"{self.description}\n") if self.description else "") + (
95
+ ""
96
+ if self.is_all_no_doc_childs
97
+ else "".join(
98
+ f"\n{child.to_doc()}"
99
+ for child in self.childs
100
+ if not child.is_no_doc_child
101
+ )
102
+ )
103
+ return (
104
+ f"{' / '.join(self.names)} {self.param}\n"
105
+ f"{textwrap.indent(description, ' │ ', lambda _: True)}" # 永远返回 True 才能保证空行前也能加字符
106
+ )
83
107
 
84
108
 
85
109
  class Cmd_type(enum.Enum):
@@ -123,6 +147,7 @@ class Cmd_type(enum.Enum):
123
147
  Cmd_type_val(("send",)),
124
148
  Cmd_type_val(("debug",)),
125
149
  ),
150
+ is_all_no_doc_childs=True,
126
151
  )
127
152
  _run_any = Cmd_type_val(
128
153
  ("$",),
@@ -146,6 +171,7 @@ class Cmd_type(enum.Enum):
146
171
  Cmd_type_val(("fd",)),
147
172
  Cmd_type_val(("cfd",)),
148
173
  ),
174
+ is_all_no_doc_childs=True,
149
175
  )
150
176
  dir = ls = Cmd_type_val(
151
177
  ("dir", "ls"),
@@ -163,55 +189,54 @@ class Cmd_type(enum.Enum):
163
189
  list = Cmd_type_val(
164
190
  ("list",),
165
191
  param="<list option>",
166
- description=(
167
- "Operate Ripper list\n"
168
- " \n"
169
- "Default:\n"
170
- " Show Ripper list\n"
171
- " \n"
172
- "clear / clean:\n"
173
- " Clear Ripper list\n"
174
- " \n"
175
- "del / pop <index>:\n"
176
- " Delete a Ripper from Ripper list\n"
177
- " \n"
178
- "sort [n][r]:\n"
179
- " Sort list\n"
180
- " 'n': Natural Sorting\n"
181
- " 'r': Reverse\n"
182
- " \n"
183
- "<int> <int>:\n"
184
- " Exchange specified index"
185
- ),
192
+ description="Operate Ripper list",
186
193
  childs=(
187
- Cmd_type_val(("clear", "clean")),
188
- Cmd_type_val(("del", "pop")),
189
- Cmd_type_val(("sort",), childs=(Cmd_type_val(("n", "r", "nr")),)),
194
+ Cmd_type_val(
195
+ ("Default",), description="Show Ripper list", is_no_prompt_child=True
196
+ ),
197
+ Cmd_type_val(("clear", "clean"), description="Clear Ripper list"),
198
+ Cmd_type_val(
199
+ ("del", "pop"),
200
+ param="<index>",
201
+ description="Delete a Ripper from Ripper list",
202
+ ),
203
+ Cmd_type_val(
204
+ ("sort",),
205
+ param="[n][r]",
206
+ description=(
207
+ "Sort list\n" # .
208
+ "'n': Natural Sorting\n"
209
+ "'r': Reverse"
210
+ ),
211
+ childs=(Cmd_type_val(("n", "r", "nr")),),
212
+ ),
213
+ Cmd_type_val(
214
+ ("<int> <int>",),
215
+ description="Exchange specified index",
216
+ is_no_prompt_child=True,
217
+ ),
190
218
  ),
191
219
  )
192
220
  run = Cmd_type_val(
193
221
  ("run",),
194
222
  param="[<run option>] [-multithreading <0 | 1>]",
195
- description=(
196
- "Run the Ripper in the Ripper list\n"
197
- "\n"
198
- "Default:\n"
199
- " Only run\n"
200
- "\n"
201
- "exit:\n"
202
- " Close program when run finished\n"
203
- "\n"
204
- "shutdown [<sec>]:\n"
205
- " Shutdown when run finished\n"
206
- " Default: 60\n"
207
- "\n"
208
- "server [<address>]:[<port>]@[<password>]:\n"
209
- " See the corresponding help for details"
210
- ),
223
+ description="Run the Ripper from the Ripper list",
211
224
  childs=(
212
- Cmd_type_val(("exit",)),
213
- Cmd_type_val(("shutdown",)),
214
- Cmd_type_val(("server",)),
225
+ Cmd_type_val(("Default",), description="Only run", is_no_prompt_child=True),
226
+ Cmd_type_val(("exit",), description="Close program when run finished"),
227
+ Cmd_type_val(
228
+ ("shutdown",),
229
+ param="[<sec>]",
230
+ description=(
231
+ "Shutdown when run finished\n" # .
232
+ "Default: 60"
233
+ ),
234
+ ),
235
+ Cmd_type_val(
236
+ ("server",),
237
+ param="[<address>]:[<port>]@[<password>]",
238
+ description="See the corresponding help for details",
239
+ ),
215
240
  ),
216
241
  )
217
242
  server = Cmd_type_val(
@@ -226,23 +251,23 @@ class Cmd_type(enum.Enum):
226
251
  config = Cmd_type_val(
227
252
  ("config",),
228
253
  param="<config option>",
229
- description=(
230
- "regenerate | clear | clean\n"
231
- " Regenerate config file\n"
232
- "open\n"
233
- " Open the directory where the config file is located\n"
234
- "list\n"
235
- " Show all config adjustable options\n"
236
- "set <key> <val>\n"
237
- " Set config\n"
238
- " e.g. config set language zh"
239
- ),
240
254
  childs=(
241
- Cmd_type_val(("regenerate", "clear", "clean")),
242
- Cmd_type_val(("open",)),
243
- Cmd_type_val(("list",)),
255
+ Cmd_type_val(
256
+ ("regenerate", "clear", "clean", "reset"),
257
+ description="Regenerate config file",
258
+ ),
259
+ Cmd_type_val(
260
+ ("open",),
261
+ description="Open the directory where the config file is located",
262
+ ),
263
+ Cmd_type_val(("list",), description="Show all config adjustable options"),
244
264
  Cmd_type_val(
245
265
  ("set",),
266
+ param="<key> <val>",
267
+ description=(
268
+ "Set config\n" # .
269
+ "e.g. config set language zh"
270
+ ),
246
271
  childs=tuple(Cmd_type_val((k,)) for k in Config_key._member_map_),
247
272
  ),
248
273
  ),
@@ -250,25 +275,21 @@ class Cmd_type(enum.Enum):
250
275
  prompt = Cmd_type_val(
251
276
  ("prompt",),
252
277
  param="<prompt option>",
253
- description=(
254
- "history\n" # .
255
- " Show prompt history\n"
256
- "history_clear\n"
257
- " Delete history file\n"
258
- "add <name:string> <cmd:string>\n"
259
- " Add a custom prompt\n"
260
- " e.g. prompt add myprompt echo my prompt\n"
261
- "del <name:string>\n"
262
- " Delete a custom prompt"
263
- "show\n"
264
- " Show custom prompt"
265
- ),
266
278
  childs=(
267
- Cmd_type_val(("history",)),
268
- Cmd_type_val(("history_clear",)),
269
- Cmd_type_val(("add",)),
270
- Cmd_type_val(("del",)),
271
- Cmd_type_val(("show",)),
279
+ Cmd_type_val(("history",), description="Show prompt history"),
280
+ Cmd_type_val(("history_clear",), description="Delete history file"),
281
+ Cmd_type_val(
282
+ ("add",),
283
+ param="<name:string> <cmd:string>",
284
+ description=(
285
+ "Add a custom prompt\n" # .
286
+ "e.g. prompt add myprompt echo my prompt"
287
+ ),
288
+ ),
289
+ Cmd_type_val(
290
+ ("del",), param="<name:string>", description="Delete a custom prompt"
291
+ ),
292
+ Cmd_type_val(("show",), description="Show custom prompt"),
272
293
  ),
273
294
  )
274
295
  translate = Cmd_type_val(
@@ -287,6 +308,7 @@ class Cmd_type(enum.Enum):
287
308
  Cmd_type_val(("fd",)),
288
309
  Cmd_type_val(("cfd",)),
289
310
  ),
311
+ is_all_no_doc_childs=True,
290
312
  )
291
313
  assinfo = Cmd_type_val(
292
314
  ("assinfo",),
@@ -296,6 +318,7 @@ class Cmd_type(enum.Enum):
296
318
  Cmd_type_val(("fd",)),
297
319
  Cmd_type_val(("cfd",)),
298
320
  ),
321
+ is_all_no_doc_childs=True,
299
322
  )
300
323
  fontinfo = Cmd_type_val(
301
324
  ("fontinfo",),
@@ -305,6 +328,7 @@ class Cmd_type(enum.Enum):
305
328
  Cmd_type_val(("fd",)),
306
329
  Cmd_type_val(("cfd",)),
307
330
  ),
331
+ is_all_no_doc_childs=True,
308
332
  )
309
333
  Option = Cmd_type_val(
310
334
  ("Option",),
@@ -325,7 +349,7 @@ class Cmd_type(enum.Enum):
325
349
 
326
350
  @classmethod
327
351
  def to_doc(cls) -> str:
328
- return "\n\n".join(ct.value.to_doc() for ct in cls)
352
+ return "\n".join(ct.value.to_doc() for ct in cls)
329
353
 
330
354
 
331
355
  class Opt_type(enum.Enum):
@@ -340,6 +364,7 @@ class Opt_type(enum.Enum):
340
364
  Cmd_type_val(("fd",)),
341
365
  Cmd_type_val(("cfd",)),
342
366
  ),
367
+ is_all_no_doc_childs=True,
343
368
  )
344
369
  _o_dir = Cmd_type_val(
345
370
  ("-o:dir",),
@@ -361,10 +386,12 @@ class Opt_type(enum.Enum):
361
386
  description=(
362
387
  "If enable, output file name will add auto infix:\n"
363
388
  " no audio: '.v'\n"
364
- " with audio: '.va'\n"
365
- "Default: 1"
389
+ " with audio: '.va'"
390
+ ),
391
+ childs=(
392
+ Cmd_type_val(("Default:",), param="1", is_no_prompt_child=True),
393
+ Cmd_type_val(("0", "1"), is_no_doc_child=True),
366
394
  ),
367
- childs=(Cmd_type_val(("0", "1")),),
368
395
  )
369
396
  _preset = _p = Cmd_type_val(
370
397
  ("-p", "-preset"),
@@ -375,7 +402,9 @@ class Opt_type(enum.Enum):
375
402
  "Preset name:\n"
376
403
  f"{Preset_name.to_help_string(' ')}"
377
404
  ),
378
- childs=(Cmd_type_val(tuple(Preset_name._value2member_map_)),),
405
+ childs=(
406
+ Cmd_type_val(tuple(Preset_name._value2member_map_), is_no_doc_child=True),
407
+ ),
379
408
  )
380
409
  _pipe = Cmd_type_val(
381
410
  ("-pipe",),
@@ -411,7 +440,7 @@ class Opt_type(enum.Enum):
411
440
  "'auto:...' can only select which match infix.\n"
412
441
  " e.g. 'auto:zh-Hans:zh-Hant'"
413
442
  ),
414
- childs=(Cmd_type_val(("auto",)),),
443
+ childs=(Cmd_type_val(("auto",), is_no_doc_child=True),),
415
444
  )
416
445
  _only_mux_sub_path = Cmd_type_val(
417
446
  ("-only-mux-sub-path",),
@@ -425,7 +454,7 @@ class Opt_type(enum.Enum):
425
454
  "Mux ASS subtitles in MKV with subset\n" # .
426
455
  "The usage of 'auto' is detailed in '-sub'"
427
456
  ),
428
- childs=(Cmd_type_val(("auto",)),),
457
+ childs=(Cmd_type_val(("auto",), is_no_doc_child=True),),
429
458
  )
430
459
  _subset_font_dir = Cmd_type_val(
431
460
  ("-subset-font-dir",),
@@ -438,20 +467,20 @@ class Opt_type(enum.Enum):
438
467
  _subset_font_in_sub = Cmd_type_val(
439
468
  ("-subset-font-in-sub",),
440
469
  param="<0 | 1>",
441
- description=(
442
- "Encode fonts into ASS file instead of standalone files\n" # .
443
- "Default: 0"
470
+ description="Encode fonts into ASS file instead of standalone files",
471
+ childs=(
472
+ Cmd_type_val(("Default:",), param="0", is_no_prompt_child=True),
473
+ Cmd_type_val(("0", "1"), is_no_doc_child=True),
444
474
  ),
445
- childs=(Cmd_type_val(("0", "1")),),
446
475
  )
447
476
  _subset_use_win_font = Cmd_type_val(
448
477
  ("-subset-use-win-font",),
449
478
  param="<0 | 1>",
450
- description=(
451
- "Use Windows fonts when can not find font in subset-font-dir\n" # .
452
- "Default: 0"
479
+ description="Use Windows fonts when can not find font in subset-font-dir",
480
+ childs=(
481
+ Cmd_type_val(("Default:",), param="0", is_no_prompt_child=True),
482
+ Cmd_type_val(("0", "1"), is_no_doc_child=True),
453
483
  ),
454
- childs=(Cmd_type_val(("0", "1")),),
455
484
  )
456
485
  _subset_use_libass_spec = Cmd_type_val(
457
486
  ("-subset-use-libass-spec",),
@@ -459,38 +488,44 @@ class Opt_type(enum.Enum):
459
488
  description=(
460
489
  "Use libass specification when subset\n"
461
490
  'e.g. "11\\{22}33" ->\n'
462
- ' "11\\33" (VSFilter)\n'
463
- ' "11{22}33" (libass)\n'
464
- "Default: 1"
491
+ ' "11\\33" (VSFilter)\n'
492
+ ' "11{22}33" (libass)'
493
+ ),
494
+ childs=(
495
+ Cmd_type_val(("Default:",), param="1", is_no_prompt_child=True),
496
+ Cmd_type_val(("0", "1"), is_no_doc_child=True),
465
497
  ),
466
- childs=(Cmd_type_val(("0", "1")),),
467
498
  )
468
499
  _subset_drop_non_render = Cmd_type_val(
469
500
  ("-subset-drop-non-render",),
470
501
  param="<0 | 1>",
471
502
  description=(
472
- "Drop non rendered content such as Comment lines, Name, Effect, etc. in ASS\n"
473
- "Default: 1"
503
+ "Drop non rendered content such as Comment lines, Name, Effect, etc. in ASS"
504
+ ),
505
+ childs=(
506
+ Cmd_type_val(("Default:",), param="1", is_no_prompt_child=True),
507
+ Cmd_type_val(("0", "1"), is_no_doc_child=True),
474
508
  ),
475
- childs=(Cmd_type_val(("0", "1")),),
476
509
  )
477
510
  _subset_drop_unkow_data = Cmd_type_val(
478
511
  ("-subset-drop-unkow-data",),
479
512
  param="<0 | 1>",
480
513
  description=(
481
- "Drop lines that are not in {[Script Info], [V4+ Styles], [Events]} in ASS\n"
482
- "Default: 1"
514
+ "Drop lines that are not in {[Script Info], [V4+ Styles], [Events]} in ASS"
515
+ ),
516
+ childs=(
517
+ Cmd_type_val(("Default:",), param="1", is_no_prompt_child=True),
518
+ Cmd_type_val(("0", "1"), is_no_doc_child=True),
483
519
  ),
484
- childs=(Cmd_type_val(("0", "1")),),
485
520
  )
486
521
  _subset_strict = Cmd_type_val(
487
522
  ("-subset-strict",),
488
523
  param="<0 | 1>",
489
- description=(
490
- "Some error will interrupt subset\n" # .
491
- "Default: 0"
524
+ description="Some error will interrupt subset",
525
+ childs=(
526
+ Cmd_type_val(("Default:",), param="0", is_no_prompt_child=True),
527
+ Cmd_type_val(("0", "1"), is_no_doc_child=True),
492
528
  ),
493
- childs=(Cmd_type_val(("0", "1")),),
494
529
  )
495
530
  _translate_sub = Cmd_type_val(
496
531
  ("-translate-sub",),
@@ -509,12 +544,15 @@ class Opt_type(enum.Enum):
509
544
  "Audio encoder:\n"
510
545
  f"{Audio_codec.to_help_string(' ')}"
511
546
  ),
512
- childs=(Cmd_type_val(tuple(Audio_codec._value2member_map_)),),
547
+ childs=(
548
+ Cmd_type_val(tuple(Audio_codec._value2member_map_), is_no_doc_child=True),
549
+ ),
513
550
  )
514
551
  _b_a = Cmd_type_val(
515
552
  ("-b:a",),
516
553
  param="<string>",
517
- description="Setting audio bitrate. Default '160k'",
554
+ description="Setting audio bitrate",
555
+ childs=(Cmd_type_val(("Default:",), param="160k", is_no_prompt_child=True),),
518
556
  )
519
557
  _muxer = Cmd_type_val(
520
558
  ("-muxer",),
@@ -523,9 +561,17 @@ class Opt_type(enum.Enum):
523
561
  "Setting muxer\n"
524
562
  "\n" # .
525
563
  "Muxer:\n"
526
- f"{Audio_codec.to_help_string(' ')}"
564
+ f"{Muxer.to_help_string(' ')}"
565
+ ),
566
+ childs=(Cmd_type_val(tuple(Muxer._value2member_map_), is_no_doc_child=True),),
567
+ )
568
+ _track_name = Cmd_type_val(
569
+ ("-track-name",),
570
+ param="<string>",
571
+ description=(
572
+ "Python list[str] format\n" # .
573
+ "e.g. \"['0:name1', '1:name2']\""
527
574
  ),
528
- childs=(Cmd_type_val(tuple(Muxer._value2member_map_)),),
529
575
  )
530
576
  _r = _fps = Cmd_type_val(
531
577
  ("-r", "-fps"),
@@ -534,7 +580,7 @@ class Opt_type(enum.Enum):
534
580
  "Setting FPS when muxing\n"
535
581
  "When using auto, the frame rate is automatically obtained from the input video and adsorbed to the nearest preset point"
536
582
  ),
537
- childs=(Cmd_type_val(("auto",)),),
583
+ childs=(Cmd_type_val(("auto",), is_no_doc_child=True),),
538
584
  )
539
585
  _chapters = Cmd_type_val(
540
586
  ("-chapters",),
@@ -564,26 +610,23 @@ class Opt_type(enum.Enum):
564
610
  _run = Cmd_type_val(
565
611
  ("-run",),
566
612
  param="[<string>]",
567
- description=(
568
- "Run the Ripper from the Ripper list\n"
569
- "\n"
570
- "Default:\n"
571
- " Only run\n"
572
- "\n"
573
- "exit:\n"
574
- " Close program when run finished\n"
575
- "\n"
576
- "shutdown [<sec>]:\n"
577
- " Shutdown when run finished\n"
578
- " Default: 60\n"
579
- "\n"
580
- "server [<address>]:[<port>]@[<password>]:\n"
581
- " See the corresponding help for details"
582
- ),
613
+ description=("Run the Ripper from the Ripper list"),
583
614
  childs=(
584
- Cmd_type_val(("exit",)),
585
- Cmd_type_val(("shutdown",)),
586
- Cmd_type_val(("server",)),
615
+ Cmd_type_val(("Default",), description="Only run", is_no_prompt_child=True),
616
+ Cmd_type_val(("exit",), description="Close program when run finished"),
617
+ Cmd_type_val(
618
+ ("shutdown",),
619
+ param="[<sec>]",
620
+ description=(
621
+ "Shutdown when run finished\n" # .
622
+ "Default: 60"
623
+ ),
624
+ ),
625
+ Cmd_type_val(
626
+ ("server",),
627
+ param="[<address>]:[<port>]@[<password>]",
628
+ description="See the corresponding help for details",
629
+ ),
587
630
  ),
588
631
  )
589
632
  _ff_params_ff = _ff_params = Cmd_type_val(
@@ -615,19 +658,15 @@ class Opt_type(enum.Enum):
615
658
  param="<string>",
616
659
  description="Use FFmpeg hwaccel (See 'ffmpeg -hwaccels' for details)",
617
660
  childs=(
618
- Cmd_type_val(
619
- names=(
620
- "cuda",
621
- "vaapi",
622
- "dxva2",
623
- "qsv",
624
- "d3d11va",
625
- "opencl",
626
- "vulkan",
627
- "d3d12va",
628
- "amf",
629
- )
630
- ),
661
+ Cmd_type_val(("cuda",)),
662
+ Cmd_type_val(("vaapi",)),
663
+ Cmd_type_val(("dxva2",)),
664
+ Cmd_type_val(("qsv",)),
665
+ Cmd_type_val(("d3d11va",)),
666
+ Cmd_type_val(("opencl",)),
667
+ Cmd_type_val(("vulkan",)),
668
+ Cmd_type_val(("d3d12va",)),
669
+ Cmd_type_val(("amf",)),
631
670
  ),
632
671
  )
633
672
  _ss = Cmd_type_val(
@@ -649,11 +688,11 @@ class Opt_type(enum.Enum):
649
688
  _hevc_strict = Cmd_type_val(
650
689
  ("-hevc-strict",),
651
690
  param="<0 | 1>",
652
- description=(
653
- "When the resolution >= 4K, close HME, and auto reduce the -ref\n" # .
654
- "Default: 1"
691
+ description="When the resolution >= 4K, close HME, and auto reduce the -ref",
692
+ childs=(
693
+ Cmd_type_val(("Default:",), param="1", is_no_prompt_child=True),
694
+ Cmd_type_val(("0", "1"), is_no_doc_child=True),
655
695
  ),
656
- childs=(Cmd_type_val(("0", "1")),),
657
696
  )
658
697
  _multithreading = Cmd_type_val(
659
698
  ("-multithreading",),
@@ -662,16 +701,24 @@ class Opt_type(enum.Enum):
662
701
  "Use multi-threading to run Ripper list, suitable for situations with low performance occupancy\n"
663
702
  "e.g. -p subset or -p copy"
664
703
  ),
665
- childs=(Cmd_type_val(("0", "1")),),
704
+ childs=(
705
+ Cmd_type_val(("Default:",), param="0", is_no_prompt_child=True),
706
+ Cmd_type_val(("0", "1"), is_no_doc_child=True),
707
+ ),
666
708
  )
667
709
  _quality_detection = Cmd_type_val(
668
710
  ("-quality-detection",),
669
711
  param="<algorithm>[:<threshold>]",
670
712
  description=(
671
713
  "Comparison of quality between detection and source after encoding is completed\n"
672
- "Algorithm: ssim psnr vmaf"
714
+ "\n"
715
+ "Algorithm:"
716
+ ),
717
+ childs=(
718
+ Cmd_type_val(("ssim",), description="Default threshold: 0.9"),
719
+ Cmd_type_val(("psnr",), description="Default threshold: 30"),
720
+ Cmd_type_val(("vmaf",), description="Default threshold: 80"),
673
721
  ),
674
- childs=(Cmd_type_val(("ssim", "psnr", "vmaf")),),
675
722
  )
676
723
 
677
724
  @classmethod
@@ -683,12 +730,13 @@ class Opt_type(enum.Enum):
683
730
 
684
731
  @classmethod
685
732
  def to_doc(cls) -> str:
686
- return "\n\n".join(ct.value.to_doc() for ct in cls)
733
+ return "\n".join(ct.value.to_doc() for ct in cls)
687
734
 
688
735
 
689
736
  Cmd_type.help.value.childs = tuple(
690
737
  ct.value for ct in itertools.chain(Cmd_type, Opt_type) if ct is not Cmd_type.help
691
738
  )
739
+ Cmd_type.help.value.is_all_no_doc_childs = True
692
740
 
693
741
 
694
742
  def get_help_doc() -> str:
easyrip/easyrip_main.py CHANGED
@@ -467,7 +467,17 @@ def run_command(command: Iterable[str] | str) -> bool:
467
467
  log.error("'{}' does not exist", cmd_list[1])
468
468
 
469
469
  case _:
470
- log.send(_want_doc_cmd_type.value.to_doc(), is_format=False)
470
+ _child = _want_doc_cmd_type.value
471
+ for s in cmd_list[2:-1]:
472
+ for ch in _child.childs:
473
+ if s in ch.names:
474
+ _child = ch
475
+ break
476
+ else:
477
+ log.error("'{}' does not exist", s)
478
+ break
479
+ else:
480
+ log.send(_child.to_doc(), is_format=False)
471
481
  else:
472
482
  log.send(get_help_doc(), is_format=False)
473
483
 
@@ -1,4 +1,4 @@
1
- from ..easyrip_command import Audio_codec, Cmd_type, Opt_type, Preset_name
1
+ from ..easyrip_command import Audio_codec, Cmd_type, Muxer, Opt_type, Preset_name
2
2
  from .global_lang_val import Lang_tag
3
3
 
4
4
  LANG_TAG = Lang_tag(
@@ -46,43 +46,27 @@ LANG_MAP: dict[str, str] = {
46
46
  Cmd_type.mkdir.value.description: "新建文件目录",
47
47
  Cmd_type.cls.value.description: "清屏",
48
48
  Cmd_type.list.value.param: "<list 选项>",
49
- Cmd_type.list.value.description: (
50
- "操作 Ripper list\n"
51
- " \n"
52
- "默认:\n"
53
- " 打印 Ripper list\n"
54
- " \n"
55
- "clear / clean:\n"
56
- " 清空 Ripper list\n"
57
- " \n"
58
- "del / pop <index>:\n"
59
- " 删除 Ripper list 中指定的一个 Ripper\n"
60
- " \n"
61
- "sort [n][r]:\n"
62
- " 排序 list\n"
63
- " 'n': 自然排序\n"
64
- " 'r': 倒序\n"
65
- " \n"
66
- "<int> <int>:\n"
67
- " 交换指定索引"
68
- ),
49
+ Cmd_type.list.value.description: "操作 Ripper list",
50
+ Cmd_type.list.value.childs[0].description: "打印 Ripper list",
51
+ Cmd_type.list.value.childs[1].description: "清空 Ripper list",
52
+ Cmd_type.list.value.childs[2].description: "删除 Ripper list 中指定的一个 Ripper",
53
+ Cmd_type.list.value.childs[3].description: (
54
+ "排序 list\n" # .
55
+ "'n': 自然排序\n"
56
+ "'r': 倒序"
57
+ ),
58
+ Cmd_type.list.value.childs[4].description: "交换指定索引",
69
59
  Cmd_type.run.value.param: "[<run 选项>] [-multithreading <0 | 1>]",
70
- Cmd_type.run.value.description: (
71
- "执行 Ripper list 中的 Ripper\n"
72
- "\n"
73
- "默认:\n"
74
- " 仅执行\n"
75
- "\n"
76
- "exit:\n"
77
- " 执行后退出程序\n"
78
- "\n"
79
- "shutdown [<秒数>]:\n"
80
- " 执行后关机\n"
81
- " 默认: 60\n"
82
- "\n"
83
- "server [<地址>]:[<端口>]@[<密码>]:\n"
84
- " 详见对应的 help"
85
- ),
60
+ Cmd_type.run.value.description: "执行 Ripper list 中的 Ripper",
61
+ Cmd_type.run.value.childs[0].description: "仅执行",
62
+ Cmd_type.run.value.childs[1].description: "执行后退出程序",
63
+ Cmd_type.run.value.childs[2].param: "[<秒数>]",
64
+ Cmd_type.run.value.childs[2].description: (
65
+ "执行后关机\n" # .
66
+ "默认: 60"
67
+ ),
68
+ Cmd_type.run.value.childs[3].param: "[<地址>]:[<端口>]@[<密码>]",
69
+ Cmd_type.run.value.childs[3].description: "详见对应的 help",
86
70
  Cmd_type.server.value.param: "[<地址>]:[<端口>]@[<密码>]",
87
71
  Cmd_type.server.value.description: (
88
72
  "启动 web 服务\n"
@@ -90,18 +74,22 @@ LANG_MAP: dict[str, str] = {
90
74
  "客户端发送命令 'kill' 可以退出 Ripper 的运行, 注意, FFmpeg需要接受多次^C信号才能强制终止, 单次^C会等待文件输出完才会终止"
91
75
  ),
92
76
  Cmd_type.config.value.param: "<config 选项>",
93
- Cmd_type.config.value.description: (
94
- "regenerate | clear | clean | reset\n"
95
- " 重新生成 config 文件\n"
96
- "open\n"
97
- " 打开 config 文件所在目录\n"
98
- "list\n"
99
- " 展示所有 config 可调选项\n"
100
- "set <key> <val>\n"
101
- " 设置 config\n"
102
- " 例如 config set language en"
77
+ Cmd_type.config.value.childs[0].description: "重新生成 config 文件",
78
+ Cmd_type.config.value.childs[1].description: "打开 config 文件所在目录",
79
+ Cmd_type.config.value.childs[2].description: "展示所有 config 可调选项",
80
+ Cmd_type.config.value.childs[3].description: (
81
+ "设置 config\n" # .
82
+ "例如 config set language en"
103
83
  ),
104
84
  Cmd_type.prompt.value.param: "<prompt 选项>",
85
+ Cmd_type.prompt.value.childs[0].description: "打印 prompt 历史",
86
+ Cmd_type.prompt.value.childs[1].description: "删除 prompt 历史文件",
87
+ Cmd_type.prompt.value.childs[2].description: (
88
+ "增加一个自定义 prompt\n" # .
89
+ "e.g. prompt add myprompt echo my prompt"
90
+ ),
91
+ Cmd_type.prompt.value.childs[3].description: "删除一个自定义 prompt",
92
+ Cmd_type.prompt.value.childs[4].description: "打印自定义 prompt",
105
93
  Cmd_type.translate.value.param: "<中缀> <目标语言标签> [-overwrite]",
106
94
  Cmd_type.translate.value.description: (
107
95
  "翻译字幕文件\n"
@@ -125,8 +113,7 @@ LANG_MAP: dict[str, str] = {
125
113
  Opt_type._auto_infix.value.description: (
126
114
  "如果启用, 输出的文件将添加自动中缀:\n" # .
127
115
  " 无音轨: '.v'\n"
128
- " 有音轨: '.va'\n"
129
- "默认: 1"
116
+ " 有音轨: '.va'"
130
117
  ),
131
118
  Opt_type._preset.value.description: (
132
119
  "设置预设\n"
@@ -162,32 +149,24 @@ LANG_MAP: dict[str, str] = {
162
149
  '默认: 优先当前目录, 其次当前目录下含有 "font" 的文件夹 (不分大小写)'
163
150
  ),
164
151
  Opt_type._subset_font_in_sub.value.description: (
165
- "将字体编码到 ASS 文件中, 而不是单独的字体文件\n" # .
166
- "默认: 0"
152
+ "将字体编码到 ASS 文件中, 而不是单独的字体文件"
167
153
  ),
168
154
  Opt_type._subset_use_win_font.value.description: (
169
- "无法从 subset-font-dir 找到字体时使用 Windows 字体\n" # .
170
- "默认: 0"
155
+ "无法从 subset-font-dir 找到字体时使用 Windows 字体"
171
156
  ),
172
157
  Opt_type._subset_use_libass_spec.value.description: (
173
158
  "子集化时使用 libass 规范\n"
174
159
  'e.g. "11\\{22}33" ->\n'
175
- ' "11\\33" (VSFilter)\n'
176
- ' "11{22}33" (libass)\n'
177
- "默认: 1"
160
+ ' "11\\33" (VSFilter)\n'
161
+ ' "11{22}33" (libass)'
178
162
  ),
179
163
  Opt_type._subset_drop_non_render.value.description: (
180
- "丢弃 ASS 中的注释行、Name、Effect等非渲染内容\n" # .
181
- "默认: 1"
164
+ "丢弃 ASS 中的注释行、Name、Effect等非渲染内容"
182
165
  ),
183
166
  Opt_type._subset_drop_unkow_data.value.description: (
184
- "丢弃 ASS 中的非 {[Script Info], [V4+ Styles], [Events]} 行\n" # .
185
- "默认: 1"
186
- ),
187
- Opt_type._subset_strict.value.description: (
188
- "子集化时报错则中断\n" # .
189
- "默认: 0"
167
+ "丢弃 ASS 中的非 {[Script Info], [V4+ Styles], [Events]} "
190
168
  ),
169
+ Opt_type._subset_strict.value.description: "子集化时报错则中断",
191
170
  Opt_type._translate_sub.value.param: "<中缀>:<语言标签>",
192
171
  Opt_type._translate_sub.value.description: (
193
172
  "临时生成字幕的翻译文件\n" # .
@@ -199,13 +178,12 @@ LANG_MAP: dict[str, str] = {
199
178
  "音频编码器:\n"
200
179
  f"{Audio_codec.to_help_string(' ')}"
201
180
  ),
202
- Opt_type._b_a.value.description: "设置音频码率。默认值 '160k'",
181
+ Opt_type._b_a.value.description: "设置音频码率",
203
182
  Opt_type._muxer.value.description: (
204
183
  "设置复用器\n"
205
184
  "\n" # .
206
185
  "可用的复用器:\n"
207
- " mp4\n"
208
- " mkv"
186
+ f"{Muxer.to_help_string(' ')}"
209
187
  ),
210
188
  Opt_type._r.value.description: (
211
189
  "设置封装的帧率\n" # .
@@ -224,22 +202,6 @@ LANG_MAP: dict[str, str] = {
224
202
  "当 -preset custom 时, 这个选项将作为输出文件的后缀\n" # .
225
203
  '默认: ""'
226
204
  ),
227
- Opt_type._run.value.description: (
228
- "执行 Ripper list 中的 Ripper\n"
229
- "\n"
230
- "默认:\n"
231
- " 仅执行\n"
232
- "\n"
233
- "exit:\n"
234
- " 执行后退出程序\n"
235
- "\n"
236
- "shutdown [<秒数>]:\n"
237
- " 执行后关机\n"
238
- " 默认: 60\n"
239
- "\n"
240
- "server [<地址>]:[<端口>]@[<密码>]:\n"
241
- " 详见对应的 help"
242
- ),
243
205
  Opt_type._ff_params_ff.value.description: (
244
206
  "设置 FFmpeg 的全局选项\n" # .
245
207
  "等同于 ffmpeg <option> ... -i ..."
@@ -262,8 +224,7 @@ LANG_MAP: dict[str, str] = {
262
224
  "等同于 ffmpeg -i ... -t <time> ..."
263
225
  ),
264
226
  Opt_type._hevc_strict.value.description: (
265
- "当分辨率 >= 4K 时, 关闭 HME, 并自动降低 -ref\n" # .
266
- "默认: 1"
227
+ "当分辨率 >= 4K 时, 关闭 HME, 并自动降低 -ref"
267
228
  ),
268
229
  Opt_type._multithreading.value.description: (
269
230
  "使用多线程执行 Ripper list, 适合性能占用低的情况\n" # .
@@ -327,6 +288,7 @@ LANG_MAP: dict[str, str] = {
327
288
  "Command run failed: status code {}\n Failed command: {}": "命令执行失败: 状态码 {}\n 失败的命令: {}",
328
289
  "There have error in running": "执行时出错",
329
290
  "{} param illegal": "{} 参数非法",
291
+ "{} param illegal: {}": "{} 参数非法: {}",
330
292
  'The file "{}" already exists, skip translating it': '文件 "{}" 已存在, 跳过翻译',
331
293
  "Subset failed, cancel mux": "子集化失败, 取消混流",
332
294
  "FFmpeg report: {}": "FFmpeg 报告: {}",
easyrip/global_val.py CHANGED
@@ -4,7 +4,7 @@ from functools import cache
4
4
  from pathlib import Path
5
5
 
6
6
  PROJECT_NAME = "Easy Rip"
7
- PROJECT_VERSION = "4.15.0"
7
+ PROJECT_VERSION = "4.15.1"
8
8
  PROJECT_TITLE = f"{PROJECT_NAME} v{PROJECT_VERSION}"
9
9
  PROJECT_URL = "https://github.com/op200/EasyRip"
10
10
  PROJECT_RELEASE_API = "https://api.github.com/repos/op200/EasyRip/releases/latest"
easyrip/ripper/ripper.py CHANGED
@@ -1,3 +1,4 @@
1
+ import ast
1
2
  import csv
2
3
  import os
3
4
  import re
@@ -14,8 +15,13 @@ from typing import Final, Self, final
14
15
 
15
16
  from .. import easyrip_web
16
17
  from ..easyrip_log import log
17
- from ..easyrip_mlang import Global_lang_val, gettext, translate_subtitles
18
- from ..utils import get_base62_time, read_text
18
+ from ..easyrip_mlang import (
19
+ Global_lang_val,
20
+ Mlang_exception,
21
+ gettext,
22
+ translate_subtitles,
23
+ )
24
+ from ..utils import get_base62_time, read_text, type_match
19
25
  from .media_info import Media_info, Stream_error
20
26
  from .param import (
21
27
  FONT_SUFFIX_SET,
@@ -245,6 +251,37 @@ class Ripper:
245
251
 
246
252
  # Muxer
247
253
  muxer_format_str_list: list[str]
254
+ _track_name_org_str = self.option_map.get("track-name", "[]")
255
+ try:
256
+ track_name_list = ast.literal_eval(_track_name_org_str)
257
+ except Exception as e:
258
+ raise Mlang_exception("{} param illegal", "-track-name") from e
259
+ if not type_match(track_name_list, list[str]):
260
+ raise Mlang_exception("{} param illegal", "-track-name")
261
+ for i in range(len(track_name_list)):
262
+ track_name = track_name_list[i]
263
+ if '"' in track_name:
264
+ if "'" in track_name:
265
+ raise Mlang_exception(
266
+ "{} param illegal: {}",
267
+ "-track-name",
268
+ "The '\"' and \"'\" can not exist simultaneously",
269
+ )
270
+ track_name = f"'{track_name}'"
271
+ else:
272
+ track_name = f'"{track_name}"'
273
+ track_name_list[i] = track_name
274
+ log.debug(f"-track-name <- {_track_name_org_str!r}", is_format=False)
275
+ log.debug(f"-track-name -> {track_name_list!r}", is_format=False)
276
+
277
+ mkv_all_need_opt_str: str = (
278
+ "".join(f"--track-name {track_name} " for track_name in track_name_list)
279
+ ) + (
280
+ f"--chapters {chapters} "
281
+ if (chapters := self.option_map.get("chapters"))
282
+ else ""
283
+ )
284
+
248
285
  if muxer := self.option_map.get("muxer"):
249
286
  muxer = Ripper.Muxer(muxer)
250
287
 
@@ -284,11 +321,7 @@ class Ripper:
284
321
  if force_fps and only_mux_sub_path is None
285
322
  else ""
286
323
  )
287
- + (
288
- f"--chapters {chapters} "
289
- if (chapters := self.option_map.get("chapters"))
290
- else ""
291
- )
324
+ + mkv_all_need_opt_str
292
325
  + (
293
326
  " ".join(
294
327
  (
@@ -428,13 +461,19 @@ class Ripper:
428
461
  _encoder_format_str += f" -rem {_audio_info.index + 1}"
429
462
  else:
430
463
  _encoder_format_str = (
431
- 'mkvmerge -o "{output}" --no-audio "{input}"'
464
+ 'mkvmerge -o "{output}" '
465
+ + mkv_all_need_opt_str
466
+ + '--no-audio "{input}"'
432
467
  )
433
468
  case "copy":
434
469
  _encoder_format_str = (
435
470
  'mp4box -add "{input}" -new "{output}"'
436
471
  if muxer == Ripper.Muxer.mp4
437
- else 'mkvmerge -o "{output}" "{input}"'
472
+ else (
473
+ 'mkvmerge -o "{output}" '
474
+ + mkv_all_need_opt_str
475
+ + '"{input}"'
476
+ )
438
477
  )
439
478
  if _encoder_format_str is not None:
440
479
  encoder_format_str_list = [_encoder_format_str]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: easyrip
3
- Version: 4.15.0
3
+ Version: 4.15.1
4
4
  Author: op200
5
5
  License-Expression: AGPL-3.0-or-later
6
6
  Project-URL: Homepage, https://github.com/op200/EasyRip
@@ -1,10 +1,10 @@
1
1
  easyrip/__init__.py,sha256=DULQoFEAEHYk7dS8Zxky56so7qDPqHm7jUc_Zop1eXw,616
2
- easyrip/__main__.py,sha256=RmJ2_bFzJmyymKnJpTqrbQc64tjK-ziGEen8sGsMm8c,5791
3
- easyrip/easyrip_command.py,sha256=ZEQ8v8FHaSB7tKKMv0HY0NXOftR1S3gRaPTslfxc9tk,30650
2
+ easyrip/__main__.py,sha256=cLtwb8SRXw34e9iddeQ1iP3CTGBVdZPWRu2ISxoCKz4,5879
3
+ easyrip/easyrip_command.py,sha256=rHphOQgBVBKFL8bqiAXjP9we1RUPqaLZyyCDt2niblk,33632
4
4
  easyrip/easyrip_log.py,sha256=R-dM3CWUBFITtG7GSD1zy4X4MhZqxkoiBPjlIpI76cY,15573
5
- easyrip/easyrip_main.py,sha256=9KMpKUal8ApWZr80AQ7UwO1n6HUSWeVo2SuJ4pMW1f8,48130
5
+ easyrip/easyrip_main.py,sha256=E1gur17OruwlJtd9h32aDXtnwhOYsjJVpb6JcVYEyRc,48586
6
6
  easyrip/easyrip_prompt.py,sha256=TOmRJDigGRz7wRWFNakJdfNI1tn9vGekq6FH5ypmQfA,7068
7
- easyrip/global_val.py,sha256=Q6PBQ8iB8H7cJN5KRaLBAj8mHdGCj47EAiLzW62cSrg,866
7
+ easyrip/global_val.py,sha256=UDCyNlA-5DQ2WznuzjHHD2Vn7TQ2xZSmHJhSr5Qqjgo,866
8
8
  easyrip/utils.py,sha256=N1rMF1MyoC-YFBgy10_u29cFoowfhR-5Viea93O7wQ4,8750
9
9
  easyrip/easyrip_config/config.py,sha256=KWXZMEYxdXYUGLQ-MR0A7nnOwR6QZdVrWBopfb2QZSA,9869
10
10
  easyrip/easyrip_config/config_key.py,sha256=_jjdKOunskUoG7UUWOz3QZK-s4LF_x6hmM9MKttyS2Q,766
@@ -12,21 +12,21 @@ easyrip/easyrip_mlang/__init__.py,sha256=QqnL0kgV_trGPeLF5gawP1qAlj0GXUadLNhMSdK
12
12
  easyrip/easyrip_mlang/global_lang_val.py,sha256=pG9DxPl6vUOZoFHYQKCM-AM0TYWbd8L4S65CUQFPRh4,4998
13
13
  easyrip/easyrip_mlang/lang_en.py,sha256=fTM9ejuPW6gEfSMbnMEW-LzlUfvj0YGfoUfmHZpRzms,121
14
14
  easyrip/easyrip_mlang/lang_tag_val.py,sha256=Ec-U0XglpSYvmkHlcEBueSj8ocTLSTH3cacElAkmYVU,5184
15
- easyrip/easyrip_mlang/lang_zh_Hans_CN.py,sha256=-GyAtjhSHqfR2x1mDgW3CdMz7odd58uJzONK67wow8g,19032
15
+ easyrip/easyrip_mlang/lang_zh_Hans_CN.py,sha256=OuWg_sNEzNkvlF1EPMFDZrucfQ1Y92zKFkIfXIfiCR8,19009
16
16
  easyrip/easyrip_mlang/translator.py,sha256=jlgZYSPHvwv1Pps3akKkSgVsGcLtV2psKaXyZH4QCbA,5870
17
17
  easyrip/easyrip_web/__init__.py,sha256=tMyEeaSGeEJjND7MF0MBv9aDiDgaO3MOnppwxA70U2c,177
18
18
  easyrip/easyrip_web/http_server.py,sha256=iyulCAFQrJlz86Lrr-Dm3fhOnNCf79Bp6fVHhr0ephY,8350
19
19
  easyrip/easyrip_web/third_party_api.py,sha256=E-60yoY6D0pPUfYW1VIh0763htyV5z6getzlLtLAdQc,4624
20
20
  easyrip/ripper/media_info.py,sha256=KdSodS6nIp2BWEer5y4mD5xwyhP15_PgNRhz2fnHmw0,5082
21
21
  easyrip/ripper/param.py,sha256=PfJzJz9LPCB5hAM9G4GjPxdn_EZRgAz-vxYzuHGQLp8,13084
22
- easyrip/ripper/ripper.py,sha256=FkbOi1H_IQtcOmr1sMnRgZvPEnxkMvINV2EiFIjSIqI,58898
22
+ easyrip/ripper/ripper.py,sha256=NynEAr3IzjdgC9dwKRPzTA0eTWf7Z3dJUU0eB2_R-6E,60364
23
23
  easyrip/ripper/sub_and_font/__init__.py,sha256=cBT7mxL7RRFaJXFPXuZ7RT-YK6FbnanaU5v6U9BOquw,153
24
24
  easyrip/ripper/sub_and_font/ass.py,sha256=EhDkVY5JXU77euWPId7H2v85j444m8ZLm7wUid7TYd8,35307
25
25
  easyrip/ripper/sub_and_font/font.py,sha256=X2dPcPzbwQf3fv_g_mxO-zY7puVAX9Nv-9QHn88q4oA,7745
26
26
  easyrip/ripper/sub_and_font/subset.py,sha256=--rAA3VH1rm_jBOC3yMs3rOJpn3tPuvfXqkimbBtx3s,18653
27
- easyrip-4.15.0.dist-info/licenses/LICENSE,sha256=hIahDEOTzuHCU5J2nd07LWwkLW7Hko4UFO__ffsvB-8,34523
28
- easyrip-4.15.0.dist-info/METADATA,sha256=EeTyI6jF0S972Q706cZggO-ffPg8Y7hUMUIF0uJWf00,3540
29
- easyrip-4.15.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
30
- easyrip-4.15.0.dist-info/entry_points.txt,sha256=D6GBMMTzZ-apgX76KyZ6jxMmIFqGYwU9neeLLni_qKI,49
31
- easyrip-4.15.0.dist-info/top_level.txt,sha256=kuEteBXm-Gf90jRQgH3-fTo-Z-Q6czSuUEqY158H4Ww,8
32
- easyrip-4.15.0.dist-info/RECORD,,
27
+ easyrip-4.15.1.dist-info/licenses/LICENSE,sha256=hIahDEOTzuHCU5J2nd07LWwkLW7Hko4UFO__ffsvB-8,34523
28
+ easyrip-4.15.1.dist-info/METADATA,sha256=OS5rQst0rSXHYrhJ7CpQbImZ_PU0nlfKAHNSPOHQ2iA,3540
29
+ easyrip-4.15.1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
30
+ easyrip-4.15.1.dist-info/entry_points.txt,sha256=D6GBMMTzZ-apgX76KyZ6jxMmIFqGYwU9neeLLni_qKI,49
31
+ easyrip-4.15.1.dist-info/top_level.txt,sha256=kuEteBXm-Gf90jRQgH3-fTo-Z-Q6czSuUEqY158H4Ww,8
32
+ easyrip-4.15.1.dist-info/RECORD,,