meeting-noter 1.1.0__tar.gz → 1.2.0__tar.gz

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.

Files changed (52) hide show
  1. {meeting_noter-1.1.0 → meeting_noter-1.2.0}/PKG-INFO +1 -3
  2. {meeting_noter-1.1.0 → meeting_noter-1.2.0}/pyproject.toml +1 -5
  3. {meeting_noter-1.1.0 → meeting_noter-1.2.0}/src/meeting_noter/__init__.py +1 -1
  4. {meeting_noter-1.1.0 → meeting_noter-1.2.0}/src/meeting_noter/cli.py +3 -97
  5. {meeting_noter-1.1.0 → meeting_noter-1.2.0}/src/meeting_noter.egg-info/PKG-INFO +1 -3
  6. {meeting_noter-1.1.0 → meeting_noter-1.2.0}/src/meeting_noter.egg-info/SOURCES.txt +0 -8
  7. {meeting_noter-1.1.0 → meeting_noter-1.2.0}/src/meeting_noter.egg-info/requires.txt +0 -2
  8. {meeting_noter-1.1.0 → meeting_noter-1.2.0}/tests/test_cli.py +1 -52
  9. meeting_noter-1.1.0/src/meeting_noter/gui/__init__.py +0 -5
  10. meeting_noter-1.1.0/src/meeting_noter/gui/__main__.py +0 -6
  11. meeting_noter-1.1.0/src/meeting_noter/gui/app.py +0 -53
  12. meeting_noter-1.1.0/src/meeting_noter/gui/main_window.py +0 -50
  13. meeting_noter-1.1.0/src/meeting_noter/gui/meetings_tab.py +0 -348
  14. meeting_noter-1.1.0/src/meeting_noter/gui/recording_tab.py +0 -358
  15. meeting_noter-1.1.0/src/meeting_noter/gui/settings_tab.py +0 -249
  16. meeting_noter-1.1.0/src/meeting_noter/menubar.py +0 -411
  17. {meeting_noter-1.1.0 → meeting_noter-1.2.0}/README.md +0 -0
  18. {meeting_noter-1.1.0 → meeting_noter-1.2.0}/setup.cfg +0 -0
  19. {meeting_noter-1.1.0 → meeting_noter-1.2.0}/src/meeting_noter/__main__.py +0 -0
  20. {meeting_noter-1.1.0 → meeting_noter-1.2.0}/src/meeting_noter/audio/__init__.py +0 -0
  21. {meeting_noter-1.1.0 → meeting_noter-1.2.0}/src/meeting_noter/audio/capture.py +0 -0
  22. {meeting_noter-1.1.0 → meeting_noter-1.2.0}/src/meeting_noter/audio/encoder.py +0 -0
  23. {meeting_noter-1.1.0 → meeting_noter-1.2.0}/src/meeting_noter/audio/system_audio.py +0 -0
  24. {meeting_noter-1.1.0 → meeting_noter-1.2.0}/src/meeting_noter/config.py +0 -0
  25. {meeting_noter-1.1.0 → meeting_noter-1.2.0}/src/meeting_noter/daemon.py +0 -0
  26. {meeting_noter-1.1.0 → meeting_noter-1.2.0}/src/meeting_noter/install/__init__.py +0 -0
  27. {meeting_noter-1.1.0 → meeting_noter-1.2.0}/src/meeting_noter/install/macos.py +0 -0
  28. {meeting_noter-1.1.0 → meeting_noter-1.2.0}/src/meeting_noter/meeting_detector.py +0 -0
  29. {meeting_noter-1.1.0 → meeting_noter-1.2.0}/src/meeting_noter/mic_monitor.py +0 -0
  30. {meeting_noter-1.1.0 → meeting_noter-1.2.0}/src/meeting_noter/output/__init__.py +0 -0
  31. {meeting_noter-1.1.0 → meeting_noter-1.2.0}/src/meeting_noter/output/writer.py +0 -0
  32. {meeting_noter-1.1.0 → meeting_noter-1.2.0}/src/meeting_noter/resources/__init__.py +0 -0
  33. {meeting_noter-1.1.0 → meeting_noter-1.2.0}/src/meeting_noter/resources/icon.icns +0 -0
  34. {meeting_noter-1.1.0 → meeting_noter-1.2.0}/src/meeting_noter/resources/icon.png +0 -0
  35. {meeting_noter-1.1.0 → meeting_noter-1.2.0}/src/meeting_noter/resources/icon_128.png +0 -0
  36. {meeting_noter-1.1.0 → meeting_noter-1.2.0}/src/meeting_noter/resources/icon_16.png +0 -0
  37. {meeting_noter-1.1.0 → meeting_noter-1.2.0}/src/meeting_noter/resources/icon_256.png +0 -0
  38. {meeting_noter-1.1.0 → meeting_noter-1.2.0}/src/meeting_noter/resources/icon_32.png +0 -0
  39. {meeting_noter-1.1.0 → meeting_noter-1.2.0}/src/meeting_noter/resources/icon_512.png +0 -0
  40. {meeting_noter-1.1.0 → meeting_noter-1.2.0}/src/meeting_noter/resources/icon_64.png +0 -0
  41. {meeting_noter-1.1.0 → meeting_noter-1.2.0}/src/meeting_noter/transcription/__init__.py +0 -0
  42. {meeting_noter-1.1.0 → meeting_noter-1.2.0}/src/meeting_noter/transcription/engine.py +0 -0
  43. {meeting_noter-1.1.0 → meeting_noter-1.2.0}/src/meeting_noter/transcription/live_transcription.py +0 -0
  44. {meeting_noter-1.1.0 → meeting_noter-1.2.0}/src/meeting_noter/update_checker.py +0 -0
  45. {meeting_noter-1.1.0 → meeting_noter-1.2.0}/src/meeting_noter.egg-info/dependency_links.txt +0 -0
  46. {meeting_noter-1.1.0 → meeting_noter-1.2.0}/src/meeting_noter.egg-info/entry_points.txt +0 -0
  47. {meeting_noter-1.1.0 → meeting_noter-1.2.0}/src/meeting_noter.egg-info/top_level.txt +0 -0
  48. {meeting_noter-1.1.0 → meeting_noter-1.2.0}/tests/test_config.py +0 -0
  49. {meeting_noter-1.1.0 → meeting_noter-1.2.0}/tests/test_daemon.py +0 -0
  50. {meeting_noter-1.1.0 → meeting_noter-1.2.0}/tests/test_meeting_detector.py +0 -0
  51. {meeting_noter-1.1.0 → meeting_noter-1.2.0}/tests/test_mic_monitor.py +0 -0
  52. {meeting_noter-1.1.0 → meeting_noter-1.2.0}/tests/test_output_writer.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: meeting-noter
