meeting-noter 1.0.0__py3-none-any.whl → 1.1.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 CHANGED
@@ -1,3 +1,3 @@
1
1
  """Meeting Noter - Offline meeting transcription with virtual audio devices."""
2
2
 
3
- __version__ = "0.1.0"
3
+ __version__ = "1.1.0"
meeting_noter/cli.py CHANGED
@@ -104,10 +104,46 @@ def _launch_gui_background():
104
104
  click.echo("Meeting Noter GUI launched.")
105
105
 
106
106
 
107
+ def _handle_version():
108
+ """Handle --version: show version, check for updates, auto-update if enabled."""
109
+ import subprocess
110
+
111
+ from meeting_noter.update_checker import check_for_update
112
+
113
+ config = get_config()
114
+
115
+ click.echo(f"meeting-noter {__version__}")
116
+
117
+ # Check for updates
118
+ click.echo("Checking for updates...", nl=False)
119
+ new_version = check_for_update()
120
+
121
+ if new_version:
122
+ click.echo(f" update available: {new_version}")
123
+
124
+ if config.auto_update:
125
+ click.echo(f"Auto-updating to {new_version}...")
126
+ result = subprocess.run(
127
+ ["pipx", "upgrade", "meeting-noter"],
128
+ capture_output=True,
129
+ text=True,
130
+ )
131
+ if result.returncode == 0:
132
+ click.echo(click.style(f"Updated to {new_version}", fg="green"))
133
+ else:
134
+ click.echo(click.style("Update failed. Run manually:", fg="yellow"))
135
+ click.echo(" pipx upgrade meeting-noter")
136
+ else:
137
+ click.echo("Run to update: pipx upgrade meeting-noter")
138
+ click.echo("Or enable auto-update: mn config auto-update true")
139
+ else:
140
+ click.echo(" up to date")
141
+
142
+
107
143
  @click.group(cls=SuggestGroup, invoke_without_command=True)
108
- @click.version_option(version=__version__)
144
+ @click.option("--version", "-V", is_flag=True, help="Show version and check for updates")
109
145
  @click.pass_context
110
- def cli(ctx):
146
+ def cli(ctx, version):
111
147
  """Meeting Noter - Offline meeting transcription.
112
148
 
113
149
  \b
@@ -124,6 +160,10 @@ def cli(ctx):
124
160
  meeting-noter config whisper-model base.en Set transcription model
125
161
  meeting-noter config auto-transcribe false Disable auto-transcribe
126
162
  """
163
+ if version:
164
+ _handle_version()
165
+ ctx.exit(0)
166
+
127
167
  if ctx.invoked_subcommand is None:
128
168
  # No subcommand - start background watcher
129
169
  ctx.invoke(watcher)
@@ -606,6 +646,7 @@ CONFIG_KEYS = {
606
646
  "transcripts-dir": ("transcripts_dir", "path", "Directory for transcripts"),
607
647
  "whisper-model": ("whisper_model", "choice:tiny.en,base.en,small.en,medium.en,large-v3", "Whisper model for transcription"),
608
648
  "auto-transcribe": ("auto_transcribe", "bool", "Auto-transcribe after recording"),
649
+ "auto-update": ("auto_update", "bool", "Auto-update when running --version"),
609
650
  "silence-timeout": ("silence_timeout", "int", "Minutes of silence before auto-stop"),
610
651
  "capture-system-audio": ("capture_system_audio", "bool", "Capture meeting participants via ScreenCaptureKit"),
611
652
  }
