meeting-noter 0.3.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 +176 -0
- meeting_noter/audio/system_audio.py +363 -0
- meeting_noter/cli.py +308 -0
- meeting_noter/config.py +197 -0
- meeting_noter/daemon.py +514 -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 +296 -0
- meeting_noter/menubar.py +432 -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 +208 -0
- meeting_noter-0.3.0.dist-info/METADATA +261 -0
- meeting_noter-0.3.0.dist-info/RECORD +38 -0
- meeting_noter-0.3.0.dist-info/WHEEL +5 -0
- meeting_noter-0.3.0.dist-info/entry_points.txt +2 -0
- meeting_noter-0.3.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,208 @@
|
|
|
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_model(model_size: str = "tiny.en"):
|
|
12
|
+
"""Load the Whisper model.
|
|
13
|
+
|
|
14
|
+
Models are cached after first download (~75MB for tiny.en).
|
|
15
|
+
"""
|
|
16
|
+
from faster_whisper import WhisperModel
|
|
17
|
+
|
|
18
|
+
click.echo(f"Loading model '{model_size}'...")
|
|
19
|
+
|
|
20
|
+
# Use INT8 for CPU efficiency
|
|
21
|
+
model = WhisperModel(
|
|
22
|
+
model_size,
|
|
23
|
+
device="cpu",
|
|
24
|
+
compute_type="int8",
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
return model
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def format_timestamp(seconds: float) -> str:
|
|
31
|
+
"""Format seconds as [HH:MM:SS] or [MM:SS]."""
|
|
32
|
+
hours = int(seconds // 3600)
|
|
33
|
+
minutes = int((seconds % 3600) // 60)
|
|
34
|
+
secs = int(seconds % 60)
|
|
35
|
+
|
|
36
|
+
if hours > 0:
|
|
37
|
+
return f"[{hours:02d}:{minutes:02d}:{secs:02d}]"
|
|
38
|
+
return f"[{minutes:02d}:{secs:02d}]"
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def transcribe_audio(audio_path: Path, model_size: str = "tiny.en") -> str:
|
|
42
|
+
"""Transcribe an audio file.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
audio_path: Path to MP3 or WAV file
|
|
46
|
+
model_size: Whisper model to use
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
Formatted transcript string
|
|
50
|
+
"""
|
|
51
|
+
model = get_model(model_size)
|
|
52
|
+
|
|
53
|
+
click.echo(f"Transcribing {audio_path.name}...")
|
|
54
|
+
|
|
55
|
+
segments, info = model.transcribe(
|
|
56
|
+
str(audio_path),
|
|
57
|
+
beam_size=5,
|
|
58
|
+
vad_filter=True, # Voice Activity Detection
|
|
59
|
+
vad_parameters=dict(
|
|
60
|
+
min_silence_duration_ms=500,
|
|
61
|
+
),
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
# Build transcript
|
|
65
|
+
lines = []
|
|
66
|
+
lines.append("Meeting Transcription")
|
|
67
|
+
lines.append(f"File: {audio_path.name}")
|
|
68
|
+
lines.append(f"Transcribed: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
|
69
|
+
lines.append(f"Language: {info.language} (probability: {info.language_probability:.2f})")
|
|
70
|
+
lines.append(f"Duration: {info.duration:.1f} seconds")
|
|
71
|
+
lines.append("-" * 40)
|
|
72
|
+
lines.append("")
|
|
73
|
+
|
|
74
|
+
for segment in segments:
|
|
75
|
+
timestamp = format_timestamp(segment.start)
|
|
76
|
+
text = segment.text.strip()
|
|
77
|
+
if text:
|
|
78
|
+
lines.append(f"{timestamp} {text}")
|
|
79
|
+
|
|
80
|
+
return "\n".join(lines)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def transcribe_file(
|
|
84
|
+
file: Optional[str],
|
|
85
|
+
output_dir: Path,
|
|
86
|
+
model_size: str = "tiny.en",
|
|
87
|
+
transcripts_dir: Optional[Path] = None,
|
|
88
|
+
):
|
|
89
|
+
"""Transcribe a recording file.
|
|
90
|
+
|
|
91
|
+
If no file specified, transcribes the most recent recording.
|
|
92
|
+
Saves transcript to transcripts_dir (defaults to same as audio file).
|
|
93
|
+
"""
|
|
94
|
+
# Find the file to transcribe
|
|
95
|
+
if file:
|
|
96
|
+
audio_path = Path(file)
|
|
97
|
+
if not audio_path.exists():
|
|
98
|
+
# Try in output dir
|
|
99
|
+
audio_path = output_dir / file
|
|
100
|
+
if not audio_path.exists():
|
|
101
|
+
click.echo(click.style(f"File not found: {file}", fg="red"))
|
|
102
|
+
return
|
|
103
|
+
else:
|
|
104
|
+
# Find most recent recording
|
|
105
|
+
mp3_files = sorted(output_dir.glob("*.mp3"), key=lambda p: p.stat().st_mtime)
|
|
106
|
+
if not mp3_files:
|
|
107
|
+
click.echo(click.style(
|
|
108
|
+
f"No recordings found in {output_dir}",
|
|
109
|
+
fg="yellow"
|
|
110
|
+
))
|
|
111
|
+
return
|
|
112
|
+
audio_path = mp3_files[-1]
|
|
113
|
+
click.echo(f"Using most recent recording: {audio_path.name}")
|
|
114
|
+
|
|
115
|
+
# Determine transcript path
|
|
116
|
+
if transcripts_dir:
|
|
117
|
+
transcripts_dir.mkdir(parents=True, exist_ok=True)
|
|
118
|
+
transcript_path = transcripts_dir / audio_path.with_suffix(".txt").name
|
|
119
|
+
else:
|
|
120
|
+
transcript_path = audio_path.with_suffix(".txt")
|
|
121
|
+
if transcript_path.exists():
|
|
122
|
+
click.echo(click.style(
|
|
123
|
+
f"Transcript already exists: {transcript_path.name}",
|
|
124
|
+
fg="yellow"
|
|
125
|
+
))
|
|
126
|
+
if not click.confirm("Overwrite?"):
|
|
127
|
+
return
|
|
128
|
+
|
|
129
|
+
# Transcribe
|
|
130
|
+
try:
|
|
131
|
+
transcript = transcribe_audio(audio_path, model_size)
|
|
132
|
+
|
|
133
|
+
# Save transcript
|
|
134
|
+
transcript_path.write_text(transcript)
|
|
135
|
+
click.echo(click.style(
|
|
136
|
+
f"\nTranscript saved: {transcript_path}",
|
|
137
|
+
fg="green"
|
|
138
|
+
))
|
|
139
|
+
|
|
140
|
+
# Show preview
|
|
141
|
+
click.echo("\n--- Preview ---")
|
|
142
|
+
lines = transcript.split("\n")
|
|
143
|
+
preview_lines = lines[:15]
|
|
144
|
+
for line in preview_lines:
|
|
145
|
+
click.echo(line)
|
|
146
|
+
if len(lines) > 15:
|
|
147
|
+
click.echo(f"... ({len(lines) - 15} more lines)")
|
|
148
|
+
|
|
149
|
+
except Exception as e:
|
|
150
|
+
click.echo(click.style(f"Transcription failed: {e}", fg="red"))
|
|
151
|
+
raise
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def transcribe_live(output_dir: Path, model_size: str = "tiny.en"):
|
|
155
|
+
"""Real-time transcription of ongoing recording.
|
|
156
|
+
|
|
157
|
+
Note: This is more CPU intensive and may have latency.
|
|
158
|
+
"""
|
|
159
|
+
click.echo(click.style(
|
|
160
|
+
"Live transcription is experimental and CPU-intensive.",
|
|
161
|
+
fg="yellow"
|
|
162
|
+
))
|
|
163
|
+
|
|
164
|
+
# Find active recording (most recent, still being written)
|
|
165
|
+
mp3_files = sorted(output_dir.glob("*.mp3"), key=lambda p: p.stat().st_mtime)
|
|
166
|
+
if not mp3_files:
|
|
167
|
+
click.echo("No recordings found. Is the daemon running?")
|
|
168
|
+
return
|
|
169
|
+
|
|
170
|
+
latest = mp3_files[-1]
|
|
171
|
+
click.echo(f"Monitoring: {latest.name}")
|
|
172
|
+
click.echo("Press Ctrl+C to stop.\n")
|
|
173
|
+
|
|
174
|
+
model = get_model(model_size)
|
|
175
|
+
last_position = 0
|
|
176
|
+
|
|
177
|
+
try:
|
|
178
|
+
import time
|
|
179
|
+
while True:
|
|
180
|
+
# Check file size
|
|
181
|
+
current_size = latest.stat().st_size
|
|
182
|
+
if current_size > last_position:
|
|
183
|
+
# New data available - transcribe the whole file
|
|
184
|
+
# (faster-whisper doesn't support streaming well)
|
|
185
|
+
try:
|
|
186
|
+
segments, info = model.transcribe(
|
|
187
|
+
str(latest),
|
|
188
|
+
beam_size=3, # Faster for live
|
|
189
|
+
vad_filter=True,
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
# Print new segments
|
|
193
|
+
for segment in segments:
|
|
194
|
+
if segment.end > last_position / 1000: # Rough estimate
|
|
195
|
+
timestamp = format_timestamp(segment.start)
|
|
196
|
+
text = segment.text.strip()
|
|
197
|
+
if text:
|
|
198
|
+
click.echo(f"{timestamp} {text}")
|
|
199
|
+
|
|
200
|
+
last_position = current_size
|
|
201
|
+
except Exception as e:
|
|
202
|
+
# File might be locked, retry
|
|
203
|
+
pass
|
|
204
|
+
|
|
205
|
+
time.sleep(5) # Check every 5 seconds
|
|
206
|
+
|
|
207
|
+
except KeyboardInterrupt:
|
|
208
|
+
click.echo("\nStopped live transcription.")
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: meeting-noter
|
|
3
|
+
Version: 0.3.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: Topic :: Multimedia :: Sound/Audio :: Speech
|
|
22
|
+
Requires-Python: >=3.9
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
Requires-Dist: click>=8.0
|
|
25
|
+
Requires-Dist: sounddevice>=0.4.6
|
|
26
|
+
Requires-Dist: numpy>=1.21
|
|
27
|
+
Requires-Dist: faster-whisper>=1.0.0
|
|
28
|
+
Requires-Dist: lameenc>=1.5.0
|
|
29
|
+
Requires-Dist: rumps>=0.4.0
|
|
30
|
+
Requires-Dist: PyQt6>=6.5.0
|
|
31
|
+
Requires-Dist: pyobjc-framework-Cocoa>=9.0; sys_platform == "darwin"
|
|
32
|
+
Requires-Dist: pyobjc-framework-Quartz>=9.0; sys_platform == "darwin"
|
|
33
|
+
Requires-Dist: pyobjc-framework-ScreenCaptureKit>=9.0; sys_platform == "darwin"
|
|
34
|
+
Requires-Dist: pyobjc-framework-AVFoundation>=9.0; sys_platform == "darwin"
|
|
35
|
+
Requires-Dist: pyobjc-framework-CoreMedia>=9.0; sys_platform == "darwin"
|
|
36
|
+
Requires-Dist: pyobjc-framework-libdispatch>=9.0; sys_platform == "darwin"
|
|
37
|
+
Provides-Extra: dev
|
|
38
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
39
|
+
Requires-Dist: pytest-cov; extra == "dev"
|
|
40
|
+
Requires-Dist: black; extra == "dev"
|
|
41
|
+
Requires-Dist: ruff; extra == "dev"
|
|
42
|
+
Requires-Dist: mypy; extra == "dev"
|
|
43
|
+
|
|
44
|
+
# Meeting Noter
|
|
45
|
+
|
|
46
|
+
Offline meeting transcription tool for macOS. Captures both your voice and meeting participants' audio, saves to MP3, and transcribes locally using Whisper.
|
|
47
|
+
|
|
48
|
+
## Features
|
|
49
|
+
|
|
50
|
+
- **No virtual audio devices needed** - Uses ScreenCaptureKit (like Notion, Discord)
|
|
51
|
+
- **Captures both sides** - Your mic + system audio (meeting participants)
|
|
52
|
+
- **Offline transcription** - Uses Whisper locally, no API calls
|
|
53
|
+
- **Auto-detection** - Detects active meetings (Zoom, Teams, Meet, Slack)
|
|
54
|
+
- **Multiple interfaces** - Menu bar app, desktop GUI, or CLI
|
|
55
|
+
- **Auto-segmentation** - One file per meeting (detects silence)
|
|
56
|
+
|
|
57
|
+
## Installation
|
|
58
|
+
|
|
59
|
+
### Option 1: Using pipx (Recommended)
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
# Install pipx if you don't have it
|
|
63
|
+
brew install pipx
|
|
64
|
+
pipx ensurepath
|
|
65
|
+
|
|
66
|
+
# Install meeting-noter
|
|
67
|
+
cd /path/to/meeting-noter
|
|
68
|
+
pipx install -e .
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Option 2: Using a virtual environment
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
cd /path/to/meeting-noter
|
|
75
|
+
python3 -m venv .venv
|
|
76
|
+
.venv/bin/pip install --upgrade pip
|
|
77
|
+
.venv/bin/pip install -e .
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Then add an alias in your `~/.zshrc`:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
echo 'alias meeting-noter="/path/to/meeting-noter/.venv/bin/meeting-noter"' >> ~/.zshrc
|
|
84
|
+
source ~/.zshrc
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Quick Start
|
|
88
|
+
|
|
89
|
+
### 1. One-time Setup
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
meeting-noter setup
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
This will request Screen Recording permission, which is needed to capture meeting participants' audio.
|
|
96
|
+
|
|
97
|
+
### 2. Launch the App
|
|
98
|
+
|
|
99
|
+
**Menu Bar App** (recommended):
|
|
100
|
+
```bash
|
|
101
|
+
meeting-noter menubar
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
**Desktop GUI**:
|
|
105
|
+
```bash
|
|
106
|
+
meeting-noter gui
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
**CLI Recording**:
|
|
110
|
+
```bash
|
|
111
|
+
meeting-noter start "Weekly Standup"
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### 3. Record a Meeting
|
|
115
|
+
|
|
116
|
+
- The menu bar app auto-detects meetings and prompts to record
|
|
117
|
+
- Or manually start recording via the GUI/CLI
|
|
118
|
+
- Press Ctrl+C (CLI) or click Stop to end recording
|
|
119
|
+
|
|
120
|
+
### 4. Transcribe
|
|
121
|
+
|
|
122
|
+
Recordings are auto-transcribed by default. Or manually:
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
# Transcribe the most recent recording
|
|
126
|
+
meeting-noter transcribe
|
|
127
|
+
|
|
128
|
+
# Transcribe a specific file
|
|
129
|
+
meeting-noter transcribe recording.mp3
|
|
130
|
+
|
|
131
|
+
# List all recordings
|
|
132
|
+
meeting-noter list
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Commands
|
|
136
|
+
|
|
137
|
+
| Command | Description |
|
|
138
|
+
|---------|-------------|
|
|
139
|
+
| `meeting-noter setup` | One-time setup (Screen Recording permission) |
|
|
140
|
+
| `meeting-noter menubar` | Launch menu bar app |
|
|
141
|
+
| `meeting-noter gui` | Launch desktop GUI |
|
|
142
|
+
| `meeting-noter start [name]` | Interactive CLI recording |
|
|
143
|
+
| `meeting-noter daemon` | Start background audio capture |
|
|
144
|
+
| `meeting-noter status` | Check daemon status |
|
|
145
|
+
| `meeting-noter stop` | Stop the daemon |
|
|
146
|
+
| `meeting-noter list` | List recent recordings |
|
|
147
|
+
| `meeting-noter transcribe` | Transcribe a recording |
|
|
148
|
+
| `meeting-noter devices` | List audio devices |
|
|
149
|
+
|
|
150
|
+
## Options
|
|
151
|
+
|
|
152
|
+
### `start`
|
|
153
|
+
- First argument: Meeting name (optional, auto-generates timestamp if omitted)
|
|
154
|
+
|
|
155
|
+
### `daemon`
|
|
156
|
+
- `-o, --output-dir`: Where to save recordings (default: `~/meetings`)
|
|
157
|
+
- `-f, --foreground`: Run in foreground instead of background
|
|
158
|
+
- `-n, --name`: Meeting name for the recording
|
|
159
|
+
|
|
160
|
+
### `transcribe`
|
|
161
|
+
- `-m, --model`: Whisper model size (tiny.en, base.en, small.en, medium.en, large-v3)
|
|
162
|
+
- `-l, --live`: Real-time transcription (experimental)
|
|
163
|
+
- `-o, --output-dir`: Directory with recordings
|
|
164
|
+
|
|
165
|
+
### `list`
|
|
166
|
+
- `-n, --limit`: Number of recordings to show
|
|
167
|
+
- `-o, --output-dir`: Directory with recordings
|
|
168
|
+
|
|
169
|
+
## How It Works
|
|
170
|
+
|
|
171
|
+
```
|
|
172
|
+
┌─────────────────────────────────────┐
|
|
173
|
+
│ Your Meeting App │
|
|
174
|
+
│ (Zoom/Teams/Meet/Slack) │
|
|
175
|
+
└──────────────────┬──────────────────┘
|
|
176
|
+
│
|
|
177
|
+
┌─────────────┴─────────────┐
|
|
178
|
+
▼ ▼
|
|
179
|
+
┌─────────┐ ┌─────────────┐
|
|
180
|
+
│ Mic │ │ System Audio│
|
|
181
|
+
│(default)│ │(ScreenCaptureKit)
|
|
182
|
+
└────┬────┘ └──────┬──────┘
|
|
183
|
+
│ │
|
|
184
|
+
└───────────┬───────────────┘
|
|
185
|
+
▼
|
|
186
|
+
┌─────────────┐
|
|
187
|
+
│Meeting Noter│
|
|
188
|
+
│ (capture) │
|
|
189
|
+
└──────┬──────┘
|
|
190
|
+
│
|
|
191
|
+
▼
|
|
192
|
+
~/meetings/2024-01-15_Weekly_Standup.mp3
|
|
193
|
+
│
|
|
194
|
+
▼ (auto or on-demand)
|
|
195
|
+
┌─────────────┐
|
|
196
|
+
│ Whisper │ (local)
|
|
197
|
+
└──────┬──────┘
|
|
198
|
+
│
|
|
199
|
+
▼
|
|
200
|
+
~/meetings/2024-01-15_Weekly_Standup.txt
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
## Permissions Required
|
|
204
|
+
|
|
205
|
+
1. **Microphone** - For capturing your voice
|
|
206
|
+
2. **Screen Recording** - For capturing system audio (meeting participants)
|
|
207
|
+
- Grant in: System Settings > Privacy & Security > Screen Recording
|
|
208
|
+
- This uses ScreenCaptureKit, the same API used by Notion, Discord, etc.
|
|
209
|
+
|
|
210
|
+
## Troubleshooting
|
|
211
|
+
|
|
212
|
+
### No system audio captured (only my voice)
|
|
213
|
+
|
|
214
|
+
Screen Recording permission not granted. Go to:
|
|
215
|
+
System Settings > Privacy & Security > Screen Recording
|
|
216
|
+
|
|
217
|
+
Enable the toggle for Terminal (or your IDE/app).
|
|
218
|
+
|
|
219
|
+
### Meeting not auto-detected
|
|
220
|
+
|
|
221
|
+
Meeting detection works for: Zoom, Microsoft Teams, Google Meet, Slack.
|
|
222
|
+
The meeting window must be open (not minimized).
|
|
223
|
+
|
|
224
|
+
### Transcription is slow
|
|
225
|
+
|
|
226
|
+
Use a smaller model:
|
|
227
|
+
|
|
228
|
+
```bash
|
|
229
|
+
meeting-noter transcribe --model tiny.en
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
Model sizes:
|
|
233
|
+
- `tiny.en` (~75MB) - Fastest, good for most cases
|
|
234
|
+
- `base.en` (~150MB) - Better accuracy
|
|
235
|
+
- `small.en` (~500MB) - High accuracy
|
|
236
|
+
- `medium.en` (~1.5GB) - Very high accuracy
|
|
237
|
+
- `large-v3` (~3GB) - Best accuracy
|
|
238
|
+
|
|
239
|
+
## Configuration
|
|
240
|
+
|
|
241
|
+
Config file: `~/.config/meeting-noter/config.json`
|
|
242
|
+
|
|
243
|
+
```json
|
|
244
|
+
{
|
|
245
|
+
"recordings_dir": "~/meetings",
|
|
246
|
+
"transcripts_dir": "~/meetings",
|
|
247
|
+
"whisper_model": "tiny.en",
|
|
248
|
+
"auto_transcribe": true,
|
|
249
|
+
"silence_timeout": 5,
|
|
250
|
+
"capture_system_audio": true
|
|
251
|
+
}
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
## Requirements
|
|
255
|
+
|
|
256
|
+
- macOS 12.3+ (for ScreenCaptureKit)
|
|
257
|
+
- Python 3.9+
|
|
258
|
+
|
|
259
|
+
## License
|
|
260
|
+
|
|
261
|
+
MIT
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
meeting_noter/__init__.py,sha256=bLOErRC3sfnQ4a4RyZUzUljZEikXy7zOiYYUz5GytPg,103
|
|
2
|
+
meeting_noter/__main__.py,sha256=6sSOqH1o3jvgvkVzsVKmF6-xVGcUAbNVQkRl2CrygdE,120
|
|
3
|
+
meeting_noter/cli.py,sha256=w4cBrvg58inkgbApK_z90csBfb4HWBuXveLdD3J1bc0,8540
|
|
4
|
+
meeting_noter/config.py,sha256=41LFBNp5o0IojYS5Hf0FJVIr7GNn7B5O1TJDE8SQkkk,5977
|
|
5
|
+
meeting_noter/daemon.py,sha256=o7U11WmdoKG58SLs70-vzS64kkjMTY5qN0F0bs0eApk,16239
|
|
6
|
+
meeting_noter/meeting_detector.py,sha256=I8zzSdSSmbfd3yyCOyzPL8AS-xSHttFCagrDE35qcho,9412
|
|
7
|
+
meeting_noter/menubar.py,sha256=Wmwaw-_f2Zky6-0DLC5Ql2o6_VBG1-sWacL85dNeGwU,15366
|
|
8
|
+
meeting_noter/audio/__init__.py,sha256=O7PU8CxHSHxMeHbc9Jdwt9kePLQzsPh81GQU7VHCtBY,44
|
|
9
|
+
meeting_noter/audio/capture.py,sha256=fDrT5oXfva8vdFlht9cv60NviKbksw2QeJ8eOtI19uE,6469
|
|
10
|
+
meeting_noter/audio/encoder.py,sha256=UT9yLbURsRfVafRbT-DRianSCx1fOEwWPLP-LULYqVo,5443
|
|
11
|
+
meeting_noter/audio/system_audio.py,sha256=jbHGjNCerI19weXap0a90Ik17lVTCT1hCEgRKYke-p8,13016
|
|
12
|
+
meeting_noter/gui/__init__.py,sha256=z5GxxaeXyjqyEa9ox0dQxuL5u_BART0bi7cI6rfntEI,103
|
|
13
|
+
meeting_noter/gui/__main__.py,sha256=A2HWdYod0bTgjQQIi21O7XpmgxLH36e_X0aygEUZLls,146
|
|
14
|
+
meeting_noter/gui/app.py,sha256=COUAWu_dR5HriNYxbE86CVGS1eGYqyteH2oUFN_YtYQ,1370
|
|
15
|
+
meeting_noter/gui/main_window.py,sha256=vSvNO86CHMgJf9Pem8AOdrqKyTV9ITp3W4nCoqIuAmI,1667
|
|
16
|
+
meeting_noter/gui/meetings_tab.py,sha256=pqXqMv5YvCj8H6yR_TF3SMzsIDMAxyLe2otbENbM2SY,12315
|
|
17
|
+
meeting_noter/gui/recording_tab.py,sha256=UlrPkUiOmkGgOKqVrfAVp4EyiY5sq86W49Y-YAhYexY,12521
|
|
18
|
+
meeting_noter/gui/settings_tab.py,sha256=NUQVKDdSpyNp_MVxPLw2dB93wxaD5VeBKiDtGS4CyoU,8446
|
|
19
|
+
meeting_noter/install/__init__.py,sha256=SX5vLFMrV8aBDEGW18jhaqBqJqnRXaeo0Ct7QVGDgvE,38
|
|
20
|
+
meeting_noter/install/macos.py,sha256=dO-86zbNKRtt0l4D8naVn7kFWjzI8TufWLWE3FRLHQ8,3400
|
|
21
|
+
meeting_noter/output/__init__.py,sha256=F7xPlOrqweZcPbZtDrhved1stBI59vnWnLYfGwdu6oY,31
|
|
22
|
+
meeting_noter/output/writer.py,sha256=zO8y6FAFUAp0EEtALY-M5e2Ja5P-hgV38JjcKW7c-bA,3017
|
|
23
|
+
meeting_noter/resources/__init__.py,sha256=yzHNxgypkuVDFZWv6xZjUygOVB_Equ9NNX_HGRvN7VM,43
|
|
24
|
+
meeting_noter/resources/icon.icns,sha256=zMWqXCq7pI5acS0tbekFgFDvLt66EKUBP5-5IgztwPM,35146
|
|
25
|
+
meeting_noter/resources/icon.png,sha256=nK0hM6CPMor_xXRFYURpm0muA2O7yzotyydUQx-ukyg,2390
|
|
26
|
+
meeting_noter/resources/icon_128.png,sha256=Wg27dIpFfMzv15HG6eQpSqg72jrbGBcqLkGjOfAptO4,1169
|
|
27
|
+
meeting_noter/resources/icon_16.png,sha256=GONIIZCP9FUMb_MWZWMz1_iWxsPCPOwW_XcrPF7W8KY,215
|
|
28
|
+
meeting_noter/resources/icon_256.png,sha256=nK0hM6CPMor_xXRFYURpm0muA2O7yzotyydUQx-ukyg,2390
|
|
29
|
+
meeting_noter/resources/icon_32.png,sha256=Bw6hJXqkd-d1OfDpFv2om7Stex7daedxnpXt32xR8Sg,344
|
|
30
|
+
meeting_noter/resources/icon_512.png,sha256=o7X3ngYcppcIAAk9AcfPx94MUmrsPRp0qBTpb9SzfX8,5369
|
|
31
|
+
meeting_noter/resources/icon_64.png,sha256=TqG7Awx3kK8YdiX1e_z1odZonosZyQI2trlkNZCzUoI,607
|
|
32
|
+
meeting_noter/transcription/__init__.py,sha256=7GY9diP06DzFyoli41wddbrPv5bVDzH35bmnWlIJev4,29
|
|
33
|
+
meeting_noter/transcription/engine.py,sha256=HK2J2QOBNIDm1MXW-gkagXP8C8cqUfK_WylHQD_LqOI,6320
|
|
34
|
+
meeting_noter-0.3.0.dist-info/METADATA,sha256=FhIlKw7Wg1tRRLsFBmrXkKLa8zf3L2csAtO1bQADbcQ,7952
|
|
35
|
+
meeting_noter-0.3.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
36
|
+
meeting_noter-0.3.0.dist-info/entry_points.txt,sha256=rKNhzjSF5-e3bLRr8LVe22FeiwcacXabCvNpoEXfu4I,56
|
|
37
|
+
meeting_noter-0.3.0.dist-info/top_level.txt,sha256=9Tuq04_0SXM0OXOHVbOHkHkB5tG3fqkrMrfzCMpbLpY,14
|
|
38
|
+
meeting_noter-0.3.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
meeting_noter
|