轻量、灵活、易上手的Python剪映草稿生成及导出工具,构建全自动化视频剪辑/混剪流水线。本项目的CapCut版本正于 https://github.com/GuanYixuan/pyCapCut 内开发
This report summarizes scanner evidence for GuanYixuan/pyJianYingDraft, a Python project with 4,528 GitHub stars. SynthScan examined 23,054 lines of code across 65 source files, recording 159 pattern matches distributed across 7 syntactic categories. The legacy adjusted pattern signal is 8.0 (Moderate pattern signal); it is not an authorship estimate.
Use the findings to ask whether maintainers can explain intent, verify dependencies, simplify unnecessary scaffolding, and focus review effort. Risk Payload v1 adds ownership, supply-chain, over-engineering, and review-effort vectors when available. These are prompts for human verification, not probabilities, security findings, or measured maintenance costs.
Use each vector to guide manual verification of the underlying findings.
Can maintainers explain intent, assumptions, and trade-offs? 76 finding(s).
Are imports declared and package/API names verified? 79 finding(s).
Are wrappers, guards, and repeated structures necessary? 65 finding(s).
Which changes need focused human review? 159 finding(s).
Run additional scans to build a history of the legacy pattern signal. Score changes are not a measure of AI adoption or code quality.
Groups findings by configured severity weight. Severity is not a confidence estimate, security rating, or proof that a package is missing. Verify imports against the project's manifests and inspect each finding in its local design context.
This horizontal bar chart shows where configured pattern scores accumulate across directories. Treat the bars as locations to inspect, not as evidence of authorship or code quality. Use the file-level findings to decide whether a directory needs a focused ownership, dependency, complexity, or review check.
The scanner identified 159 distinct pattern matches across 7 syntactic categories. Each entry below represents a discrete location in the source code where the engine recorded a detector match to inspect in context. Expand any category row to inspect the individual file paths, line numbers, code snippets, and the lexical context (CODE, COMMENT, or STRING) in which each match was detected.
Reading the findings table: The Severity column indicates the configured finding weight (CRITICAL / HIGH / MEDIUM / LOW). The Context column identifies whether the match occurred inside executable code, an inline comment, or a string literal — legacy comment-context matches receive a ×1.5 score weight. The ⚡ bolt icon marks clustered matches: three or more patterns within a 10-line window, each receiving an additional ×1.5 density multiplier under the legacy scoring model.
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | tools/generate_pypi_readme.py | 4 | CODE | |
| LOW | tools/build_dist.py | 4 | CODE | |
| LOW | tools/build_dist.py | 43 | CODE | |
| LOW | tests/conftest.py | 3 | CODE | |
| LOW | tests/helpers.py | 3 | CODE | |
| LOW | pyJianYingDraft/_script_file_tracks.py | 1 | CODE | |
| LOW | pyJianYingDraft/__init__.py | 3 | CODE | |
| LOW | pyJianYingDraft/__init__.py | 4 | CODE | |
| LOW | pyJianYingDraft/__init__.py | 5 | CODE | |
| LOW | pyJianYingDraft/__init__.py | 5 | CODE | |
| LOW | pyJianYingDraft/__init__.py | 6 | CODE | |
| LOW | pyJianYingDraft/__init__.py | 7 | CODE | |
| LOW | pyJianYingDraft/__init__.py | 7 | CODE | |
| LOW | pyJianYingDraft/__init__.py | 7 | CODE | |
| LOW | pyJianYingDraft/__init__.py | 8 | CODE | |
| LOW | pyJianYingDraft/__init__.py | 9 | CODE | |
| LOW | pyJianYingDraft/__init__.py | 10 | CODE | |
| LOW | pyJianYingDraft/__init__.py | 11 | CODE | |
| LOW | pyJianYingDraft/__init__.py | 11 | CODE | |
| LOW | pyJianYingDraft/__init__.py | 11 | CODE | |
| LOW | pyJianYingDraft/__init__.py | 11 | CODE | |
| LOW | pyJianYingDraft/__init__.py | 11 | CODE | |
| LOW | pyJianYingDraft/__init__.py | 11 | CODE | |
| LOW | pyJianYingDraft/__init__.py | 11 | CODE | |
| LOW | pyJianYingDraft/__init__.py | 12 | CODE | |
| LOW | pyJianYingDraft/__init__.py | 12 | CODE | |
| LOW | pyJianYingDraft/__init__.py | 12 | CODE | |
| LOW | pyJianYingDraft/__init__.py | 12 | CODE | |
| LOW | pyJianYingDraft/__init__.py | 13 | CODE | |
| LOW | pyJianYingDraft/__init__.py | 13 | CODE | |
| LOW | pyJianYingDraft/__init__.py | 14 | CODE | |
| LOW | pyJianYingDraft/__init__.py | 15 | CODE | |
| LOW | pyJianYingDraft/__init__.py | 15 | CODE | |
| LOW | pyJianYingDraft/__init__.py | 16 | CODE | |
| LOW | pyJianYingDraft/__init__.py | 16 | CODE | |
| LOW | pyJianYingDraft/__init__.py | 16 | CODE | |
| LOW | pyJianYingDraft/__init__.py | 16 | CODE | |
| LOW | pyJianYingDraft/__init__.py | 16 | CODE | |
| LOW | pyJianYingDraft/__init__.py | 17 | CODE | |
| LOW | pyJianYingDraft/__init__.py | 17 | CODE | |
| LOW | pyJianYingDraft/__init__.py | 17 | CODE | |
| LOW | pyJianYingDraft/__init__.py | 17 | CODE | |
| LOW | pyJianYingDraft/__init__.py | 17 | CODE | |
| LOW | pyJianYingDraft/__init__.py | 18 | CODE | |
| LOW | pyJianYingDraft/__init__.py | 18 | CODE | |
| LOW | pyJianYingDraft/__init__.py | 18 | CODE | |
| LOW | pyJianYingDraft/__init__.py | 19 | CODE | |
| LOW | pyJianYingDraft/__init__.py | 19 | CODE | |
| LOW | pyJianYingDraft/__init__.py | 19 | CODE | |
| LOW | pyJianYingDraft/__init__.py | 23 | CODE | |
| LOW | pyJianYingDraft/__init__.py | 23 | CODE | |
| LOW | pyJianYingDraft/__init__.py | 23 | CODE | |
| LOW | pyJianYingDraft/_script_file_template.py | 1 | CODE | |
| LOW | pyJianYingDraft/_script_file_template.py | 12 | CODE | |
| LOW | pyJianYingDraft/_script_file_segments.py | 1 | CODE | |
| LOW | pyJianYingDraft/_script_file_protocol.py | 1 | CODE | |
| LOW | pyJianYingDraft/_script_file_protocol.py | 13 | CODE | |
| LOW | pyJianYingDraft/_script_file_protocol.py | 14 | CODE | |
| LOW | pyJianYingDraft/script_material.py | 1 | CODE | |
| LOW | pyJianYingDraft/metadata/__init__.py | 6 | CODE | |
| 19 more matches not shown… | ||||
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | tools/build_dist.py | 50 | def assert_no_tests_in_tarball(path: Path) -> None: | CODE |
| LOW | tests/test_template_loading.py | 18 | def test_load_template_raises_stable_error_for_non_plain_content_without_fallback_loader(tmp_path): | CODE |
| LOW | tests/test_template_loading.py | 28 | def test_load_template_uses_fallback_loader_for_non_plain_content(tmp_path): | CODE |
| LOW | tests/test_template_loading.py | 49 | def test_load_template_accepts_dict_from_fallback_loader(tmp_path): | CODE |
| LOW | tests/test_template_loading.py | 64 | def test_duplicate_as_template_reuses_fallback_loader(tmp_path): | CODE |
| LOW | tests/test_template_loading.py | 76 | def test_load_template_normalizes_sparse_template_content(tmp_path): | CODE |
| LOW | tests/test_template_loading.py | 115 | def test_template_dump_preserves_original_track_array_order(tmp_path): | CODE |
| LOW | tests/test_template_loading.py | 160 | def test_list_imported_tracks_preserves_internal_order_and_supports_type_filter(tmp_path): | CODE |
| LOW | tests/test_template_loading.py | 185 | def test_list_imported_tracks_returns_live_references(tmp_path): | CODE |
| LOW | tests/test_template_loading.py | 209 | def test_get_imported_track_uses_internal_order_for_indexing(tmp_path): | CODE |
| LOW | tests/test_template_loading.py | 230 | def test_import_track_supports_explicit_insert_position(tmp_path): | CODE |
| LOW | tests/test_video_chroma_export.py | 8 | def test_video_chroma_exports_minimal_shape(): | CODE |
| LOW | tests/test_video_chroma_export.py | 35 | def test_video_chroma_exports_adjusted_params(): | CODE |
| LOW | tests/test_video_chroma_export.py | 59 | def test_video_chroma_rejects_duplicate_or_invalid_values(): | CODE |
| LOW⚡ | tests/test_time_util.py | 6 | def test_tim_parses_common_duration_strings(): | CODE |
| LOW⚡ | tests/test_time_util.py | 13 | def test_tim_supports_negative_offsets(): | CODE |
| LOW⚡ | tests/test_time_util.py | 17 | def test_trange_uses_duration_not_end_time(): | CODE |
| LOW⚡ | tests/test_time_util.py | 25 | def test_trange_seconds_accepts_end_or_duration(): | CODE |
| LOW⚡ | tests/test_time_util.py | 34 | def test_trange_seconds_rounds_absolute_boundaries(): | CODE |
| LOW⚡ | tests/test_time_util.py | 44 | def test_trange_seconds_requires_numeric_seconds(): | CODE |
| LOW⚡ | tests/test_time_util.py | 48 | def test_timerange_overlap_semantics(): | CODE |
| LOW⚡ | tests/test_time_util.py | 56 | def test_timerange_import_json_defaults_missing_start_to_zero(): | CODE |
| LOW | tests/test_audio_effect_export.py | 8 | def test_tone_effect_type_is_exported_from_top_level(): | CODE |
| LOW | tests/test_audio_effect_export.py | 12 | def test_tone_effect_exports_minimal_shape(): | CODE |
| LOW | tests/test_audio_effect_export.py | 31 | def test_audio_scene_effect_keeps_legacy_export_shape(): | CODE |
| LOW⚡ | tests/test_track_basics.py | 10 | def test_track_accepts_matching_segment_type(): | CODE |
| LOW⚡ | tests/test_track_basics.py | 20 | def test_track_rejects_mismatched_segment_type(): | CODE |
| LOW⚡ | tests/test_track_basics.py | 28 | def test_track_rejects_overlapping_segments(): | CODE |
| LOW | tests/test_track_basics.py | 39 | def test_track_accepts_beat_aligned_segments_in_seconds(): | CODE |
| LOW | tests/test_track_basics.py | 55 | def test_track_still_rejects_one_microsecond_overlap(): | CODE |
| LOW | tests/test_track_basics.py | 64 | def test_track_end_time_reflects_last_segment_end(): | CODE |
| LOW | tests/test_video_mix_mode_export.py | 13 | def _assert_only_last_mix_mode(script, segment): | CODE |
| LOW | tests/test_video_mix_mode_export.py | 25 | def test_repeated_mix_mode_before_adding_segment_keeps_only_last_mode(): | CODE |
| LOW | tests/test_video_mix_mode_export.py | 38 | def test_repeated_mix_mode_after_adding_segment_updates_registered_material(): | CODE |
| LOW | tests/test_track_append_api.py | 9 | def test_append_track_returns_track_ref_and_appends_to_end(): | CODE |
| LOW | tests/test_track_append_api.py | 20 | def test_append_tracks_preserves_input_order(): | CODE |
| LOW | tests/test_track_append_api.py | 34 | def test_add_segment_accepts_track_ref(): | CODE |
| LOW | tests/test_track_append_api.py | 46 | def test_add_segment_rejects_foreign_track_ref(): | CODE |
| LOW | tests/test_track_insert_api.py | 28 | def test_insert_track_before_imported_track_preserves_overall_order(tmp_path): | CODE |
| LOW | tests/test_track_insert_api.py | 49 | def test_insert_tracks_over_track_ref_preserves_block_order(): | CODE |
| LOW | tests/test_track_insert_api.py | 69 | def test_insert_track_at_index_uses_full_track_order(tmp_path): | CODE |
| LOW | tests/test_track_insert_api.py | 89 | def test_insert_track_rejects_multiple_location_hints(): | CODE |
| LOW | tests/test_track_insert_api.py | 101 | def test_insert_track_rejects_foreign_imported_track_anchor(tmp_path): | CODE |
| LOW | tests/test_track_insert_api.py | 122 | def test_insert_track_rejects_out_of_range_index(): | CODE |
| LOW | tests/test_import_srt.py | 6 | def test_import_srt_creates_text_track_when_missing(tmp_path): | CODE |
| LOW | tests/test_import_srt.py | 21 | def test_import_srt_applies_time_offset(tmp_path): | CODE |
| LOW | tests/test_import_srt.py | 35 | def test_import_srt_uses_style_reference_when_provided(tmp_path): | CODE |
| LOW | tests/test_import_srt.py | 57 | def test_import_srt_inserts_new_text_track_after_video_and_text_tracks(tmp_path): | CODE |
| LOW⚡ | tests/test_scriptfile_building.py | 7 | def test_append_track_allows_first_unnamed_track_per_type(): | CODE |
| LOW⚡ | tests/test_scriptfile_building.py | 16 | def test_append_track_requires_name_for_second_same_type_track(): | CODE |
| LOW⚡ | tests/test_scriptfile_building.py | 24 | def test_add_segment_updates_script_duration(): | CODE |
| LOW⚡ | tests/test_scriptfile_building.py | 34 | def test_add_segment_auto_registers_materials_and_effect_refs(): | CODE |
| LOW | tests/test_scriptfile_building.py | 55 | def test_dumps_orders_tracks_by_track_order(): | CODE |
| LOW | pyJianYingDraft/_script_file_tracks.py | 20 | def _list_all_tracks_in_order(self: ScriptFileProtocol) -> List[BaseTrack]: | CODE |
| LOW | pyJianYingDraft/_script_file_template.py | 18 | def _get_imported_material_list(self: ScriptFileProtocol, material_type: str) -> List[Dict[str, Any]]: | CODE |
| LOW | pyJianYingDraft/_script_file_protocol.py | 38 | def _get_imported_material_list(self, material_type: str) -> List[Dict[str, Any]]: ... | CODE |
| LOW | pyJianYingDraft/_script_file_protocol.py | 42 | def _list_all_tracks_in_order(self) -> List[BaseTrack]: ... | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| HIGH | pyJianYingDraft/draft_folder.py | 122 | 在文件夹中打开一个草稿作为模板, 并在其上进行编辑 Args: draft_name (`str`): 草稿名称, 即相应文件夹名称 Returns: `S | STRING |
| HIGH | pyJianYingDraft/draft_folder.py | 145 | 复制一份给定的草稿, 并在复制出的新草稿上进行编辑 Args: template_name (`str`): 原草稿名称 new_draft_name (`str`): 新草 | STRING |
| HIGH | pyJianYingDraft/_script_file_tracks.py | 110 | 追加一个新轨道到当前最上层 Args: track_spec (`TrackSpec`): 待追加轨道的描述对象 Returns: `TrackRef`: | STRING |
| HIGH | pyJianYingDraft/_script_file_tracks.py | 130 | 按给定顺序将多个新轨道追加到当前最上层 Args: track_specs (`Sequence[TrackSpec]`): 待追加轨道描述列表 Returns: | STRING |
| HIGH | pyJianYingDraft/_script_file_tracks.py | 151 | 将一个新轨道插入到指定位置 Args: track_spec (`TrackSpec`): 待插入轨道的描述对象 under_track (`TrackRef` or `Im | STRING |
| HIGH | pyJianYingDraft/_script_file_tracks.py | 179 | 将多个新轨道作为一个顺序块插入到指定位置 Args: track_specs (`Sequence[TrackSpec]`): 待插入轨道描述列表 under_track ( | STRING |
| HIGH | pyJianYingDraft/_script_file_template.py | 28 | 获取唯一匹配的导入轨道, 以便在其上进行替换 推荐使用轨道名称进行筛选(若已知轨道名称) Args: track_type (`TrackType.video`, `TrackTy | STRING |
| HIGH | pyJianYingDraft/assets/__init__.py | 18 | 获取指定资源文件的完整路径 Args: asset_name: 资源名称(ASSET_FILES中的key) Returns: Path: 资源文件的完整路径 Rais | STRING |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | pyJianYingDraft/util.py | 10 | CODE | |
| LOW | pyJianYingDraft/_script_file_template.py | 83 | CODE | |
| LOW | pyJianYingDraft/_script_file_template.py | 243 | CODE | |
| LOW | pyJianYingDraft/_script_file_segments.py | 51 | CODE | |
| LOW | pyJianYingDraft/_script_file_segments.py | 178 | CODE | |
| LOW | pyJianYingDraft/template_mode.py | 153 | CODE | |
| LOW | pyJianYingDraft/metadata/effect_meta.py | 76 | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | pyJianYingDraft/text_segment.py | 50 | CODE | |
| LOW | pyJianYingDraft/video_segment.py | 569 | CODE | |
| LOW | pyJianYingDraft/local_materials.py | 54 | CODE | |
| LOW | pyJianYingDraft/segment.py | 147 | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | pyJianYingDraft/__init__.py | 26 | __all__ = [ | CODE |
| LOW | pyJianYingDraft/assets/__init__.py | 42 | __all__ = [ | CODE |
| LOW | pyJianYingDraft/metadata/__init__.py | 34 | __all__ = [ | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | pyJianYingDraft/draft_content_loader.py | 29 | except Exception as exc: | CODE |