meeting_noter/config.py CHANGED
@@ -32,6 +32,7 @@ DEFAULT_CONFIG = {
32
32
  "transcripts_dir": str(Path.home() / "meetings"),
33
33
  "whisper_model": "tiny.en",
34
34
  "auto_transcribe": True,
35
+ "auto_update": True, # Auto-update when running --version
35
36
  "silence_timeout": 5, # Minutes of silence before stopping recording
36
37
  "capture_system_audio": True, # Capture other participants via ScreenCaptureKit
37
38
  "show_menubar": False,
@@ -139,6 +140,16 @@ class Config:
139
140
  """Set show menubar setting."""
140
141
  self._data["show_menubar"] = value
141
142
 
143
+ @property
144
+ def auto_update(self) -> bool:
145
+ """Get auto-update setting."""
146
+ return self._data.get("auto_update", True)
147
+
148
+ @auto_update.setter
149
+ def auto_update(self, value: bool) -> None:
150
+ """Set auto-update setting."""
151
+ self._data["auto_update"] = value
152
+
142
153
  @property
143
154
  def setup_complete(self) -> bool:
144
155
  """Check if setup has been completed."""
@@ -0,0 +1,65 @@
1
+ """Check for updates from PyPI."""
2
+
3
+ import threading
4
+ import urllib.request
5
+ import json
6
+ from typing import Optional, Tuple
7
+
8
+ from meeting_noter import __version__
9
+
10
+
11
+ PYPI_URL = "https://pypi.org/pypi/meeting-noter/json"
12
+
13
+
14
+ def parse_version(version: str) -> Tuple[int, ...]:
15
+ """Parse version string into tuple for comparison."""
16
+ try:
17
+ return tuple(int(x) for x in version.split("."))
18
+ except ValueError:
19
+ return (0, 0, 0)
20
+
21
+
22
+ def get_latest_version() -> Optional[str]:
23
+ """Fetch the latest version from PyPI."""
24
+ try:
25
+ req = urllib.request.Request(
26
+ PYPI_URL,
27
+ headers={"Accept": "application/json", "User-Agent": "meeting-noter"}
28
+ )
29
+ with urllib.request.urlopen(req, timeout=5) as response:
30
+ data = json.loads(response.read().decode())
31
+ return data.get("info", {}).get("version")
32
+ except Exception:
33
+ return None
34
+
35
+
36
+ def check_for_update() -> Optional[str]:
37
+ """Check if an update is available.
38
+
39
+ Returns the new version string if an update is available, None otherwise.
40
+ """
41
+ latest = get_latest_version()
42
+ if not latest:
43
+ return None
44
+
45
+ current = parse_version(__version__)
46
+ latest_parsed = parse_version(latest)
47
+
48
+ if latest_parsed > current:
49
+ return latest
50
+ return None
51
+
52
+
53
+ def check_for_update_async(callback):
54
+ """Check for updates in a background thread.
55
+
56
+ Args:
57
+ callback: Function to call with the new version string (or None if no update).
58
+ """
59
+ def _check():
60
+ new_version = check_for_update()
61
+ if new_version:
62
+ callback(new_version)
63
+
64
+ thread = threading.Thread(target=_check, daemon=True)
65
+ thread.start()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: meeting-noter
3
- Version: 1.0.0
3
+ Version: 1.1.0
4
4
  Summary: Offline meeting transcription for macOS with automatic meeting detection
5
5
  Author: Victor
6
6
  License: MIT
@@ -1,11 +1,12 @@
1
- meeting_noter/__init__.py,sha256=bLOErRC3sfnQ4a4RyZUzUljZEikXy7zOiYYUz5GytPg,103
1
+ meeting_noter/__init__.py,sha256=OuitWUFUeSb7eg41mnnDD6QiJrcGVpXpdhDzyxtCmmU,103
2
2
  meeting_noter/__main__.py,sha256=6sSOqH1o3jvgvkVzsVKmF6-xVGcUAbNVQkRl2CrygdE,120
3
- meeting_noter/cli.py,sha256=brLJ_2kuqEi5wq868hFaMGtKPSkyBpD-OS1u27rJb1k,32859
4
- meeting_noter/config.py,sha256=41LFBNp5o0IojYS5Hf0FJVIr7GNn7B5O1TJDE8SQkkk,5977
3
+ meeting_noter/cli.py,sha256=BAoUnQ-FPgwTp6-ON6WQkO-7Biwk1mYhwFaGdchHGHQ,34267
4
+ meeting_noter/config.py,sha256=_Jy-gZDTCN3TwH5fiLEWk-qFhoSGISO2xDQmACV_zRc,6334
5
5
  meeting_noter/daemon.py,sha256=u9VrYe94o3lxabuIS9MDVPHSH7MqKqzTqGTuA7TNAIc,19767
6
6
  meeting_noter/meeting_detector.py,sha256=St0qoMkvUERP4BaxnXO1M6fZDJpWqBf9In7z2SgWcWg,10564
7
7
  meeting_noter/menubar.py,sha256=Gn6p8y5jA_HCWf1T3ademxH-vndpONHkf9vUlKs6XEo,14379
8
8
  meeting_noter/mic_monitor.py,sha256=P8vF4qaZcGrEzzJyVos78Vuf38NXHGNRREDsD-HyBHc,16211
9
+ meeting_noter/update_checker.py,sha256=sMmIiiZJL6K7wqLWE64Aj4hS8uspjUOirr6BG_IlL1I,1701
9
10
  meeting_noter/audio/__init__.py,sha256=O7PU8CxHSHxMeHbc9Jdwt9kePLQzsPh81GQU7VHCtBY,44
10
11
  meeting_noter/audio/capture.py,sha256=fDrT5oXfva8vdFlht9cv60NviKbksw2QeJ8eOtI19uE,6469
11
12
  meeting_noter/audio/encoder.py,sha256=OBsgUmlZPz-YZQZ7Rp8MAlMRaQxTsccjuTgCtvRebmc,6573
@@ -33,8 +34,8 @@ meeting_noter/resources/icon_64.png,sha256=TqG7Awx3kK8YdiX1e_z1odZonosZyQI2trlkN
33
34
  meeting_noter/transcription/__init__.py,sha256=7GY9diP06DzFyoli41wddbrPv5bVDzH35bmnWlIJev4,29
34
35
  meeting_noter/transcription/engine.py,sha256=G9NcSS6Q-UhW7PlQ0E85hQXn6BWao64nIvyw4NR2yxI,7208
35
36
  meeting_noter/transcription/live_transcription.py,sha256=AslB1T1_gxu7eSp7xc79_2SdfGrNJq7L_8bA1t6YoU4,9277
36
- meeting_noter-1.0.0.dist-info/METADATA,sha256=m7Pi8_-haGOHX0DbA7YXTc1KMpMWAxLHHgbE3kB3-FM,6995
37
- meeting_noter-1.0.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
38
- meeting_noter-1.0.0.dist-info/entry_points.txt,sha256=osZoOmm-UBPCJ4b6DGH6JOAm7mofM2fK06eK6blplmg,83
39
- meeting_noter-1.0.0.dist-info/top_level.txt,sha256=9Tuq04_0SXM0OXOHVbOHkHkB5tG3fqkrMrfzCMpbLpY,14
40
- meeting_noter-1.0.0.dist-info/RECORD,,
37
+ meeting_noter-1.1.0.dist-info/METADATA,sha256=bGwwBq9AeFcxYKf0vpD3WtENmibBNLl4mzf1fLDyiVs,6995
38
+ meeting_noter-1.1.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
39
+ meeting_noter-1.1.0.dist-info/entry_points.txt,sha256=osZoOmm-UBPCJ4b6DGH6JOAm7mofM2fK06eK6blplmg,83
40
+ meeting_noter-1.1.0.dist-info/top_level.txt,sha256=9Tuq04_0SXM0OXOHVbOHkHkB5tG3fqkrMrfzCMpbLpY,14
41
+ meeting_noter-1.1.0.dist-info/RECORD,,