meeting-noter 0.7.0__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.
Potentially problematic release.
This version of meeting-noter might be problematic. Click here for more details.
- meeting_noter/__init__.py +3 -0
- meeting_noter/__main__.py +6 -0
- meeting_noter/audio/__init__.py +1 -0
- meeting_noter/audio/capture.py +209 -0
- meeting_noter/audio/encoder.py +208 -0
- meeting_noter/audio/system_audio.py +363 -0
- meeting_noter/cli.py +837 -0
- meeting_noter/config.py +197 -0
- meeting_noter/daemon.py +519 -0
- meeting_noter/gui/__init__.py +5 -0
- meeting_noter/gui/__main__.py +6 -0
- meeting_noter/gui/app.py +53 -0
- meeting_noter/gui/main_window.py +50 -0
- meeting_noter/gui/meetings_tab.py +348 -0
- meeting_noter/gui/recording_tab.py +358 -0
- meeting_noter/gui/settings_tab.py +249 -0
- meeting_noter/install/__init__.py +1 -0
- meeting_noter/install/macos.py +102 -0
- meeting_noter/meeting_detector.py +333 -0
- meeting_noter/menubar.py +411 -0
- meeting_noter/mic_monitor.py +456 -0
- meeting_noter/output/__init__.py +1 -0
- meeting_noter/output/writer.py +96 -0
- meeting_noter/resources/__init__.py +1 -0
- meeting_noter/resources/icon.icns +0 -0
- meeting_noter/resources/icon.png +0 -0
- meeting_noter/resources/icon_128.png +0 -0
- meeting_noter/resources/icon_16.png +0 -0
- meeting_noter/resources/icon_256.png +0 -0
- meeting_noter/resources/icon_32.png +0 -0
- meeting_noter/resources/icon_512.png +0 -0
- meeting_noter/resources/icon_64.png +0 -0
- meeting_noter/transcription/__init__.py +1 -0
- meeting_noter/transcription/engine.py +234 -0
- meeting_noter-0.7.0.dist-info/METADATA +224 -0
- meeting_noter-0.7.0.dist-info/RECORD +39 -0
- meeting_noter-0.7.0.dist-info/WHEEL +5 -0
- meeting_noter-0.7.0.dist-info/entry_points.txt +2 -0
- meeting_noter-0.7.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
"""Transcription engine using faster-whisper."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import click
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from datetime import datetime
|
|
8
|
+
from typing import Optional
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _get_bundled_model_path() -> Optional[Path]:
|
|
12
|
+
"""Check if bundled model is available via meeting-noter-models package."""
|
|
13
|
+
try:
|
|
14
|
+
from meeting_noter_models import get_model_path, MODEL_NAME
|
|
15
|
+
model_path = get_model_path()
|
|
16
|
+
if model_path.exists() and (model_path / "model.bin").exists():
|
|
17
|
+
return model_path
|
|
18
|
+
except ImportError:
|
|
19
|
+
pass
|
|
20
|
+
return None
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def get_model(model_size: str = "tiny.en"):
|
|
24
|
+
"""Load the Whisper model.
|
|
25
|
+
|
|
26
|
+
Priority:
|
|
27
|
+
1. Bundled model from meeting-noter-models package (offline)
|
|
28
|
+
2. Download from Hugging Face Hub (cached after first download)
|
|
29
|
+
"""
|
|
30
|
+
from faster_whisper import WhisperModel
|
|
31
|
+
|
|
32
|
+
# Check for bundled model first (for offline use)
|
|
33
|
+
bundled_path = _get_bundled_model_path()
|
|
34
|
+
if bundled_path and model_size == "tiny.en":
|
|
35
|
+
click.echo(f"Loading bundled model '{model_size}'...")
|
|
36
|
+
model = WhisperModel(
|
|
37
|
+
str(bundled_path),
|
|
38
|
+
device="cpu",
|
|
39
|
+
compute_type="int8",
|
|
40
|
+
)
|
|
41
|
+
return model
|
|
42
|
+
|
|
43
|
+
# Fall back to Hugging Face download
|
|
44
|
+
click.echo(f"Loading model '{model_size}'...")
|
|
45
|
+
|
|
46
|
+
# Use INT8 for CPU efficiency
|
|
47
|
+
model = WhisperModel(
|
|
48
|
+
model_size,
|
|
49
|
+
device="cpu",
|
|
50
|
+
compute_type="int8",
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
return model
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def format_timestamp(seconds: float) -> str:
|
|
57
|
+
"""Format seconds as [HH:MM:SS] or [MM:SS]."""
|
|
58
|
+
hours = int(seconds // 3600)
|
|
59
|
+
minutes = int((seconds % 3600) // 60)
|
|
60
|
+
secs = int(seconds % 60)
|
|
61
|
+
|
|
62
|
+
if hours > 0:
|
|
63
|
+
return f"[{hours:02d}:{minutes:02d}:{secs:02d}]"
|
|
64
|
+
return f"[{minutes:02d}:{secs:02d}]"
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def transcribe_audio(audio_path: Path, model_size: str = "tiny.en") -> str:
|
|
68
|
+
"""Transcribe an audio file.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
audio_path: Path to MP3 or WAV file
|
|
72
|
+
model_size: Whisper model to use
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
Formatted transcript string
|
|
76
|
+
"""
|
|
77
|
+
model = get_model(model_size)
|
|
78
|
+
|
|
79
|
+
click.echo(f"Transcribing {audio_path.name}...")
|
|
80
|
+
|
|
81
|
+
segments, info = model.transcribe(
|
|
82
|
+
str(audio_path),
|
|
83
|
+
beam_size=5,
|
|
84
|
+
vad_filter=True, # Voice Activity Detection
|
|
85
|
+
vad_parameters=dict(
|
|
86
|
+
min_silence_duration_ms=500,
|
|
87
|
+
),
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
# Build transcript
|
|
91
|
+
lines = []
|
|
92
|
+
lines.append("Meeting Transcription")
|
|
93
|
+
lines.append(f"File: {audio_path.name}")
|
|
94
|
+
lines.append(f"Transcribed: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
|
95
|
+
lines.append(f"Language: {info.language} (probability: {info.language_probability:.2f})")
|
|
96
|
+
lines.append(f"Duration: {info.duration:.1f} seconds")
|
|
97
|
+
lines.append("-" * 40)
|
|
98
|
+
lines.append("")
|
|
99
|
+
|
|
100
|
+
for segment in segments:
|
|
101
|
+
timestamp = format_timestamp(segment.start)
|
|
102
|
+
text = segment.text.strip()
|
|
103
|
+
if text:
|
|
104
|
+
lines.append(f"{timestamp} {text}")
|
|
105
|
+
|
|
106
|
+
return "\n".join(lines)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def transcribe_file(
|
|
110
|
+
file: Optional[str],
|
|
111
|
+
output_dir: Path,
|
|
112
|
+
model_size: str = "tiny.en",
|
|
113
|
+
transcripts_dir: Optional[Path] = None,
|
|
114
|
+
):
|
|
115
|
+
"""Transcribe a recording file.
|
|
116
|
+
|
|
117
|
+
If no file specified, transcribes the most recent recording.
|
|
118
|
+
Saves transcript to transcripts_dir (defaults to same as audio file).
|
|
119
|
+
"""
|
|
120
|
+
# Find the file to transcribe
|
|
121
|
+
if file:
|
|
122
|
+
audio_path = Path(file)
|
|
123
|
+
if not audio_path.exists():
|
|
124
|
+
# Try in output dir
|
|
125
|
+
audio_path = output_dir / file
|
|
126
|
+
if not audio_path.exists():
|
|
127
|
+
click.echo(click.style(f"File not found: {file}", fg="red"))
|
|
128
|
+
return
|
|
129
|
+
else:
|
|
130
|
+
# Find most recent recording
|
|
131
|
+
mp3_files = sorted(output_dir.glob("*.mp3"), key=lambda p: p.stat().st_mtime)
|
|
132
|
+
if not mp3_files:
|
|
133
|
+
click.echo(click.style(
|
|
134
|
+
f"No recordings found in {output_dir}",
|
|
135
|
+
fg="yellow"
|
|
136
|
+
))
|
|
137
|
+
return
|
|
138
|
+
audio_path = mp3_files[-1]
|
|
139
|
+
click.echo(f"Using most recent recording: {audio_path.name}")
|
|
140
|
+
|
|
141
|
+
# Determine transcript path
|
|
142
|
+
if transcripts_dir:
|
|
143
|
+
transcripts_dir.mkdir(parents=True, exist_ok=True)
|
|
144
|
+
transcript_path = transcripts_dir / audio_path.with_suffix(".txt").name
|
|
145
|
+
else:
|
|
146
|
+
transcript_path = audio_path.with_suffix(".txt")
|
|
147
|
+
if transcript_path.exists():
|
|
148
|
+
click.echo(click.style(
|
|
149
|
+
f"Transcript already exists: {transcript_path.name}",
|
|
150
|
+
fg="yellow"
|
|
151
|
+
))
|
|
152
|
+
if not click.confirm("Overwrite?"):
|
|
153
|
+
return
|
|
154
|
+
|
|
155
|
+
# Transcribe
|
|
156
|
+
try:
|
|
157
|
+
transcript = transcribe_audio(audio_path, model_size)
|
|
158
|
+
|
|
159
|
+
# Save transcript
|
|
160
|
+
transcript_path.write_text(transcript)
|
|
161
|
+
click.echo(click.style(
|
|
162
|
+
f"\nTranscript saved: {transcript_path}",
|
|
163
|
+
fg="green"
|
|
164
|
+
))
|
|
165
|
+
|
|
166
|
+
# Show preview
|
|
167
|
+
click.echo("\n--- Preview ---")
|
|
168
|
+
lines = transcript.split("\n")
|
|
169
|
+
preview_lines = lines[:15]
|
|
170
|
+
for line in preview_lines:
|
|
171
|
+
click.echo(line)
|
|
172
|
+
if len(lines) > 15:
|
|
173
|
+
click.echo(f"... ({len(lines) - 15} more lines)")
|
|
174
|
+
|
|
175
|
+
except Exception as e:
|
|
176
|
+
click.echo(click.style(f"Transcription failed: {e}", fg="red"))
|
|
177
|
+
raise
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def transcribe_live(output_dir: Path, model_size: str = "tiny.en"):
|
|
181
|
+
"""Real-time transcription of ongoing recording.
|
|
182
|
+
|
|
183
|
+
Note: This is more CPU intensive and may have latency.
|
|
184
|
+
"""
|
|
185
|
+
click.echo(click.style(
|
|
186
|
+
"Live transcription is experimental and CPU-intensive.",
|
|
187
|
+
fg="yellow"
|
|
188
|
+
))
|
|
189
|
+
|
|
190
|
+
# Find active recording (most recent, still being written)
|
|
191
|
+
mp3_files = sorted(output_dir.glob("*.mp3"), key=lambda p: p.stat().st_mtime)
|
|
192
|
+
if not mp3_files:
|
|
193
|
+
click.echo("No recordings found. Is the daemon running?")
|
|
194
|
+
return
|
|
195
|
+
|
|
196
|
+
latest = mp3_files[-1]
|
|
197
|
+
click.echo(f"Monitoring: {latest.name}")
|
|
198
|
+
click.echo("Press Ctrl+C to stop.\n")
|
|
199
|
+
|
|
200
|
+
model = get_model(model_size)
|
|
201
|
+
last_position = 0
|
|
202
|
+
|
|
203
|
+
try:
|
|
204
|
+
import time
|
|
205
|
+
while True:
|
|
206
|
+
# Check file size
|
|
207
|
+
current_size = latest.stat().st_size
|
|
208
|
+
if current_size > last_position:
|
|
209
|
+
# New data available - transcribe the whole file
|
|
210
|
+
# (faster-whisper doesn't support streaming well)
|
|
211
|
+
try:
|
|
212
|
+
segments, info = model.transcribe(
|
|
213
|
+
str(latest),
|
|
214
|
+
beam_size=3, # Faster for live
|
|
215
|
+
vad_filter=True,
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
# Print new segments
|
|
219
|
+
for segment in segments:
|
|
220
|
+
if segment.end > last_position / 1000: # Rough estimate
|
|
221
|
+
timestamp = format_timestamp(segment.start)
|
|
222
|
+
text = segment.text.strip()
|
|
223
|
+
if text:
|
|
224
|
+
click.echo(f"{timestamp} {text}")
|
|
225
|
+
|
|
226
|
+
last_position = current_size
|
|
227
|
+
except Exception as e:
|
|
228
|
+
# File might be locked, retry
|
|
229
|
+
pass
|
|
230
|
+
|
|
231
|
+
time.sleep(5) # Check every 5 seconds
|
|
232
|
+
|
|
233
|
+
except KeyboardInterrupt:
|
|
234
|
+
click.echo("\nStopped live transcription.")
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: meeting-noter
|
|
3
|
+
Version: 0.7.0
|
|
4
|
+
Summary: Offline meeting transcription for macOS with automatic meeting detection
|
|
5
|
+
Author: Victor
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/tech4vision/meeting-noter
|
|
8
|
+
Project-URL: Repository, https://github.com/tech4vision/meeting-noter
|
|
9
|
+
Project-URL: Issues, https://github.com/tech4vision/meeting-noter/issues
|
|
10
|
+
Keywords: meeting,transcription,whisper,offline,speech-to-text,audio
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Operating System :: MacOS
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Topic :: Multimedia :: Sound/Audio :: Speech
|
|
23
|
+
Requires-Python: >=3.9
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
Requires-Dist: click>=8.0
|
|
26
|
+
Requires-Dist: sounddevice>=0.4.6
|
|
27
|
+
Requires-Dist: numpy>=1.24
|
|
28
|
+
Requires-Dist: faster-whisper>=1.0.0
|
|
29
|
+
Requires-Dist: rumps>=0.4.0
|
|
30
|
+
Requires-Dist: PyQt6>=6.5.0
|
|
31
|
+
Requires-Dist: imageio-ffmpeg>=0.4.9
|
|
32
|
+
Requires-Dist: pyobjc-framework-Cocoa>=9.0; sys_platform == "darwin"
|
|
33
|
+
Requires-Dist: pyobjc-framework-Quartz>=9.0; sys_platform == "darwin"
|
|
34
|
+
Requires-Dist: pyobjc-framework-ScreenCaptureKit>=9.0; sys_platform == "darwin"
|
|
35
|
+
Requires-Dist: pyobjc-framework-AVFoundation>=9.0; sys_platform == "darwin"
|
|
36
|
+
Requires-Dist: pyobjc-framework-CoreMedia>=9.0; sys_platform == "darwin"
|
|
37
|
+
Requires-Dist: pyobjc-framework-libdispatch>=9.0; sys_platform == "darwin"
|
|
38
|
+
Provides-Extra: offline
|
|
39
|
+
Requires-Dist: meeting-noter-models>=0.1.0; extra == "offline"
|
|
40
|
+
Provides-Extra: dev
|
|
41
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
42
|
+
Requires-Dist: pytest-cov; extra == "dev"
|
|
43
|
+
Requires-Dist: black; extra == "dev"
|
|
44
|
+
Requires-Dist: ruff; extra == "dev"
|
|
45
|
+
Requires-Dist: mypy; extra == "dev"
|
|
46
|
+
|
|
47
|
+
# Meeting Noter
|
|
48
|
+
|
|
49
|
+
Offline meeting transcription tool for macOS. Captures both your voice and meeting participants' audio, saves to MP3, and transcribes locally using Whisper.
|
|
50
|
+
|
|
51
|
+
## Features
|
|
52
|
+
|
|
53
|
+
- **No setup required** - Just install and run
|
|
54
|
+
- **Captures both sides** - Your mic + system audio (meeting participants)
|
|
55
|
+
- **Offline transcription** - Uses Whisper locally, no API calls
|
|
56
|
+
- **Auto-detection** - Detects active meetings (Zoom, Teams, Meet, Slack)
|
|
57
|
+
- **Multiple interfaces** - Menu bar app, desktop GUI, or CLI
|
|
58
|
+
- **Auto-segmentation** - One file per meeting (detects silence)
|
|
59
|
+
|
|
60
|
+
## Installation
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
pipx install meeting-noter
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
**For corporate/offline environments** (bundles Whisper model, no download needed):
|
|
67
|
+
```bash
|
|
68
|
+
pipx install "meeting-noter[offline]"
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Upgrading
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
# Standard
|
|
75
|
+
pipx upgrade meeting-noter
|
|
76
|
+
|
|
77
|
+
# With offline model
|
|
78
|
+
pipx reinstall "meeting-noter[offline]"
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
No system dependencies required - ffmpeg is bundled automatically.
|
|
82
|
+
|
|
83
|
+
## Quick Start
|
|
84
|
+
|
|
85
|
+
**Menu Bar App** (recommended):
|
|
86
|
+
```bash
|
|
87
|
+
meeting-noter menubar
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
**Desktop GUI**:
|
|
91
|
+
```bash
|
|
92
|
+
meeting-noter gui
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
**CLI Recording**:
|
|
96
|
+
```bash
|
|
97
|
+
meeting-noter start "Weekly Standup"
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
The app will request microphone and Screen Recording permissions on first use.
|
|
101
|
+
|
|
102
|
+
## Usage
|
|
103
|
+
|
|
104
|
+
### Recording
|
|
105
|
+
- The menu bar app auto-detects meetings and prompts to record
|
|
106
|
+
- Or manually start recording via the GUI/CLI
|
|
107
|
+
- Press Ctrl+C (CLI) or click Stop to end recording
|
|
108
|
+
|
|
109
|
+
### Transcription
|
|
110
|
+
Recordings are auto-transcribed by default. Or manually:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
# Transcribe the most recent recording
|
|
114
|
+
meeting-noter transcribe
|
|
115
|
+
|
|
116
|
+
# Transcribe a specific file
|
|
117
|
+
meeting-noter transcribe recording.mp3
|
|
118
|
+
|
|
119
|
+
# List all recordings
|
|
120
|
+
meeting-noter list
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Commands
|
|
124
|
+
|
|
125
|
+
| Command | Description |
|
|
126
|
+
|---------|-------------|
|
|
127
|
+
| `meeting-noter` | Launch desktop GUI |
|
|
128
|
+
| `meeting-noter menubar` | Launch menu bar app |
|
|
129
|
+
| `meeting-noter gui` | Launch desktop GUI |
|
|
130
|
+
| `meeting-noter start [name]` | Interactive CLI recording |
|
|
131
|
+
| `meeting-noter list` | List recent recordings |
|
|
132
|
+
| `meeting-noter transcribe` | Transcribe a recording |
|
|
133
|
+
| `meeting-noter devices` | List audio devices |
|
|
134
|
+
| `meeting-noter status` | Check daemon status |
|
|
135
|
+
|
|
136
|
+
## Options
|
|
137
|
+
|
|
138
|
+
### `start`
|
|
139
|
+
- First argument: Meeting name (optional, auto-generates timestamp if omitted)
|
|
140
|
+
|
|
141
|
+
### `transcribe`
|
|
142
|
+
- `-m, --model`: Whisper model size (tiny.en, base.en, small.en, medium.en, large-v3)
|
|
143
|
+
- `-o, --output-dir`: Directory with recordings
|
|
144
|
+
|
|
145
|
+
### `list`
|
|
146
|
+
- `-n, --limit`: Number of recordings to show
|
|
147
|
+
- `-o, --output-dir`: Directory with recordings
|
|
148
|
+
|
|
149
|
+
## How It Works
|
|
150
|
+
|
|
151
|
+
```
|
|
152
|
+
┌─────────────────────────────────────┐
|
|
153
|
+
│ Your Meeting App │
|
|
154
|
+
│ (Zoom/Teams/Meet/Slack) │
|
|
155
|
+
└──────────────────┬──────────────────┘
|
|
156
|
+
│
|
|
157
|
+
┌─────────────┴─────────────┐
|
|
158
|
+
▼ ▼
|
|
159
|
+
┌─────────┐ ┌─────────────┐
|
|
160
|
+
│ Mic │ │ System Audio│
|
|
161
|
+
│(default)│ │(ScreenCaptureKit)
|
|
162
|
+
└────┬────┘ └──────┬──────┘
|
|
163
|
+
│ │
|
|
164
|
+
└───────────┬───────────────┘
|
|
165
|
+
▼
|
|
166
|
+
┌─────────────┐
|
|
167
|
+
│Meeting Noter│
|
|
168
|
+
│ (capture) │
|
|
169
|
+
└──────┬──────┘
|
|
170
|
+
│
|
|
171
|
+
▼
|
|
172
|
+
~/meetings/2024-01-15_Weekly_Standup.mp3
|
|
173
|
+
│
|
|
174
|
+
▼ (auto or on-demand)
|
|
175
|
+
┌─────────────┐
|
|
176
|
+
│ Whisper │ (local)
|
|
177
|
+
└──────┬──────┘
|
|
178
|
+
│
|
|
179
|
+
▼
|
|
180
|
+
~/meetings/2024-01-15_Weekly_Standup.txt
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## Permissions
|
|
184
|
+
|
|
185
|
+
On first use, macOS will ask for:
|
|
186
|
+
|
|
187
|
+
1. **Microphone** - For capturing your voice
|
|
188
|
+
2. **Screen Recording** - For capturing system audio (meeting participants)
|
|
189
|
+
|
|
190
|
+
Grant these in System Settings > Privacy & Security.
|
|
191
|
+
|
|
192
|
+
## Configuration
|
|
193
|
+
|
|
194
|
+
Config file: `~/.config/meeting-noter/config.json`
|
|
195
|
+
|
|
196
|
+
```json
|
|
197
|
+
{
|
|
198
|
+
"recordings_dir": "~/meetings",
|
|
199
|
+
"transcripts_dir": "~/meetings",
|
|
200
|
+
"whisper_model": "tiny.en",
|
|
201
|
+
"auto_transcribe": true,
|
|
202
|
+
"silence_timeout": 5,
|
|
203
|
+
"capture_system_audio": true
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
## Whisper Models
|
|
208
|
+
|
|
209
|
+
| Model | Size | Speed | Accuracy |
|
|
210
|
+
|-------|------|-------|----------|
|
|
211
|
+
| `tiny.en` | ~75MB | Fastest | Good |
|
|
212
|
+
| `base.en` | ~150MB | Fast | Better |
|
|
213
|
+
| `small.en` | ~500MB | Medium | High |
|
|
214
|
+
| `medium.en` | ~1.5GB | Slow | Very High |
|
|
215
|
+
| `large-v3` | ~3GB | Slowest | Best |
|
|
216
|
+
|
|
217
|
+
## Requirements
|
|
218
|
+
|
|
219
|
+
- macOS 12.3+ (for ScreenCaptureKit)
|
|
220
|
+
- Python 3.9+
|
|
221
|
+
|
|
222
|
+
## License
|
|
223
|
+
|
|
224
|
+
MIT
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
meeting_noter/__init__.py,sha256=bLOErRC3sfnQ4a4RyZUzUljZEikXy7zOiYYUz5GytPg,103
|
|
2
|
+
meeting_noter/__main__.py,sha256=6sSOqH1o3jvgvkVzsVKmF6-xVGcUAbNVQkRl2CrygdE,120
|
|
3
|
+
meeting_noter/cli.py,sha256=OAlLc7CEgov3akDo89I0JUMl9wCXs81x3QO2mBcqLvE,27769
|
|
4
|
+
meeting_noter/config.py,sha256=41LFBNp5o0IojYS5Hf0FJVIr7GNn7B5O1TJDE8SQkkk,5977
|
|
5
|
+
meeting_noter/daemon.py,sha256=BYMc-sAz8zo9ZFMRr_Th8iw_l7rc71ldEf1mwsJJgaw,16502
|
|
6
|
+
meeting_noter/meeting_detector.py,sha256=St0qoMkvUERP4BaxnXO1M6fZDJpWqBf9In7z2SgWcWg,10564
|
|
7
|
+
meeting_noter/menubar.py,sha256=Gn6p8y5jA_HCWf1T3ademxH-vndpONHkf9vUlKs6XEo,14379
|
|
8
|
+
meeting_noter/mic_monitor.py,sha256=zgcNypn29gWTyslalu5-JFseG47DVHEtcHCGiEUugAs,16021
|
|
9
|
+
meeting_noter/audio/__init__.py,sha256=O7PU8CxHSHxMeHbc9Jdwt9kePLQzsPh81GQU7VHCtBY,44
|
|
10
|
+
meeting_noter/audio/capture.py,sha256=fDrT5oXfva8vdFlht9cv60NviKbksw2QeJ8eOtI19uE,6469
|
|
11
|
+
meeting_noter/audio/encoder.py,sha256=6UgEYLFACSQEIx2nhH1Qq-cBh3qPJziMGkrm39k6Nz8,6401
|
|
12
|
+
meeting_noter/audio/system_audio.py,sha256=jbHGjNCerI19weXap0a90Ik17lVTCT1hCEgRKYke-p8,13016
|
|
13
|
+
meeting_noter/gui/__init__.py,sha256=z5GxxaeXyjqyEa9ox0dQxuL5u_BART0bi7cI6rfntEI,103
|
|
14
|
+
meeting_noter/gui/__main__.py,sha256=A2HWdYod0bTgjQQIi21O7XpmgxLH36e_X0aygEUZLls,146
|
|
15
|
+
meeting_noter/gui/app.py,sha256=COUAWu_dR5HriNYxbE86CVGS1eGYqyteH2oUFN_YtYQ,1370
|
|
16
|
+
meeting_noter/gui/main_window.py,sha256=vSvNO86CHMgJf9Pem8AOdrqKyTV9ITp3W4nCoqIuAmI,1667
|
|
17
|
+
meeting_noter/gui/meetings_tab.py,sha256=pqXqMv5YvCj8H6yR_TF3SMzsIDMAxyLe2otbENbM2SY,12315
|
|
18
|
+
meeting_noter/gui/recording_tab.py,sha256=UlrPkUiOmkGgOKqVrfAVp4EyiY5sq86W49Y-YAhYexY,12521
|
|
19
|
+
meeting_noter/gui/settings_tab.py,sha256=NUQVKDdSpyNp_MVxPLw2dB93wxaD5VeBKiDtGS4CyoU,8446
|
|
20
|
+
meeting_noter/install/__init__.py,sha256=SX5vLFMrV8aBDEGW18jhaqBqJqnRXaeo0Ct7QVGDgvE,38
|
|
21
|
+
meeting_noter/install/macos.py,sha256=dO-86zbNKRtt0l4D8naVn7kFWjzI8TufWLWE3FRLHQ8,3400
|
|
22
|
+
meeting_noter/output/__init__.py,sha256=F7xPlOrqweZcPbZtDrhved1stBI59vnWnLYfGwdu6oY,31
|
|
23
|
+
meeting_noter/output/writer.py,sha256=zO8y6FAFUAp0EEtALY-M5e2Ja5P-hgV38JjcKW7c-bA,3017
|
|
24
|
+
meeting_noter/resources/__init__.py,sha256=yzHNxgypkuVDFZWv6xZjUygOVB_Equ9NNX_HGRvN7VM,43
|
|
25
|
+
meeting_noter/resources/icon.icns,sha256=zMWqXCq7pI5acS0tbekFgFDvLt66EKUBP5-5IgztwPM,35146
|
|
26
|
+
meeting_noter/resources/icon.png,sha256=nK0hM6CPMor_xXRFYURpm0muA2O7yzotyydUQx-ukyg,2390
|
|
27
|
+
meeting_noter/resources/icon_128.png,sha256=Wg27dIpFfMzv15HG6eQpSqg72jrbGBcqLkGjOfAptO4,1169
|
|
28
|
+
meeting_noter/resources/icon_16.png,sha256=GONIIZCP9FUMb_MWZWMz1_iWxsPCPOwW_XcrPF7W8KY,215
|
|
29
|
+
meeting_noter/resources/icon_256.png,sha256=nK0hM6CPMor_xXRFYURpm0muA2O7yzotyydUQx-ukyg,2390
|
|
30
|
+
meeting_noter/resources/icon_32.png,sha256=Bw6hJXqkd-d1OfDpFv2om7Stex7daedxnpXt32xR8Sg,344
|
|
31
|
+
meeting_noter/resources/icon_512.png,sha256=o7X3ngYcppcIAAk9AcfPx94MUmrsPRp0qBTpb9SzfX8,5369
|
|
32
|
+
meeting_noter/resources/icon_64.png,sha256=TqG7Awx3kK8YdiX1e_z1odZonosZyQI2trlkNZCzUoI,607
|
|
33
|
+
meeting_noter/transcription/__init__.py,sha256=7GY9diP06DzFyoli41wddbrPv5bVDzH35bmnWlIJev4,29
|
|
34
|
+
meeting_noter/transcription/engine.py,sha256=G9NcSS6Q-UhW7PlQ0E85hQXn6BWao64nIvyw4NR2yxI,7208
|
|
35
|
+
meeting_noter-0.7.0.dist-info/METADATA,sha256=8V-ZO5eL8tFm3PauQU9dCZVw0ZbkyCWl6xklfcH3a-c,6952
|
|
36
|
+
meeting_noter-0.7.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
37
|
+
meeting_noter-0.7.0.dist-info/entry_points.txt,sha256=rKNhzjSF5-e3bLRr8LVe22FeiwcacXabCvNpoEXfu4I,56
|
|
38
|
+
meeting_noter-0.7.0.dist-info/top_level.txt,sha256=9Tuq04_0SXM0OXOHVbOHkHkB5tG3fqkrMrfzCMpbLpY,14
|
|
39
|
+
meeting_noter-0.7.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
meeting_noter
|