pycapcut 0.0.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.
- pyJianYingDraft/__init__.py +276 -0
- pyJianYingDraft/animation.py +140 -0
- pyJianYingDraft/assets/__init__.py +45 -0
- pyJianYingDraft/assets/draft_content_template.json +125 -0
- pyJianYingDraft/assets/draft_meta_info.json +67 -0
- pyJianYingDraft/audio_segment.py +225 -0
- pyJianYingDraft/draft_content_template.json +119 -0
- pyJianYingDraft/draft_folder.py +153 -0
- pyJianYingDraft/effect_segment.py +36 -0
- pyJianYingDraft/exceptions.py +23 -0
- pyJianYingDraft/jianying_controller.py +230 -0
- pyJianYingDraft/keyframe.py +94 -0
- pyJianYingDraft/local_materials.py +198 -0
- pyJianYingDraft/metadata/__init__.py +51 -0
- pyJianYingDraft/metadata/animation_meta.py +689 -0
- pyJianYingDraft/metadata/audio_effect_meta.py +367 -0
- pyJianYingDraft/metadata/audio_scene_effect.py +252 -0
- pyJianYingDraft/metadata/effect_meta.py +191 -0
- pyJianYingDraft/metadata/filter_meta.py +603 -0
- pyJianYingDraft/metadata/font_meta.py +341 -0
- pyJianYingDraft/metadata/mask_meta.py +15 -0
- pyJianYingDraft/metadata/speech_to_song.py +12 -0
- pyJianYingDraft/metadata/text_intro.py +152 -0
- pyJianYingDraft/metadata/text_loop.py +105 -0
- pyJianYingDraft/metadata/text_outro.py +105 -0
- pyJianYingDraft/metadata/tone_effect.py +107 -0
- pyJianYingDraft/metadata/transition_meta.py +733 -0
- pyJianYingDraft/metadata/video_character_effect.py +1913 -0
- pyJianYingDraft/metadata/video_effect_meta.py +9619 -0
- pyJianYingDraft/metadata/video_group_animation.py +131 -0
- pyJianYingDraft/metadata/video_intro.py +103 -0
- pyJianYingDraft/metadata/video_outro.py +80 -0
- pyJianYingDraft/metadata/video_scene_effect.py +7706 -0
- pyJianYingDraft/script_file.py +808 -0
- pyJianYingDraft/segment.py +256 -0
- pyJianYingDraft/template_mode.py +221 -0
- pyJianYingDraft/text_segment.py +436 -0
- pyJianYingDraft/time_util.py +99 -0
- pyJianYingDraft/track.py +138 -0
- pyJianYingDraft/util.py +58 -0
- pyJianYingDraft/video_segment.py +526 -0
- pycapcut/__init__.py +276 -0
- pycapcut/animation.py +140 -0
- pycapcut/assets/__init__.py +45 -0
- pycapcut/assets/draft_content_template.json +132 -0
- pycapcut/assets/draft_meta_info.json +73 -0
- pycapcut/audio_segment.py +225 -0
- pycapcut/draft_folder.py +153 -0
- pycapcut/effect_segment.py +36 -0
- pycapcut/exceptions.py +23 -0
- pycapcut/jianying_controller.py +230 -0
- pycapcut/keyframe.py +94 -0
- pycapcut/local_materials.py +198 -0
- pycapcut/metadata/__init__.py +51 -0
- pycapcut/metadata/audio_scene_effect.py +252 -0
- pycapcut/metadata/effect_meta.py +191 -0
- pycapcut/metadata/filter_meta.py +603 -0
- pycapcut/metadata/font_meta.py +341 -0
- pycapcut/metadata/mask_meta.py +15 -0
- pycapcut/metadata/speech_to_song.py +12 -0
- pycapcut/metadata/text_intro.py +152 -0
- pycapcut/metadata/text_loop.py +105 -0
- pycapcut/metadata/text_outro.py +105 -0
- pycapcut/metadata/tone_effect.py +107 -0
- pycapcut/metadata/transition_meta.py +733 -0
- pycapcut/metadata/video_character_effect.py +1913 -0
- pycapcut/metadata/video_group_animation.py +131 -0
- pycapcut/metadata/video_intro.py +103 -0
- pycapcut/metadata/video_outro.py +80 -0
- pycapcut/metadata/video_scene_effect.py +7706 -0
- pycapcut/script_file.py +808 -0
- pycapcut/segment.py +256 -0
- pycapcut/template_mode.py +221 -0
- pycapcut/text_segment.py +436 -0
- pycapcut/time_util.py +99 -0
- pycapcut/track.py +138 -0
- pycapcut/util.py +58 -0
- pycapcut/video_segment.py +526 -0
- pycapcut-0.0.1.dist-info/METADATA +77 -0
- pycapcut-0.0.1.dist-info/RECORD +88 -0
- pycapcut-0.0.1.dist-info/WHEEL +5 -0
- pycapcut-0.0.1.dist-info/top_level.txt +1 -0
- tools/__init__.py +17 -0
- tools/build_release.py +270 -0
- tools/code_generator.py +247 -0
- tools/extract_all.py +256 -0
- tools/extractor_base.py +132 -0
- tools/jianying_extractor.py +262 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
pyJianYingDraft/__init__.py,sha256=D-HxNjWHSZGxofVNjL5IZOWGZPYlRu9jk75Rfx5hoow,9752
|
|
2
|
+
pyJianYingDraft/animation.py,sha256=BBApvSU_cKL-pUGnJsl0sxrfpkt-TvZ_F6Sjm4HWqfA,5371
|
|
3
|
+
pyJianYingDraft/audio_segment.py,sha256=LIsHInhEdvEXzwUKEuFoyYm41j46DzkFJXHWBBAdYWY,9585
|
|
4
|
+
pyJianYingDraft/draft_content_template.json,sha256=WijS_Xjl9vBpVpyRqcbruxsvJJ1oDWUvFqJzN3A2ejU,3301
|
|
5
|
+
pyJianYingDraft/draft_folder.py,sha256=40qvONrkPeJEy8nG9snRFxCWnyGBtQab_ZNCMPfs8ZA,6017
|
|
6
|
+
pyJianYingDraft/effect_segment.py,sha256=rIiLPvd4WO8JnfnB6OeDI9kV8YCStVkVhKcIrFTb6E0,1298
|
|
7
|
+
pyJianYingDraft/exceptions.py,sha256=B5-AQlyvz2lKfSQhjj-s9lWwkuaXX-tprg6IHYtZr9g,702
|
|
8
|
+
pyJianYingDraft/jianying_controller.py,sha256=b4cZE6_bp8hs6PLU_iON6FfCwOPWa9xJxiWEJ8xVhz4,9709
|
|
9
|
+
pyJianYingDraft/keyframe.py,sha256=K2gqjutmbBBKKSGXsjQ2jRVOBO4N7-vZ2wvFKD1Rpdc,3611
|
|
10
|
+
pyJianYingDraft/local_materials.py,sha256=0NmEZOxnUG9skaMI1QLC8TLeGVM6wWH8KjZDIhRLbJM,7965
|
|
11
|
+
pyJianYingDraft/script_file.py,sha256=Ldpm9lYQAyNqBz0Pi6rQM6-rm8oDB7bc9vwU6hXqBC4,38540
|
|
12
|
+
pyJianYingDraft/segment.py,sha256=u78sNCpcqSQS1E2G-fMi3pjQo3BBONOu6u4hx_1R3SQ,9955
|
|
13
|
+
pyJianYingDraft/template_mode.py,sha256=mYzZGk5Ma3zPXPv7fHW7PUuI6Of4i09py1mXb-Bo6F4,8623
|
|
14
|
+
pyJianYingDraft/text_segment.py,sha256=rSKMVlnvl43y3z2wbjO78Ljmp4qgjFx8Pv9jlqgceEI,17602
|
|
15
|
+
pyJianYingDraft/time_util.py,sha256=eTt6suV_AlhE2o4Cuzvceq5Gw6dBPuJzerpB0_-C3xc,3226
|
|
16
|
+
pyJianYingDraft/track.py,sha256=jPy-DI9301r61cG_lubCihGQBCdpLfwlP9nCbAETdkc,4681
|
|
17
|
+
pyJianYingDraft/util.py,sha256=ALl9rZsGD3Ut0RPS_QzpGGBlC0TQnwzpa2UphWsXgFQ,2176
|
|
18
|
+
pyJianYingDraft/video_segment.py,sha256=GDl94Xe3FjfqQG0F1WrV-28Tsa4ZK4iy55_LCmoOIs4,22376
|
|
19
|
+
pyJianYingDraft/assets/__init__.py,sha256=yYumQq1KDEz1zR9pnTc8DSNQqP7KmRdXGUG2iSn-fqw,1191
|
|
20
|
+
pyJianYingDraft/assets/draft_content_template.json,sha256=mx-_JDqW35RYwvbQabJJByuQujOp5lcTJ5eUu2dpIE0,3440
|
|
21
|
+
pyJianYingDraft/assets/draft_meta_info.json,sha256=8qd273AX--xLUFrU7PQzgMIa13a2wgktHnUxb_qXFdQ,1440
|
|
22
|
+
pyJianYingDraft/metadata/__init__.py,sha256=gY056roOtdld9m0ByJyECsSGk7JndG6AzQv0pBcJH8k,1326
|
|
23
|
+
pyJianYingDraft/metadata/animation_meta.py,sha256=Q2oCKqQ3tr2sP6q5iqwtEumokviJFZTdQoQWUjdYyI8,84811
|
|
24
|
+
pyJianYingDraft/metadata/audio_effect_meta.py,sha256=ka4PmWdlk81nOREbqEqO3ZDdL6DEiC68GV6omf94wYQ,24295
|
|
25
|
+
pyJianYingDraft/metadata/audio_scene_effect.py,sha256=DZDnpBEY6W9uENTa55TYnfR2_Xj2bmWcn5GebFMUfiE,14192
|
|
26
|
+
pyJianYingDraft/metadata/effect_meta.py,sha256=8gW0f9LP5-ozjDFcjRju9v75_9JeQRLuEkjK1INZLaY,5791
|
|
27
|
+
pyJianYingDraft/metadata/filter_meta.py,sha256=BWC6TlFehsh6lJuOUPGXu1--BTocZuErb80KGto4Iwo,71735
|
|
28
|
+
pyJianYingDraft/metadata/font_meta.py,sha256=Bc3xSUksL8nVHSkNKc9lGiZQ9nTITbc4p1p6e3A-f-s,45580
|
|
29
|
+
pyJianYingDraft/metadata/mask_meta.py,sha256=4uSJJ3PI0CNTWHxJ8VaK2aA2uKcGVcXEdd22C2SmhbQ,937
|
|
30
|
+
pyJianYingDraft/metadata/speech_to_song.py,sha256=6qZKDVg62J5CruEY1xphrGTt2cJkZDu4t6aWJAbG5Lg,972
|
|
31
|
+
pyJianYingDraft/metadata/text_intro.py,sha256=SOjStWJYDEgT0Z_r6IFK4Y8tYcfEsUjB21sw5_0ajmI,19352
|
|
32
|
+
pyJianYingDraft/metadata/text_loop.py,sha256=tcz_ks04_dX0Ab0-ypNz1pSozuf2bciqYh051mBYQ-E,12574
|
|
33
|
+
pyJianYingDraft/metadata/text_outro.py,sha256=xLmYJcH0eckdPx6wfVt3e2GVUXMPIJErIeVyoLr3b48,13036
|
|
34
|
+
pyJianYingDraft/metadata/tone_effect.py,sha256=qoi7gdPrNvD0IDqAl13c2dgdmvhM43jJ_oUbElrxwu8,9292
|
|
35
|
+
pyJianYingDraft/metadata/transition_meta.py,sha256=TK_xohKvVWEk9UrSDaV-e8GKWfbK67cF_P94CrVG5nc,63944
|
|
36
|
+
pyJianYingDraft/metadata/video_character_effect.py,sha256=STAP1nE52zxYOHCHVIT_i6caU3dZRmiu5jNVnGlFq4M,130489
|
|
37
|
+
pyJianYingDraft/metadata/video_effect_meta.py,sha256=AmF6ig3bV4L2LnO1AYoRVzzcMox4sSoA-F_RD0OqocA,660895
|
|
38
|
+
pyJianYingDraft/metadata/video_group_animation.py,sha256=alw6jQY4GA87rmh0XmYyDOSy3zutigmOhW5pZvNVJek,16798
|
|
39
|
+
pyJianYingDraft/metadata/video_intro.py,sha256=lhK2PRAzFjl2jkL6h2LVpm1ibcesyozz8REKuAEyiOE,12976
|
|
40
|
+
pyJianYingDraft/metadata/video_outro.py,sha256=two5zzCM1Hs7smR9hduC0Wly7ZSImkCGAgjzd-f4j-Q,9844
|
|
41
|
+
pyJianYingDraft/metadata/video_scene_effect.py,sha256=SOsK48rhfYV96XApOTmI8vrZe1MVm8P1Re_HaKfUpq4,530418
|
|
42
|
+
pycapcut/__init__.py,sha256=D-HxNjWHSZGxofVNjL5IZOWGZPYlRu9jk75Rfx5hoow,9752
|
|
43
|
+
pycapcut/animation.py,sha256=BBApvSU_cKL-pUGnJsl0sxrfpkt-TvZ_F6Sjm4HWqfA,5371
|
|
44
|
+
pycapcut/audio_segment.py,sha256=LIsHInhEdvEXzwUKEuFoyYm41j46DzkFJXHWBBAdYWY,9585
|
|
45
|
+
pycapcut/draft_folder.py,sha256=40qvONrkPeJEy8nG9snRFxCWnyGBtQab_ZNCMPfs8ZA,6017
|
|
46
|
+
pycapcut/effect_segment.py,sha256=rIiLPvd4WO8JnfnB6OeDI9kV8YCStVkVhKcIrFTb6E0,1298
|
|
47
|
+
pycapcut/exceptions.py,sha256=B5-AQlyvz2lKfSQhjj-s9lWwkuaXX-tprg6IHYtZr9g,702
|
|
48
|
+
pycapcut/jianying_controller.py,sha256=b4cZE6_bp8hs6PLU_iON6FfCwOPWa9xJxiWEJ8xVhz4,9709
|
|
49
|
+
pycapcut/keyframe.py,sha256=K2gqjutmbBBKKSGXsjQ2jRVOBO4N7-vZ2wvFKD1Rpdc,3611
|
|
50
|
+
pycapcut/local_materials.py,sha256=0NmEZOxnUG9skaMI1QLC8TLeGVM6wWH8KjZDIhRLbJM,7965
|
|
51
|
+
pycapcut/script_file.py,sha256=Ldpm9lYQAyNqBz0Pi6rQM6-rm8oDB7bc9vwU6hXqBC4,38540
|
|
52
|
+
pycapcut/segment.py,sha256=u78sNCpcqSQS1E2G-fMi3pjQo3BBONOu6u4hx_1R3SQ,9955
|
|
53
|
+
pycapcut/template_mode.py,sha256=mYzZGk5Ma3zPXPv7fHW7PUuI6Of4i09py1mXb-Bo6F4,8623
|
|
54
|
+
pycapcut/text_segment.py,sha256=rSKMVlnvl43y3z2wbjO78Ljmp4qgjFx8Pv9jlqgceEI,17602
|
|
55
|
+
pycapcut/time_util.py,sha256=eTt6suV_AlhE2o4Cuzvceq5Gw6dBPuJzerpB0_-C3xc,3226
|
|
56
|
+
pycapcut/track.py,sha256=jPy-DI9301r61cG_lubCihGQBCdpLfwlP9nCbAETdkc,4681
|
|
57
|
+
pycapcut/util.py,sha256=ALl9rZsGD3Ut0RPS_QzpGGBlC0TQnwzpa2UphWsXgFQ,2176
|
|
58
|
+
pycapcut/video_segment.py,sha256=GDl94Xe3FjfqQG0F1WrV-28Tsa4ZK4iy55_LCmoOIs4,22376
|
|
59
|
+
pycapcut/assets/__init__.py,sha256=yYumQq1KDEz1zR9pnTc8DSNQqP7KmRdXGUG2iSn-fqw,1191
|
|
60
|
+
pycapcut/assets/draft_content_template.json,sha256=Jl6IKkGdhHgGwp4wqhav7OJcZlpN407L-EesOqAAeoM,3701
|
|
61
|
+
pycapcut/assets/draft_meta_info.json,sha256=akTMUyRBr96fWbBSnFIcetYxT68HQZLMNCaOCWCNweQ,1997
|
|
62
|
+
pycapcut/metadata/__init__.py,sha256=gY056roOtdld9m0ByJyECsSGk7JndG6AzQv0pBcJH8k,1326
|
|
63
|
+
pycapcut/metadata/audio_scene_effect.py,sha256=DZDnpBEY6W9uENTa55TYnfR2_Xj2bmWcn5GebFMUfiE,14192
|
|
64
|
+
pycapcut/metadata/effect_meta.py,sha256=8gW0f9LP5-ozjDFcjRju9v75_9JeQRLuEkjK1INZLaY,5791
|
|
65
|
+
pycapcut/metadata/filter_meta.py,sha256=BWC6TlFehsh6lJuOUPGXu1--BTocZuErb80KGto4Iwo,71735
|
|
66
|
+
pycapcut/metadata/font_meta.py,sha256=Bc3xSUksL8nVHSkNKc9lGiZQ9nTITbc4p1p6e3A-f-s,45580
|
|
67
|
+
pycapcut/metadata/mask_meta.py,sha256=4uSJJ3PI0CNTWHxJ8VaK2aA2uKcGVcXEdd22C2SmhbQ,937
|
|
68
|
+
pycapcut/metadata/speech_to_song.py,sha256=6qZKDVg62J5CruEY1xphrGTt2cJkZDu4t6aWJAbG5Lg,972
|
|
69
|
+
pycapcut/metadata/text_intro.py,sha256=SOjStWJYDEgT0Z_r6IFK4Y8tYcfEsUjB21sw5_0ajmI,19352
|
|
70
|
+
pycapcut/metadata/text_loop.py,sha256=tcz_ks04_dX0Ab0-ypNz1pSozuf2bciqYh051mBYQ-E,12574
|
|
71
|
+
pycapcut/metadata/text_outro.py,sha256=xLmYJcH0eckdPx6wfVt3e2GVUXMPIJErIeVyoLr3b48,13036
|
|
72
|
+
pycapcut/metadata/tone_effect.py,sha256=qoi7gdPrNvD0IDqAl13c2dgdmvhM43jJ_oUbElrxwu8,9292
|
|
73
|
+
pycapcut/metadata/transition_meta.py,sha256=TK_xohKvVWEk9UrSDaV-e8GKWfbK67cF_P94CrVG5nc,63944
|
|
74
|
+
pycapcut/metadata/video_character_effect.py,sha256=STAP1nE52zxYOHCHVIT_i6caU3dZRmiu5jNVnGlFq4M,130489
|
|
75
|
+
pycapcut/metadata/video_group_animation.py,sha256=alw6jQY4GA87rmh0XmYyDOSy3zutigmOhW5pZvNVJek,16798
|
|
76
|
+
pycapcut/metadata/video_intro.py,sha256=lhK2PRAzFjl2jkL6h2LVpm1ibcesyozz8REKuAEyiOE,12976
|
|
77
|
+
pycapcut/metadata/video_outro.py,sha256=two5zzCM1Hs7smR9hduC0Wly7ZSImkCGAgjzd-f4j-Q,9844
|
|
78
|
+
pycapcut/metadata/video_scene_effect.py,sha256=SOsK48rhfYV96XApOTmI8vrZe1MVm8P1Re_HaKfUpq4,530418
|
|
79
|
+
tools/__init__.py,sha256=_HsD8EymbC5PHeDrnhIu789Um4KYTwjWakSd628F4XQ,467
|
|
80
|
+
tools/build_release.py,sha256=FhXYRm6kU7ke_qSW_reGN-GU4Tq4et4KCPQJelWb_8A,9630
|
|
81
|
+
tools/code_generator.py,sha256=GbrQViYK7d1wI_VVN07sGI82mY6Cu0AW9jbyYQ0ya78,11937
|
|
82
|
+
tools/extract_all.py,sha256=urSSq29BRk25l6FQ1ZaY4SSEQStU7O72Hyqle-Og7ts,10640
|
|
83
|
+
tools/extractor_base.py,sha256=WY5Fazt87XEA1-spzOCregiXz4fbLjgkpDsnZ6to0bQ,4319
|
|
84
|
+
tools/jianying_extractor.py,sha256=qJySmis83iMjR-tNJXH8R9U5ns_kI8hX0PgmxXKTT8M,8730
|
|
85
|
+
pycapcut-0.0.1.dist-info/METADATA,sha256=8qZ9XYRT347Mbi8lwOkOe-laBsZPQVj2HXD9Y4pkicY,4368
|
|
86
|
+
pycapcut-0.0.1.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
|
87
|
+
pycapcut-0.0.1.dist-info/top_level.txt,sha256=0Y5w2_62iHAdmGngg0UXB_rwUOARnEy2IMA8e98RVVQ,9
|
|
88
|
+
pycapcut-0.0.1.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pycapcut
|
tools/__init__.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""
|
|
2
|
+
剪映元数据提取工具包
|
|
3
|
+
|
|
4
|
+
此包包含从剪映原始数据中提取和生成元数据的工具。
|
|
5
|
+
这些工具仅用于内部开发,不包含在公开发布的包中。
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from .extractor_base import MetadataExtractor, ExtractorConfig
|
|
9
|
+
from .jianying_extractor import JianYingExtractor
|
|
10
|
+
from .code_generator import CodeGenerator
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"MetadataExtractor",
|
|
14
|
+
"ExtractorConfig",
|
|
15
|
+
"JianYingExtractor",
|
|
16
|
+
"CodeGenerator"
|
|
17
|
+
]
|
tools/build_release.py
ADDED
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
|
|
2
|
+
"""
|
|
3
|
+
发布构建脚本
|
|
4
|
+
|
|
5
|
+
用于构建用于公开发布的清洁版本数据
|
|
6
|
+
- 提取所有元数据
|
|
7
|
+
- 生成Python代码文件
|
|
8
|
+
- 创建公开数据文件
|
|
9
|
+
- 清理敏感信息
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import json
|
|
13
|
+
import shutil
|
|
14
|
+
import yaml
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
from typing import Dict, List, Any
|
|
17
|
+
|
|
18
|
+
from .extract_all import MetadataExtractorManager
|
|
19
|
+
from .code_generator import CodeGenerator
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class ReleaseBuilder:
|
|
23
|
+
"""发布版本构建器"""
|
|
24
|
+
|
|
25
|
+
def __init__(self, config_file: str = "tools/config.yaml"):
|
|
26
|
+
self.config_file = config_file
|
|
27
|
+
self.manager = MetadataExtractorManager(config_file)
|
|
28
|
+
self.config = self.manager.config
|
|
29
|
+
|
|
30
|
+
def build_full_release(self) -> None:
|
|
31
|
+
"""构建完整的发布版本"""
|
|
32
|
+
print("=== Starting Release Build ===")
|
|
33
|
+
|
|
34
|
+
# 1. 提取所有元数据到JSON
|
|
35
|
+
print("\n1. Extracting metadata to JSON...")
|
|
36
|
+
self.manager.extract_all(legacy_format=False)
|
|
37
|
+
|
|
38
|
+
# 2. 生成Python代码文件
|
|
39
|
+
print("\n2. Generating Python code files...")
|
|
40
|
+
self._generate_all_code_files()
|
|
41
|
+
|
|
42
|
+
# 3. 创建公开数据文件
|
|
43
|
+
print("\n3. Creating public data file...")
|
|
44
|
+
self._create_public_data()
|
|
45
|
+
|
|
46
|
+
# 4. 验证生成的文件
|
|
47
|
+
print("\n4. Validating generated files...")
|
|
48
|
+
self._validate_output()
|
|
49
|
+
|
|
50
|
+
print("\n=== Release Build Completed ===")
|
|
51
|
+
|
|
52
|
+
def _generate_all_code_files(self) -> None:
|
|
53
|
+
"""生成所有Python代码文件"""
|
|
54
|
+
class_mapping = self.config["code_generation"]["class_mapping"]
|
|
55
|
+
|
|
56
|
+
for data_type, mapping in class_mapping.items():
|
|
57
|
+
input_file = f"{self.config['data_dir']}/{data_type}.json"
|
|
58
|
+
|
|
59
|
+
if not Path(input_file).exists():
|
|
60
|
+
print(f"Warning: {input_file} not found, skipping...")
|
|
61
|
+
continue
|
|
62
|
+
|
|
63
|
+
try:
|
|
64
|
+
# 加载数据
|
|
65
|
+
with open(input_file, 'r', encoding='utf-8') as f:
|
|
66
|
+
data_list = json.load(f)
|
|
67
|
+
|
|
68
|
+
if not data_list:
|
|
69
|
+
print(f"Warning: {data_type} has no data, skipping...")
|
|
70
|
+
continue
|
|
71
|
+
|
|
72
|
+
import pandas as pd
|
|
73
|
+
data = pd.DataFrame(data_list)
|
|
74
|
+
|
|
75
|
+
# 生成代码
|
|
76
|
+
generator = CodeGenerator(data, data_type)
|
|
77
|
+
output_file = f"{self.config['output_dir']}/{mapping['file_name']}"
|
|
78
|
+
|
|
79
|
+
generator.save_to_file(
|
|
80
|
+
output_file,
|
|
81
|
+
mapping['class_name'],
|
|
82
|
+
mapping['base_class'],
|
|
83
|
+
mapping['meta_class']
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
print(f"Generated {mapping['class_name']} -> {output_file}")
|
|
87
|
+
|
|
88
|
+
except Exception as e:
|
|
89
|
+
print(f"Error generating code for {data_type}: {e}")
|
|
90
|
+
|
|
91
|
+
def _create_public_data(self) -> None:
|
|
92
|
+
"""创建公开数据文件"""
|
|
93
|
+
public_config = self.config["release"]["public_data"]
|
|
94
|
+
include_fields = public_config["include_fields"]
|
|
95
|
+
|
|
96
|
+
public_data = {}
|
|
97
|
+
data_dir = Path(self.config['data_dir'])
|
|
98
|
+
|
|
99
|
+
for data_type, fields in include_fields.items():
|
|
100
|
+
input_file = data_dir / f"{data_type}.json"
|
|
101
|
+
|
|
102
|
+
if not input_file.exists():
|
|
103
|
+
print(f"Warning: {input_file} not found for public data")
|
|
104
|
+
continue
|
|
105
|
+
|
|
106
|
+
try:
|
|
107
|
+
with open(input_file, 'r', encoding='utf-8') as f:
|
|
108
|
+
full_data = json.load(f)
|
|
109
|
+
|
|
110
|
+
# 清理数据,只保留指定字段
|
|
111
|
+
cleaned_data = []
|
|
112
|
+
for item in full_data:
|
|
113
|
+
cleaned_item = {}
|
|
114
|
+
for field in fields:
|
|
115
|
+
if field in item:
|
|
116
|
+
cleaned_item[field] = item[field]
|
|
117
|
+
elif field == "has_params":
|
|
118
|
+
# 特殊处理:检查是否有参数但不暴露具体参数
|
|
119
|
+
cleaned_item[field] = bool(item.get("params", []))
|
|
120
|
+
|
|
121
|
+
if cleaned_item:
|
|
122
|
+
cleaned_data.append(cleaned_item)
|
|
123
|
+
|
|
124
|
+
public_data[data_type] = cleaned_data
|
|
125
|
+
print(f"Cleaned {data_type}: {len(cleaned_data)} items")
|
|
126
|
+
|
|
127
|
+
except Exception as e:
|
|
128
|
+
print(f"Error processing {data_type} for public data: {e}")
|
|
129
|
+
|
|
130
|
+
# 保存公开数据文件
|
|
131
|
+
output_path = data_dir / public_config["filename"]
|
|
132
|
+
with open(output_path, 'w', encoding='utf-8') as f:
|
|
133
|
+
json.dump(public_data, f, ensure_ascii=False, indent=2)
|
|
134
|
+
|
|
135
|
+
print(f"Public data saved to: {output_path}")
|
|
136
|
+
|
|
137
|
+
def _validate_output(self) -> None:
|
|
138
|
+
"""验证输出文件"""
|
|
139
|
+
issues = []
|
|
140
|
+
|
|
141
|
+
# 检查Python代码文件
|
|
142
|
+
metadata_dir = Path(self.config['output_dir'])
|
|
143
|
+
expected_files = [
|
|
144
|
+
"font_meta.py",
|
|
145
|
+
"animation_meta.py",
|
|
146
|
+
"mask_meta.py",
|
|
147
|
+
"filter_meta.py",
|
|
148
|
+
"transition_meta.py",
|
|
149
|
+
"video_effect_meta.py",
|
|
150
|
+
"audio_effect_meta.py"
|
|
151
|
+
]
|
|
152
|
+
|
|
153
|
+
for filename in expected_files:
|
|
154
|
+
filepath = metadata_dir / filename
|
|
155
|
+
if not filepath.exists():
|
|
156
|
+
issues.append(f"Missing metadata file: {filepath}")
|
|
157
|
+
elif filepath.stat().st_size == 0:
|
|
158
|
+
issues.append(f"Empty metadata file: {filepath}")
|
|
159
|
+
|
|
160
|
+
# 检查数据文件
|
|
161
|
+
data_dir = Path(self.config['data_dir'])
|
|
162
|
+
if not (data_dir / "public_metadata.json").exists():
|
|
163
|
+
issues.append("Missing public_metadata.json")
|
|
164
|
+
|
|
165
|
+
# 检查语法错误
|
|
166
|
+
for py_file in metadata_dir.glob("*.py"):
|
|
167
|
+
try:
|
|
168
|
+
with open(py_file, 'r', encoding='utf-8') as f:
|
|
169
|
+
content = f.read()
|
|
170
|
+
compile(content, str(py_file), 'exec')
|
|
171
|
+
except SyntaxError as e:
|
|
172
|
+
issues.append(f"Syntax error in {py_file}: {e}")
|
|
173
|
+
|
|
174
|
+
if issues:
|
|
175
|
+
print("\nValidation Issues Found:")
|
|
176
|
+
for issue in issues:
|
|
177
|
+
print(f" - {issue}")
|
|
178
|
+
else:
|
|
179
|
+
print("✅ All output files validated successfully")
|
|
180
|
+
|
|
181
|
+
def clean_for_public_release(self) -> None:
|
|
182
|
+
"""清理项目以准备公开发布"""
|
|
183
|
+
print("=== Cleaning for Public Release ===")
|
|
184
|
+
|
|
185
|
+
exclude_patterns = self.config["release"]["exclude_from_public"]
|
|
186
|
+
cleaned_count = 0
|
|
187
|
+
|
|
188
|
+
for pattern in exclude_patterns:
|
|
189
|
+
for path in Path(".").glob(pattern):
|
|
190
|
+
if path.exists():
|
|
191
|
+
if path.is_dir():
|
|
192
|
+
shutil.rmtree(path)
|
|
193
|
+
else:
|
|
194
|
+
path.unlink()
|
|
195
|
+
print(f"Removed: {path}")
|
|
196
|
+
cleaned_count += 1
|
|
197
|
+
|
|
198
|
+
print(f"Cleaned {cleaned_count} items for public release")
|
|
199
|
+
|
|
200
|
+
def create_gitignore_update(self) -> None:
|
|
201
|
+
"""更新.gitignore文件"""
|
|
202
|
+
gitignore_path = Path(".gitignore")
|
|
203
|
+
|
|
204
|
+
# 需要忽略的工具目录内容
|
|
205
|
+
tool_ignores = [
|
|
206
|
+
"",
|
|
207
|
+
"# Internal metadata extraction tools (not for public release)",
|
|
208
|
+
"tools/",
|
|
209
|
+
"ignored/",
|
|
210
|
+
"*.db",
|
|
211
|
+
"rp.db*",
|
|
212
|
+
"**/raw_data/",
|
|
213
|
+
"**/*_cache/",
|
|
214
|
+
""
|
|
215
|
+
]
|
|
216
|
+
|
|
217
|
+
# 读取现有.gitignore
|
|
218
|
+
existing_lines = []
|
|
219
|
+
if gitignore_path.exists():
|
|
220
|
+
with open(gitignore_path, 'r', encoding='utf-8') as f:
|
|
221
|
+
existing_lines = [line.strip() for line in f.readlines()]
|
|
222
|
+
|
|
223
|
+
# 检查是否需要添加新规则
|
|
224
|
+
needs_update = False
|
|
225
|
+
for ignore_rule in tool_ignores:
|
|
226
|
+
if ignore_rule.strip() and ignore_rule not in existing_lines:
|
|
227
|
+
needs_update = True
|
|
228
|
+
break
|
|
229
|
+
|
|
230
|
+
if needs_update:
|
|
231
|
+
with open(gitignore_path, 'a', encoding='utf-8') as f:
|
|
232
|
+
f.write('\n'.join(tool_ignores))
|
|
233
|
+
print("Updated .gitignore with tool exclusions")
|
|
234
|
+
else:
|
|
235
|
+
print(".gitignore already up to date")
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
def main():
|
|
239
|
+
import argparse
|
|
240
|
+
|
|
241
|
+
parser = argparse.ArgumentParser(description="构建发布版本")
|
|
242
|
+
parser.add_argument("--full", action="store_true", help="执行完整构建")
|
|
243
|
+
parser.add_argument("--code-only", action="store_true", help="只生成代码文件")
|
|
244
|
+
parser.add_argument("--public-data", action="store_true", help="只生成公开数据")
|
|
245
|
+
parser.add_argument("--clean", action="store_true", help="清理敏感文件")
|
|
246
|
+
parser.add_argument("--update-gitignore", action="store_true", help="更新.gitignore")
|
|
247
|
+
parser.add_argument("--config", default="tools/config.yaml", help="配置文件路径")
|
|
248
|
+
|
|
249
|
+
args = parser.parse_args()
|
|
250
|
+
|
|
251
|
+
if not any([args.full, args.code_only, args.public_data, args.clean, args.update_gitignore]):
|
|
252
|
+
parser.print_help()
|
|
253
|
+
return
|
|
254
|
+
|
|
255
|
+
builder = ReleaseBuilder(args.config)
|
|
256
|
+
|
|
257
|
+
if args.full:
|
|
258
|
+
builder.build_full_release()
|
|
259
|
+
elif args.code_only:
|
|
260
|
+
builder._generate_all_code_files()
|
|
261
|
+
elif args.public_data:
|
|
262
|
+
builder._create_public_data()
|
|
263
|
+
elif args.clean:
|
|
264
|
+
builder.clean_for_public_release()
|
|
265
|
+
elif args.update_gitignore:
|
|
266
|
+
builder.create_gitignore_update()
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
if __name__ == "__main__":
|
|
270
|
+
main()
|
tools/code_generator.py
ADDED
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
"""
|
|
2
|
+
代码生成器
|
|
3
|
+
用于将提取的元数据生成Python代码
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import pandas as pd
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Dict, Any, List, Optional
|
|
9
|
+
from .extractor_base import MetadataExtractor
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class CodeGenerator:
|
|
13
|
+
"""Python代码生成器"""
|
|
14
|
+
|
|
15
|
+
def __init__(self, data: pd.DataFrame, data_type: str):
|
|
16
|
+
self.data = data
|
|
17
|
+
self.data_type = data_type
|
|
18
|
+
|
|
19
|
+
def generate_enum_code(self,
|
|
20
|
+
class_name: str,
|
|
21
|
+
base_class: str = "EffectEnum",
|
|
22
|
+
meta_class: str = "EffectMeta") -> str:
|
|
23
|
+
"""生成枚举类代码"""
|
|
24
|
+
|
|
25
|
+
# 生成头部
|
|
26
|
+
header = f'''"""记录剪映{self.data_type}元数据, 此文件的内容主要由程序生成"""
|
|
27
|
+
|
|
28
|
+
from .effect_meta import {base_class}
|
|
29
|
+
from .effect_meta import {meta_class}
|
|
30
|
+
|
|
31
|
+
class {class_name}({base_class}):
|
|
32
|
+
'''
|
|
33
|
+
|
|
34
|
+
# 生成枚举项
|
|
35
|
+
items = []
|
|
36
|
+
for _, row in self.data.iterrows():
|
|
37
|
+
item_code = self._generate_item_code(row, meta_class)
|
|
38
|
+
if item_code:
|
|
39
|
+
items.append(item_code)
|
|
40
|
+
|
|
41
|
+
return header + "\n".join(items)
|
|
42
|
+
|
|
43
|
+
def _generate_item_code(self, row: pd.Series, meta_class: str) -> str:
|
|
44
|
+
"""生成单个枚举项代码"""
|
|
45
|
+
name = self._clean_identifier(row.get('name', row.get('title', '')))
|
|
46
|
+
title = row.get('title', row.get('name', ''))
|
|
47
|
+
is_vip = row.get('is_vip', False)
|
|
48
|
+
resource_id = row.get('resource_id', '')
|
|
49
|
+
effect_id = row.get('effect_id', '')
|
|
50
|
+
md5 = row.get('md5', '')
|
|
51
|
+
|
|
52
|
+
# 根据数据类型生成不同的代码
|
|
53
|
+
if self.data_type == "fonts":
|
|
54
|
+
return self._generate_font_item(name, title, is_vip, resource_id, effect_id, md5, meta_class)
|
|
55
|
+
elif self.data_type == "text_animations":
|
|
56
|
+
duration = row.get('animation_duration', 0.0)
|
|
57
|
+
return self._generate_animation_item(name, title, is_vip, duration, resource_id, effect_id, md5)
|
|
58
|
+
elif self.data_type == "transitions":
|
|
59
|
+
duration = row.get('default_duration', 1.0)
|
|
60
|
+
is_overlap = row.get('is_overlap', False)
|
|
61
|
+
return self._generate_transition_item(name, title, is_vip, resource_id, effect_id, md5, duration, is_overlap)
|
|
62
|
+
elif self.data_type in ["filters", "video_effects"]:
|
|
63
|
+
params = row.get('params', [])
|
|
64
|
+
return self._generate_effect_with_params(name, title, is_vip, resource_id, effect_id, md5, params, meta_class)
|
|
65
|
+
elif self.data_type == "audio_effects":
|
|
66
|
+
params = row.get('params', [])
|
|
67
|
+
return self._generate_audio_effect(name, title, is_vip, resource_id, effect_id, md5, params)
|
|
68
|
+
else:
|
|
69
|
+
# 默认简单特效
|
|
70
|
+
return self._generate_simple_effect(name, title, is_vip, resource_id, effect_id, md5, meta_class)
|
|
71
|
+
|
|
72
|
+
def _generate_font_item(self, name: str, title: str, is_vip: bool,
|
|
73
|
+
resource_id: str, effect_id: str, md5: str, meta_class: str) -> str:
|
|
74
|
+
"""生成字体项"""
|
|
75
|
+
padded_name = self._pad_to_length(name, 20)
|
|
76
|
+
return f' {padded_name}= {meta_class}("{title}", {is_vip}, "{resource_id}", "{effect_id}", "{md5}")'
|
|
77
|
+
|
|
78
|
+
def _generate_animation_item(self, name: str, title: str, is_vip: bool, duration: float,
|
|
79
|
+
resource_id: str, effect_id: str, md5: str) -> str:
|
|
80
|
+
"""生成动画项"""
|
|
81
|
+
padded_name = self._pad_to_length(name, 12)
|
|
82
|
+
return f' {padded_name}= Animation_meta("{title}", {is_vip}, {duration:.3f}, "{resource_id}", "{effect_id}", "{md5}")'
|
|
83
|
+
|
|
84
|
+
def _generate_transition_item(self, name: str, title: str, is_vip: bool,
|
|
85
|
+
resource_id: str, effect_id: str, md5: str,
|
|
86
|
+
duration: float, is_overlap: bool) -> str:
|
|
87
|
+
"""生成转场项"""
|
|
88
|
+
padded_name = self._pad_to_length(name, 12)
|
|
89
|
+
code = f' {padded_name}= Transition_meta("{title}", {is_vip}, "{resource_id}", "{effect_id}", "{md5}", {duration:.6f}, {is_overlap})\n'
|
|
90
|
+
code += f' """默认时长: {duration:.2f}s"""\n'
|
|
91
|
+
return code
|
|
92
|
+
|
|
93
|
+
def _generate_effect_with_params(self, name: str, title: str, is_vip: bool,
|
|
94
|
+
resource_id: str, effect_id: str, md5: str,
|
|
95
|
+
params: List, meta_class: str) -> str:
|
|
96
|
+
"""生成带参数的特效项"""
|
|
97
|
+
padded_name = self._pad_to_length(name, 12)
|
|
98
|
+
|
|
99
|
+
# 生成参数字符串
|
|
100
|
+
param_str = ""
|
|
101
|
+
annotation_str = ' """参数:\n'
|
|
102
|
+
|
|
103
|
+
if params:
|
|
104
|
+
param_lines = []
|
|
105
|
+
for param in params:
|
|
106
|
+
if len(param) >= 4:
|
|
107
|
+
param_lines.append(f' Effect_param("{param[0]}", {param[1]:.3f}, {param[2]:.3f}, {param[3]:.3f})')
|
|
108
|
+
annotation_str += f' - {param[0]}: 默认{param[1]:.2f}, {param[2]:.2f} ~ {param[3]:.2f}\n'
|
|
109
|
+
|
|
110
|
+
if param_lines:
|
|
111
|
+
param_str = "[\n" + ",\n".join(param_lines) + "\n ]"
|
|
112
|
+
annotation_str += ' """\n'
|
|
113
|
+
else:
|
|
114
|
+
param_str = "[]"
|
|
115
|
+
annotation_str = ""
|
|
116
|
+
else:
|
|
117
|
+
param_str = "[]"
|
|
118
|
+
annotation_str = ""
|
|
119
|
+
|
|
120
|
+
code = f' {padded_name}= {meta_class}("{title}", {is_vip}, "{resource_id}", "{effect_id}", "{md5}", {param_str})\n'
|
|
121
|
+
if annotation_str and annotation_str != ' """参数:\n """\n':
|
|
122
|
+
code += annotation_str
|
|
123
|
+
|
|
124
|
+
return code
|
|
125
|
+
|
|
126
|
+
def _generate_audio_effect(self, name: str, title: str, is_vip: bool,
|
|
127
|
+
resource_id: str, effect_id: str, md5: str, params: List) -> str:
|
|
128
|
+
"""生成音频特效项"""
|
|
129
|
+
padded_name = self._pad_to_length(name, 12)
|
|
130
|
+
|
|
131
|
+
# 生成参数字符串
|
|
132
|
+
param_str = ""
|
|
133
|
+
annotation_str = ' """参数:\n'
|
|
134
|
+
|
|
135
|
+
if params:
|
|
136
|
+
param_lines = []
|
|
137
|
+
for param in params:
|
|
138
|
+
if len(param) >= 4:
|
|
139
|
+
param_lines.append(f' Audio_effect_param("{param[0]}", {param[1]:.3f}, {param[2]:.3f}, {param[3]:.3f})')
|
|
140
|
+
annotation_str += f' - {param[0]}: 默认{param[1]:.2f}, {param[2]:.2f} ~ {param[3]:.2f}\n'
|
|
141
|
+
|
|
142
|
+
if param_lines:
|
|
143
|
+
param_str = "[\n" + ",\n".join(param_lines) + "\n ]"
|
|
144
|
+
annotation_str += ' """\n'
|
|
145
|
+
else:
|
|
146
|
+
param_str = "[]"
|
|
147
|
+
annotation_str = ""
|
|
148
|
+
else:
|
|
149
|
+
param_str = "[]"
|
|
150
|
+
annotation_str = ""
|
|
151
|
+
|
|
152
|
+
code = f' {padded_name}= Audio_effect_meta("{title}", {is_vip}, "{resource_id}", "{effect_id}", "{md5}", {param_str})\n'
|
|
153
|
+
if annotation_str and annotation_str != ' """参数:\n """\n':
|
|
154
|
+
code += annotation_str
|
|
155
|
+
|
|
156
|
+
return code
|
|
157
|
+
|
|
158
|
+
def _generate_simple_effect(self, name: str, title: str, is_vip: bool,
|
|
159
|
+
resource_id: str, effect_id: str, md5: str, meta_class: str) -> str:
|
|
160
|
+
"""生成简单特效项"""
|
|
161
|
+
padded_name = self._pad_to_length(name, 12)
|
|
162
|
+
return f' {padded_name}= {meta_class}("{title}", {is_vip}, "{resource_id}", "{effect_id}", "{md5}", [])'
|
|
163
|
+
|
|
164
|
+
@staticmethod
|
|
165
|
+
def _pad_to_length(s: str, length: int, pad_char: str = " ") -> str:
|
|
166
|
+
"""字符串填充,考虑中文字符宽度"""
|
|
167
|
+
non_ascii_count = sum(1 for c in s if ord(c) > 127)
|
|
168
|
+
return s.ljust(length - non_ascii_count, pad_char)
|
|
169
|
+
|
|
170
|
+
@staticmethod
|
|
171
|
+
def _clean_identifier(name: str) -> str:
|
|
172
|
+
"""清理标识符名称,用于Python变量名"""
|
|
173
|
+
cleaned = name.replace(' ', '_').replace('-', '_').replace('.', '_')
|
|
174
|
+
# 如果以数字开头,添加下划线前缀
|
|
175
|
+
if cleaned and cleaned[0].isdigit():
|
|
176
|
+
cleaned = "_" + cleaned
|
|
177
|
+
return cleaned
|
|
178
|
+
|
|
179
|
+
def save_to_file(self, output_file: str, class_name: str,
|
|
180
|
+
base_class: str = "EffectEnum", meta_class: str = "EffectMeta") -> None:
|
|
181
|
+
"""保存代码到文件"""
|
|
182
|
+
code = self.generate_enum_code(class_name, base_class, meta_class)
|
|
183
|
+
|
|
184
|
+
output_path = Path(output_file)
|
|
185
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
186
|
+
|
|
187
|
+
with open(output_path, 'w', encoding='utf-8') as f:
|
|
188
|
+
f.write(code)
|
|
189
|
+
|
|
190
|
+
print(f"Generated {len(self.data)} items to {output_file}")
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
class LegacyCodeGenerator:
|
|
194
|
+
"""兼容旧版代码生成格式的生成器"""
|
|
195
|
+
|
|
196
|
+
@staticmethod
|
|
197
|
+
def generate_from_extractor(extractor: MetadataExtractor, output_file: str,
|
|
198
|
+
template_type: str = "effect") -> None:
|
|
199
|
+
"""从提取器生成代码(兼容旧版格式)"""
|
|
200
|
+
data = extractor.extract()
|
|
201
|
+
|
|
202
|
+
with open(output_file, 'w', encoding='utf-8') as f:
|
|
203
|
+
for _, row in data.iterrows():
|
|
204
|
+
if template_type == "font":
|
|
205
|
+
LegacyCodeGenerator._write_font_line(f, row)
|
|
206
|
+
elif template_type == "animation":
|
|
207
|
+
LegacyCodeGenerator._write_animation_line(f, row)
|
|
208
|
+
elif template_type == "transition":
|
|
209
|
+
LegacyCodeGenerator._write_transition_line(f, row)
|
|
210
|
+
elif template_type == "effect":
|
|
211
|
+
LegacyCodeGenerator._write_effect_line(f, row)
|
|
212
|
+
elif template_type == "audio_effect":
|
|
213
|
+
LegacyCodeGenerator._write_audio_effect_line(f, row)
|
|
214
|
+
|
|
215
|
+
print(f"Generated {len(data)} items to {output_file}")
|
|
216
|
+
|
|
217
|
+
@staticmethod
|
|
218
|
+
def _write_font_line(f, row):
|
|
219
|
+
name = MetadataExtractor.clean_identifier(row['title'])
|
|
220
|
+
padded_name = MetadataExtractor.pad_to_length(name, 20)
|
|
221
|
+
f.write(f' {padded_name}= Effect_meta("{row["title"]}", {row["is_vip"]}, "{row["resource_id"]}", "{row["effect_id"]}", "{row["md5"]}")\n')
|
|
222
|
+
|
|
223
|
+
@staticmethod
|
|
224
|
+
def _write_animation_line(f, row):
|
|
225
|
+
name = MetadataExtractor.clean_identifier(row['name'])
|
|
226
|
+
padded_name = MetadataExtractor.pad_to_length(name, 12)
|
|
227
|
+
duration = row.get('animation_duration', 0.0)
|
|
228
|
+
f.write(f' {padded_name}= Animation_meta("{row["name"]}", {row["is_vip"]}, {duration:.3f}, "{row["resource_id"]}", "{row["effect_id"]}", "{row["md5"]}")\n')
|
|
229
|
+
|
|
230
|
+
@staticmethod
|
|
231
|
+
def _write_transition_line(f, row):
|
|
232
|
+
name = MetadataExtractor.clean_identifier(row['name'])
|
|
233
|
+
padded_name = MetadataExtractor.pad_to_length(name, 12)
|
|
234
|
+
f.write(f' {padded_name}= Transition_meta("{row["name"]}", {row["is_vip"]}, "{row["resource_id"]}", "{row["effect_id"]}", "{row["md5"]}", {row["default_duration"]:.6f}, {row["is_overlap"]})\n')
|
|
235
|
+
f.write(f' """默认时长: {row["default_duration"]:.2f}s"""\n')
|
|
236
|
+
|
|
237
|
+
@staticmethod
|
|
238
|
+
def _write_effect_line(f, row):
|
|
239
|
+
name = MetadataExtractor.clean_identifier(row.get('title', row.get('name', '')))
|
|
240
|
+
padded_name = MetadataExtractor.pad_to_length(name, 12)
|
|
241
|
+
f.write(f' {padded_name}= Effect_meta("{row.get("title", row.get("name", ""))}", {row["is_vip"]}, "{row["resource_id"]}", "{row["effect_id"]}", "{row["md5"]}", [])\n')
|
|
242
|
+
|
|
243
|
+
@staticmethod
|
|
244
|
+
def _write_audio_effect_line(f, row):
|
|
245
|
+
name = MetadataExtractor.clean_identifier(row['effect_name'])
|
|
246
|
+
padded_name = MetadataExtractor.pad_to_length(name, 12)
|
|
247
|
+
f.write(f' {padded_name}= Audio_effect_meta("{row["effect_name"]}", {row["is_vip"]}, "{row["resource_id"]}", "{row["effect_id"]}", "{row["md5"]}", [])\n')
|