返回 ppt-master
discovery.py
1 """Find SVG and notes files in a project directory."""
2
3 from __future__ import annotations
4
5 import re
6 from pathlib import Path
7
8 from slide_roster import discover_slide_svgs
9
10
11 class NotesFileReadError(RuntimeError):
12 """Report a matched notes file that cannot be decoded or read."""
13
14
15 def find_svg_files(
16 project_path: Path,
17 source: str = 'output',
18 *,
19 allow_fallback: bool = True,
20 ) -> tuple[list[Path], str]:
21 """Find SVG files in the project.
22
23 Args:
24 project_path: Project directory path.
25 source: SVG source directory alias or name.
26 - 'output': svg_output (hand-authored source; native default)
27 - 'final': svg_final (post-processed preview; diagnostic input)
28 - or any subdirectory name
29 allow_fallback: Try svg_output and then the project root when the
30 requested directory is missing.
31
32 Returns:
33 (list_of_svg_files, actual_directory_name) tuple.
34 """
35 dir_map = {
36 'output': 'svg_output',
37 'final': 'svg_final',
38 }
39
40 dir_name = dir_map.get(source, source)
41 svg_dir = project_path / dir_name
42
43 if not svg_dir.exists():
44 if not allow_fallback:
45 return [], dir_name
46 print(f" Warning: {dir_name} directory does not exist, trying svg_output")
47 dir_name = 'svg_output'
48 svg_dir = project_path / dir_name
49
50 if not svg_dir.exists():
51 if project_path.is_dir():
52 svg_dir = project_path
53 dir_name = project_path.name
54 else:
55 return [], ''
56
57 return discover_slide_svgs(svg_dir), dir_name
58
59
60 def find_notes_files(
61 project_path: Path,
62 svg_files: list[Path] | None = None,
63 ) -> dict[str, str]:
64 """Find notes files and map them to SVG files.
65
66 Supports two matching modes (mixed matching supported):
67 1. Match by filename (priority): notes/01_cover.md -> 01_cover.svg
68 2. Match by index (backward compatible): notes/slide01.md -> 1st SVG
69
70 Args:
71 project_path: Project directory path.
72 svg_files: SVG file list (for filename matching).
73
74 Returns:
75 Dict mapping SVG filename stem to notes content.
76 """
77 notes_dir = project_path / 'notes'
78 notes: dict[str, str] = {}
79
80 if not notes_dir.exists():
81 return notes
82
83 svg_stems_mapping: dict[str, int] = {}
84 svg_index_mapping: dict[int, str] = {}
85 if svg_files:
86 for i, svg_path in enumerate(svg_files, 1):
87 svg_stems_mapping[svg_path.stem] = i
88 svg_index_mapping[i] = svg_path.stem
89
90 for notes_file in notes_dir.glob('*.md'):
91 stem = notes_file.stem
92
93 # Try index-based matching (backward compat with slide01.md format).
94 match = re.search(r'slide[_]?(\d+)', stem)
95 mapped_stem = (
96 svg_index_mapping.get(int(match.group(1)))
97 if match
98 else None
99 )
100 filename_match = stem in svg_stems_mapping
101 if mapped_stem is None and not filename_match:
102 continue
103
104 try:
105 content = notes_file.read_text(encoding='utf-8').strip()
106 except (OSError, UnicodeError) as exc:
107 raise NotesFileReadError(
108 f"Cannot read matched notes file {notes_file}: {exc}"
109 ) from exc
110 if not content:
111 continue
112
113 if mapped_stem:
114 notes[mapped_stem] = content
115
116 # Filename-based matching overrides index-based matching.
117 if filename_match:
118 notes[stem] = content
119
120 return notes
121
121 lines PYTHON