3
- Version: 1.1.0
3
+ Version: 1.2.0
4
4
  Summary: Offline meeting transcription for macOS with automatic meeting detection
5
5
  Author: Victor
6
6
  License: MIT
@@ -26,8 +26,6 @@ Requires-Dist: click>=8.0
26
26
  Requires-Dist: sounddevice>=0.4.6
27
27
  Requires-Dist: numpy>=1.24
28
28
  Requires-Dist: faster-whisper>=1.0.0
29
- Requires-Dist: rumps>=0.4.0
30
- Requires-Dist: PyQt6>=6.5.0
31
29
  Requires-Dist: imageio-ffmpeg>=0.4.9
32
30
  Requires-Dist: pyobjc-framework-Cocoa>=9.0; sys_platform == "darwin"
33
31
  Requires-Dist: pyobjc-framework-Quartz>=9.0; sys_platform == "darwin"
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "meeting-noter"
7
- version = "1.1.0"
7
+ version = "1.2.0"
8
8
  description = "Offline meeting transcription for macOS with automatic meeting detection"
9
9
  readme = "README.md"
10
10
  license = {text = "MIT"}
@@ -33,8 +33,6 @@ dependencies = [
33
33
  "sounddevice>=0.4.6",
34
34
  "numpy>=1.24",
35
35
  "faster-whisper>=1.0.0",
36
- "rumps>=0.4.0",
37
- "PyQt6>=6.5.0",
38
36
  "imageio-ffmpeg>=0.4.9", # Bundles ffmpeg binary for MP3 encoding
39
37
  "pyobjc-framework-Cocoa>=9.0; sys_platform == 'darwin'",
40
38
  "pyobjc-framework-Quartz>=9.0; sys_platform == 'darwin'",
@@ -98,8 +96,6 @@ filterwarnings = [
98
96
  source = ["src/meeting_noter"]
99
97
  branch = true
100
98
  omit = [
101
- "*/gui/*",
102
- "*/menubar.py",
103
99
  "*/__main__.py",
104
100
  "*/audio/system_audio.py", # ScreenCaptureKit-based, requires macOS runtime
105
101
  ]
@@ -1,3 +1,3 @@
1
1
  """Meeting Noter - Offline meeting transcription with virtual audio devices."""
2
2
 
3
- __version__ = "1.1.0"
3
+ __version__ = "1.2.0"
@@ -90,20 +90,6 @@ class SuggestCommand(click.Command):
90
90
  SuggestGroup.command_class = SuggestCommand
91
91
 
92
92
 
93
- def _launch_gui_background():
94
- """Launch the GUI in background and return immediately."""
95
- import subprocess
96
- import sys
97
-
98
- subprocess.Popen(
99
- [sys.executable, "-m", "meeting_noter.gui"],
100
- stdout=subprocess.DEVNULL,
101
- stderr=subprocess.DEVNULL,
102
- start_new_session=True,
103
- )
104
- click.echo("Meeting Noter GUI launched.")
105
-
106
-
107
93
  def _handle_version():
108
94
  """Handle --version: show version, check for updates, auto-update if enabled."""
109
95
  import subprocess
@@ -290,17 +276,6 @@ def status():
290
276
  except (ProcessLookupError, ValueError, FileNotFoundError):
291
277
  pass
292
278
 
293
- # Check menubar
294
- menubar_running = False
295
- menubar_pid_file = Path.home() / ".meeting-noter-menubar.pid"
296
- if menubar_pid_file.exists():
297
- try:
298
- pid = int(menubar_pid_file.read_text().strip())
299
- os.kill(pid, 0)
300
- menubar_running = True
301
- except (ProcessLookupError, ValueError, FileNotFoundError):
302
- pass
303
-
304
279
  # Check daemon (recording)
305
280
  daemon_running = False
306
281
  daemon_pid = read_pid_file(DEFAULT_PID_FILE)
@@ -313,9 +288,8 @@ def status():
313
288
  # Get current recording name from log
314
289
  recording_name = _get_current_recording_name()
315
290
  click.echo(f"🔴 Recording: {recording_name or 'In progress'}")
316
- elif watcher_running or menubar_running:
317
- mode = "watcher" if watcher_running else "menubar"
318
- click.echo(f"👀 Ready to record ({mode} active)")
291
+ elif watcher_running:
292
+ click.echo("👀 Ready to record (watcher active)")
319
293
  else:
320
294
  click.echo("⏹️ Stopped (run 'meeting-noter' to start)")
321
295
 
@@ -324,7 +298,6 @@ def status():
324
298
  # Show details
325
299
  click.echo("Components:")
326
300
  click.echo(f" Watcher: {'running' if watcher_running else 'stopped'}")
327
- click.echo(f" Menubar: {'running' if menubar_running else 'stopped'}")
328
301
  click.echo(f" Recorder: {'recording' if daemon_running else 'idle'}")
329
302
  click.echo()
330
303
 
@@ -358,7 +331,7 @@ def _get_current_recording_name() -> str | None:
358
331
 
359
332
  @cli.command()
360
333
  def shutdown():
361
- """Stop all Meeting Noter processes (daemon, watcher, menubar, GUI)."""
334
+ """Stop all Meeting Noter processes (daemon, watcher)."""
362
335
  import subprocess
363
336
  import os
364
337
  import signal
@@ -381,17 +354,6 @@ def shutdown():
381
354
  except (ProcessLookupError, ValueError):
382
355
  WATCHER_PID_FILE.unlink(missing_ok=True)
383
356
 
384
- # Stop menubar
385
- menubar_pid_file = Path.home() / ".meeting-noter-menubar.pid"
386
- if menubar_pid_file.exists():
387
- try:
388
- pid = int(menubar_pid_file.read_text().strip())
389
- os.kill(pid, signal.SIGTERM)
390
- menubar_pid_file.unlink()
391
- stopped.append("menubar")
392
- except (ProcessLookupError, ValueError):
393
- menubar_pid_file.unlink(missing_ok=True)
394
-
395
357
  # Kill any remaining meeting-noter processes
396
358
  result = subprocess.run(
397
359
  ["pkill", "-f", "meeting_noter"],
@@ -584,62 +546,6 @@ def live():
584
546
  click.echo("\n" + click.style("Stopped watching.", fg="cyan"))
585
547
 
586
548
 
587
- @cli.command()
588
- @click.option(
589
- "--foreground", "-f",
590
- is_flag=True,
591
- help="Run in foreground instead of background",
592
- )
593
- @require_setup
594
- def menubar(foreground: bool):
595
- """Launch menu bar app for daemon control.
596
-
597
- Adds a menu bar icon for one-click start/stop of the recording daemon.
598
- The icon shows "MN" when idle and "MN [filename]" when recording.
599
-
600
- By default, runs in background. Use -f for foreground (debugging).
601
- """
602
- import subprocess
603
- import sys
604
-
605
- if foreground:
606
- from meeting_noter.menubar import run_menubar
607
- run_menubar()
608
- else:
609
- # Spawn as background process
610
- subprocess.Popen(
611
- [sys.executable, "-m", "meeting_noter.menubar"],
612
- stdout=subprocess.DEVNULL,
613
- stderr=subprocess.DEVNULL,
614
- start_new_session=True,
615
- )
616
- click.echo("Menu bar app started in background.")
617
-
618
-
619
- @cli.command()
620
- @click.option(
621
- "--foreground", "-f",
622
- is_flag=True,
623
- help="Run in foreground instead of background",
624
- )
625
- @require_setup
626
- def gui(foreground: bool):
627
- """Launch the desktop GUI application.
628
-
629
- Opens a window with tabs for:
630
- - Recording: Start/stop recordings with meeting names
631
- - Meetings: Browse, play, and manage recordings
632
- - Settings: Configure directories, models, and preferences
633
-
634
- By default runs in background. Use -f for foreground.
635
- """
636
- if foreground:
637
- from meeting_noter.gui import run_gui
638
- run_gui()
639
- else:
640
- _launch_gui_background()
641
-
642
-
643
549
  # Config key mappings (CLI name -> config attribute)
644
550
  CONFIG_KEYS = {
645
551
  "recordings-dir": ("recordings_dir", "path", "Directory for audio recordings"),
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: meeting-noter
3
- Version: 1.1.0
3
+ Version: 1.2.0
4
4
  Summary: Offline meeting transcription for macOS with automatic meeting detection
5
5
  Author: Victor
6
6
  License: MIT
@@ -26,8 +26,6 @@ Requires-Dist: click>=8.0
26
26
  Requires-Dist: sounddevice>=0.4.6
27
27
  Requires-Dist: numpy>=1.24
28
28
  Requires-Dist: faster-whisper>=1.0.0
29
- Requires-Dist: rumps>=0.4.0
30
- Requires-Dist: PyQt6>=6.5.0
31
29
  Requires-Dist: imageio-ffmpeg>=0.4.9
32
30
  Requires-Dist: pyobjc-framework-Cocoa>=9.0; sys_platform == "darwin"
33
31
  Requires-Dist: pyobjc-framework-Quartz>=9.0; sys_platform == "darwin"
@@ -6,7 +6,6 @@ src/meeting_noter/cli.py
6
6
  src/meeting_noter/config.py
7
7
  src/meeting_noter/daemon.py
8
8
  src/meeting_noter/meeting_detector.py
9
- src/meeting_noter/menubar.py
10
9
  src/meeting_noter/mic_monitor.py
11
10
  src/meeting_noter/update_checker.py
12
11
  src/meeting_noter.egg-info/PKG-INFO
@@ -19,13 +18,6 @@ src/meeting_noter/audio/__init__.py
19
18
  src/meeting_noter/audio/capture.py
20
19
  src/meeting_noter/audio/encoder.py
21
20
  src/meeting_noter/audio/system_audio.py
22
- src/meeting_noter/gui/__init__.py
23
- src/meeting_noter/gui/__main__.py
24
- src/meeting_noter/gui/app.py
25
- src/meeting_noter/gui/main_window.py
26
- src/meeting_noter/gui/meetings_tab.py
27
- src/meeting_noter/gui/recording_tab.py
28
- src/meeting_noter/gui/settings_tab.py
29
21
  src/meeting_noter/install/__init__.py
30
22
  src/meeting_noter/install/macos.py
31
23
  src/meeting_noter/output/__init__.py
@@ -2,8 +2,6 @@ click>=8.0
2
2
  sounddevice>=0.4.6
3
3
  numpy>=1.24
4
4
  faster-whisper>=1.0.0
5
- rumps>=0.4.0
6
- PyQt6>=6.5.0
7
5
  imageio-ffmpeg>=0.4.9
8
6
 
9
7
  [:sys_platform == "darwin"]
@@ -28,7 +28,7 @@ class TestCliHelp:
28
28
  result = cli_runner.invoke(cli, ["--version"])
29
29
 
30
30
  assert result.exit_code == 0
31
- assert "version" in result.output.lower()
31
+ assert "meeting-noter" in result.output.lower()
32
32
 
33
33
 
34
34
  class TestStartCommand:
@@ -271,33 +271,6 @@ class TestSuggestCommand:
271
271
  # Should mention available options or suggest
272
272
 
273
273
 
274
- class TestMenubarCommand:
275
- """Tests for the menubar command."""
276
-
277
- def test_menubar_background(self, cli_runner: CliRunner, mock_config, mocker):
278
- """menubar should launch in background by default."""
279
- mock_popen = mocker.patch("subprocess.Popen")
280
-
281
- result = cli_runner.invoke(cli, ["menubar"])
282
-
283
- assert result.exit_code == 0
284
- assert mock_popen.called
285
- assert "background" in result.output.lower()
286
-
287
-
288
- class TestGuiCommand:
289
- """Tests for the gui command."""
290
-
291
- def test_gui_background(self, cli_runner: CliRunner, mock_config, mocker):
292
- """gui should launch in background by default."""
293
- mock_popen = mocker.patch("subprocess.Popen")
294
-
295
- result = cli_runner.invoke(cli, ["gui"])
296
-
297
- assert result.exit_code == 0
298
- assert mock_popen.called
299
-
300
-
301
274
  class TestWatcherCommand:
302
275
  """Tests for the watcher command."""
303
276
 
@@ -749,30 +722,6 @@ class TestOpenCommandExtended:
749
722
  assert new_dir.exists()
750
723
 
751
724
 
752
- class TestMenubarForeground:
753
- """Tests for menubar command in foreground mode."""
754
-
755
- def test_menubar_foreground(self, cli_runner: CliRunner, mock_config, mocker):
756
- """menubar --foreground should run in foreground."""
757
- mock_run_menubar = mocker.patch("meeting_noter.menubar.run_menubar")
758
-
759
- result = cli_runner.invoke(cli, ["menubar", "--foreground"])
760
-
761
- mock_run_menubar.assert_called_once()
762
-
763
-
764
- class TestGuiForeground:
765
- """Tests for gui command in foreground mode."""
766
-
767
- def test_gui_foreground(self, cli_runner: CliRunner, mock_config, mocker):
768
- """gui --foreground should run in foreground."""
769
- mock_run_gui = mocker.patch("meeting_noter.gui.run_gui")
770
-
771
- result = cli_runner.invoke(cli, ["gui", "--foreground"])
772
-
773
- mock_run_gui.assert_called_once()
774
-
775
-
776
725
  class TestConfigInvalidPath:
777
726
  """Tests for config command with invalid paths."""
778
727
 
@@ -1,5 +0,0 @@
1
- """Desktop GUI for Meeting Noter."""
2
-
3
- from meeting_noter.gui.app import run_gui
4
-
5
- __all__ = ["run_gui"]
@@ -1,6 +0,0 @@
1
- """Allow running gui as a module: python -m meeting_noter.gui"""
2
-
3
- from meeting_noter.gui import run_gui
4
-
5
- if __name__ == "__main__":
6
- run_gui()
@@ -1,53 +0,0 @@
1
- """PyQt6 application entry point."""
2
-
3
- from __future__ import annotations
4
-
5
- import sys
6
- from pathlib import Path
7
-
8
- from PyQt6.QtGui import QIcon
9
- from PyQt6.QtWidgets import QApplication
10
-
11
- from meeting_noter.gui.main_window import MainWindow
12
-
13
-
14
- def _set_macos_dock_icon(icon_path: Path):
15
- """Set the macOS dock icon using AppKit."""
16
- try:
17
- from AppKit import NSApplication, NSImage
18
- ns_app = NSApplication.sharedApplication()
19
- icon = NSImage.alloc().initWithContentsOfFile_(str(icon_path))
20
- if icon:
21
- ns_app.setApplicationIconImage_(icon)
22
- except ImportError:
23
- pass # pyobjc not installed
24
-
25
-
26
- def run_gui():
27
- """Launch the Meeting Noter GUI application."""
28
- resources = Path(__file__).parent.parent / "resources"
29
-
30
- app = QApplication(sys.argv)
31
- app.setApplicationName("Meeting Noter")
32
- app.setOrganizationName("Meeting Noter")
33
-
34
- # Set window icon
35
- icon_path = resources / "icon.png"
36
- if icon_path.exists():
37
- app.setWindowIcon(QIcon(str(icon_path)))
38
-
39
- window = MainWindow()
40
- window.show()
41
-
42
- # Set macOS dock icon AFTER window is shown
43
- if sys.platform == "darwin":
44
- icns_path = resources / "icon.icns"
45
- if icns_path.exists():
46
- _set_macos_dock_icon(icns_path)
47
- app.processEvents()
48
-
49
- sys.exit(app.exec())
50
-
51
-
52
- if __name__ == "__main__":
53
- run_gui()
@@ -1,50 +0,0 @@
1
- """Main window with tab interface."""
2
-
3
- from __future__ import annotations
4
-
5
- from PyQt6.QtWidgets import QMainWindow, QTabWidget, QWidget, QVBoxLayout
6
-
7
- from meeting_noter.gui.recording_tab import RecordingTab
8
- from meeting_noter.gui.meetings_tab import MeetingsTab
9
- from meeting_noter.gui.settings_tab import SettingsTab
10
-
11
-
12
- class MainWindow(QMainWindow):
13
- """Main application window with tabbed interface."""
14
-
15
- def __init__(self):
16
- super().__init__()
17
- self.setWindowTitle("Meeting Noter")
18
- self.setMinimumSize(800, 600)
19
-
20
- # Create central widget with tabs
21
- central_widget = QWidget()
22
- self.setCentralWidget(central_widget)
23
-
24
- layout = QVBoxLayout(central_widget)
25
- layout.setContentsMargins(0, 0, 0, 0)
26
-
27
- # Create tab widget
28
- self.tabs = QTabWidget()
29
- layout.addWidget(self.tabs)
30
-
31
- # Create tabs
32
- self.recording_tab = RecordingTab()
33
- self.meetings_tab = MeetingsTab()
34
- self.settings_tab = SettingsTab()
35
-
36
- self.tabs.addTab(self.recording_tab, "Record")
37
- self.tabs.addTab(self.meetings_tab, "Meetings")
38
- self.tabs.addTab(self.settings_tab, "Settings")
39
-
40
- # Connect settings changes to refresh meetings list
41
- self.settings_tab.settings_saved.connect(self.meetings_tab.refresh)
42
-
43
- # Connect recording completion to refresh meetings list
44
- self.recording_tab.recording_saved.connect(self.meetings_tab.refresh)
45
-
46
- def closeEvent(self, event):
47
- """Handle window close - stop any active recording."""
48
- if self.recording_tab.is_recording:
49
- self.recording_tab.stop_recording()
50
- event.accept()