From 277ccc7ea55db547fe99704ab7211adb7f07c77d Mon Sep 17 00:00:00 2001 From: openhands Date: Wed, 25 Jun 2025 19:57:16 +0000 Subject: [PATCH] Add mobile app companion for Voice Copilot - Created Flask API server with /health, /status, and /transcribe endpoints - Built React Native mobile app with large microphone button and recording - Added combined service to run desktop hotkeys + API server together - Implemented local WiFi and internet (ngrok) connectivity options - Added helper scripts for network setup and ngrok integration - Created comprehensive setup guides and documentation - Added API testing and demo scripts - Updated dependencies and project configuration Features: - Large, intuitive microphone button for easy recording - Real-time recording feedback and connection status - Server URL configuration and connection testing - Professional Material Design UI - Cross-platform support (iOS/Android via Expo) - Local network and internet access via ngrok - Integration with existing Whisper transcription engine - Complete documentation and setup guides --- MOBILE_APP_SUMMARY.md | 235 +++++++++++ MOBILE_SETUP.md | 301 ++++++++++++++ README.md | 78 +++- demo.py | 176 +++++++++ mobile-app/App.js | 449 +++++++++++++++++++++ mobile-app/README.md | 160 ++++++++ mobile-app/app.json | 37 ++ mobile-app/assets/README.md | 53 +++ mobile-app/babel.config.js | 6 + mobile-app/package.json | 26 ++ poetry.lock | 548 +++++++++++++++++++++++++- pyproject.toml | 6 +- scripts/get-local-ip.py | 133 +++++++ scripts/start-with-ngrok.sh | 144 +++++++ test_api.py | 108 +++++ voice_transcriber/api_server.py | 179 +++++++++ voice_transcriber/combined_service.py | 129 ++++++ 17 files changed, 2750 insertions(+), 18 deletions(-) create mode 100644 MOBILE_APP_SUMMARY.md create mode 100644 MOBILE_SETUP.md create mode 100755 demo.py create mode 100644 mobile-app/App.js create mode 100644 mobile-app/README.md create mode 100644 mobile-app/app.json create mode 100644 mobile-app/assets/README.md create mode 100644 mobile-app/babel.config.js create mode 100644 mobile-app/package.json create mode 100755 scripts/get-local-ip.py create mode 100755 scripts/start-with-ngrok.sh create mode 100644 test_api.py create mode 100644 voice_transcriber/api_server.py create mode 100644 voice_transcriber/combined_service.py diff --git a/MOBILE_APP_SUMMARY.md b/MOBILE_APP_SUMMARY.md new file mode 100644 index 0000000..050132e --- /dev/null +++ b/MOBILE_APP_SUMMARY.md @@ -0,0 +1,235 @@ +# Voice Copilot Mobile App - Implementation Summary + +## What Was Built + +I've successfully created a complete mobile app companion for your Voice Copilot service. Here's what was implemented: + +### ๐Ÿ–ฅ๏ธ Server-Side Components + +1. **API Server** (`voice_transcriber/api_server.py`) + - Flask-based REST API + - Handles audio file uploads + - Integrates with existing Whisper transcription + - CORS enabled for mobile access + - Health and status endpoints + +2. **Combined Service** (`voice_transcriber/combined_service.py`) + - Runs both desktop hotkeys AND API server + - Single command to start everything + - Shared transcription engine + +3. **Helper Scripts** + - `scripts/start-with-ngrok.sh` - Easy ngrok setup + - `scripts/get-local-ip.py` - Network configuration helper + - `test_api.py` - API testing script + - `demo.py` - Complete demo setup + +### ๐Ÿ“ฑ Mobile App Components + +1. **React Native App** (`mobile-app/App.js`) + - Large, intuitive microphone button + - Real-time recording feedback + - Connection status indicator + - Server URL configuration + - Transcription results display + - Material Design UI + +2. **Expo Configuration** + - Cross-platform (iOS/Android) + - Audio recording permissions + - Professional app structure + - Easy development workflow + +### ๐ŸŒ Network Support + +**Local WiFi:** +- Direct connection to your computer +- Fast and private +- No external dependencies + +**Internet via ngrok:** +- Access from anywhere +- Automatic tunnel setup +- Handles firewall/NAT issues + +## Key Features + +### Mobile App Features +- ๐ŸŽค **Large Microphone Button**: Easy tap-and-hold recording +- ๐Ÿ“Š **Real-time Feedback**: Recording duration, processing status +- ๐Ÿ”„ **Connection Management**: Auto-detect server status +- โš™๏ธ **Easy Configuration**: Simple server URL input +- ๐Ÿ“ **Results Display**: Clear transcription output +- ๐ŸŽจ **Professional UI**: Material Design components + +### Server Features +- ๐Ÿ”Œ **REST API**: Standard HTTP endpoints +- ๐Ÿ”„ **File Upload**: Handles WAV audio files +- ๐Ÿง  **Whisper Integration**: Uses existing transcription engine +- ๐Ÿ“Š **Status Monitoring**: Health and configuration endpoints +- ๐ŸŒ **CORS Support**: Mobile app compatibility +- ๐Ÿ”ง **Flexible Configuration**: Multiple model sizes, ports + +### Network Features +- ๐Ÿ  **Local Network**: WiFi-based connection +- ๐ŸŒ **Internet Access**: ngrok tunnel support +- ๐Ÿ”’ **Security**: Local processing, no cloud dependencies +- โšก **Performance**: Direct connection options + +## How to Use + +### Quick Start (Local Network) + +1. **Start the server:** + ```bash + cd voice-copilot + poetry run voice-transcriber-api --host 0.0.0.0 --port 8000 + ``` + +2. **Find your IP:** + ```bash + python3 scripts/get-local-ip.py + ``` + +3. **Setup mobile app:** + ```bash + cd mobile-app + npm install + npm start + ``` + +4. **Connect and use:** + - Scan QR code with Expo Go + - Enter server URL (e.g., `http://192.168.1.100:8000`) + - Test connection + - Start recording! + +### Internet Access (ngrok) + +1. **Install ngrok:** https://ngrok.com/download + +2. **Start with ngrok:** + ```bash + ./scripts/start-with-ngrok.sh + ``` + +3. **Use ngrok URL in mobile app** + +### Combined Service (Desktop + Mobile) + +```bash +poetry run voice-transcriber-combined --api-host 0.0.0.0 --api-port 8000 +``` + +This runs both desktop hotkeys AND mobile API server. + +## Technical Architecture + +``` +Mobile App (React Native) + โ†“ HTTP POST /transcribe +API Server (Flask) + โ†“ Audio file +Transcriber (Whisper) + โ†“ Text result +API Server + โ†“ JSON response +Mobile App (Display) +``` + +### API Endpoints + +- `GET /health` - Server health check +- `GET /status` - Configuration and model info +- `POST /transcribe` - Upload audio, get transcription + +### Audio Flow + +1. Mobile app records WAV audio (16kHz, mono) +2. Uploads via multipart/form-data +3. Server saves to temp file +4. Whisper processes audio +5. Returns transcribed text +6. Temp file cleaned up + +## Files Created/Modified + +### New Files +- `voice_transcriber/api_server.py` - Main API server +- `voice_transcriber/combined_service.py` - Combined service +- `mobile-app/` - Complete React Native app +- `scripts/start-with-ngrok.sh` - ngrok helper +- `scripts/get-local-ip.py` - Network helper +- `test_api.py` - API testing +- `demo.py` - Demo script +- `MOBILE_SETUP.md` - Setup guide + +### Modified Files +- `pyproject.toml` - Added Flask dependencies and scripts +- `README.md` - Added mobile app documentation + +## Next Steps + +### For Production Use + +1. **Build Mobile App:** + ```bash + cd mobile-app + expo build:android # or expo build:ios + ``` + +2. **Server Deployment:** + - Use production WSGI server (gunicorn) + - Add authentication if needed + - Configure firewall rules + +3. **Security Enhancements:** + - Add API keys + - Rate limiting + - HTTPS certificates + +### For Development + +1. **Customize UI:** + - Modify `mobile-app/App.js` + - Change colors, layout, features + +2. **Add Features:** + - Audio playback + - Recording history + - Multiple server profiles + - Offline mode + +3. **Improve API:** + - Streaming transcription + - Multiple audio formats + - Batch processing + +## Testing + +The implementation has been tested with: +- โœ… API server startup +- โœ… Health endpoint +- โœ… Status endpoint +- โœ… Audio upload handling +- โœ… Whisper model loading +- โœ… Error handling + +## Support + +For issues or questions: +1. Check `MOBILE_SETUP.md` for detailed setup +2. Run `python test_api.py` to test server +3. Check server logs for errors +4. Verify network connectivity + +## Summary + +You now have a complete mobile app companion for Voice Copilot that: +- Works over local WiFi or internet +- Has a professional, easy-to-use interface +- Integrates seamlessly with your existing service +- Supports both development and production use +- Includes comprehensive documentation and helpers + +The mobile app provides the "massive microphone input" you requested and successfully sends voice recordings to your server for transcription, working both over wireless (same network) and internet (via ngrok) as specified! \ No newline at end of file diff --git a/MOBILE_SETUP.md b/MOBILE_SETUP.md new file mode 100644 index 0000000..ddf72aa --- /dev/null +++ b/MOBILE_SETUP.md @@ -0,0 +1,301 @@ +# Voice Copilot Mobile App Setup Guide + +This guide will help you set up the Voice Copilot mobile app companion to work with your desktop service. + +## Overview + +The Voice Copilot mobile app allows you to: +- Record voice remotely from your phone +- Send recordings to your desktop for transcription +- View transcribed text on your mobile device +- Work over local WiFi or internet via ngrok + +## Quick Start + +### 1. Start the API Server + +Choose one of these options: + +**Option A: API Server Only** +```bash +cd voice-copilot +poetry run voice-transcriber-api --host 0.0.0.0 --port 8000 +``` + +**Option B: Combined Service (Desktop + API)** +```bash +cd voice-copilot +poetry run voice-transcriber-combined --api-host 0.0.0.0 --api-port 8000 +``` + +**Option C: With ngrok for Internet Access** +```bash +cd voice-copilot +./scripts/start-with-ngrok.sh +``` + +### 2. Find Your Network Configuration + +```bash +cd voice-copilot +python3 scripts/get-local-ip.py +``` + +This will show you: +- Your local IP address +- Suggested server URLs +- Network interface information + +### 3. Set Up the Mobile App + +```bash +cd voice-copilot/mobile-app +npm install +npm start +``` + +### 4. Configure and Test + +1. Install Expo Go on your phone +2. Scan the QR code from the terminal +3. Enter your server URL in the app +4. Test the connection +5. Start recording! + +## Network Setup Options + +### Local WiFi (Recommended) + +**Pros:** +- Fast and responsive +- Private and secure +- No external dependencies + +**Cons:** +- Only works on same WiFi network +- Need to know your computer's IP address + +**Setup:** +1. Ensure both devices are on the same WiFi +2. Find your computer's IP: `python3 scripts/get-local-ip.py` +3. Use URL like: `http://192.168.1.100:8000` + +### Internet via ngrok + +**Pros:** +- Works from anywhere +- Easy to share with others +- Handles firewall/NAT issues + +**Cons:** +- Requires ngrok account (free tier available) +- Slightly higher latency +- Data goes through ngrok servers + +**Setup:** +1. Install ngrok: https://ngrok.com/download +2. Run: `./scripts/start-with-ngrok.sh` +3. Use the ngrok URL (e.g., `https://abc123.ngrok.io`) + +## Mobile App Features + +### Main Interface +- **Large Microphone Button**: Tap and hold to record +- **Connection Status**: Shows if connected to server +- **Server URL Input**: Configure your server address +- **Test Connection**: Verify server is reachable + +### Recording +- **Visual Feedback**: Button changes color when recording +- **Duration Display**: Shows recording time +- **Processing Indicator**: Shows when transcribing + +### Results +- **Transcription Display**: Shows the transcribed text +- **Clear Button**: Remove current transcription +- **Status Messages**: Success/error notifications + +## Troubleshooting + +### Connection Issues + +**"Cannot connect to server"** +- Check that the API server is running +- Verify the IP address and port are correct +- Ensure both devices are on the same WiFi (for local setup) +- Try using ngrok for internet access + +**"Connection timeout"** +- Server might be overloaded or crashed +- Check server logs: `tail -f api_server.log` +- Restart the server + +### Audio Issues + +**"No speech detected"** +- Speak louder and clearer +- Ensure you're holding the record button while speaking +- Check microphone permissions on your phone +- Try recording for at least 1-2 seconds + +**"Recording failed"** +- Grant microphone permissions when prompted +- Close and reopen the app +- Check if other apps are using the microphone + +### Performance Issues + +**Slow transcription** +- Use a smaller Whisper model (tiny/base instead of large) +- Ensure your computer has sufficient resources +- Close other resource-intensive applications + +**App crashes** +- Update Expo Go app +- Clear app cache: close and reopen +- Check for JavaScript errors in Expo logs + +## Advanced Configuration + +### Server Options + +```bash +# Different Whisper models +poetry run voice-transcriber-api --model tiny # Fastest +poetry run voice-transcriber-api --model base # Balanced +poetry run voice-transcriber-api --model small # Better accuracy +poetry run voice-transcriber-api --model medium # High accuracy +poetry run voice-transcriber-api --model large # Best accuracy + +# Custom host/port +poetry run voice-transcriber-api --host 0.0.0.0 --port 9000 + +# Debug mode +poetry run voice-transcriber-api --debug +``` + +### Mobile App Customization + +The mobile app automatically adapts to your server configuration, but you can modify: + +- Server URL (in the app interface) +- Recording quality (modify App.js) +- UI theme (modify App.js theme object) + +### Security Considerations + +**Local Network:** +- Traffic stays on your local network +- No external data transmission +- Firewall may block connections + +**ngrok:** +- Data passes through ngrok servers +- Use HTTPS URLs when possible +- Consider ngrok's privacy policy + +## API Reference + +The mobile app uses these endpoints: + +### GET /health +Check if server is running +```json +{ + "status": "healthy", + "service": "voice-transcriber-api", + "timestamp": 1234567890 +} +``` + +### GET /status +Get server configuration and model info +```json +{ + "success": true, + "config": { + "model_name": "tiny", + "sample_rate": 16000, + "channels": 1 + }, + "model_info": { + "is_loaded": false, + "load_time": 0, + "last_used": 0 + } +} +``` + +### POST /transcribe +Upload audio file for transcription +- **Content-Type**: multipart/form-data +- **Field**: audio (WAV file) + +**Success Response:** +```json +{ + "success": true, + "text": "Hello world", + "transcription_time": 0.5, + "timestamp": 1234567890 +} +``` + +**Error Response:** +```json +{ + "success": false, + "error": "No speech detected", + "transcription_time": 0.5, + "timestamp": 1234567890 +} +``` + +## Development + +### Building the Mobile App + +For development: +```bash +cd mobile-app +npm start +``` + +For production builds: +```bash +# Android +expo build:android + +# iOS (requires Apple Developer account) +expo build:ios +``` + +### Modifying the Server + +The API server code is in `voice_transcriber/api_server.py`. Key areas: + +- **Routes**: Add new endpoints +- **Audio Processing**: Modify transcription logic +- **Error Handling**: Improve error messages +- **Authentication**: Add API keys if needed + +### Contributing + +1. Fork the repository +2. Create a feature branch +3. Make your changes +4. Test with both local and ngrok setups +5. Submit a pull request + +## Support + +If you encounter issues: + +1. Check the troubleshooting section above +2. Review server logs: `tail -f api_server.log` +3. Test with the provided test script: `python test_api.py` +4. Open an issue on GitHub with: + - Your setup (local/ngrok) + - Error messages + - Server logs + - Mobile app version \ No newline at end of file diff --git a/README.md b/README.md index 27daabd..4b862fe 100644 --- a/README.md +++ b/README.md @@ -16,9 +16,16 @@ A fast, local, privacy-focused speech-to-text and text-to-speech application for - High-quality Microsoft Edge TTS voices - Works offline after initial voice download +๐Ÿ“ฑ **Mobile App Companion** +- React Native mobile app for remote voice recording +- Large microphone button for easy recording +- Works over local WiFi or internet via ngrok +- Real-time transcription feedback +- Cross-platform (iOS and Android) + ๐Ÿ”’ **Privacy & Local Processing** - All speech processing happens locally -- No data sent to external services +- No data sent to external services (except via your own network) - Temporary audio files are automatically cleaned up โšก **Performance** @@ -82,21 +89,31 @@ poetry run voice-transcriber --hotkey ctrl+shift+space --tts-hotkey ctrl+shift+r ### Command Line Options ```bash +# Desktop-only service usage: voice-transcriber [-h] [--model {tiny,base,small,medium,large}] [--hotkey HOTKEY] [--tts-hotkey TTS_HOTKEY] [--no-tray] [--debug] +# API server only +usage: voice-transcriber-api [-h] [--host HOST] [--port PORT] [--model MODEL] [--debug] + +# Combined service (desktop + API) +usage: voice-transcriber-combined [-h] [--api-host HOST] [--api-port PORT] + [--model MODEL] [--hotkey HOTKEY] [--no-tray] [--debug] + Local Speech-to-Text and Text-to-Speech Service options: -h, --help show this help message and exit --model {tiny,base,small,medium,large} - Whisper model size (default: tiny) + Whisper model size (default: base) --hotkey HOTKEY Recording hotkey (default: ctrl+f1) --tts-hotkey TTS_HOTKEY Text-to-speech hotkey (default: ctrl+f2) --no-tray Disable system tray icon --debug Enable debug logging + --host HOST API server host (default: 0.0.0.0) + --port PORT API server port (default: 8000) ``` ## Autostart Setup @@ -166,6 +183,63 @@ cp voice_transcriber.env.example voice_transcriber.env nano voice_transcriber.env ``` +## Mobile App Setup + +The Voice Copilot includes a React Native mobile app companion that allows you to record voice remotely and send it to your desktop service for transcription. + +### Quick Mobile Setup + +1. **Start the API server** (choose one option): + ```bash + # Option 1: API server only + poetry run voice-transcriber-api --host 0.0.0.0 --port 8000 + + # Option 2: Combined service (desktop hotkeys + API) + poetry run voice-transcriber-combined --api-host 0.0.0.0 --api-port 8000 + + # Option 3: With ngrok for internet access + ./scripts/start-with-ngrok.sh + ``` + +2. **Find your local IP address**: + ```bash + python3 scripts/get-local-ip.py + ``` + +3. **Setup the mobile app**: + ```bash + cd mobile-app + npm install + npm start + ``` + +4. **Configure the mobile app**: + - Scan the QR code with Expo Go app + - Enter your server URL (e.g., `http://192.168.1.100:8000`) + - Test the connection + - Start recording! + +### Network Options + +**Local WiFi (Recommended)** +- Both devices on same WiFi network +- Fast and private +- Use IP address like `http://192.168.1.100:8000` + +**Internet via ngrok** +- Access from anywhere +- Requires ngrok installation +- Use ngrok URL like `https://abc123.ngrok.io` + +### Mobile App Features + +- ๐ŸŽค Large, easy-to-use microphone button +- ๐Ÿ“Š Real-time recording duration display +- ๐Ÿ”„ Connection status indicator +- ๐Ÿ“ Transcription results display +- โš™๏ธ Server URL configuration +- ๐Ÿ“ฑ Works on both iOS and Android + ## Configuration The application uses sensible defaults but can be customized: diff --git a/demo.py b/demo.py new file mode 100755 index 0000000..934248e --- /dev/null +++ b/demo.py @@ -0,0 +1,176 @@ +#!/usr/bin/env python3 +"""Demo script showing Voice Copilot mobile app integration.""" + +import subprocess +import time +import requests +import sys +import os + +def check_dependencies(): + """Check if required dependencies are available.""" + print("๐Ÿ” Checking dependencies...") + + # Check if poetry is available + try: + subprocess.run(['poetry', '--version'], capture_output=True, check=True) + print(" โœ… Poetry found") + except (subprocess.CalledProcessError, FileNotFoundError): + print(" โŒ Poetry not found. Please install Poetry first.") + return False + + # Check if npm is available (for mobile app) + try: + subprocess.run(['npm', '--version'], capture_output=True, check=True) + print(" โœ… npm found") + except (subprocess.CalledProcessError, FileNotFoundError): + print(" โš ๏ธ npm not found. Mobile app setup will be skipped.") + + return True + +def start_api_server(): + """Start the API server.""" + print("๐Ÿš€ Starting Voice Copilot API server...") + + # Start server in background + process = subprocess.Popen([ + 'poetry', 'run', 'voice-transcriber-api', + '--host', '0.0.0.0', + '--port', '8000', + '--model', 'tiny' + ], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + + # Wait for server to start + for i in range(10): + try: + response = requests.get('http://localhost:8000/health', timeout=2) + if response.status_code == 200: + print(" โœ… API server started successfully") + return process + except requests.exceptions.RequestException: + pass + + time.sleep(1) + print(f" โณ Waiting for server to start... ({i+1}/10)") + + print(" โŒ Failed to start API server") + process.terminate() + return None + +def show_network_info(): + """Show network configuration information.""" + print("๐ŸŒ Network Configuration:") + + try: + result = subprocess.run(['python3', 'scripts/get-local-ip.py'], + capture_output=True, text=True) + if result.returncode == 0: + print(result.stdout) + else: + print(" โŒ Failed to get network info") + except Exception as e: + print(f" โŒ Error getting network info: {e}") + +def test_api(): + """Test the API endpoints.""" + print("๐Ÿงช Testing API endpoints...") + + base_url = "http://localhost:8000" + + # Test health + try: + response = requests.get(f"{base_url}/health") + if response.status_code == 200: + print(" โœ… Health endpoint working") + else: + print(f" โŒ Health endpoint failed: {response.status_code}") + except Exception as e: + print(f" โŒ Health endpoint error: {e}") + + # Test status + try: + response = requests.get(f"{base_url}/status") + if response.status_code == 200: + data = response.json() + print(f" โœ… Status endpoint working (model: {data['config']['model_name']})") + else: + print(f" โŒ Status endpoint failed: {response.status_code}") + except Exception as e: + print(f" โŒ Status endpoint error: {e}") + +def show_mobile_setup(): + """Show mobile app setup instructions.""" + print("๐Ÿ“ฑ Mobile App Setup:") + print(" 1. Open a new terminal") + print(" 2. cd voice-copilot/mobile-app") + print(" 3. npm install") + print(" 4. npm start") + print(" 5. Install Expo Go on your phone") + print(" 6. Scan the QR code") + print(" 7. Enter server URL: http://YOUR_IP:8000") + print("") + +def show_ngrok_setup(): + """Show ngrok setup instructions.""" + print("๐ŸŒ For Internet Access (ngrok):") + print(" 1. Install ngrok: https://ngrok.com/download") + print(" 2. Run: ./scripts/start-with-ngrok.sh") + print(" 3. Use the ngrok URL in your mobile app") + print("") + +def main(): + """Main demo function.""" + print("๐ŸŽค Voice Copilot Mobile App Demo") + print("=" * 50) + print("") + + # Check dependencies + if not check_dependencies(): + return 1 + + print("") + + # Start API server + server_process = start_api_server() + if not server_process: + return 1 + + print("") + + try: + # Test API + test_api() + print("") + + # Show network info + show_network_info() + print("") + + # Show setup instructions + show_mobile_setup() + show_ngrok_setup() + + print("๐ŸŽ‰ Demo setup complete!") + print("") + print("The API server is running at: http://localhost:8000") + print("You can now set up the mobile app following the instructions above.") + print("") + print("Press Ctrl+C to stop the server...") + + # Keep server running + try: + while True: + time.sleep(1) + except KeyboardInterrupt: + print("\n๐Ÿ›‘ Stopping server...") + + finally: + # Clean up + if server_process: + server_process.terminate() + server_process.wait() + + return 0 + +if __name__ == "__main__": + sys.exit(main()) \ No newline at end of file diff --git a/mobile-app/App.js b/mobile-app/App.js new file mode 100644 index 0000000..0118ead --- /dev/null +++ b/mobile-app/App.js @@ -0,0 +1,449 @@ +import React, { useState, useEffect } from 'react'; +import { StyleSheet, View, Alert, Dimensions, ScrollView } from 'react-native'; +import { + Provider as PaperProvider, + DefaultTheme, + Button, + Card, + Title, + Paragraph, + TextInput, + Chip, + ActivityIndicator, + Snackbar, + IconButton, + Text +} from 'react-native-paper'; +import { Audio } from 'expo-av'; +import * as FileSystem from 'expo-file-system'; +import { StatusBar } from 'expo-status-bar'; + +const { width, height } = Dimensions.get('window'); + +const theme = { + ...DefaultTheme, + colors: { + ...DefaultTheme.colors, + primary: '#6200ee', + accent: '#03dac4', + }, +}; + +export default function App() { + const [recording, setRecording] = useState(null); + const [isRecording, setIsRecording] = useState(false); + const [isProcessing, setIsProcessing] = useState(false); + const [transcribedText, setTranscribedText] = useState(''); + const [serverUrl, setServerUrl] = useState('http://192.168.1.100:8000'); + const [connectionStatus, setConnectionStatus] = useState('disconnected'); + const [snackbarVisible, setSnackbarVisible] = useState(false); + const [snackbarMessage, setSnackbarMessage] = useState(''); + const [recordingDuration, setRecordingDuration] = useState(0); + const [recordingTimer, setRecordingTimer] = useState(null); + + useEffect(() => { + checkServerConnection(); + setupAudio(); + + return () => { + if (recordingTimer) { + clearInterval(recordingTimer); + } + }; + }, []); + + const setupAudio = async () => { + try { + await Audio.requestPermissionsAsync(); + await Audio.setAudioModeAsync({ + allowsRecordingIOS: true, + playsInSilentModeIOS: true, + }); + } catch (error) { + console.error('Failed to setup audio:', error); + showSnackbar('Failed to setup audio permissions'); + } + }; + + const checkServerConnection = async () => { + try { + const response = await fetch(`${serverUrl}/health`, { + method: 'GET', + timeout: 5000, + }); + + if (response.ok) { + setConnectionStatus('connected'); + showSnackbar('Connected to server'); + } else { + setConnectionStatus('error'); + showSnackbar('Server responded with error'); + } + } catch (error) { + console.error('Connection check failed:', error); + setConnectionStatus('disconnected'); + showSnackbar('Cannot connect to server'); + } + }; + + const showSnackbar = (message) => { + setSnackbarMessage(message); + setSnackbarVisible(true); + }; + + const startRecording = async () => { + try { + if (connectionStatus !== 'connected') { + showSnackbar('Please connect to server first'); + return; + } + + console.log('Starting recording...'); + + const { recording } = await Audio.Recording.createAsync({ + android: { + extension: '.wav', + outputFormat: Audio.RECORDING_OPTION_ANDROID_OUTPUT_FORMAT_DEFAULT, + audioEncoder: Audio.RECORDING_OPTION_ANDROID_AUDIO_ENCODER_DEFAULT, + sampleRate: 16000, + numberOfChannels: 1, + bitRate: 128000, + }, + ios: { + extension: '.wav', + outputFormat: Audio.RECORDING_OPTION_IOS_OUTPUT_FORMAT_LINEARPCM, + audioQuality: Audio.RECORDING_OPTION_IOS_AUDIO_QUALITY_HIGH, + sampleRate: 16000, + numberOfChannels: 1, + bitRate: 128000, + linearPCMBitDepth: 16, + linearPCMIsBigEndian: false, + linearPCMIsFloat: false, + }, + }); + + setRecording(recording); + setIsRecording(true); + setRecordingDuration(0); + + // Start timer + const timer = setInterval(() => { + setRecordingDuration(prev => prev + 1); + }, 1000); + setRecordingTimer(timer); + + console.log('Recording started'); + showSnackbar('Recording started - hold the button'); + + } catch (error) { + console.error('Failed to start recording:', error); + showSnackbar('Failed to start recording'); + } + }; + + const stopRecording = async () => { + try { + if (!recording) return; + + console.log('Stopping recording...'); + setIsRecording(false); + + // Clear timer + if (recordingTimer) { + clearInterval(recordingTimer); + setRecordingTimer(null); + } + + await recording.stopAndUnloadAsync(); + const uri = recording.getURI(); + setRecording(null); + + if (uri) { + console.log('Recording saved to:', uri); + await uploadAndTranscribe(uri); + } + + } catch (error) { + console.error('Failed to stop recording:', error); + showSnackbar('Failed to stop recording'); + } + }; + + const uploadAndTranscribe = async (audioUri) => { + try { + setIsProcessing(true); + showSnackbar('Processing audio...'); + + // Create form data + const formData = new FormData(); + formData.append('audio', { + uri: audioUri, + type: 'audio/wav', + name: 'recording.wav', + }); + + console.log('Uploading to:', `${serverUrl}/transcribe`); + + const response = await fetch(`${serverUrl}/transcribe`, { + method: 'POST', + body: formData, + headers: { + 'Content-Type': 'multipart/form-data', + }, + }); + + const result = await response.json(); + + if (result.success && result.text) { + setTranscribedText(result.text); + showSnackbar(`Transcribed in ${result.transcription_time?.toFixed(2)}s`); + } else { + showSnackbar(result.error || 'Transcription failed'); + } + + } catch (error) { + console.error('Upload failed:', error); + showSnackbar('Failed to upload audio'); + } finally { + setIsProcessing(false); + } + }; + + const formatDuration = (seconds) => { + const mins = Math.floor(seconds / 60); + const secs = seconds % 60; + return `${mins}:${secs.toString().padStart(2, '0')}`; + }; + + const getConnectionColor = () => { + switch (connectionStatus) { + case 'connected': return '#4caf50'; + case 'error': return '#ff9800'; + case 'disconnected': return '#f44336'; + default: return '#9e9e9e'; + } + }; + + return ( + + + + + + {/* Header */} + + + Voice Copilot + + + {connectionStatus} + + + + + + + {/* Server Configuration */} + + + + + + + + {/* Recording Section */} + + + {isRecording && ( + + Recording... + {formatDuration(recordingDuration)} + + )} + + {isProcessing && ( + + + Processing audio... + + )} + + + + + {connectionStatus === 'connected' + ? "Tap and hold the button while speaking" + : "Connect to server first" + } + + + + + {/* Transcription Result */} + {transcribedText ? ( + + + Transcription + + {transcribedText} + + + + + ) : null} + + + setSnackbarVisible(false)} + duration={3000} + > + {snackbarMessage} + + + + ); +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + backgroundColor: '#f5f5f5', + }, + scrollContent: { + padding: 16, + paddingBottom: 32, + }, + headerCard: { + marginBottom: 16, + }, + title: { + textAlign: 'center', + fontSize: 28, + fontWeight: 'bold', + }, + connectionRow: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', + marginTop: 8, + }, + statusChip: { + marginRight: 8, + }, + configCard: { + marginBottom: 16, + }, + serverInput: { + marginBottom: 12, + }, + testButton: { + marginTop: 8, + }, + recordingCard: { + marginBottom: 16, + minHeight: 200, + }, + recordingContent: { + alignItems: 'center', + justifyContent: 'center', + minHeight: 160, + }, + recordingInfo: { + alignItems: 'center', + marginBottom: 20, + }, + recordingText: { + fontSize: 18, + fontWeight: 'bold', + color: '#f44336', + }, + durationText: { + fontSize: 24, + fontWeight: 'bold', + color: '#f44336', + marginTop: 4, + }, + processingInfo: { + alignItems: 'center', + marginBottom: 20, + }, + processingText: { + fontSize: 16, + marginTop: 12, + color: '#666', + }, + recordButton: { + width: width * 0.7, + marginBottom: 16, + }, + recordingButton: { + backgroundColor: '#f44336', + }, + recordButtonContent: { + height: 60, + }, + recordButtonLabel: { + fontSize: 18, + fontWeight: 'bold', + }, + instructionText: { + textAlign: 'center', + color: '#666', + fontSize: 14, + }, + resultCard: { + marginBottom: 16, + }, + transcribedText: { + fontSize: 16, + lineHeight: 24, + marginVertical: 12, + padding: 12, + backgroundColor: '#f0f0f0', + borderRadius: 8, + }, + clearButton: { + marginTop: 8, + }, +}); \ No newline at end of file diff --git a/mobile-app/README.md b/mobile-app/README.md new file mode 100644 index 0000000..edf6ac6 --- /dev/null +++ b/mobile-app/README.md @@ -0,0 +1,160 @@ +# Voice Copilot Mobile App + +A React Native mobile companion app for the Voice Copilot service that allows you to record voice and get transcriptions from your desktop service. + +## Features + +- ๐ŸŽค **Large Microphone Button**: Easy-to-use recording interface +- ๐ŸŒ **Network Connectivity**: Works over local WiFi or internet via ngrok +- ๐Ÿ“ฑ **Cross-Platform**: Built with React Native/Expo for iOS and Android +- ๐Ÿ”„ **Real-time Status**: Shows connection status and processing feedback +- ๐Ÿ“ **Transcription Display**: Shows transcribed text with clear formatting + +## Setup + +### Prerequisites + +- Node.js (v16 or later) +- Expo CLI: `npm install -g expo-cli` +- Expo Go app on your mobile device (for testing) + +### Installation + +1. Navigate to the mobile app directory: + ```bash + cd mobile-app + ``` + +2. Install dependencies: + ```bash + npm install + ``` + +3. Start the development server: + ```bash + npm start + ``` + +4. Scan the QR code with Expo Go app on your phone + +## Configuration + +### Local Network Setup + +1. Find your computer's IP address: + ```bash + # On Linux/Mac + ip addr show | grep inet + # or + ifconfig | grep inet + + # On Windows + ipconfig + ``` + +2. Start the Voice Copilot API server on your desktop: + ```bash + # In the main voice-copilot directory + poetry run voice-transcriber-api --host 0.0.0.0 --port 8000 + + # Or run the combined service (desktop + API) + poetry run voice-transcriber-combined --api-host 0.0.0.0 --api-port 8000 + ``` + +3. In the mobile app, set the server URL to: + ``` + http://YOUR_COMPUTER_IP:8000 + ``` + Example: `http://192.168.1.100:8000` + +### Internet Access via ngrok + +1. Install ngrok: https://ngrok.com/download + +2. Start the API server: + ```bash + poetry run voice-transcriber-api --host 0.0.0.0 --port 8000 + ``` + +3. In another terminal, expose the port via ngrok: + ```bash + ngrok http 8000 + ``` + +4. Copy the ngrok URL (e.g., `https://abc123.ngrok.io`) and use it in the mobile app + +## Usage + +1. **Connect to Server**: Enter your server URL and tap "Test Connection" +2. **Record Audio**: Tap and hold the microphone button while speaking +3. **View Transcription**: The transcribed text will appear below the recording area +4. **Clear Results**: Tap "Clear" to remove the transcription + +## API Endpoints + +The mobile app communicates with these endpoints: + +- `GET /health` - Check server health +- `POST /transcribe` - Upload audio file for transcription +- `GET /status` - Get transcriber status + +## Troubleshooting + +### Connection Issues + +- Ensure your phone and computer are on the same WiFi network (for local setup) +- Check that the API server is running and accessible +- Verify the IP address and port are correct +- Try using ngrok for internet access if local network doesn't work + +### Audio Issues + +- Grant microphone permissions when prompted +- Ensure you're holding the record button while speaking +- Check that audio recording works in other apps on your device + +### Build Issues + +- Make sure you have the latest Expo CLI +- Clear cache: `expo r -c` +- Reinstall dependencies: `rm -rf node_modules && npm install` + +## Development + +### Project Structure + +``` +mobile-app/ +โ”œโ”€โ”€ App.js # Main app component +โ”œโ”€โ”€ package.json # Dependencies and scripts +โ”œโ”€โ”€ app.json # Expo configuration +โ”œโ”€โ”€ babel.config.js # Babel configuration +โ””โ”€โ”€ assets/ # App icons and images +``` + +### Key Dependencies + +- **Expo**: React Native framework +- **expo-av**: Audio recording and playback +- **react-native-paper**: Material Design components +- **expo-file-system**: File system operations + +## Building for Production + +### Android + +```bash +expo build:android +``` + +### iOS + +```bash +expo build:ios +``` + +Note: You'll need an Apple Developer account for iOS builds. + +## License + +Same as the main Voice Copilot project (MIT License). \ No newline at end of file diff --git a/mobile-app/app.json b/mobile-app/app.json new file mode 100644 index 0000000..d56824d --- /dev/null +++ b/mobile-app/app.json @@ -0,0 +1,37 @@ +{ + "expo": { + "name": "Voice Copilot", + "slug": "voice-copilot-mobile", + "version": "1.0.0", + "orientation": "portrait", + "icon": "./assets/icon.png", + "userInterfaceStyle": "light", + "splash": { + "image": "./assets/splash.png", + "resizeMode": "contain", + "backgroundColor": "#ffffff" + }, + "assetBundlePatterns": [ + "**/*" + ], + "ios": { + "supportsTablet": true, + "infoPlist": { + "NSMicrophoneUsageDescription": "This app needs access to microphone to record voice for transcription." + } + }, + "android": { + "adaptiveIcon": { + "foregroundImage": "./assets/adaptive-icon.png", + "backgroundColor": "#FFFFFF" + }, + "permissions": [ + "android.permission.RECORD_AUDIO", + "android.permission.INTERNET" + ] + }, + "web": { + "favicon": "./assets/favicon.png" + } + } +} \ No newline at end of file diff --git a/mobile-app/assets/README.md b/mobile-app/assets/README.md new file mode 100644 index 0000000..be0b7a1 --- /dev/null +++ b/mobile-app/assets/README.md @@ -0,0 +1,53 @@ +# Assets Directory + +This directory contains the app icons and images for the Voice Copilot mobile app. + +## Required Files + +For a complete Expo app, you need these files: + +- `icon.png` - App icon (1024x1024 px) +- `adaptive-icon.png` - Android adaptive icon (1024x1024 px) +- `splash.png` - Splash screen image (1284x2778 px for iPhone 12) +- `favicon.png` - Web favicon (48x48 px) + +## Generating Assets + +You can use Expo's asset generation tools: + +```bash +# Install the asset generator +npm install -g @expo/image-utils + +# Generate all assets from a single 1024x1024 icon +npx expo install expo-asset-utils +``` + +Or create them manually using any image editor. + +## Placeholder Assets + +For development, you can use simple colored squares: + +- **icon.png**: 1024x1024 blue square with microphone symbol +- **adaptive-icon.png**: Same as icon.png +- **splash.png**: 1284x2778 white background with app name +- **favicon.png**: 48x48 version of the icon + +## Design Guidelines + +### App Icon +- Use a microphone or voice wave symbol +- Keep it simple and recognizable at small sizes +- Use the app's primary colors (blue/purple theme) +- Ensure good contrast + +### Splash Screen +- Keep it minimal +- Show app name and/or logo +- Use consistent branding +- Fast loading is more important than complex graphics + +## Current Status + +This directory currently contains placeholder files. Replace them with proper assets before building for production. \ No newline at end of file diff --git a/mobile-app/babel.config.js b/mobile-app/babel.config.js new file mode 100644 index 0000000..716b424 --- /dev/null +++ b/mobile-app/babel.config.js @@ -0,0 +1,6 @@ +module.exports = function(api) { + api.cache(true); + return { + presets: ['babel-preset-expo'], + }; +}; \ No newline at end of file diff --git a/mobile-app/package.json b/mobile-app/package.json new file mode 100644 index 0000000..3b010aa --- /dev/null +++ b/mobile-app/package.json @@ -0,0 +1,26 @@ +{ + "name": "VoiceCopilotMobile", + "version": "1.0.0", + "main": "node_modules/expo/AppEntry.js", + "scripts": { + "start": "expo start", + "android": "expo start --android", + "ios": "expo start --ios", + "web": "expo start --web" + }, + "dependencies": { + "expo": "~50.0.0", + "expo-av": "~13.10.4", + "expo-file-system": "~16.0.6", + "expo-permissions": "~14.4.0", + "react": "18.2.0", + "react-native": "0.73.4", + "react-native-paper": "^5.12.3", + "react-native-vector-icons": "^10.0.3", + "expo-status-bar": "~1.11.1" + }, + "devDependencies": { + "@babel/core": "^7.20.0" + }, + "private": true +} \ No newline at end of file diff --git a/poetry.lock b/poetry.lock index 3aa143a..5a62715 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.1.3 and should not be changed by hand. [[package]] name = "aiohappyeyeballs" @@ -6,6 +6,7 @@ version = "2.6.1" description = "Happy Eyeballs for asyncio" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8"}, {file = "aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558"}, @@ -17,6 +18,7 @@ version = "3.12.13" description = "Async http client/server framework (asyncio)" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "aiohttp-3.12.13-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5421af8f22a98f640261ee48aae3a37f0c41371e99412d55eaf2f8a46d5dad29"}, {file = "aiohttp-3.12.13-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0fcda86f6cb318ba36ed8f1396a6a4a3fd8f856f84d426584392083d10da4de0"}, @@ -117,7 +119,7 @@ propcache = ">=0.2.0" yarl = ">=1.17.0,<2.0" [package.extras] -speedups = ["Brotli", "aiodns (>=3.3.0)", "brotlicffi"] +speedups = ["Brotli ; platform_python_implementation == \"CPython\"", "aiodns (>=3.3.0)", "brotlicffi ; platform_python_implementation != \"CPython\""] [[package]] name = "aiosignal" @@ -125,6 +127,7 @@ version = "1.3.2" description = "aiosignal: a list of registered asynchronous callbacks" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "aiosignal-1.3.2-py2.py3-none-any.whl", hash = "sha256:45cde58e409a301715980c2b01d0c28bdde3770d8290b5eb2173759d9acb31a5"}, {file = "aiosignal-1.3.2.tar.gz", hash = "sha256:a8c255c66fafb1e499c9351d0bf32ff2d8a0321595ebac3b93713656d2436f54"}, @@ -139,6 +142,8 @@ version = "5.0.1" description = "Timeout context manager for asyncio programs" optional = false python-versions = ">=3.8" +groups = ["main"] +markers = "python_version == \"3.10\"" files = [ {file = "async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c"}, {file = "async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3"}, @@ -150,18 +155,19 @@ version = "25.3.0" description = "Classes Without Boilerplate" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3"}, {file = "attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b"}, ] [package.extras] -benchmark = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -cov = ["cloudpickle", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +benchmark = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] +cov = ["cloudpickle ; platform_python_implementation == \"CPython\"", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] +dev = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier"] -tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"] +tests = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] +tests-mypy = ["mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\""] [[package]] name = "black" @@ -169,6 +175,7 @@ version = "23.12.1" description = "The uncompromising code formatter." optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "black-23.12.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0aaf6041986767a5e0ce663c7a2f0e9eaf21e6ff87a5f95cbf3675bfd4c41d2"}, {file = "black-23.12.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c88b3711d12905b74206227109272673edce0cb29f27e1385f33b0163c414bba"}, @@ -205,16 +212,29 @@ typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} [package.extras] colorama = ["colorama (>=0.4.3)"] -d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"] +d = ["aiohttp (>=3.7.4) ; sys_platform != \"win32\" or implementation_name != \"pypy\"", "aiohttp (>=3.7.4,!=3.9.0) ; sys_platform == \"win32\" and implementation_name == \"pypy\""] jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] uvloop = ["uvloop (>=0.15.2)"] +[[package]] +name = "blinker" +version = "1.9.0" +description = "Fast, simple object-to-object and broadcast signaling" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc"}, + {file = "blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf"}, +] + [[package]] name = "certifi" version = "2025.6.15" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "certifi-2025.6.15-py3-none-any.whl", hash = "sha256:2e0c7ce7cb5d8f8634ca55d2ba7e6ec2689a2fd6537d8dec1296a477a4910057"}, {file = "certifi-2025.6.15.tar.gz", hash = "sha256:d747aa5a8b9bbbb1bb8c22bb13e22bd1f18e9796defa16bab421f7f7a317323b"}, @@ -226,6 +246,7 @@ version = "1.17.1" description = "Foreign Function Interface for Python calling C code." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"}, {file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"}, @@ -305,6 +326,7 @@ version = "3.4.2" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "charset_normalizer-3.4.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c48ed483eb946e6c04ccbe02c6b4d1d48e51944b6db70f697e089c193404941"}, {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2d318c11350e10662026ad0eb71bb51c7812fc8590825304ae0bdd4ac283acd"}, @@ -406,6 +428,7 @@ version = "8.2.1" description = "Composable command line interface toolkit" optional = false python-versions = ">=3.10" +groups = ["main", "dev"] files = [ {file = "click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b"}, {file = "click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202"}, @@ -420,10 +443,12 @@ version = "0.4.6" description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["main", "dev"] files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] +markers = {main = "platform_system == \"Windows\"", dev = "platform_system == \"Windows\" or sys_platform == \"win32\""} [[package]] name = "comtypes" @@ -431,6 +456,8 @@ version = "1.4.11" description = "Pure Python COM package" optional = false python-versions = ">=3.8" +groups = ["main"] +markers = "platform_system == \"Windows\"" files = [ {file = "comtypes-1.4.11-py3-none-any.whl", hash = "sha256:1760d5059ca7ca1d61b574c998378d879c271a86c41f88926619ea97497592bb"}, {file = "comtypes-1.4.11.zip", hash = "sha256:0a4259370ec48b685fe4483b0944ba1df0aa45163922073fe9b7df1d187db09e"}, @@ -442,6 +469,7 @@ version = "6.1.19" description = "Microsoft Edge's TTS" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "edge_tts-6.1.19-py3-none-any.whl", hash = "sha256:af49ea0539f54b0d76a73bc76cefd9deb2010a11c5ee2a189967e15830a6ba37"}, {file = "edge_tts-6.1.19.tar.gz", hash = "sha256:52fdfbaef2b3afee98bbba5e5c6365b5a6156fe78fe4e81e55749f7a563add38"}, @@ -460,6 +488,8 @@ version = "1.9.2" description = "Bindings to the Linux input handling subsystem" optional = false python-versions = ">=3.8" +groups = ["main"] +markers = "sys_platform in \"linux\"" files = [ {file = "evdev-1.9.2.tar.gz", hash = "sha256:5d3278892ce1f92a74d6bf888cc8525d9f68af85dbe336c95d1c87fb8f423069"}, ] @@ -470,6 +500,8 @@ version = "1.3.0" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" +groups = ["dev"] +markers = "python_version == \"3.10\"" files = [ {file = "exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10"}, {file = "exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88"}, @@ -487,6 +519,7 @@ version = "3.18.0" description = "A platform independent file lock." optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "filelock-3.18.0-py3-none-any.whl", hash = "sha256:c401f4f8377c4464e6db25fff06205fd89bdd83b65eb0488ed1b160f780e21de"}, {file = "filelock-3.18.0.tar.gz", hash = "sha256:adbc88eabb99d2fec8c9c1b229b171f18afa655400173ddc653d5d01501fb9f2"}, @@ -495,7 +528,7 @@ files = [ [package.extras] docs = ["furo (>=2024.8.6)", "sphinx (>=8.1.3)", "sphinx-autodoc-typehints (>=3)"] testing = ["covdefaults (>=2.3)", "coverage (>=7.6.10)", "diff-cover (>=9.2.1)", "pytest (>=8.3.4)", "pytest-asyncio (>=0.25.2)", "pytest-cov (>=6)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.28.1)"] -typing = ["typing-extensions (>=4.12.2)"] +typing = ["typing-extensions (>=4.12.2) ; python_version < \"3.11\""] [[package]] name = "flake8" @@ -503,6 +536,7 @@ version = "6.1.0" description = "the modular source code checker: pep8 pyflakes and co" optional = false python-versions = ">=3.8.1" +groups = ["dev"] files = [ {file = "flake8-6.1.0-py2.py3-none-any.whl", hash = "sha256:ffdfce58ea94c6580c77888a86506937f9a1a227dfcd15f245d694ae20a6b6e5"}, {file = "flake8-6.1.0.tar.gz", hash = "sha256:d5b3857f07c030bdb5bf41c7f53799571d75c4491748a3adcd47de929e34cd23"}, @@ -513,12 +547,52 @@ mccabe = ">=0.7.0,<0.8.0" pycodestyle = ">=2.11.0,<2.12.0" pyflakes = ">=3.1.0,<3.2.0" +[[package]] +name = "flask" +version = "3.1.1" +description = "A simple framework for building complex web applications." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "flask-3.1.1-py3-none-any.whl", hash = "sha256:07aae2bb5eaf77993ef57e357491839f5fd9f4dc281593a81a9e4d79a24f295c"}, + {file = "flask-3.1.1.tar.gz", hash = "sha256:284c7b8f2f58cb737f0cf1c30fd7eaf0ccfcde196099d24ecede3fc2005aa59e"}, +] + +[package.dependencies] +blinker = ">=1.9.0" +click = ">=8.1.3" +itsdangerous = ">=2.2.0" +jinja2 = ">=3.1.2" +markupsafe = ">=2.1.1" +werkzeug = ">=3.1.0" + +[package.extras] +async = ["asgiref (>=3.2)"] +dotenv = ["python-dotenv"] + +[[package]] +name = "flask-cors" +version = "4.0.2" +description = "A Flask extension adding a decorator for CORS support" +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "Flask_Cors-4.0.2-py2.py3-none-any.whl", hash = "sha256:38364faf1a7a5d0a55bd1d2e2f83ee9e359039182f5e6a029557e1f56d92c09a"}, + {file = "flask_cors-4.0.2.tar.gz", hash = "sha256:493b98e2d1e2f1a4720a7af25693ef2fe32fbafec09a2f72c59f3e475eda61d2"}, +] + +[package.dependencies] +Flask = ">=0.9" + [[package]] name = "frozenlist" version = "1.7.0" description = "A list-like structure which implements collections.abc.MutableSequence" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "frozenlist-1.7.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cc4df77d638aa2ed703b878dd093725b72a824c3c546c076e8fdf276f78ee84a"}, {file = "frozenlist-1.7.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:716a9973a2cc963160394f701964fe25012600f3d311f60c790400b00e568b61"}, @@ -632,6 +706,7 @@ version = "2025.5.1" description = "File-system specification" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "fsspec-2025.5.1-py3-none-any.whl", hash = "sha256:24d3a2e663d5fc735ab256263c4075f374a174c3410c0b25e5bd1970bceaa462"}, {file = "fsspec-2025.5.1.tar.gz", hash = "sha256:2e55e47a540b91843b755e83ded97c6e897fa0942b11490113f09e9c443c2475"}, @@ -671,6 +746,7 @@ version = "3.10" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.6" +groups = ["main"] files = [ {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, @@ -685,6 +761,7 @@ version = "2.1.0" description = "brain-dead simple config-ini parsing" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760"}, {file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"}, @@ -696,6 +773,8 @@ version = "2021.4.0" description = "Intel OpenMP* Runtime Library" optional = false python-versions = "*" +groups = ["main"] +markers = "platform_system == \"Windows\"" files = [ {file = "intel_openmp-2021.4.0-py2.py3-none-macosx_10_15_x86_64.macosx_11_0_x86_64.whl", hash = "sha256:41c01e266a7fdb631a7609191709322da2bbf24b252ba763f125dd651bcc7675"}, {file = "intel_openmp-2021.4.0-py2.py3-none-manylinux1_i686.whl", hash = "sha256:3b921236a38384e2016f0f3d65af6732cf2c12918087128a9163225451e776f2"}, @@ -704,12 +783,25 @@ files = [ {file = "intel_openmp-2021.4.0-py2.py3-none-win_amd64.whl", hash = "sha256:eef4c8bcc8acefd7f5cd3b9384dbf73d59e2c99fc56545712ded913f43c4a94f"}, ] +[[package]] +name = "itsdangerous" +version = "2.2.0" +description = "Safely pass data to untrusted environments and back." +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef"}, + {file = "itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173"}, +] + [[package]] name = "jinja2" version = "3.1.6" description = "A very fast and expressive template engine." optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"}, {file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"}, @@ -727,6 +819,7 @@ version = "0.44.0" description = "lightweight wrapper around basic LLVM functionality" optional = false python-versions = ">=3.10" +groups = ["main"] files = [ {file = "llvmlite-0.44.0-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:9fbadbfba8422123bab5535b293da1cf72f9f478a65645ecd73e781f962ca614"}, {file = "llvmlite-0.44.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cccf8eb28f24840f2689fb1a45f9c0f7e582dd24e088dcf96e424834af11f791"}, @@ -757,6 +850,7 @@ version = "3.0.2" description = "Safely add untrusted strings to HTML/XML markup." optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"}, {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"}, @@ -827,6 +921,7 @@ version = "0.7.0" description = "McCabe checker, plugin for flake8" optional = false python-versions = ">=3.6" +groups = ["dev"] files = [ {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, @@ -838,6 +933,8 @@ version = "2021.4.0" description = "Intelยฎ oneAPI Math Kernel Library" optional = false python-versions = "*" +groups = ["main"] +markers = "platform_system == \"Windows\"" files = [ {file = "mkl-2021.4.0-py2.py3-none-macosx_10_15_x86_64.macosx_11_0_x86_64.whl", hash = "sha256:67460f5cd7e30e405b54d70d1ed3ca78118370b65f7327d495e9c8847705e2fb"}, {file = "mkl-2021.4.0-py2.py3-none-manylinux1_i686.whl", hash = "sha256:636d07d90e68ccc9630c654d47ce9fdeb036bb46e2b193b3a9ac8cfea683cce5"}, @@ -856,6 +953,7 @@ version = "10.7.0" description = "More routines for operating on iterables, beyond itertools" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "more_itertools-10.7.0-py3-none-any.whl", hash = "sha256:d43980384673cb07d2f7d2d918c616b30c659c089ee23953f601d6609c67510e"}, {file = "more_itertools-10.7.0.tar.gz", hash = "sha256:9fddd5403be01a94b204faadcff459ec3568cf110265d3c54323e1e866ad29d3"}, @@ -867,6 +965,7 @@ version = "0.1.3" description = "An application to display XY position and RGB color information for the pixel currently under the mouse. Works on Python 2 and 3." optional = false python-versions = "*" +groups = ["main"] files = [ {file = "MouseInfo-0.1.3.tar.gz", hash = "sha256:2c62fb8885062b8e520a3cce0a297c657adcc08c60952eb05bc8256ef6f7f6e7"}, ] @@ -882,6 +981,7 @@ version = "1.3.0" description = "Python library for arbitrary-precision floating-point arithmetic" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c"}, {file = "mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f"}, @@ -890,7 +990,7 @@ files = [ [package.extras] develop = ["codecov", "pycodestyle", "pytest (>=4.6)", "pytest-cov", "wheel"] docs = ["sphinx"] -gmpy = ["gmpy2 (>=2.1.0a4)"] +gmpy = ["gmpy2 (>=2.1.0a4) ; platform_python_implementation != \"PyPy\""] tests = ["pytest (>=4.6)"] [[package]] @@ -899,6 +999,7 @@ version = "6.5.0" description = "multidict implementation" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "multidict-6.5.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2e118a202904623b1d2606d1c8614e14c9444b59d64454b0c355044058066469"}, {file = "multidict-6.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a42995bdcaff4e22cb1280ae7752c3ed3fbb398090c6991a2797a4a0e5ed16a9"}, @@ -1021,6 +1122,7 @@ version = "1.1.0" description = "Type system extensions for programs checked with the mypy type checker." optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505"}, {file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"}, @@ -1032,6 +1134,7 @@ version = "3.4.2" description = "Python package for creating and manipulating graphs and networks" optional = false python-versions = ">=3.10" +groups = ["main"] files = [ {file = "networkx-3.4.2-py3-none-any.whl", hash = "sha256:df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f"}, {file = "networkx-3.4.2.tar.gz", hash = "sha256:307c3669428c5362aab27c8a1260aa8f47c4e91d3891f48be0141738d8d053e1"}, @@ -1051,6 +1154,7 @@ version = "0.61.2" description = "compiling Python code using LLVM" optional = false python-versions = ">=3.10" +groups = ["main"] files = [ {file = "numba-0.61.2-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:cf9f9fc00d6eca0c23fc840817ce9f439b9f03c8f03d6246c0e7f0cb15b7162a"}, {file = "numba-0.61.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ea0247617edcb5dd61f6106a56255baab031acc4257bddaeddb3a1003b4ca3fd"}, @@ -1085,6 +1189,7 @@ version = "1.26.4" description = "Fundamental package for array computing in Python" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0"}, {file = "numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a"}, @@ -1130,6 +1235,8 @@ version = "12.1.3.1" description = "CUBLAS native runtime libraries" optional = false python-versions = ">=3" +groups = ["main"] +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" files = [ {file = "nvidia_cublas_cu12-12.1.3.1-py3-none-manylinux1_x86_64.whl", hash = "sha256:ee53ccca76a6fc08fb9701aa95b6ceb242cdaab118c3bb152af4e579af792728"}, {file = "nvidia_cublas_cu12-12.1.3.1-py3-none-win_amd64.whl", hash = "sha256:2b964d60e8cf11b5e1073d179d85fa340c120e99b3067558f3cf98dd69d02906"}, @@ -1141,6 +1248,8 @@ version = "12.1.105" description = "CUDA profiling tools runtime libs." optional = false python-versions = ">=3" +groups = ["main"] +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" files = [ {file = "nvidia_cuda_cupti_cu12-12.1.105-py3-none-manylinux1_x86_64.whl", hash = "sha256:e54fde3983165c624cb79254ae9818a456eb6e87a7fd4d56a2352c24ee542d7e"}, {file = "nvidia_cuda_cupti_cu12-12.1.105-py3-none-win_amd64.whl", hash = "sha256:bea8236d13a0ac7190bd2919c3e8e6ce1e402104276e6f9694479e48bb0eb2a4"}, @@ -1152,6 +1261,8 @@ version = "12.1.105" description = "NVRTC native runtime libraries" optional = false python-versions = ">=3" +groups = ["main"] +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" files = [ {file = "nvidia_cuda_nvrtc_cu12-12.1.105-py3-none-manylinux1_x86_64.whl", hash = "sha256:339b385f50c309763ca65456ec75e17bbefcbbf2893f462cb8b90584cd27a1c2"}, {file = "nvidia_cuda_nvrtc_cu12-12.1.105-py3-none-win_amd64.whl", hash = "sha256:0a98a522d9ff138b96c010a65e145dc1b4850e9ecb75a0172371793752fd46ed"}, @@ -1163,6 +1274,8 @@ version = "12.1.105" description = "CUDA Runtime native Libraries" optional = false python-versions = ">=3" +groups = ["main"] +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" files = [ {file = "nvidia_cuda_runtime_cu12-12.1.105-py3-none-manylinux1_x86_64.whl", hash = "sha256:6e258468ddf5796e25f1dc591a31029fa317d97a0a94ed93468fc86301d61e40"}, {file = "nvidia_cuda_runtime_cu12-12.1.105-py3-none-win_amd64.whl", hash = "sha256:dfb46ef84d73fababab44cf03e3b83f80700d27ca300e537f85f636fac474344"}, @@ -1174,6 +1287,8 @@ version = "8.9.2.26" description = "cuDNN runtime libraries" optional = false python-versions = ">=3" +groups = ["main"] +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" files = [ {file = "nvidia_cudnn_cu12-8.9.2.26-py3-none-manylinux1_x86_64.whl", hash = "sha256:5ccb288774fdfb07a7e7025ffec286971c06d8d7b4fb162525334616d7629ff9"}, ] @@ -1187,6 +1302,8 @@ version = "11.0.2.54" description = "CUFFT native runtime libraries" optional = false python-versions = ">=3" +groups = ["main"] +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" files = [ {file = "nvidia_cufft_cu12-11.0.2.54-py3-none-manylinux1_x86_64.whl", hash = "sha256:794e3948a1aa71fd817c3775866943936774d1c14e7628c74f6f7417224cdf56"}, {file = "nvidia_cufft_cu12-11.0.2.54-py3-none-win_amd64.whl", hash = "sha256:d9ac353f78ff89951da4af698f80870b1534ed69993f10a4cf1d96f21357e253"}, @@ -1198,6 +1315,8 @@ version = "10.3.2.106" description = "CURAND native runtime libraries" optional = false python-versions = ">=3" +groups = ["main"] +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" files = [ {file = "nvidia_curand_cu12-10.3.2.106-py3-none-manylinux1_x86_64.whl", hash = "sha256:9d264c5036dde4e64f1de8c50ae753237c12e0b1348738169cd0f8a536c0e1e0"}, {file = "nvidia_curand_cu12-10.3.2.106-py3-none-win_amd64.whl", hash = "sha256:75b6b0c574c0037839121317e17fd01f8a69fd2ef8e25853d826fec30bdba74a"}, @@ -1209,6 +1328,8 @@ version = "11.4.5.107" description = "CUDA solver native runtime libraries" optional = false python-versions = ">=3" +groups = ["main"] +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" files = [ {file = "nvidia_cusolver_cu12-11.4.5.107-py3-none-manylinux1_x86_64.whl", hash = "sha256:8a7ec542f0412294b15072fa7dab71d31334014a69f953004ea7a118206fe0dd"}, {file = "nvidia_cusolver_cu12-11.4.5.107-py3-none-win_amd64.whl", hash = "sha256:74e0c3a24c78612192a74fcd90dd117f1cf21dea4822e66d89e8ea80e3cd2da5"}, @@ -1225,6 +1346,8 @@ version = "12.1.0.106" description = "CUSPARSE native runtime libraries" optional = false python-versions = ">=3" +groups = ["main"] +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" files = [ {file = "nvidia_cusparse_cu12-12.1.0.106-py3-none-manylinux1_x86_64.whl", hash = "sha256:f3b50f42cf363f86ab21f720998517a659a48131e8d538dc02f8768237bd884c"}, {file = "nvidia_cusparse_cu12-12.1.0.106-py3-none-win_amd64.whl", hash = "sha256:b798237e81b9719373e8fae8d4f091b70a0cf09d9d85c95a557e11df2d8e9a5a"}, @@ -1239,6 +1362,8 @@ version = "2.20.5" description = "NVIDIA Collective Communication Library (NCCL) Runtime" optional = false python-versions = ">=3" +groups = ["main"] +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" files = [ {file = "nvidia_nccl_cu12-2.20.5-py3-none-manylinux2014_aarch64.whl", hash = "sha256:1fc150d5c3250b170b29410ba682384b14581db722b2531b0d8d33c595f33d01"}, {file = "nvidia_nccl_cu12-2.20.5-py3-none-manylinux2014_x86_64.whl", hash = "sha256:057f6bf9685f75215d0c53bf3ac4a10b3e6578351de307abad9e18a99182af56"}, @@ -1250,6 +1375,8 @@ version = "12.9.86" description = "Nvidia JIT LTO Library" optional = false python-versions = ">=3" +groups = ["main"] +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" files = [ {file = "nvidia_nvjitlink_cu12-12.9.86-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:e3f1171dbdc83c5932a45f0f4c99180a70de9bd2718c1ab77d14104f6d7147f9"}, {file = "nvidia_nvjitlink_cu12-12.9.86-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:994a05ef08ef4b0b299829cde613a424382aff7efb08a7172c1fa616cc3af2ca"}, @@ -1262,6 +1389,8 @@ version = "12.1.105" description = "NVIDIA Tools Extension" optional = false python-versions = ">=3" +groups = ["main"] +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" files = [ {file = "nvidia_nvtx_cu12-12.1.105-py3-none-manylinux1_x86_64.whl", hash = "sha256:dc21cf308ca5691e7c04d962e213f8a4aa9bbfa23d95412f452254c2caeb09e5"}, {file = "nvidia_nvtx_cu12-12.1.105-py3-none-win_amd64.whl", hash = "sha256:65f4d98982b31b60026e0e6de73fbdfc09d08a96f4656dd3665ca616a11e1e82"}, @@ -1273,6 +1402,7 @@ version = "20231117" description = "Robust Speech Recognition via Large-Scale Weak Supervision" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "openai-whisper-20231117.tar.gz", hash = "sha256:7af424181436f1800cc0b7d75cf40ede34e9ddf1ba4983a910832fcf4aade4a4"}, ] @@ -1295,6 +1425,7 @@ version = "25.0" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"}, {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"}, @@ -1306,6 +1437,7 @@ version = "0.12.1" description = "Utility library for gitignore style pattern matching of file paths." optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, @@ -1317,6 +1449,7 @@ version = "10.4.0" description = "Python Imaging Library (Fork)" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "pillow-10.4.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:4d9667937cfa347525b319ae34375c37b9ee6b525440f3ef48542fcf66f2731e"}, {file = "pillow-10.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:543f3dc61c18dafb755773efc89aae60d06b6596a63914107f75459cf984164d"}, @@ -1405,7 +1538,7 @@ docs = ["furo", "olefile", "sphinx (>=7.3)", "sphinx-copybutton", "sphinx-inline fpx = ["olefile"] mic = ["olefile"] tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"] -typing = ["typing-extensions"] +typing = ["typing-extensions ; python_version < \"3.10\""] xmp = ["defusedxml"] [[package]] @@ -1414,6 +1547,7 @@ version = "4.3.8" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "platformdirs-4.3.8-py3-none-any.whl", hash = "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4"}, {file = "platformdirs-4.3.8.tar.gz", hash = "sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc"}, @@ -1430,6 +1564,7 @@ version = "1.6.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746"}, {file = "pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3"}, @@ -1445,6 +1580,7 @@ version = "2.1.0" description = "Platform-independent wrapper for platform-dependent APIs" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "plyer-2.1.0-py2.py3-none-any.whl", hash = "sha256:1b1772060df8b3045ed4f08231690ec8f7de30f5a004aa1724665a9074eed113"}, {file = "plyer-2.1.0.tar.gz", hash = "sha256:65b7dfb7e11e07af37a8487eb2aa69524276ef70dad500b07228ce64736baa61"}, @@ -1462,6 +1598,7 @@ version = "0.3.2" description = "Accelerated property cache" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "propcache-0.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:22d9962a358aedbb7a2e36187ff273adeaab9743373a272976d2e348d08c7770"}, {file = "propcache-0.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0d0fda578d1dc3f77b6b5a5dce3b9ad69a8250a891760a548df850a5e8da87f3"}, @@ -1569,6 +1706,7 @@ version = "0.9.54" description = "PyAutoGUI lets Python control the mouse and keyboard, and other GUI automation tasks. For Windows, macOS, and Linux, on Python 3 and 2." optional = false python-versions = "*" +groups = ["main"] files = [ {file = "PyAutoGUI-0.9.54.tar.gz", hash = "sha256:dd1d29e8fd118941cb193f74df57e5c6ff8e9253b99c7b04f39cfc69f3ae04b2"}, ] @@ -1589,6 +1727,7 @@ version = "2.11.1" description = "Python style guide checker" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "pycodestyle-2.11.1-py2.py3-none-any.whl", hash = "sha256:44fe31000b2d866f2e41841b18528a505fbd7fef9017b04eff4e2648a0fadc67"}, {file = "pycodestyle-2.11.1.tar.gz", hash = "sha256:41ba0e7afc9752dfb53ced5489e89f8186be00e599e712660695b7a75ff2663f"}, @@ -1600,6 +1739,7 @@ version = "2.22" description = "C parser in Python" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, @@ -1611,6 +1751,7 @@ version = "3.1.0" description = "passive checker of Python programs" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "pyflakes-3.1.0-py2.py3-none-any.whl", hash = "sha256:4132f6d49cb4dae6819e5379898f2b8cce3c5f23994194c24b77d5da2e36f774"}, {file = "pyflakes-3.1.0.tar.gz", hash = "sha256:a0aae034c444db0071aa077972ba4768d40c830d9539fd45bf4cd3f8f6992efc"}, @@ -1622,6 +1763,7 @@ version = "2.6.1" description = "Python Game Development" optional = false python-versions = ">=3.6" +groups = ["main"] files = [ {file = "pygame-2.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9beeb647e555afb5657111fa83acb74b99ad88761108eaea66472e8b8547b55b"}, {file = "pygame-2.6.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:10e3d2a55f001f6c0a6eb44aa79ea7607091c9352b946692acedb2ac1482f1c9"}, @@ -1691,6 +1833,7 @@ version = "0.0.9" description = "A simple, cross-platform module for obtaining GUI information on application's windows." optional = false python-versions = "*" +groups = ["main"] files = [ {file = "PyGetWindow-0.0.9.tar.gz", hash = "sha256:17894355e7d2b305cd832d717708384017c1698a90ce24f6f7fbf0242dd0a688"}, ] @@ -1704,6 +1847,7 @@ version = "1.0.9" description = "A simple, cross-platform, pure Python module for JavaScript-like message boxes." optional = false python-versions = "*" +groups = ["main"] files = [ {file = "PyMsgBox-1.0.9.tar.gz", hash = "sha256:2194227de8bff7a3d6da541848705a155dcbb2a06ee120d9f280a1d7f51263ff"}, ] @@ -1714,6 +1858,7 @@ version = "1.8.1" description = "Monitor and control user input devices" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "pynput-1.8.1-py2.py3-none-any.whl", hash = "sha256:42dfcf27404459ca16ca889c8fb8ffe42a9fe54f722fd1a3e130728e59e768d2"}, {file = "pynput-1.8.1.tar.gz", hash = "sha256:70d7c8373ee98911004a7c938742242840a5628c004573d84ba849d4601df81e"}, @@ -1732,6 +1877,8 @@ version = "11.1" description = "Python<->ObjC Interoperability Module" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\"" files = [ {file = "pyobjc-11.1-py3-none-any.whl", hash = "sha256:903f822cba40be53d408b8eaf834514937ec0b4e6af1c5ecc24fcb652812dd85"}, {file = "pyobjc-11.1.tar.gz", hash = "sha256:a71b14389657811d658526ba4d5faba4ef7eadbddcf9fe8bf4fb3a6261effba3"}, @@ -1907,6 +2054,8 @@ version = "11.1" description = "Python<->ObjC Interoperability Module" optional = false python-versions = ">=3.8" +groups = ["main"] +markers = "sys_platform == \"darwin\" or platform_system == \"Darwin\"" files = [ {file = "pyobjc_core-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:4c7536f3e94de0a3eae6bb382d75f1219280aa867cdf37beef39d9e7d580173c"}, {file = "pyobjc_core-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ec36680b5c14e2f73d432b03ba7c1457dc6ca70fa59fd7daea1073f2b4157d33"}, @@ -1925,6 +2074,8 @@ version = "11.1" description = "Wrappers for the framework Accessibility on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"20.0\"" files = [ {file = "pyobjc_framework_accessibility-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8bbe921650607461fcaba6cfb921e8cb0d301e870553fb5353d7f1787a355696"}, {file = "pyobjc_framework_accessibility-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:332263153d829b946b311ddc8b9a4402b52d40a572b44c69c3242451ced1b008"}, @@ -1948,6 +2099,8 @@ version = "11.1" description = "Wrappers for the framework Accounts on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"12.0\"" files = [ {file = "pyobjc_framework_accounts-11.1-py2.py3-none-any.whl", hash = "sha256:9c3fe342be7b8e73cba735e5a38affbe349cf8bc19091aa4fd788eabf2074b72"}, {file = "pyobjc_framework_accounts-11.1.tar.gz", hash = "sha256:384fec156e13ff75253bb094339013f4013464f6dfd47e2f7de3e2ae7441c030"}, @@ -1963,6 +2116,8 @@ version = "11.1" description = "Wrappers for the framework AddressBook on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\"" files = [ {file = "pyobjc_framework_addressbook-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:013db030aebe7c09752492ed8f9b12ff41b1264ed119e9858241d57276961e75"}, {file = "pyobjc_framework_addressbook-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d1d69330b5a87a29d26feea95dcf40681fd00ba3b40ac89579072ce536b6b647"}, @@ -1985,6 +2140,8 @@ version = "11.1" description = "Wrappers for the framework AdServices on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"20.0\"" files = [ {file = "pyobjc_framework_adservices-11.1-py2.py3-none-any.whl", hash = "sha256:1744f59a75b2375e139c39f3e85658e62cd10cc0f12b158a80421f18734e9ffc"}, {file = "pyobjc_framework_adservices-11.1.tar.gz", hash = "sha256:44c72f8163705c9aa41baca938fdb17dde257639e5797e6a5c3a2b2d8afdade9"}, @@ -2000,6 +2157,8 @@ version = "11.1" description = "Wrappers for the framework AdSupport on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"18.0\"" files = [ {file = "pyobjc_framework_adsupport-11.1-py2.py3-none-any.whl", hash = "sha256:c3e009612778948910d3a7135b9d77b9b7c06aab29d40957770834c083acf825"}, {file = "pyobjc_framework_adsupport-11.1.tar.gz", hash = "sha256:78b9667c275785df96219d205bd4309731869c3298d0931e32aed83bede29096"}, @@ -2015,6 +2174,8 @@ version = "11.1" description = "Wrappers for the framework AppleScriptKit on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\"" files = [ {file = "pyobjc_framework_applescriptkit-11.1-py2.py3-none-any.whl", hash = "sha256:e22cbc9d1a25a4a713f21aa94dd017c311186b02062fc7ffbde3009495fb0067"}, {file = "pyobjc_framework_applescriptkit-11.1.tar.gz", hash = "sha256:477707352eaa6cc4a5f8c593759dc3227a19d5958481b1482f0d59394a4601c3"}, @@ -2030,6 +2191,8 @@ version = "11.1" description = "Wrappers for the framework AppleScriptObjC on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"10.0\"" files = [ {file = "pyobjc_framework_applescriptobjc-11.1-py2.py3-none-any.whl", hash = "sha256:ac22526fd1f0a3b07ac1d77f90046b77f10ec9549182114f2428ee1e96d3de2b"}, {file = "pyobjc_framework_applescriptobjc-11.1.tar.gz", hash = "sha256:c8a0ec975b64411a4f16a1280c5ea8dbe949fd361e723edd343102f0f95aba6e"}, @@ -2045,6 +2208,8 @@ version = "11.1" description = "Wrappers for the framework ApplicationServices on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "sys_platform == \"darwin\" or platform_system == \"Darwin\"" files = [ {file = "pyobjc_framework_applicationservices-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:89aa713f16f1de66efd82f3be77c632ad1068e51e0ef0c2b0237ac7c7f580814"}, {file = "pyobjc_framework_applicationservices-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:cf45d15eddae36dec2330a9992fc852476b61c8f529874b9ec2805c768a75482"}, @@ -2069,6 +2234,8 @@ version = "11.1" description = "Wrappers for the framework AppTrackingTransparency on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"20.0\"" files = [ {file = "pyobjc_framework_apptrackingtransparency-11.1-py2.py3-none-any.whl", hash = "sha256:e25c3eae25d24ee8b523b7ecc4d2b07af37c7733444b80c4964071dea7b0cb19"}, {file = "pyobjc_framework_apptrackingtransparency-11.1.tar.gz", hash = "sha256:796cc5f83346c10973806cfb535d4200b894a5d2626ff2eeb1972d594d14fed4"}, @@ -2084,6 +2251,8 @@ version = "11.1" description = "Wrappers for the framework AudioVideoBridging on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"12.0\"" files = [ {file = "pyobjc_framework_audiovideobridging-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:dd88f7083cc7858c21bfc151a9745e6c24d4f4fa1c3ad5a50673f34c42e17111"}, {file = "pyobjc_framework_audiovideobridging-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:db570433910d1df49cc45d25f7a966227033c794fb41133d59212689b86b1ac6"}, @@ -2106,6 +2275,8 @@ version = "11.1" description = "Wrappers for the framework AuthenticationServices on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"19.0\"" files = [ {file = "pyobjc_framework_authenticationservices-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:4454c2f69c04fc31c0ec0924ccb3aa9bfe8a11d5632d83172904b5b4cc34d8b5"}, {file = "pyobjc_framework_authenticationservices-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3987b7fc9493c2ba77b773df99f6631bff1ee9b957d99e34afa6b4e1c9d48bfb"}, @@ -2128,6 +2299,8 @@ version = "11.1" description = "Wrappers for the framework AutomaticAssessmentConfiguration on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"19.0\"" files = [ {file = "pyobjc_framework_automaticassessmentconfiguration-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a88e75b600c570939190795ead28e097c62aa040467088352c550df52d96e8d4"}, {file = "pyobjc_framework_automaticassessmentconfiguration-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:50cc5466bec1f58f79921d49544b525b56897cb985dfcfabf825ee231c27bcfc"}, @@ -2150,6 +2323,8 @@ version = "11.1" description = "Wrappers for the framework Automator on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\"" files = [ {file = "pyobjc_framework_automator-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:569f9fedcd107721c59eccce89c5befe429baace59616f9f1ceeb9689a65f273"}, {file = "pyobjc_framework_automator-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:bf675a19edd97de9c19dcfd0fea9af9ebbd3409786c162670d1d71cb2738e341"}, @@ -2172,6 +2347,8 @@ version = "11.1" description = "Wrappers for the framework AVFoundation on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"11.0\"" files = [ {file = "pyobjc_framework_avfoundation-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:09542590d1f3aa96d4d1a37712b98fd9657e250d9ea06ecdf2a8a59c837a2cb6"}, {file = "pyobjc_framework_avfoundation-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8a0ccbdba46b69dec1d12eea52eef56fcd63c492f73e41011bb72508b2aa2d0e"}, @@ -2197,6 +2374,8 @@ version = "11.1" description = "Wrappers for the framework AVKit on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"13.0\"" files = [ {file = "pyobjc_framework_avkit-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e211c8dce60b7dd7ad2994ad404041a50e183a039b253f891dbb8cab48f5e687"}, {file = "pyobjc_framework_avkit-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:88f70e2a399e43ce7bc3124b3b35d65537daddb358ea542fbb0146fa6406be8a"}, @@ -2220,6 +2399,8 @@ version = "11.1" description = "Wrappers for the framework AVRouting on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"22.0\"" files = [ {file = "pyobjc_framework_avrouting-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:230daf3e5135f6ad0ab0acd6cf3a01a4b0d6b07eb82d63d6e8037479b6cac4ea"}, {file = "pyobjc_framework_avrouting-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:45cbabbf69764b2467d78adb8f3b7f209d1a8ee690e19f9a32d05c62a9c3a131"}, @@ -2242,6 +2423,8 @@ version = "11.1" description = "Wrappers for the framework BackgroundAssets on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"22.0\"" files = [ {file = "pyobjc_framework_backgroundassets-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:30b4fe4b711e1dacf48074f10b8cad680b4e2c422652ae3c32b1f89bb5bac54e"}, {file = "pyobjc_framework_backgroundassets-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:bd371ce08d1b79f540d5994139898097b83b1d4e4471c264892433d448b24de0"}, @@ -2264,6 +2447,8 @@ version = "11.1" description = "Wrappers for the framework BrowserEngineKit on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"23.4\"" files = [ {file = "pyobjc_framework_browserenginekit-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cbfb0183378a0dc836bcdf3798c358c6d217aeaac726e1d44be7cc123994c0fa"}, {file = "pyobjc_framework_browserenginekit-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:29b5f5949170af0235485e79aa465a7af2b2e0913d0c2c9ab1ac033224a90edb"}, @@ -2289,6 +2474,8 @@ version = "11.1" description = "Wrappers for the framework BusinessChat on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"18.0\"" files = [ {file = "pyobjc_framework_businesschat-11.1-py2.py3-none-any.whl", hash = "sha256:7fdc1219b988ce3ae896bffd01f547c06cec3b4e4b2d0aa04d251444d7f1c2db"}, {file = "pyobjc_framework_businesschat-11.1.tar.gz", hash = "sha256:69589d2f0cb4e7892e5ecc6aed79b1abd1ec55c099a7faacae6a326bc921259d"}, @@ -2304,6 +2491,8 @@ version = "11.1" description = "Wrappers for the framework CalendarStore on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"9.0\"" files = [ {file = "pyobjc_framework_calendarstore-11.1-py2.py3-none-any.whl", hash = "sha256:bf066e17392c978becf17a61863eb81727bf593a2bfdab261177126072557e24"}, {file = "pyobjc_framework_calendarstore-11.1.tar.gz", hash = "sha256:858ee00e6a380d9c086c2d7db82c116a6c406234038e0ec8fc2ad02e385dc437"}, @@ -2319,6 +2508,8 @@ version = "11.1" description = "Wrappers for the framework CallKit on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"20.0\"" files = [ {file = "pyobjc_framework_callkit-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:afa1520c462b571458d0d6139681820b4abd41d7dbd7d4892fb2617dd9037846"}, {file = "pyobjc_framework_callkit-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1db8b74abd6489d73c8619972730bea87a7d1f55d47649150fc1a30fdc6840fb"}, @@ -2341,6 +2532,8 @@ version = "11.1" description = "Wrappers for the framework Carbon on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\"" files = [ {file = "pyobjc_framework_carbon-11.1-py2.py3-none-any.whl", hash = "sha256:1bf66853e939315ad7ee968170b16dd12cb838c42b80dfcd5354687760998825"}, {file = "pyobjc_framework_carbon-11.1.tar.gz", hash = "sha256:047f098535479efa3ab89da1ebdf3cf9ec0b439a33a4f32806193886e9fcea71"}, @@ -2356,6 +2549,8 @@ version = "11.1" description = "Wrappers for the framework CFNetwork on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\"" files = [ {file = "pyobjc_framework_cfnetwork-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8cf313e3ac580ee0d3c2345771e6cafc4ba95a10418e3e535feeda4c62b68295"}, {file = "pyobjc_framework_cfnetwork-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d7a24746d0754b3a0042def2cd64aa205e5614f12ea0de9461c8e26d97633c72"}, @@ -2378,6 +2573,8 @@ version = "11.1" description = "Wrappers for the framework Cinematic on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"23.0\"" files = [ {file = "pyobjc_framework_cinematic-11.1-py2.py3-none-any.whl", hash = "sha256:b62c024c1a9c7890481bc2fdfaf0cd3c251a4a08357d57dc1795d98920fcdbd1"}, {file = "pyobjc_framework_cinematic-11.1.tar.gz", hash = "sha256:efde39a6a2379e1738dbc5434b2470cd187cf3114ffb81390b3b1abda470b382"}, @@ -2396,6 +2593,8 @@ version = "11.1" description = "Wrappers for the framework ClassKit on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"20.0\"" files = [ {file = "pyobjc_framework_classkit-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1cb2a2b68fb4c773e9ff250f2ab87c41b7464778d4c7e0174600de3cf5f7b52e"}, {file = "pyobjc_framework_classkit-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:018da363d06f3615c07a8623cbdb024a31b1f8b96a933ff2656c0e903063842c"}, @@ -2418,6 +2617,8 @@ version = "11.1" description = "Wrappers for the framework CloudKit on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"14.0\"" files = [ {file = "pyobjc_framework_cloudkit-11.1-py2.py3-none-any.whl", hash = "sha256:c583e40c710cf85ebe34173d1d2995e832a20127edc8899b2f35b13f98498af1"}, {file = "pyobjc_framework_cloudkit-11.1.tar.gz", hash = "sha256:40d2dc4bf28c5be9b836b01e4d267a15d847d756c2a65530e1fcd79b2825e86d"}, @@ -2436,6 +2637,8 @@ version = "11.1" description = "Wrappers for the Cocoa frameworks on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "sys_platform == \"darwin\" or platform_system == \"Darwin\"" files = [ {file = "pyobjc_framework_cocoa-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b27a5bdb3ab6cdeb998443ff3fce194ffae5f518c6a079b832dbafc4426937f9"}, {file = "pyobjc_framework_cocoa-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7b9a9b8ba07f5bf84866399e3de2aa311ed1c34d5d2788a995bdbe82cc36cfa0"}, @@ -2457,6 +2660,8 @@ version = "11.1" description = "Wrappers for the framework Collaboration on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"9.0\"" files = [ {file = "pyobjc_framework_collaboration-11.1-py2.py3-none-any.whl", hash = "sha256:3629ea5b56c513fb330d43952afabb2df2a2ac2f9048b8ec6e8ab4486191390a"}, {file = "pyobjc_framework_collaboration-11.1.tar.gz", hash = "sha256:4564e3931bfc51773623d4f57f2431b58a39b75cb964ae5c48d27ee4dde2f4ea"}, @@ -2472,6 +2677,8 @@ version = "11.1" description = "Wrappers for the framework ColorSync on Mac OS X" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"17.0\"" files = [ {file = "pyobjc_framework_colorsync-11.1-py2.py3-none-any.whl", hash = "sha256:d19d6da2c7175a3896a63c9b40a8ab98ade0779a5b40062789681501c33efd5c"}, {file = "pyobjc_framework_colorsync-11.1.tar.gz", hash = "sha256:7a346f71f34b2ccd1b020a34c219b85bf8b6f6e05283d503185aeb7767a269dd"}, @@ -2487,6 +2694,8 @@ version = "11.1" description = "Wrappers for the framework Contacts on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"15.0\"" files = [ {file = "pyobjc_framework_contacts-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:01a83ac9e03cab16ee7eb8755c87ce1790c8465487a07994da5569d843facc05"}, {file = "pyobjc_framework_contacts-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:68148653f27c1eaeff2ad4831b5e68393071a382aab773629cd047ce55556726"}, @@ -2509,6 +2718,8 @@ version = "11.1" description = "Wrappers for the framework ContactsUI on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"15.0\"" files = [ {file = "pyobjc_framework_contactsui-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cf3468444ba88dd6814e02ffa624d13b7857a7baf042462452bb292337c013da"}, {file = "pyobjc_framework_contactsui-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1c0f03c71e63daf5dbf760bf0e45620618a6f1ea62f8c17e288463c1fd4d2685"}, @@ -2532,6 +2743,8 @@ version = "11.1" description = "Wrappers for the framework CoreAudio on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\"" files = [ {file = "pyobjc_framework_coreaudio-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:551c8aac6fdfbd34c3e2d4ce90b36a411e81be20581b978fa4da1a495489792d"}, {file = "pyobjc_framework_coreaudio-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:73a46f0db2fa8ca2e8c47c3ddcc2751e67a0f8600246a6718553b15ee0dbbdb6"}, @@ -2554,6 +2767,8 @@ version = "11.1" description = "Wrappers for the framework CoreAudioKit on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\"" files = [ {file = "pyobjc_framework_coreaudiokit-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3be9e254d607324cfc059e3f11fe528fc95d59bb72e585d4bb4ecf92ef493000"}, {file = "pyobjc_framework_coreaudiokit-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4743fbd210159cffffb0a7b8e06bf8b8527ba4bf01e76806fae2696fd6990e77"}, @@ -2577,6 +2792,8 @@ version = "11.1" description = "Wrappers for the framework CoreBluetooth on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"14.0\"" files = [ {file = "pyobjc_framework_corebluetooth-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ab509994503a5f0ec0f446a7ccc9f9a672d5a427d40dba4563dd00e8e17dfb06"}, {file = "pyobjc_framework_corebluetooth-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:433b8593eb1ea8b6262b243ec903e1de4434b768ce103ebe15aac249b890cc2a"}, @@ -2599,6 +2816,8 @@ version = "11.1" description = "Wrappers for the framework CoreData on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\"" files = [ {file = "pyobjc_framework_coredata-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ceeba4f9d156610f17e643fc8bdf40bd785bda92fad6f4cbf0954894aa4db165"}, {file = "pyobjc_framework_coredata-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c66ae04cc658eafdfb987f9705e21f9782edee6773a8adb6bfa190500e4e7e29"}, @@ -2621,6 +2840,8 @@ version = "11.1" description = "Wrappers for the framework CoreHaptics on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"19.0\"" files = [ {file = "pyobjc_framework_corehaptics-11.1-py2.py3-none-any.whl", hash = "sha256:8f8c47ccca5052d07f95d2f35e6e399c5ac1f2072ba9d9eaae902edf4e3a7af4"}, {file = "pyobjc_framework_corehaptics-11.1.tar.gz", hash = "sha256:e5da3a97ed6aca9b7268c8c5196c0a339773a50baa72d1502d3435dc1a2a80f1"}, @@ -2636,6 +2857,8 @@ version = "11.1" description = "Wrappers for the framework CoreLocation on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"10.0\"" files = [ {file = "pyobjc_framework_corelocation-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:90d7811a2b730f604b0a2ac54c3c822e6e048287e2cd1db80fd3bd1caac6c1c0"}, {file = "pyobjc_framework_corelocation-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ea261e7d87c6f62f1b03c252c273ea7fd6f314e3e73c69c6fb3fe807bf183462"}, @@ -2658,6 +2881,8 @@ version = "11.1" description = "Wrappers for the framework CoreMedia on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"11.0\"" files = [ {file = "pyobjc_framework_coremedia-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91231957d25b6d191983166cf218189b5a01e267dadde35eb3a4c359dc473ccb"}, {file = "pyobjc_framework_coremedia-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:aacf47006e1c6bf6124fb2b5016a8d5fd5cf504b6b488f9eba4e389ab0f0a051"}, @@ -2680,6 +2905,8 @@ version = "11.1" description = "Wrappers for the framework CoreMediaIO on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"11.0\"" files = [ {file = "pyobjc_framework_coremediaio-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:49120679162416ad5a4cf67b49830cf3d38b60bd94496e2a4cad3895496b558d"}, {file = "pyobjc_framework_coremediaio-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4438713ee4611d5310f4f2e71e557b6138bc79c0363e3d45ecb8c09227dfa58e"}, @@ -2702,6 +2929,8 @@ version = "11.1" description = "Wrappers for the framework CoreMIDI on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\"" files = [ {file = "pyobjc_framework_coremidi-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5dbd846a2c3f23795a49f363c1e22f0dd4d91ac675f9d52fb5ba93a2bd212d1f"}, {file = "pyobjc_framework_coremidi-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5f8c2fdc9d1b7967e2a5ec0d5281eaddc00477bed9753aa14d5b881dc3a9ad8f"}, @@ -2724,6 +2953,8 @@ version = "11.1" description = "Wrappers for the framework CoreML on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"17.0\"" files = [ {file = "pyobjc_framework_coreml-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b1b1b849ca91e0d62ed6dfd200d95ca8d023d6edff854aae77ba54eb0542415f"}, {file = "pyobjc_framework_coreml-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b5be7889ad99da1aca040238fd99af9ee87ea8a6628f24d33e2e4890b88dd139"}, @@ -2746,6 +2977,8 @@ version = "11.1" description = "Wrappers for the framework CoreMotion on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"19.0\"" files = [ {file = "pyobjc_framework_coremotion-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:87e642511279c080dd9d0c7b0af3903191a6400a6c3a3caeb54233cb642a6966"}, {file = "pyobjc_framework_coremotion-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:501248a726816e05552d1c1f7e2be2c7305cda792c46905d9aee7079dfad2eea"}, @@ -2768,6 +3001,8 @@ version = "11.1" description = "Wrappers for the framework CoreServices on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\"" files = [ {file = "pyobjc_framework_coreservices-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:96578c31035fed361d030b0168ae5fc593aa26aa78f6c9946b8da6007e46e08e"}, {file = "pyobjc_framework_coreservices-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f7260e09a0550d57756ad655f3d3815f21fc3f0386aed014be4b46194c346941"}, @@ -2791,6 +3026,8 @@ version = "11.1" description = "Wrappers for the framework CoreSpotlight on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"17.0\"" files = [ {file = "pyobjc_framework_corespotlight-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b2d3ddabf74ef04933eb28b1a1c5ed93748b31e64b9c29d5eb88fafab5605c87"}, {file = "pyobjc_framework_corespotlight-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d3c571289ce9107f1ade92ad036633f81355f22f70e8ba82d7335f1757381b89"}, @@ -2813,6 +3050,8 @@ version = "11.1" description = "Wrappers for the framework CoreText on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "sys_platform == \"darwin\" or platform_system == \"Darwin\"" files = [ {file = "pyobjc_framework_coretext-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:515be6beb48c084ee413c00c4e9fbd6e730c1b8a24270f4c618fc6c7ba0011ce"}, {file = "pyobjc_framework_coretext-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b4f4d2d2a6331fa64465247358d7aafce98e4fb654b99301a490627a073d021e"}, @@ -2836,6 +3075,8 @@ version = "11.1" description = "Wrappers for the framework CoreWLAN on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"10.0\"" files = [ {file = "pyobjc_framework_corewlan-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8a30698aea3a2c5130f4ff309bda45029f66ef76574d3cefce6159e9a5cc6bdd"}, {file = "pyobjc_framework_corewlan-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e12f127b37a7ab8f349167332633392f2d6d29b87c9b98137a289d0fc1e07b5b"}, @@ -2858,6 +3099,8 @@ version = "11.1" description = "Wrappers for the framework CryptoTokenKit on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"14.0\"" files = [ {file = "pyobjc_framework_cryptotokenkit-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d53ef13571afab5b2df5b2c118c3f296abae095abe6f0c9ebd105bab31527369"}, {file = "pyobjc_framework_cryptotokenkit-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2b76fb928bc398091141dc52b26e02511065afd0b6de5533fa0e71ab13c51589"}, @@ -2880,6 +3123,8 @@ version = "11.1" description = "Wrappers for the framework DataDetection on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"21.0\"" files = [ {file = "pyobjc_framework_datadetection-11.1-py2.py3-none-any.whl", hash = "sha256:5afd3dde7bba3324befb7a3133c9aeaa5088efd72dccc0804267a74799f4a12f"}, {file = "pyobjc_framework_datadetection-11.1.tar.gz", hash = "sha256:cbe0080b51e09b2f91eaf2a9babec3dcf2883d7966bc0abd8393ef7abfcfc5db"}, @@ -2895,6 +3140,8 @@ version = "11.1" description = "Wrappers for the framework DeviceCheck on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"19.0\"" files = [ {file = "pyobjc_framework_devicecheck-11.1-py2.py3-none-any.whl", hash = "sha256:8edb36329cdd5d55e2c2c57c379cb5ba1f500f74a08fe8d2612b1a69b7a26435"}, {file = "pyobjc_framework_devicecheck-11.1.tar.gz", hash = "sha256:8b05973eb2673571144d81346336e749a21cec90bd7fcaade76ffd3b147a0741"}, @@ -2910,6 +3157,8 @@ version = "11.1" description = "Wrappers for the framework DeviceDiscoveryExtension on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"24.0\"" files = [ {file = "pyobjc_framework_devicediscoveryextension-11.1-py2.py3-none-any.whl", hash = "sha256:96e5b13c718bd0e6c80fbd4e14b8073cffc88b3ab9bb1bbb4dab7893a62e4f11"}, {file = "pyobjc_framework_devicediscoveryextension-11.1.tar.gz", hash = "sha256:ae160ea40f25d3ee5e7ce80ac9c1b315f94d0a4c7ccb86920396f71c6bf799a0"}, @@ -2925,6 +3174,8 @@ version = "11.1" description = "Wrappers for the framework DictionaryServices on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"9.0\"" files = [ {file = "pyobjc_framework_dictionaryservices-11.1-py2.py3-none-any.whl", hash = "sha256:92f4871066653f18e2394ac93b0a2ab50588d60020f6b3bd93e97b67cd511326"}, {file = "pyobjc_framework_dictionaryservices-11.1.tar.gz", hash = "sha256:39c24452d0ddd037afeb73a1742614c94535f15b1c024a8a6cc7ff081e1d22e7"}, @@ -2940,6 +3191,8 @@ version = "11.1" description = "Wrappers for the framework DiscRecording on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\"" files = [ {file = "pyobjc_framework_discrecording-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:9bae2419669ec3aadd3e7bf98dd92c80839242c7af4ab94364f5008cfe8e5603"}, {file = "pyobjc_framework_discrecording-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:dc8a7820fc193c2bfcd843c31de945dc45e77e5413089eabbc72be16a4f52e53"}, @@ -2962,6 +3215,8 @@ version = "11.1" description = "Wrappers for the framework DiscRecordingUI on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\"" files = [ {file = "pyobjc_framework_discrecordingui-11.1-py2.py3-none-any.whl", hash = "sha256:33233b87d7b85ce277a51d27acca0f5b38485cf1d1dc8e28a065910047766ee2"}, {file = "pyobjc_framework_discrecordingui-11.1.tar.gz", hash = "sha256:a9f10e2e7ee19582c77f0755ae11a64e3d61c652cbd8a5bf52756f599be24797"}, @@ -2978,6 +3233,8 @@ version = "11.1" description = "Wrappers for the framework DiskArbitration on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\"" files = [ {file = "pyobjc_framework_diskarbitration-11.1-py2.py3-none-any.whl", hash = "sha256:6a8e551e54df481a9081abba6fd680f6633babe5c7735f649731b22896bb6f08"}, {file = "pyobjc_framework_diskarbitration-11.1.tar.gz", hash = "sha256:a933efc6624779a393fafe0313e43378bcae2b85d6d15cff95ac30048c1ef490"}, @@ -2993,6 +3250,8 @@ version = "11.1" description = "Wrappers for the framework DVDPlayback on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\"" files = [ {file = "pyobjc_framework_dvdplayback-11.1-py2.py3-none-any.whl", hash = "sha256:6094e4651ea29540ac817294b27e1596b9d1883d30e78fb5f9619daf94ed30cb"}, {file = "pyobjc_framework_dvdplayback-11.1.tar.gz", hash = "sha256:b44c36a62c8479e649133216e22941859407cca5796b5f778815ef9340a838f4"}, @@ -3008,6 +3267,8 @@ version = "11.1" description = "Wrappers for the framework Accounts on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"12.0\"" files = [ {file = "pyobjc_framework_eventkit-11.1-py2.py3-none-any.whl", hash = "sha256:c303207610d9c742f4090799f60103cede466002f3c89cf66011c8bf1987750b"}, {file = "pyobjc_framework_eventkit-11.1.tar.gz", hash = "sha256:5643150f584243681099c5e9435efa833a913e93fe9ca81f62007e287349b561"}, @@ -3023,6 +3284,8 @@ version = "11.1" description = "Wrappers for the framework ExceptionHandling on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\"" files = [ {file = "pyobjc_framework_exceptionhandling-11.1-py2.py3-none-any.whl", hash = "sha256:31e6538160dfd7526ac0549bc0fce5d039932aea84c36abbe7b49c79ffc62437"}, {file = "pyobjc_framework_exceptionhandling-11.1.tar.gz", hash = "sha256:e010f56bf60ab4e9e3225954ebb53e9d7135d37097043ac6dd2a3f35770d4efa"}, @@ -3038,6 +3301,8 @@ version = "11.1" description = "Wrappers for the framework ExecutionPolicy on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"19.0\"" files = [ {file = "pyobjc_framework_executionpolicy-11.1-py2.py3-none-any.whl", hash = "sha256:7d4141e572cb916e73bb34bb74f6f976a8aa0a396a0bffd1cf66e5505f7c76c8"}, {file = "pyobjc_framework_executionpolicy-11.1.tar.gz", hash = "sha256:3280ad2f4c5eaf45901f310cee0c52db940c0c63e959ad082efb8df41055d986"}, @@ -3053,6 +3318,8 @@ version = "11.1" description = "Wrappers for the framework ExtensionKit on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"22.0\"" files = [ {file = "pyobjc_framework_extensionkit-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:eb766b18ba23f15eeb1235c2a42f487591ff905644f9f12e44efe987ce3fbd38"}, {file = "pyobjc_framework_extensionkit-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:61fd9f9758f95bcff2bf26fe475f679dfff9457d7130f114089e88fd5009675a"}, @@ -3075,6 +3342,8 @@ version = "11.1" description = "Wrappers for the framework ExternalAccessory on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"17.0\"" files = [ {file = "pyobjc_framework_externalaccessory-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a36e2718d364373b10ac7b8151cffe8e3dedfcc72470fe2b6eed4e9c5d954034"}, {file = "pyobjc_framework_externalaccessory-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a2b22f72b83721d841e5a3128df29fc41d785597357c6bbce84555a2b51a1e9d"}, @@ -3097,6 +3366,8 @@ version = "11.1" description = "Wrappers for the framework FileProvider on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"19.0\"" files = [ {file = "pyobjc_framework_fileprovider-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:17e0da2e00900a1b25aca1cdbbda2c8097573ce07d6650d572968dff45c06ca7"}, {file = "pyobjc_framework_fileprovider-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:888d6fb3fd625889ce0e409320c3379330473a386095cb4eda2b4caf0198ff66"}, @@ -3119,6 +3390,8 @@ version = "11.1" description = "Wrappers for the framework FileProviderUI on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"19.0\"" files = [ {file = "pyobjc_framework_fileproviderui-11.1-py2.py3-none-any.whl", hash = "sha256:f2765f114c2f4356aa41fb45c621fa8f0a4fae0b6d3c6b1a274366f5fe7fe829"}, {file = "pyobjc_framework_fileproviderui-11.1.tar.gz", hash = "sha256:162a23e67f59e1bb247e84dda88d513d7944d815144901a46be6fe051b6c7970"}, @@ -3134,6 +3407,8 @@ version = "11.1" description = "Wrappers for the framework FinderSync on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"14.0\"" files = [ {file = "pyobjc_framework_findersync-11.1-py2.py3-none-any.whl", hash = "sha256:c72b0fd8b746b99cfa498da36c5bb333121b2080ad73fa8cbea05cd47db1fa82"}, {file = "pyobjc_framework_findersync-11.1.tar.gz", hash = "sha256:692364937f418f0e4e4abd395a09a7d4a0cdd55fd4e0184de85ee59642defb6e"}, @@ -3149,6 +3424,8 @@ version = "11.1" description = "Wrappers for the framework FSEvents on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\"" files = [ {file = "pyobjc_framework_fsevents-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0f51d55e94fd84bc585a5c4ee63634297e192b256298a1372405649054220d13"}, {file = "pyobjc_framework_fsevents-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:95cc5d839d298b8e95175fb72df8a8e1b08773fd2e0d031efe91eee23e0c8830"}, @@ -3171,6 +3448,8 @@ version = "11.1" description = "Wrappers for the framework FSKit on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"24.4\"" files = [ {file = "pyobjc_framework_fskit-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:db96e20789186b5f3be132cc7041e38cdaf98904da82b80fbcb2564365738517"}, {file = "pyobjc_framework_fskit-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:59a939ac8442d648f73a3da75923aa3637ac4693850d995f1914260c8f4f7947"}, @@ -3193,6 +3472,8 @@ version = "11.1" description = "Wrappers for the framework GameCenter on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"12.0\"" files = [ {file = "pyobjc_framework_gamecenter-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8543725d4fad635bbbe3aaeea0df8d31a419f2cab0d9f9b411ae2212c8fac5eb"}, {file = "pyobjc_framework_gamecenter-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:81abe136292ea157acb6c54871915fe6d386146a9386179ded0b974ac435045c"}, @@ -3215,6 +3496,8 @@ version = "11.1" description = "Wrappers for the framework GameController on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"13.0\"" files = [ {file = "pyobjc_framework_gamecontroller-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f19e4e645966e99c08552d0841c9e535326506dfc0c0ef097a6ad62f71b7e99d"}, {file = "pyobjc_framework_gamecontroller-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:782779f080508acf869187c0cbd3a48c55ee059d3a14fe89ccd6349537923214"}, @@ -3237,6 +3520,8 @@ version = "11.1" description = "Wrappers for the framework GameKit on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"12.0\"" files = [ {file = "pyobjc_framework_gamekit-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:18ce0e373613a0b9f78969218b884c3191958e353e3462fbfc6d51d758ada41c"}, {file = "pyobjc_framework_gamekit-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5e07c25eab051905c6bd46f368d8b341ef8603dce588ff6dbd82d609dd4fbf71"}, @@ -3260,6 +3545,8 @@ version = "11.1" description = "Wrappers for the framework GameplayKit on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"15.0\"" files = [ {file = "pyobjc_framework_gameplaykit-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8cc9b2a476f79d593d9617fdb8c5ac27d1cf9256063379e3df9b6519c462eb48"}, {file = "pyobjc_framework_gameplaykit-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ac9f50941988c30175149af481a49b2026c56a9a497c6dbf2974ffb50ffe0af8"}, @@ -3283,6 +3570,8 @@ version = "11.1" description = "Wrappers for the framework HealthKit on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"22.0\"" files = [ {file = "pyobjc_framework_healthkit-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f15f2cff20a09f42f251752f908a54c5fe3adabb03ec8d3fb2b66ff7b0b4709e"}, {file = "pyobjc_framework_healthkit-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:34bce3d144c461af7e577fcf6bbb7739d0537bf42f081960122923a7ef2e06c0"}, @@ -3305,6 +3594,8 @@ version = "11.1" description = "Wrappers for the framework ImageCaptureCore on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"10.0\"" files = [ {file = "pyobjc_framework_imagecapturecore-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:69f91c9f17bf0b8332b5826033bc5292493fe575fdb841cd7f58ab493053de38"}, {file = "pyobjc_framework_imagecapturecore-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ede4c15da909a4d819c732a5554b8282a7b56a1b73d82aef908124147921945a"}, @@ -3327,6 +3618,8 @@ version = "11.1" description = "Wrappers for the framework InputMethodKit on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"9.0\"" files = [ {file = "pyobjc_framework_inputmethodkit-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7ccf8697a13e7ab5e3ec446b930f40069da43823bfc678c4c426ad03f980c14f"}, {file = "pyobjc_framework_inputmethodkit-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9b0e47c3bc7f1e628c906436c1735041ed2e9aa7cba3f70084b6311c63c508be"}, @@ -3349,6 +3642,8 @@ version = "11.1" description = "Wrappers for the framework InstallerPlugins on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\"" files = [ {file = "pyobjc_framework_installerplugins-11.1-py2.py3-none-any.whl", hash = "sha256:f92b06c9595f3c800b7aabf1c1a235bfb4b2de3f5406d5f604d8e2ddd0aecb4e"}, {file = "pyobjc_framework_installerplugins-11.1.tar.gz", hash = "sha256:363e59c7e05553d881f0facd41884f17b489ff443d7856e33dd0312064c746d9"}, @@ -3364,6 +3659,8 @@ version = "11.1" description = "Wrappers for the framework InstantMessage on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"9.0\"" files = [ {file = "pyobjc_framework_instantmessage-11.1-py2.py3-none-any.whl", hash = "sha256:a70b716e279135eec5666af031f536c0f32dec57cfeae55cc9ff8457f10d4f3d"}, {file = "pyobjc_framework_instantmessage-11.1.tar.gz", hash = "sha256:c222aa61eb009704b333f6e63df01a0e690136e7e495907e5396882779bf9525"}, @@ -3380,6 +3677,8 @@ version = "11.1" description = "Wrappers for the framework Intents on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"16.0\"" files = [ {file = "pyobjc_framework_intents-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:315f8572336dee42ab582435e85176a14455928ac451fcb1f7c62786d17e8758"}, {file = "pyobjc_framework_intents-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:da2f11ee64c75cfbebb1c2be52a20b3618f32b6c47863809ff64c61e8a1dffb9"}, @@ -3402,6 +3701,8 @@ version = "11.1" description = "Wrappers for the framework Intents on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"21.0\"" files = [ {file = "pyobjc_framework_intentsui-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:381c14d60170f71e89b5fd4eae84c0821c50a70b08ce9994286177fa37b8d79e"}, {file = "pyobjc_framework_intentsui-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:252f7833fabb036cd56d59b445922b25cda1561b54c0989702618a5561d8e748"}, @@ -3424,6 +3725,8 @@ version = "11.1" description = "Wrappers for the framework IOBluetooth on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\"" files = [ {file = "pyobjc_framework_iobluetooth-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d512252b8ee2a23c88d5e0a188f8949858f1ef3b99c279fb412f3e00508ec367"}, {file = "pyobjc_framework_iobluetooth-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7d8858cf2e4b2ef5e8bf29b76c06d4f2e6a2264c325146d07dfab94c46633329"}, @@ -3446,6 +3749,8 @@ version = "11.1" description = "Wrappers for the framework IOBluetoothUI on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\"" files = [ {file = "pyobjc_framework_iobluetoothui-11.1-py2.py3-none-any.whl", hash = "sha256:3c5a382d81f319a1ab9ab11b7ead04e53b758fdfeb604755d39c3039485eaac6"}, {file = "pyobjc_framework_iobluetoothui-11.1.tar.gz", hash = "sha256:060c721f1cd8af4452493e8153b72b572edcd2a7e3b635d79d844f885afee860"}, @@ -3461,6 +3766,8 @@ version = "11.1" description = "Wrappers for the framework IOSurface on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"10.0\"" files = [ {file = "pyobjc_framework_iosurface-11.1-py2.py3-none-any.whl", hash = "sha256:0c36ad56f8ec675dd07616418a2bc29126412b54627655abd21de31bcafe2a79"}, {file = "pyobjc_framework_iosurface-11.1.tar.gz", hash = "sha256:a468b3a31e8cd70a2675a3ddc7176ab13aa521c035f11188b7a3af8fff8b148b"}, @@ -3476,6 +3783,8 @@ version = "11.1" description = "Wrappers for the framework iTunesLibrary on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"10.0\"" files = [ {file = "pyobjc_framework_ituneslibrary-11.1-py2.py3-none-any.whl", hash = "sha256:4e87d41f82acb6d98cf70ac3c932a568ceb3c2035383cbf177f54e63de6b815f"}, {file = "pyobjc_framework_ituneslibrary-11.1.tar.gz", hash = "sha256:e2212a9340e4328056ade3c2f9d4305c71f3f6af050204a135f9fa9aa3ba9c5e"}, @@ -3491,6 +3800,8 @@ version = "11.1" description = "Wrappers for the framework KernelManagement on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"20.0\"" files = [ {file = "pyobjc_framework_kernelmanagement-11.1-py2.py3-none-any.whl", hash = "sha256:ec74690bd3383a7945c4a038cc4e1553ec5c1d2408b60e2b0003a3564bff7c47"}, {file = "pyobjc_framework_kernelmanagement-11.1.tar.gz", hash = "sha256:e934d1638cd89e38d6c6c5d4d9901b4295acee2d39cbfe0bd91aae9832961b44"}, @@ -3506,6 +3817,8 @@ version = "11.1" description = "Wrappers for the framework LatentSemanticMapping on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\"" files = [ {file = "pyobjc_framework_latentsemanticmapping-11.1-py2.py3-none-any.whl", hash = "sha256:57f3b183021759a100d2847a4d8aa314f4033be3d2845038b62e5e823d96e871"}, {file = "pyobjc_framework_latentsemanticmapping-11.1.tar.gz", hash = "sha256:c6c3142301e4d375c24a47dfaeebc2f3d0fc33128a1c0a755794865b9a371145"}, @@ -3521,6 +3834,8 @@ version = "11.1" description = "Wrappers for the framework LaunchServices on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\"" files = [ {file = "pyobjc_framework_launchservices-11.1-py2.py3-none-any.whl", hash = "sha256:8b58f1156651058b2905c87ce48468f4799db86a7edf760e1897fedd057a3908"}, {file = "pyobjc_framework_launchservices-11.1.tar.gz", hash = "sha256:80b55368b1e208d6c2c58395cc7bc12a630a2a402e00e4930493e9bace22b7bb"}, @@ -3536,6 +3851,8 @@ version = "11.1" description = "Wrappers for libdispatch on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"12.0\"" files = [ {file = "pyobjc_framework_libdispatch-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:9c598c073a541b5956b5457b94bd33b9ce19ef8d867235439a0fad22d6beab49"}, {file = "pyobjc_framework_libdispatch-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2ddca472c2cbc6bb192e05b8b501d528ce49333abe7ef0eef28df3133a8e18b7"}, @@ -3558,6 +3875,8 @@ version = "11.1" description = "Wrappers for xpc on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"12.0\"" files = [ {file = "pyobjc_framework_libxpc-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:427ce45f700720198c365a099fb2f4f2fa28dbf85a7c4076371f61dbd16a0b6f"}, {file = "pyobjc_framework_libxpc-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4ec8a7df24d85a561fc21d0eb0db89e8cddefeedec71c69bccf17f99804068ed"}, @@ -3580,6 +3899,8 @@ version = "11.1" description = "Wrappers for the framework LinkPresentation on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"19.0\"" files = [ {file = "pyobjc_framework_linkpresentation-11.1-py2.py3-none-any.whl", hash = "sha256:018093469d780a45d98f4e159f1ea90771caec456b1599abcc6f3bf3c6873094"}, {file = "pyobjc_framework_linkpresentation-11.1.tar.gz", hash = "sha256:a785f393b01fdaada6d7d6d8de46b7173babba205b13b44f1dc884b3695c2fc9"}, @@ -3596,6 +3917,8 @@ version = "11.1" description = "Wrappers for the framework LocalAuthentication on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"14.0\"" files = [ {file = "pyobjc_framework_localauthentication-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f433e611a910d89a1e327f87e2b3bd9bf33576fd8b964767487e6f278003b030"}, {file = "pyobjc_framework_localauthentication-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1b6d52d07abd2240f7bc02b01ea1c630c280ed3fbc3fabe1e43b7444cfd41788"}, @@ -3619,6 +3942,8 @@ version = "11.1" description = "Wrappers for the framework LocalAuthenticationEmbeddedUI on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"21.0\"" files = [ {file = "pyobjc_framework_localauthenticationembeddedui-11.1-py2.py3-none-any.whl", hash = "sha256:3539a947b102b41ea6e40e7c145f27280d2f36a2a9a1211de32fa675d91585eb"}, {file = "pyobjc_framework_localauthenticationembeddedui-11.1.tar.gz", hash = "sha256:22baf3aae606e5204e194f02bb205f244e27841ea7b4a4431303955475b4fa56"}, @@ -3635,6 +3960,8 @@ version = "11.1" description = "Wrappers for the framework MailKit on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"21.0\"" files = [ {file = "pyobjc_framework_mailkit-11.1-py2.py3-none-any.whl", hash = "sha256:8e6026462567baba194468e710e83787f29d9e8c98ea0583f7b401ea9515966e"}, {file = "pyobjc_framework_mailkit-11.1.tar.gz", hash = "sha256:bf97dc44cb09b9eb9d591660dc0a41f077699976144b954caa4b9f0479211fd7"}, @@ -3650,6 +3977,8 @@ version = "11.1" description = "Wrappers for the framework MapKit on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"13.0\"" files = [ {file = "pyobjc_framework_mapkit-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0304816336b179a9508b6df9b7558c66e058acadf911900437db2d5b50eebecd"}, {file = "pyobjc_framework_mapkit-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:daee6bedc3acc23e62d1e7c3ab97e10425ca57e0c3cc47d2b212254705cc5c44"}, @@ -3674,6 +4003,8 @@ version = "11.1" description = "Wrappers for the framework MediaAccessibility on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"13.0\"" files = [ {file = "pyobjc_framework_mediaaccessibility-11.1-py2.py3-none-any.whl", hash = "sha256:cd07e7fc375ff1e8d225e0aa2bd9c2c1497a4d3aa5a80bfb13b08800fcd7f034"}, {file = "pyobjc_framework_mediaaccessibility-11.1.tar.gz", hash = "sha256:52479a998fec3d079d2d4590a945fc78c41fe7ac8c76f1964c9d8156880565a4"}, @@ -3689,6 +4020,8 @@ version = "11.1" description = "Wrappers for the framework MediaExtension on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"24.0\"" files = [ {file = "pyobjc_framework_mediaextension-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7f8a41ae51c1c70ea273f29857adc24c1d7bafc8071f0e6b50cb12b8ec5c4eb2"}, {file = "pyobjc_framework_mediaextension-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:915c0cbb04913beb1f1ac8939dc0e615da8ddfba3927863a476af49f193415c5"}, @@ -3713,6 +4046,8 @@ version = "11.1" description = "Wrappers for the framework MediaLibrary on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"13.0\"" files = [ {file = "pyobjc_framework_medialibrary-11.1-py2.py3-none-any.whl", hash = "sha256:779be84bd280f63837ce02028ca46b41b090902aa4205887ffd5777f49377669"}, {file = "pyobjc_framework_medialibrary-11.1.tar.gz", hash = "sha256:102f4326f789734b7b2dfe689abd3840ca75a76fb8058bd3e4f85398ae2ce29d"}, @@ -3729,6 +4064,8 @@ version = "11.1" description = "Wrappers for the framework MediaPlayer on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"16.0\"" files = [ {file = "pyobjc_framework_mediaplayer-11.1-py2.py3-none-any.whl", hash = "sha256:b655cf537ea52d73209eb12935a047301c30239b318a366600f0f44335d51c9a"}, {file = "pyobjc_framework_mediaplayer-11.1.tar.gz", hash = "sha256:d07a634b98e1b9eedd82d76f35e616525da096bd341051ea74f0971e0f2f2ddd"}, @@ -3744,6 +4081,8 @@ version = "11.1" description = "Wrappers for the framework MediaToolbox on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"13.0\"" files = [ {file = "pyobjc_framework_mediatoolbox-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c6beb3be7bb3e899b8e6e7c328c5d94e706b64f10023a49a108d74c03d132545"}, {file = "pyobjc_framework_mediatoolbox-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:da60c0409b18dfb9fa60a60589881e1382c007700b99722926270feadcf3bfc1"}, @@ -3766,6 +4105,8 @@ version = "11.1" description = "Wrappers for the framework Metal on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"15.0\"" files = [ {file = "pyobjc_framework_metal-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:9c77f71b7499a27f90d43a34ccd41de15c1ee8c33f9fb4293e1395d88c2aaae1"}, {file = "pyobjc_framework_metal-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:157a0052be459ffb35a3687f77a96ea87b42caf4cdd0b9f7245242b100edb4f0"}, @@ -3788,6 +4129,8 @@ version = "11.1" description = "Wrappers for the framework MetalFX on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"22.0\"" files = [ {file = "pyobjc_framework_metalfx-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fae511ea96f4ce8aff89ee71294c26294863b5a87b6665e9b4c1b47fd7ebe6ea"}, {file = "pyobjc_framework_metalfx-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:cbfca74f437fcde89de85d14de33c2e617d3084f5fc2b4d614a700e516324f55"}, @@ -3810,6 +4153,8 @@ version = "11.1" description = "Wrappers for the framework MetalKit on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"15.0\"" files = [ {file = "pyobjc_framework_metalkit-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e0be12860e68d960631bba4704b82670e4964191b5a20dbb48b4e1d840553ea9"}, {file = "pyobjc_framework_metalkit-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:95abb993d17be7a9d1174701594cc040e557983d0a0e9f49b1dfa9868ef20ed6"}, @@ -3833,6 +4178,8 @@ version = "11.1" description = "Wrappers for the framework MetalPerformanceShaders on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"17.0\"" files = [ {file = "pyobjc_framework_metalperformanceshaders-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:045efaf395f7f08380a2a16cd21d75a7c295edb0311728cf37133b6c1842f1ec"}, {file = "pyobjc_framework_metalperformanceshaders-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:81ec1f85c55d11529008e6a0fb1329d5184620f04d89751c11bf14d7dd9798ee"}, @@ -3855,6 +4202,8 @@ version = "11.1" description = "Wrappers for the framework MetalPerformanceShadersGraph on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"20.0\"" files = [ {file = "pyobjc_framework_metalperformanceshadersgraph-11.1-py2.py3-none-any.whl", hash = "sha256:9b8b014e8301c2ae608a25f73bbf23c8f3f73a6f5fdbafddad509a21b84df681"}, {file = "pyobjc_framework_metalperformanceshadersgraph-11.1.tar.gz", hash = "sha256:d25225aab4edc6f786b29fe3d9badc4f3e2d0caeab1054cd4f224258c1b6dbe2"}, @@ -3870,6 +4219,8 @@ version = "11.1" description = "Wrappers for the framework MetricKit on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"21.0\"" files = [ {file = "pyobjc_framework_metrickit-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:41896afbbaf6ad817b3f1c595f4c728026bf04a0e0adaafc5157b3a4d078cb76"}, {file = "pyobjc_framework_metrickit-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a5d2b394f7acadd17d8947d188106424f59393b45dd4a842ac3cc50935170e3e"}, @@ -3892,6 +4243,8 @@ version = "11.1" description = "Wrappers for the framework MLCompute on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"20.0\"" files = [ {file = "pyobjc_framework_mlcompute-11.1-py2.py3-none-any.whl", hash = "sha256:975150725e919f8d3d33f830898f3cd2fd19a440999faab320609487f4eae19d"}, {file = "pyobjc_framework_mlcompute-11.1.tar.gz", hash = "sha256:f6c4c3ea6a62e4e3927abf9783c40495aa8bb9a8c89def744b0822da58c2354b"}, @@ -3907,6 +4260,8 @@ version = "11.1" description = "Wrappers for the framework ModelIO on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"15.0\"" files = [ {file = "pyobjc_framework_modelio-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:deb2d703f092a6f2b0b7d5044b0c3825a4c2c2f068f38bc2052a76e93f777bb0"}, {file = "pyobjc_framework_modelio-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4365fb96eb42b71c12efdfa2ff9d44755d5c292b8d1c78b947833d84271e359f"}, @@ -3930,6 +4285,8 @@ version = "11.1" description = "Wrappers for the framework MultipeerConnectivity on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"14.0\"" files = [ {file = "pyobjc_framework_multipeerconnectivity-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:092fc396d235a8f3513b2ba4f8ff35fbd325d858eb9babe4df9c07d063ed647e"}, {file = "pyobjc_framework_multipeerconnectivity-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b3c9d4d36e0c142b4ce91033740ed5bca19fe7ec96870d90610d2942ecd3cd39"}, @@ -3952,6 +4309,8 @@ version = "11.1" description = "Wrappers for the framework NaturalLanguage on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"18.0\"" files = [ {file = "pyobjc_framework_naturallanguage-11.1-py2.py3-none-any.whl", hash = "sha256:65a780273d2cdd12a3fa304e9c9ad822cb71facd9281f1b35a71640c53826f7c"}, {file = "pyobjc_framework_naturallanguage-11.1.tar.gz", hash = "sha256:ab1fc711713aa29c32719774fc623bf2d32168aed21883970d4896e901ff4b41"}, @@ -3967,6 +4326,8 @@ version = "11.1" description = "Wrappers for the framework NetFS on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"10.0\"" files = [ {file = "pyobjc_framework_netfs-11.1-py2.py3-none-any.whl", hash = "sha256:f202e8e0c2e73516d3eac7a43b1c66f9911cdbb37ea32750ed197d82162c994a"}, {file = "pyobjc_framework_netfs-11.1.tar.gz", hash = "sha256:9c49f050c8171dc37e54d05dd12a63979c8b6b565c10f05092923a2250446f50"}, @@ -3982,6 +4343,8 @@ version = "11.1" description = "Wrappers for the framework Network on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"18.0\"" files = [ {file = "pyobjc_framework_network-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:53469903051aafbdd099c57c75b825f04167f1e3889634806af2bb762081d704"}, {file = "pyobjc_framework_network-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e56691507584c09cdb50f1cd69b5f57b42fd55c396e8c34fab8c5b81b44d36ed"}, @@ -4004,6 +4367,8 @@ version = "11.1" description = "Wrappers for the framework NetworkExtension on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"15.0\"" files = [ {file = "pyobjc_framework_networkextension-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7a679a2b17038de2fc3d66fce68361fb8152bd4e18cf95c15ccdbdef83d9da74"}, {file = "pyobjc_framework_networkextension-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:55e5ca70c81a864896b603cfcabf4c065783f64395460d16fe16db2bf0866d60"}, @@ -4026,6 +4391,8 @@ version = "11.1" description = "Wrappers for the framework NotificationCenter on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"14.0\"" files = [ {file = "pyobjc_framework_notificationcenter-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:70704ba076eb30a2e25fd9d738d4ed2cf4a684c87a9129b1fc0c570301f53eee"}, {file = "pyobjc_framework_notificationcenter-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3d44413818e7fa3662f784cdcf0730c86676dd7333b7d24a7da13d4ffcde491b"}, @@ -4048,6 +4415,8 @@ version = "11.1" description = "Wrappers for the framework OpenDirectory on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"10.0\"" files = [ {file = "pyobjc_framework_opendirectory-11.1-py2.py3-none-any.whl", hash = "sha256:bb4219b0d98dff4a952c50a79b1855ce74e1defd0d241f3013def5b09256fd7b"}, {file = "pyobjc_framework_opendirectory-11.1.tar.gz", hash = "sha256:319ac3424ed0350be458b78148914468a8fc13a069d62e7869e3079108e4f118"}, @@ -4063,6 +4432,8 @@ version = "11.1" description = "Wrappers for the framework OSAKit on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\"" files = [ {file = "pyobjc_framework_osakit-11.1-py2.py3-none-any.whl", hash = "sha256:1b0c0cc537ffb8a8365ef9a8b46f717a7cc2906414b6a3983777a6c0e4d53d5a"}, {file = "pyobjc_framework_osakit-11.1.tar.gz", hash = "sha256:920987da78b67578367c315d208f87e8fab01dd35825d72242909f29fb43c820"}, @@ -4078,6 +4449,8 @@ version = "11.1" description = "Wrappers for the framework OSLog on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"19.0\"" files = [ {file = "pyobjc_framework_oslog-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d064b4ed8960bb65a277af16938043ebb4fb1d38fd47129bc9b9aeb6d385d4bc"}, {file = "pyobjc_framework_oslog-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5dab25ef1cde4237cd2957c1f61c2888968e924304f7b9d9699eceeb330e9817"}, @@ -4102,6 +4475,8 @@ version = "11.1" description = "Wrappers for the framework PassKit on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"20.0\"" files = [ {file = "pyobjc_framework_passkit-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:9f195a9c7d0ad46c975d22a0e3362ea6ccdb01e4cb1f81221db1037aee8225ff"}, {file = "pyobjc_framework_passkit-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:67b7b1ee9454919c073c2cba7bdba444a766a4e1dd15a5e906f4fa0c61525347"}, @@ -4124,6 +4499,8 @@ version = "11.1" description = "Wrappers for the framework PencilKit on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"19.0\"" files = [ {file = "pyobjc_framework_pencilkit-11.1-py2.py3-none-any.whl", hash = "sha256:b7824907bbcf28812f588dda730e78f662313baf40befd485c6f2fcb49018019"}, {file = "pyobjc_framework_pencilkit-11.1.tar.gz", hash = "sha256:9c173e0fe70179feadc3558de113a8baad61b584fe70789b263af202bfa4c6be"}, @@ -4139,6 +4516,8 @@ version = "11.1" description = "Wrappers for the framework PHASE on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"21.0\"" files = [ {file = "pyobjc_framework_phase-11.1-py2.py3-none-any.whl", hash = "sha256:cfa61f9c6c004161913946501538258aed48c448b886adbf9ed035957d93fa15"}, {file = "pyobjc_framework_phase-11.1.tar.gz", hash = "sha256:a940d81ac5c393ae3da94144cf40af33932e0a9731244e2cfd5c9c8eb851e3fc"}, @@ -4154,6 +4533,8 @@ version = "11.1" description = "Wrappers for the framework Photos on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"15.0\"" files = [ {file = "pyobjc_framework_photos-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1cd54a6b60a7ad2f810c02ec2c4d676feec4a25d08c9328ff839034b29c15bf7"}, {file = "pyobjc_framework_photos-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:959dfc82f20513366b85cd37d8541bb0a6ab4f3bfa2f8094e9758a5245032d67"}, @@ -4176,6 +4557,8 @@ version = "11.1" description = "Wrappers for the framework PhotosUI on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"15.0\"" files = [ {file = "pyobjc_framework_photosui-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c2648031c62c30089ac8170a63ffbe92e6469447a488590504edd94cd51fd45a"}, {file = "pyobjc_framework_photosui-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d93722aeb8c134569035fd7e6632d0247e1bcb18c3cc4e0a288664218f241b85"}, @@ -4198,6 +4581,8 @@ version = "11.1" description = "Wrappers for the framework PreferencePanes on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\"" files = [ {file = "pyobjc_framework_preferencepanes-11.1-py2.py3-none-any.whl", hash = "sha256:6ee5f5a7eb294e03ea3bac522ac4b69e6dc83ceceff627a0a2d289afe1e01ad9"}, {file = "pyobjc_framework_preferencepanes-11.1.tar.gz", hash = "sha256:6e4a55195ec9fc921e0eaad6b3038d0ab91f0bb2f39206aa6fccd24b14a0f1d8"}, @@ -4213,6 +4598,8 @@ version = "11.1" description = "Wrappers for the framework PubSub on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_release >= \"9.0\" and platform_release < \"18.0\" and platform_system == \"Darwin\"" files = [ {file = "pyobjc_framework_pubsub-11.1-py2.py3-none-any.whl", hash = "sha256:cea6bd9e0af46f9ea1c8d002a92e462576dd5a772a7e0688d40c7903755af11f"}, {file = "pyobjc_framework_pubsub-11.1.tar.gz", hash = "sha256:47221f63466c523516ab5d2297dd3c644d915a77ca1f1867b0055d735486f1f8"}, @@ -4228,6 +4615,8 @@ version = "11.1" description = "Wrappers for the framework PushKit on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"19.0\"" files = [ {file = "pyobjc_framework_pushkit-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:48c38a7d3bef449c23aa799b70283586e0b7d9203cf17b0666bc61278b663ed2"}, {file = "pyobjc_framework_pushkit-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5e2f08b667035df6b11a0a26f038610df1eebbedf9f3f111c241b5afaaf7c5fd"}, @@ -4250,6 +4639,8 @@ version = "11.1" description = "Wrappers for the Quartz frameworks on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "sys_platform == \"darwin\" or platform_system == \"Darwin\"" files = [ {file = "pyobjc_framework_quartz-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b5ef75c416b0209e25b2eb07a27bd7eedf14a8c6b2f968711969d45ceceb0f84"}, {file = "pyobjc_framework_quartz-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2d501fe95ef15d8acf587cb7dc4ab4be3c5a84e2252017da8dbb7df1bbe7a72a"}, @@ -4272,6 +4663,8 @@ version = "11.1" description = "Wrappers for the framework QuickLookThumbnailing on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"19.0\"" files = [ {file = "pyobjc_framework_quicklookthumbnailing-11.1-py2.py3-none-any.whl", hash = "sha256:4d1863c6c83c2a199c1dbe704b4f8b71287168f4090ed218d37dc59277f0d9c9"}, {file = "pyobjc_framework_quicklookthumbnailing-11.1.tar.gz", hash = "sha256:1614dc108c1d45bbf899ea84b8691288a5b1d25f2d6f0c57dfffa962b7a478c3"}, @@ -4288,6 +4681,8 @@ version = "11.1" description = "Wrappers for the framework ReplayKit on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"20.0\"" files = [ {file = "pyobjc_framework_replaykit-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:634b18c7b0f2ea548421307d6c59339d69094dfde9b638ce0ca3d6d3016de470"}, {file = "pyobjc_framework_replaykit-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4d88c3867349865d8a3a06ea064f15aed7e5be20d22882ac8a647d9b6959594e"}, @@ -4310,6 +4705,8 @@ version = "11.1" description = "Wrappers for the framework SafariServices on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"16.0\"" files = [ {file = "pyobjc_framework_safariservices-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:62e70805477b04d1abc6dfa1f22d2ee41af8a5784fa98d3dcbd9fca00b6dd521"}, {file = "pyobjc_framework_safariservices-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a441a2e99f7d6475bea00c3d53de924143b8f90052be226aee16f1f6d9cfdc8c"}, @@ -4332,6 +4729,8 @@ version = "11.1" description = "Wrappers for the framework SafetyKit on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"22.0\"" files = [ {file = "pyobjc_framework_safetykit-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:45c1fb59246ca9eef99149f3b325491a1aec7f775dd136f6de86aa69911cc43f"}, {file = "pyobjc_framework_safetykit-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3333e8e53a1e8c8133936684813a2254e5d1b4fe313333a3d0273e31b9158cf7"}, @@ -4355,6 +4754,8 @@ version = "11.1" description = "Wrappers for the framework SceneKit on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"11.0\"" files = [ {file = "pyobjc_framework_scenekit-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c828200919573e1c5a02f8702b2e0f8a6c46edddd2d690666d8cf16575f4578"}, {file = "pyobjc_framework_scenekit-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3e777dacb563946ad0c2351e6cfe3f16b8587a65772ec0654e2be9f75764d234"}, @@ -4378,6 +4779,8 @@ version = "11.1" description = "Wrappers for the framework ScreenCaptureKit on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"21.4\"" files = [ {file = "pyobjc_framework_screencapturekit-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:11de78f270d405bd14b784b15d4bb04a13b3d25613abd5f9aaaf2b8ef108dc60"}, {file = "pyobjc_framework_screencapturekit-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7203108d28d7373501c455cd4a8bbcd2eb7849906dbc7859ac17a350b141553c"}, @@ -4401,6 +4804,8 @@ version = "11.1" description = "Wrappers for the framework ScreenSaver on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\"" files = [ {file = "pyobjc_framework_screensaver-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:656651d0b6870bffeea01b65f4748936603a62dbbdc8e7a61c125ea6ebf8299c"}, {file = "pyobjc_framework_screensaver-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8b959761fddf06d9fb3fed6cd0cea6009d60473317e11490f66dcf0444011d5f"}, @@ -4423,6 +4828,8 @@ version = "11.1" description = "Wrappers for the framework ScreenTime on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"20.0\"" files = [ {file = "pyobjc_framework_screentime-11.1-py2.py3-none-any.whl", hash = "sha256:50a4e4ab33d6643a52616e990aa1c697d5e3e8f9f9bdab8d631e6d42d8287b4f"}, {file = "pyobjc_framework_screentime-11.1.tar.gz", hash = "sha256:9bb8269456bbb674e1421182efe49f9168ceefd4e7c497047c7bf63e2f510a34"}, @@ -4438,6 +4845,8 @@ version = "11.1" description = "Wrappers for the framework ScriptingBridge on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"9.0\"" files = [ {file = "pyobjc_framework_scriptingbridge-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2cf247dfe9f98aa3c8210395d045a708a4133a5d6164673213eb39afc4f6dd31"}, {file = "pyobjc_framework_scriptingbridge-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d6020c69c14872105852ff99aab7cd2b2671e61ded3faefb071dc40a8916c527"}, @@ -4460,6 +4869,8 @@ version = "11.1" description = "Wrappers for the framework SearchKit on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\"" files = [ {file = "pyobjc_framework_searchkit-11.1-py2.py3-none-any.whl", hash = "sha256:9c9d6ca71cef637ccc3627225fb924a460b3d0618ed79bb0b3c12fcbe9270323"}, {file = "pyobjc_framework_searchkit-11.1.tar.gz", hash = "sha256:13a194eefcf1359ce9972cd92f2aadddf103f3efb1b18fd578ba5367dff3c10c"}, @@ -4475,6 +4886,8 @@ version = "11.1" description = "Wrappers for the framework Security on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\"" files = [ {file = "pyobjc_framework_security-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ffe21933b554098709087fbc4e629ab4875e75d74ffb741de508063dba56c73e"}, {file = "pyobjc_framework_security-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d361231697486e97cfdafadf56709190696ab26a6a086dbba5f170e042e13daa"}, @@ -4497,6 +4910,8 @@ version = "11.1" description = "Wrappers for the framework SecurityFoundation on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\"" files = [ {file = "pyobjc_framework_securityfoundation-11.1-py2.py3-none-any.whl", hash = "sha256:25f2cf10f80c122f462e9d4d43efe9fd697299c194e0c357e76650e234e6d286"}, {file = "pyobjc_framework_securityfoundation-11.1.tar.gz", hash = "sha256:b3c4cf70735a93e9df40f3a14478143959c415778f27be8c0dc9ae0c5b696b92"}, @@ -4513,6 +4928,8 @@ version = "11.1" description = "Wrappers for the framework SecurityInterface on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\"" files = [ {file = "pyobjc_framework_securityinterface-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:127da21b8fd4d8df0f1d680f581cef714eeb8c2db31e72b2c5395e2ad41936ff"}, {file = "pyobjc_framework_securityinterface-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3e884620b22918d462764f0665f6ac0cbb8142bb160fcd27c4f4357f81da73b7"}, @@ -4536,6 +4953,8 @@ version = "11.1" description = "Wrappers for the framework SecurityUI on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"24.4\"" files = [ {file = "pyobjc_framework_securityui-11.1-py2.py3-none-any.whl", hash = "sha256:3cdb101b03459fcf8e4064b90021d06761003f669181e02f43ff585e6ba2403d"}, {file = "pyobjc_framework_securityui-11.1.tar.gz", hash = "sha256:e80c93e8a56bf89e4c0333047b9f8219752dd6de290681e9e2e2b2e26d69e92d"}, @@ -4552,6 +4971,8 @@ version = "11.1" description = "Wrappers for the framework SensitiveContentAnalysis on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"23.0\"" files = [ {file = "pyobjc_framework_sensitivecontentanalysis-11.1-py2.py3-none-any.whl", hash = "sha256:dbb78f5917f986a63878bb91263bceba28bd86fc381bad9461cf391646db369f"}, {file = "pyobjc_framework_sensitivecontentanalysis-11.1.tar.gz", hash = "sha256:5b310515c7386f7afaf13e4632d7d9590688182bb7b563f8026c304bdf317308"}, @@ -4568,6 +4989,8 @@ version = "11.1" description = "Wrappers for the framework ServiceManagement on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"10.0\"" files = [ {file = "pyobjc_framework_servicemanagement-11.1-py2.py3-none-any.whl", hash = "sha256:104f56557342a05ad68cd0c9daf63b7f4678957fe1f919f03a872f1607a50710"}, {file = "pyobjc_framework_servicemanagement-11.1.tar.gz", hash = "sha256:90a07164da49338480e0e135b445acc6ae7c08549a2037d1e512d2605fedd80a"}, @@ -4583,6 +5006,8 @@ version = "11.1" description = "Wrappers for the framework SharedWithYou on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"22.0\"" files = [ {file = "pyobjc_framework_sharedwithyou-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2a218b3c89253a5c3a0ca974854872b68f58d46373a3e38ab20a82c9484a1062"}, {file = "pyobjc_framework_sharedwithyou-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ce1c37d5f8cf5b0fe8a261e4e7256da677162fd5aa7b724e83532cdfe58d8f94"}, @@ -4605,6 +5030,8 @@ version = "11.1" description = "Wrappers for the framework SharedWithYouCore on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"22.0\"" files = [ {file = "pyobjc_framework_sharedwithyoucore-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:daa8de2cbf5ec8e768e4d8b7b7cd410747d92ca83ccf7d114563537448099136"}, {file = "pyobjc_framework_sharedwithyoucore-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9a7fe5ffcc65093ef7cd25903769ad557c3d3c5a59155a31f3f934cf555101e6"}, @@ -4627,6 +5054,8 @@ version = "11.1" description = "Wrappers for the framework ShazamKit on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"21.0\"" files = [ {file = "pyobjc_framework_shazamkit-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5f19e1f307d84c53271af7ed70a3c39f134a46e358672fb8c74ced7205949551"}, {file = "pyobjc_framework_shazamkit-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2fe6990d0ec1b40d4efd0d0e49c2deb65198f49b963e6215c608c140b3149151"}, @@ -4649,6 +5078,8 @@ version = "11.1" description = "Wrappers for the framework Social on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"12.0\"" files = [ {file = "pyobjc_framework_social-11.1-py2.py3-none-any.whl", hash = "sha256:ab5878c47d7a0639704c191cee43eeb259e09688808f0905c42551b9f79e1d57"}, {file = "pyobjc_framework_social-11.1.tar.gz", hash = "sha256:fbc09d7b00dad45b547f9b2329f4dcee3f5a50e2348de1870de0bd7be853a5b7"}, @@ -4664,6 +5095,8 @@ version = "11.1" description = "Wrappers for the framework SoundAnalysis on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"19.0\"" files = [ {file = "pyobjc_framework_soundanalysis-11.1-py2.py3-none-any.whl", hash = "sha256:6cf983c24fb2ad2aa5e7499ab2d30ff134d887fe91fd2641acf7472e546ab4e5"}, {file = "pyobjc_framework_soundanalysis-11.1.tar.gz", hash = "sha256:42cd25b7e0f343d8b59367f72b5dae96cf65696bdb8eeead8d7424ed37aa1434"}, @@ -4679,6 +5112,8 @@ version = "11.1" description = "Wrappers for the framework Speech on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"19.0\"" files = [ {file = "pyobjc_framework_speech-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5fcbe46060f0b25963e32fa7488a34fb3f929fa099797a10e30012d3d6ee328a"}, {file = "pyobjc_framework_speech-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d3e0276a66d2fa4357959a6f6fb5def03f8e0fd3aa43711d6a81ab2573b9415f"}, @@ -4701,6 +5136,8 @@ version = "11.1" description = "Wrappers for the framework SpriteKit on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"13.0\"" files = [ {file = "pyobjc_framework_spritekit-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5adddbeea27ca748d4fd4588ffa79299fb7a7b369038bc6e3425570d1cab9b0a"}, {file = "pyobjc_framework_spritekit-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1c8c94d37c054b6e3c22c237f6458c12649776e5ac921d066ab99dee2e580909"}, @@ -4724,6 +5161,8 @@ version = "11.1" description = "Wrappers for the framework StoreKit on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"11.0\"" files = [ {file = "pyobjc_framework_storekit-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:850b8157c30aa023c16a883a140538ca229d7b30db6c17568ea69532b19256ad"}, {file = "pyobjc_framework_storekit-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:624105bd26a9ce5a097b3f96653e2700d33bb095828ed65ee0f4679b34d9f1e1"}, @@ -4746,6 +5185,8 @@ version = "11.1" description = "Wrappers for the framework Symbols on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"23.0\"" files = [ {file = "pyobjc_framework_symbols-11.1-py2.py3-none-any.whl", hash = "sha256:1de6fc3af15fc8d5fd4869663a3250311844ec33e99ec8a1991a352ab61d641d"}, {file = "pyobjc_framework_symbols-11.1.tar.gz", hash = "sha256:0e09b7813ef2ebdca7567d3179807444dd60f3f393202b35b755d4e1baf99982"}, @@ -4761,6 +5202,8 @@ version = "11.1" description = "Wrappers for the framework SyncServices on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\"" files = [ {file = "pyobjc_framework_syncservices-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:108619faf4cafb894022ca923b52d45008eb6ad3af2123ca4e187101a74ddaee"}, {file = "pyobjc_framework_syncservices-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:bc6159bda4597149c6999b052a35ffd9fc4817988293da6e54a1e073fa571653"}, @@ -4784,6 +5227,8 @@ version = "11.1" description = "Wrappers for the framework SystemConfiguration on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\"" files = [ {file = "pyobjc_framework_systemconfiguration-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:45ede697a3f9d4f97f1554a3f5636197aee83923d3adbe0901935da8ddb559a9"}, {file = "pyobjc_framework_systemconfiguration-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d12d5078611c905162bc951dffbb2a989b0dfd156952ba1884736c8dcbe38f7f"}, @@ -4806,6 +5251,8 @@ version = "11.1" description = "Wrappers for the framework SystemExtensions on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"19.0\"" files = [ {file = "pyobjc_framework_systemextensions-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:55e33ce532c16e36e0960e34501748d07d019f8088aa4efde10c5c91ccbce5aa"}, {file = "pyobjc_framework_systemextensions-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7e742ae51cdd86c0e609fe47189ea446de98d13b235b0a138a3f2e37e98cd359"}, @@ -4828,6 +5275,8 @@ version = "11.1" description = "Wrappers for the framework ThreadNetwork on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"22.0\"" files = [ {file = "pyobjc_framework_threadnetwork-11.1-py2.py3-none-any.whl", hash = "sha256:55021455215a0d3ad4e40152f94154e29062e73655558c5f6e71ab097d90083e"}, {file = "pyobjc_framework_threadnetwork-11.1.tar.gz", hash = "sha256:73a32782f44b61ca0f8a4a9811c36b1ca1cdcf96c8a3ba4de35d8e8e58a86ad5"}, @@ -4843,6 +5292,8 @@ version = "11.1" description = "Wrappers for the framework UniformTypeIdentifiers on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"20.0\"" files = [ {file = "pyobjc_framework_uniformtypeidentifiers-11.1-py2.py3-none-any.whl", hash = "sha256:6e2e8ea89eb8ca03bc2bc8e506fff901e71d916276475c8d81fbf0280059cb4c"}, {file = "pyobjc_framework_uniformtypeidentifiers-11.1.tar.gz", hash = "sha256:86c499bec8953aeb0c95af39b63f2592832384f09f12523405650b5d5f1ed5e9"}, @@ -4858,6 +5309,8 @@ version = "11.1" description = "Wrappers for the framework UserNotifications on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"18.0\"" files = [ {file = "pyobjc_framework_usernotifications-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:863f9c680ce9d4b0d398a61803210e4c7ff770487b6506f00742dd45cd4d4347"}, {file = "pyobjc_framework_usernotifications-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7140d337dd9dc3635add2177086429fdd6ef24970935b22fffdc5ec7f02ebf60"}, @@ -4880,6 +5333,8 @@ version = "11.1" description = "Wrappers for the framework UserNotificationsUI on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"20.0\"" files = [ {file = "pyobjc_framework_usernotificationsui-11.1-py2.py3-none-any.whl", hash = "sha256:b84d73d90ab319acf8fad5c59b7a5e2b6023fbb2efd68c58b532e3b3b52f647a"}, {file = "pyobjc_framework_usernotificationsui-11.1.tar.gz", hash = "sha256:18e0182bddd10381884530d6a28634ebb3280912592f8f2ad5bac2a9308c6a65"}, @@ -4896,6 +5351,8 @@ version = "11.1" description = "Wrappers for the framework VideoSubscriberAccount on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"18.0\"" files = [ {file = "pyobjc_framework_videosubscriberaccount-11.1-py2.py3-none-any.whl", hash = "sha256:d5a95ae9f2a6f0180a5bbb10e76c064f0fd327aae00a2fe90aa7b65ed4dad7ef"}, {file = "pyobjc_framework_videosubscriberaccount-11.1.tar.gz", hash = "sha256:2dd78586260fcee51044e129197e8bf2e157176e02babeec2f873afa4235d8c6"}, @@ -4911,6 +5368,8 @@ version = "11.1" description = "Wrappers for the framework VideoToolbox on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"12.0\"" files = [ {file = "pyobjc_framework_videotoolbox-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:dad01cdc1fe2b5ca4ba4f2472eb62fca87898e1a4ade3b692177bb09a07d4254"}, {file = "pyobjc_framework_videotoolbox-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:94c17bffe0f4692db2e7641390dfdcd0f73ddbb0afa6c81ef504219be0777930"}, @@ -4935,6 +5394,8 @@ version = "11.1" description = "Wrappers for the framework Virtualization on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"20.0\"" files = [ {file = "pyobjc_framework_virtualization-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:27b3149426ab80583d8b40a0c0829d0968621b2c406abeeee1ac7ba3f25f9949"}, {file = "pyobjc_framework_virtualization-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c2a812da4c995e1f8076678130d0b0a63042aa48219f8fb43b70e13eabcbdbc2"}, @@ -4957,6 +5418,8 @@ version = "11.1" description = "Wrappers for the framework Vision on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\" and platform_release >= \"17.0\"" files = [ {file = "pyobjc_framework_vision-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3c6f46df632096f070e16ba902a483fcb95c01fe12856a071bc2b25ac4a89bf3"}, {file = "pyobjc_framework_vision-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:bfbde43c9d4296e1d26548b6d30ae413e2029425968cd8bce96d3c5a735e8f2c"}, @@ -4981,6 +5444,8 @@ version = "11.1" description = "Wrappers for the framework WebKit on macOS" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\"" files = [ {file = "pyobjc_framework_webkit-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5e7c254ba37b7a41fe9ffd31565495cad961a82ab22727949cdb4aface7f3fa6"}, {file = "pyobjc_framework_webkit-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:10ec89d727af8f216ba5911ff5553f84a5b660f5ddf75b07788e3a439c281165"}, @@ -5003,6 +5468,7 @@ version = "1.9.0" description = "A cross-platform clipboard module for Python. (Only handles plain text for now.)" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "pyperclip-1.9.0.tar.gz", hash = "sha256:b7de0142ddc81bfc5c7507eea19da920b92252b548b96186caf94a5e2527d310"}, ] @@ -5013,6 +5479,8 @@ version = "223" description = "" optional = false python-versions = "*" +groups = ["main"] +markers = "platform_system == \"Windows\"" files = [ {file = "pypiwin32-223-py3-none-any.whl", hash = "sha256:67adf399debc1d5d14dffc1ab5acacb800da569754fafdc576b2a039485aa775"}, {file = "pypiwin32-223.tar.gz", hash = "sha256:71be40c1fbd28594214ecaecb58e7aa8b708eabfa0125c8a109ebd51edbd776a"}, @@ -5027,6 +5495,7 @@ version = "0.2.0" description = "PyRect is a simple module with a Rect class for Pygame-like rectangular areas." optional = false python-versions = "*" +groups = ["main"] files = [ {file = "PyRect-0.2.0.tar.gz", hash = "sha256:f65155f6df9b929b67caffbd57c0947c5ae5449d3b580d178074bffb47a09b78"}, ] @@ -5037,6 +5506,7 @@ version = "1.0.1" description = "A simple, cross-platform screenshot module for Python 2 and 3." optional = false python-versions = "*" +groups = ["main"] files = [ {file = "pyscreeze-1.0.1.tar.gz", hash = "sha256:cf1662710f1b46aa5ff229ee23f367da9e20af4a78e6e365bee973cad0ead4be"}, ] @@ -5053,6 +5523,7 @@ version = "0.19.5" description = "Provides systray integration" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "pystray-0.19.5-py2.py3-none-any.whl", hash = "sha256:a0c2229d02cf87207297c22d86ffc57c86c227517b038c0d3c59df79295ac617"}, ] @@ -5069,6 +5540,7 @@ version = "7.4.4" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"}, {file = "pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280"}, @@ -5091,6 +5563,8 @@ version = "0.33" description = "Python X Library" optional = false python-versions = "*" +groups = ["main"] +markers = "sys_platform in \"linux\"" files = [ {file = "python-xlib-0.33.tar.gz", hash = "sha256:55af7906a2c75ce6cb280a584776080602444f75815a7aff4d287bb2d7018b32"}, {file = "python_xlib-0.33-py2.py3-none-any.whl", hash = "sha256:c3534038d42e0df2f1392a1b30a15a4ff5fdc2b86cfa94f072bf11b10a164398"}, @@ -5105,6 +5579,8 @@ version = "0.15" description = "Python3 X Library" optional = false python-versions = "*" +groups = ["main"] +markers = "platform_system == \"Linux\"" files = [ {file = "python3-xlib-0.15.tar.gz", hash = "sha256:dc4245f3ae4aa5949c1d112ee4723901ade37a96721ba9645f2bfa56e5b383f8"}, ] @@ -5115,6 +5591,7 @@ version = "2.98" description = "Text to Speech (TTS) library for Python 3. Works without internet connection or delay. Supports multiple TTS engines, including Sapi5, nsss, and espeak." optional = false python-versions = "*" +groups = ["main"] files = [ {file = "pyttsx3-2.98-py3-none-any.whl", hash = "sha256:b3fb4ca4d5ae4f8e6836d6b37bf5fee0fd51d157ffa27fb9064be6e7be3da37a"}, {file = "pyttsx3-2.98.tar.gz", hash = "sha256:cc609466151d8c4a69c1c765a945f893c00c1fd2569c2db55e17dc22121e9162"}, @@ -5132,6 +5609,7 @@ version = "1.2.0" description = "A collection of tweening (aka easing) functions." optional = false python-versions = "*" +groups = ["main"] files = [ {file = "pytweening-1.2.0.tar.gz", hash = "sha256:243318b7736698066c5f362ec5c2b6434ecf4297c3c8e7caa8abfe6af4cac71b"}, ] @@ -5142,6 +5620,8 @@ version = "310" description = "Python for Window Extensions" optional = false python-versions = "*" +groups = ["main"] +markers = "platform_system == \"Windows\"" files = [ {file = "pywin32-310-cp310-cp310-win32.whl", hash = "sha256:6dd97011efc8bf51d6793a82292419eba2c71cf8e7250cfac03bba284454abc1"}, {file = "pywin32-310-cp310-cp310-win_amd64.whl", hash = "sha256:c3e78706e4229b915a0821941a84e7ef420bf2b77e08c9dae3c76fd03fd2ae3d"}, @@ -5167,6 +5647,7 @@ version = "2024.11.6" description = "Alternative regular expression module, to replace re." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "regex-2024.11.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ff590880083d60acc0433f9c3f713c51f7ac6ebb9adf889c79a261ecf541aa91"}, {file = "regex-2024.11.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:658f90550f38270639e83ce492f27d2c8d2cd63805c65a13a14d36ca126753f0"}, @@ -5270,6 +5751,7 @@ version = "2.32.4" description = "Python HTTP for Humans." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "requests-2.32.4-py3-none-any.whl", hash = "sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c"}, {file = "requests-2.32.4.tar.gz", hash = "sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422"}, @@ -5291,6 +5773,8 @@ version = "0.5.1" description = "A bridge between an Objective C runtime environment and Python." optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system == \"Darwin\"" files = [ {file = "rubicon_objc-0.5.1-py3-none-any.whl", hash = "sha256:17092756241b8370231cfaad45ad6e8ce99534987f2acbc944d65df5bdf8f6cd"}, {file = "rubicon_objc-0.5.1.tar.gz", hash = "sha256:90bee9fc1de4515e17615e15648989b88bb8d4d2ffc8c7c52748272cd7f30a66"}, @@ -5306,6 +5790,7 @@ version = "1.15.3" description = "Fundamental algorithms for scientific computing in Python" optional = false python-versions = ">=3.10" +groups = ["main"] files = [ {file = "scipy-1.15.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:a345928c86d535060c9c2b25e71e87c39ab2f22fc96e9636bd74d1dbf9de448c"}, {file = "scipy-1.15.3-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:ad3432cb0f9ed87477a8d97f03b763fd1d57709f1bbde3c9369b1dff5503b253"}, @@ -5361,7 +5846,7 @@ numpy = ">=1.23.5,<2.5" [package.extras] dev = ["cython-lint (>=0.12.2)", "doit (>=0.36.0)", "mypy (==1.10.0)", "pycodestyle", "pydevtool", "rich-click", "ruff (>=0.0.292)", "types-psutil", "typing_extensions"] doc = ["intersphinx_registry", "jupyterlite-pyodide-kernel", "jupyterlite-sphinx (>=0.19.1)", "jupytext", "matplotlib (>=3.5)", "myst-nb", "numpydoc", "pooch", "pydata-sphinx-theme (>=0.15.2)", "sphinx (>=5.0.0,<8.0.0)", "sphinx-copybutton", "sphinx-design (>=0.4.0)"] -test = ["Cython", "array-api-strict (>=2.0,<2.1.1)", "asv", "gmpy2", "hypothesis (>=6.30)", "meson", "mpmath", "ninja", "pooch", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "scikit-umfpack", "threadpoolctl"] +test = ["Cython", "array-api-strict (>=2.0,<2.1.1)", "asv", "gmpy2", "hypothesis (>=6.30)", "meson", "mpmath", "ninja ; sys_platform != \"emscripten\"", "pooch", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "scikit-umfpack", "threadpoolctl"] [[package]] name = "six" @@ -5369,6 +5854,7 @@ version = "1.17.0" description = "Python 2 and 3 compatibility utilities" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +groups = ["main"] files = [ {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, @@ -5380,6 +5866,7 @@ version = "0.4.7" description = "Play and Record Sound with Python" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "sounddevice-0.4.7-py3-none-any.whl", hash = "sha256:1c3f18bfa4d9a257f5715f2ab83f2c0eb412a09f3e6a9fa73720886ca88f6bc7"}, {file = "sounddevice-0.4.7-py3-none-macosx_10_6_x86_64.macosx_10_6_universal2.whl", hash = "sha256:d6ddfd341ad7412b14ca001f2c4dbf5fa2503bdc9eb15ad2c3105f6c260b698a"}, @@ -5400,6 +5887,7 @@ version = "1.14.0" description = "Computer algebra system (CAS) in Python" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5"}, {file = "sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517"}, @@ -5417,6 +5905,8 @@ version = "2021.13.1" description = "Intelยฎ oneAPI Threading Building Blocks (oneTBB)" optional = false python-versions = "*" +groups = ["main"] +markers = "platform_system == \"Windows\"" files = [ {file = "tbb-2021.13.1-py2.py3-none-manylinux1_i686.whl", hash = "sha256:bb5bdea0c0e9e6ad0739e7a8796c2635ce9eccca86dd48c426cd8027ac70fb1d"}, {file = "tbb-2021.13.1-py2.py3-none-manylinux1_x86_64.whl", hash = "sha256:d916359dc685579d09e4b344241550afc1cc034f7f5ec7234c258b6680912d70"}, @@ -5430,6 +5920,7 @@ version = "0.9.0" description = "tiktoken is a fast BPE tokeniser for use with OpenAI's models" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "tiktoken-0.9.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:586c16358138b96ea804c034b8acf3f5d3f0258bd2bc3b0227af4af5d622e382"}, {file = "tiktoken-0.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d9c59ccc528c6c5dd51820b3474402f69d9a9e1d656226848ad68a8d5b2e5108"}, @@ -5477,6 +5968,8 @@ version = "2.2.1" description = "A lil' TOML parser" optional = false python-versions = ">=3.8" +groups = ["dev"] +markers = "python_version == \"3.10\"" files = [ {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, @@ -5518,6 +6011,7 @@ version = "2.3.1" description = "Tensors and Dynamic neural networks in Python with strong GPU acceleration" optional = false python-versions = ">=3.8.0" +groups = ["main"] files = [ {file = "torch-2.3.1-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:605a25b23944be5ab7c3467e843580e1d888b8066e5aaf17ff7bf9cc30001cc3"}, {file = "torch-2.3.1-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:f2357eb0965583a0954d6f9ad005bba0091f956aef879822274b1bcdb11bd308"}, @@ -5572,6 +6066,7 @@ version = "4.67.1" description = "Fast, Extensible Progress Meter" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2"}, {file = "tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2"}, @@ -5593,6 +6088,7 @@ version = "2.3.1" description = "A language and compiler for custom Deep Learning operations" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "triton-2.3.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c84595cbe5e546b1b290d2a58b1494df5a2ef066dd890655e5b8a8a92205c33"}, {file = "triton-2.3.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9d64ae33bcb3a7a18081e3a746e8cf87ca8623ca13d2c362413ce7a486f893e"}, @@ -5616,10 +6112,12 @@ version = "4.14.0" description = "Backported and Experimental Type Hints for Python 3.9+" optional = false python-versions = ">=3.9" +groups = ["main", "dev"] files = [ {file = "typing_extensions-4.14.0-py3-none-any.whl", hash = "sha256:a1514509136dd0b477638fc68d6a91497af5076466ad0fa6c338e44e359944af"}, {file = "typing_extensions-4.14.0.tar.gz", hash = "sha256:8676b788e32f02ab42d9e7c61324048ae4c6d844a399eebace3d4979d75ceef4"}, ] +markers = {dev = "python_version == \"3.10\""} [[package]] name = "urllib3" @@ -5627,23 +6125,43 @@ version = "2.5.0" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc"}, {file = "urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760"}, ] [package.extras] -brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""] h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] +[[package]] +name = "werkzeug" +version = "3.1.3" +description = "The comprehensive WSGI web application library." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "werkzeug-3.1.3-py3-none-any.whl", hash = "sha256:54b78bf3716d19a65be4fceccc0d1d7b89e608834989dfae50ea87564639213e"}, + {file = "werkzeug-3.1.3.tar.gz", hash = "sha256:60723ce945c19328679790e3282cc758aa4a6040e4bb330f53d30fa546d44746"}, +] + +[package.dependencies] +MarkupSafe = ">=2.1.1" + +[package.extras] +watchdog = ["watchdog (>=2.3)"] + [[package]] name = "yarl" version = "1.20.1" description = "Yet another URL library" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "yarl-1.20.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6032e6da6abd41e4acda34d75a816012717000fa6839f37124a47fcefc49bec4"}, {file = "yarl-1.20.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2c7b34d804b8cf9b214f05015c4fee2ebe7ed05cf581e7192c06555c71f4446a"}, @@ -5757,6 +6275,6 @@ multidict = ">=4.0" propcache = ">=0.2.1" [metadata] -lock-version = "2.0" +lock-version = "2.1" python-versions = "^3.10" -content-hash = "4e77d5c57a6b855591690157c5a50cbf7f9095e996b5e9d51702685bc71966fb" +content-hash = "2c7d5b04371aa82fcd1b31d4686cd215670db1a38895f4b5d82038b321492c7c" diff --git a/pyproject.toml b/pyproject.toml index 2a32fd4..2e9e54d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,6 +21,8 @@ pyttsx3 = "^2.90" pyperclip = "^1.8.2" edge-tts = "^6.1.0" pygame = "^2.5.0" +flask = "^3.0.0" +flask-cors = "^4.0.0" [tool.poetry.group.dev.dependencies] pytest = "^7.0.0" @@ -32,4 +34,6 @@ requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" [tool.poetry.scripts] -voice-transcriber = "voice_transcriber.main:main" \ No newline at end of file +voice-transcriber = "voice_transcriber.main:main" +voice-transcriber-api = "voice_transcriber.api_server:main" +voice-transcriber-combined = "voice_transcriber.combined_service:main" \ No newline at end of file diff --git a/scripts/get-local-ip.py b/scripts/get-local-ip.py new file mode 100755 index 0000000..068e6bf --- /dev/null +++ b/scripts/get-local-ip.py @@ -0,0 +1,133 @@ +#!/usr/bin/env python3 +"""Get local IP address for mobile app configuration.""" + +import socket +import subprocess +import sys +import platform + +def get_local_ip(): + """Get the local IP address.""" + try: + # Connect to a remote address to determine local IP + with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s: + # Connect to Google DNS (doesn't actually send data) + s.connect(("8.8.8.8", 80)) + local_ip = s.getsockname()[0] + return local_ip + except Exception: + return None + +def get_network_interfaces(): + """Get all network interfaces and their IPs.""" + interfaces = [] + + try: + if platform.system() == "Linux": + # Use ip command on Linux + result = subprocess.run(['ip', 'addr', 'show'], + capture_output=True, text=True) + if result.returncode == 0: + lines = result.stdout.split('\n') + current_interface = None + for line in lines: + line = line.strip() + if line and not line.startswith(' '): + # Interface line + parts = line.split(':') + if len(parts) >= 2: + current_interface = parts[1].strip() + elif 'inet ' in line and current_interface: + # IP address line + parts = line.split() + for i, part in enumerate(parts): + if part == 'inet' and i + 1 < len(parts): + ip = parts[i + 1].split('/')[0] + if not ip.startswith('127.'): + interfaces.append((current_interface, ip)) + break + + elif platform.system() == "Darwin": # macOS + result = subprocess.run(['ifconfig'], capture_output=True, text=True) + if result.returncode == 0: + lines = result.stdout.split('\n') + current_interface = None + for line in lines: + if line and not line.startswith('\t') and not line.startswith(' '): + # Interface line + current_interface = line.split(':')[0] + elif '\tinet ' in line and current_interface: + # IP address line + parts = line.strip().split() + if len(parts) >= 2: + ip = parts[1] + if not ip.startswith('127.'): + interfaces.append((current_interface, ip)) + + elif platform.system() == "Windows": + result = subprocess.run(['ipconfig'], capture_output=True, text=True) + if result.returncode == 0: + lines = result.stdout.split('\n') + current_interface = None + for line in lines: + line = line.strip() + if 'adapter' in line.lower(): + current_interface = line + elif 'IPv4 Address' in line and current_interface: + parts = line.split(':') + if len(parts) >= 2: + ip = parts[1].strip() + if not ip.startswith('127.'): + interfaces.append((current_interface, ip)) + + except Exception as e: + print(f"Error getting network interfaces: {e}") + + return interfaces + +def main(): + """Main function.""" + print("๐ŸŒ Voice Copilot - Network Configuration Helper") + print("=" * 50) + + # Get primary local IP + local_ip = get_local_ip() + if local_ip: + print(f"๐Ÿ“ Primary Local IP: {local_ip}") + print(f"๐Ÿ”— Suggested Server URL: http://{local_ip}:8000") + print() + + # Get all network interfaces + print("๐Ÿ“ก All Network Interfaces:") + interfaces = get_network_interfaces() + + if interfaces: + for interface, ip in interfaces: + print(f" {interface}: {ip}") + print() + + # Find WiFi-like interfaces + wifi_interfaces = [ + (iface, ip) for iface, ip in interfaces + if any(keyword in iface.lower() for keyword in ['wlan', 'wifi', 'wireless', 'wi-fi']) + ] + + if wifi_interfaces: + print("๐Ÿ“ถ WiFi Interfaces (recommended for mobile):") + for interface, ip in wifi_interfaces: + print(f" {interface}: http://{ip}:8000") + else: + print(" No network interfaces found") + + print() + print("๐Ÿ“‹ Setup Instructions:") + print("1. Start the Voice Copilot API server:") + print(" poetry run voice-transcriber-api --host 0.0.0.0 --port 8000") + print() + print("2. Use one of the URLs above in your mobile app") + print() + print("3. For internet access, use ngrok:") + print(" ./scripts/start-with-ngrok.sh") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/scripts/start-with-ngrok.sh b/scripts/start-with-ngrok.sh new file mode 100755 index 0000000..dfebab7 --- /dev/null +++ b/scripts/start-with-ngrok.sh @@ -0,0 +1,144 @@ +#!/bin/bash + +# Start Voice Copilot with ngrok tunnel for mobile access +# This script starts both the API server and creates an ngrok tunnel + +set -e + +# Default values +PORT=8000 +MODEL="base" +NGROK_REGION="us" + +# Parse command line arguments +while [[ $# -gt 0 ]]; do + case $1 in + --port) + PORT="$2" + shift 2 + ;; + --model) + MODEL="$2" + shift 2 + ;; + --region) + NGROK_REGION="$2" + shift 2 + ;; + --help) + echo "Usage: $0 [OPTIONS]" + echo "" + echo "Options:" + echo " --port PORT Port to run API server on (default: 8000)" + echo " --model MODEL Whisper model to use (default: base)" + echo " --region REGION ngrok region (default: us)" + echo " --help Show this help message" + exit 0 + ;; + *) + echo "Unknown option: $1" + exit 1 + ;; + esac +done + +echo "๐ŸŽค Starting Voice Copilot with ngrok tunnel..." +echo "Port: $PORT" +echo "Model: $MODEL" +echo "Region: $NGROK_REGION" +echo "" + +# Check if ngrok is installed +if ! command -v ngrok &> /dev/null; then + echo "โŒ ngrok is not installed. Please install it from https://ngrok.com/download" + exit 1 +fi + +# Check if poetry is available +if ! command -v poetry &> /dev/null; then + echo "โŒ Poetry is not installed. Please install it first." + exit 1 +fi + +# Function to cleanup background processes +cleanup() { + echo "" + echo "๐Ÿ›‘ Shutting down services..." + if [[ -n $API_PID ]]; then + kill $API_PID 2>/dev/null || true + fi + if [[ -n $NGROK_PID ]]; then + kill $NGROK_PID 2>/dev/null || true + fi + exit 0 +} + +# Set up signal handlers +trap cleanup SIGINT SIGTERM + +# Start the API server in background +echo "๐Ÿš€ Starting Voice Copilot API server..." +poetry run voice-transcriber-api --host 0.0.0.0 --port $PORT --model $MODEL & +API_PID=$! + +# Wait a moment for the server to start +sleep 3 + +# Check if API server is running +if ! kill -0 $API_PID 2>/dev/null; then + echo "โŒ Failed to start API server" + exit 1 +fi + +echo "โœ… API server started (PID: $API_PID)" + +# Start ngrok tunnel +echo "๐ŸŒ Starting ngrok tunnel..." +ngrok http $PORT --region $NGROK_REGION & +NGROK_PID=$! + +# Wait a moment for ngrok to start +sleep 3 + +# Check if ngrok is running +if ! kill -0 $NGROK_PID 2>/dev/null; then + echo "โŒ Failed to start ngrok tunnel" + kill $API_PID 2>/dev/null || true + exit 1 +fi + +echo "โœ… ngrok tunnel started (PID: $NGROK_PID)" +echo "" + +# Try to get the ngrok URL +sleep 2 +NGROK_URL="" +for i in {1..10}; do + NGROK_URL=$(curl -s http://localhost:4040/api/tunnels 2>/dev/null | grep -o 'https://[^"]*\.ngrok\.io' | head -1 || true) + if [[ -n $NGROK_URL ]]; then + break + fi + sleep 1 +done + +if [[ -n $NGROK_URL ]]; then + echo "๐ŸŽ‰ Voice Copilot is now accessible at:" + echo " $NGROK_URL" + echo "" + echo "๐Ÿ“ฑ Use this URL in your mobile app!" + echo "" + echo "๐Ÿ”— ngrok dashboard: http://localhost:4040" +else + echo "โš ๏ธ Could not retrieve ngrok URL automatically." + echo " Check the ngrok dashboard at: http://localhost:4040" +fi + +echo "" +echo "๐Ÿ“‹ Service Status:" +echo " API Server: http://localhost:$PORT (PID: $API_PID)" +echo " ngrok Tunnel: Running (PID: $NGROK_PID)" +echo "" +echo "Press Ctrl+C to stop all services" + +# Wait for user interrupt +wait \ No newline at end of file diff --git a/test_api.py b/test_api.py new file mode 100644 index 0000000..55a7e24 --- /dev/null +++ b/test_api.py @@ -0,0 +1,108 @@ +#!/usr/bin/env python3 +"""Test script for the Voice Transcriber API.""" + +import requests +import tempfile +import numpy as np +from scipy.io import wavfile +import time + +def create_test_audio(): + """Create a simple test audio file (silence).""" + # Create 2 seconds of silence at 16kHz + sample_rate = 16000 + duration = 2.0 + samples = int(sample_rate * duration) + + # Generate some simple audio (sine wave) + t = np.linspace(0, duration, samples, False) + frequency = 440 # A4 note + audio_data = np.sin(2 * np.pi * frequency * t) * 0.3 + + # Convert to int16 + audio_data = (audio_data * 32767).astype(np.int16) + + # Save to temporary file + temp_file = tempfile.NamedTemporaryFile(suffix='.wav', delete=False) + temp_filename = temp_file.name + temp_file.close() + + wavfile.write(temp_filename, sample_rate, audio_data) + return temp_filename + +def test_api(): + """Test the Voice Transcriber API.""" + base_url = "http://localhost:12001" + + print("๐Ÿงช Testing Voice Transcriber API") + print("=" * 40) + + # Test health endpoint + print("1. Testing health endpoint...") + try: + response = requests.get(f"{base_url}/health") + if response.status_code == 200: + data = response.json() + print(f" โœ… Health check passed: {data['status']}") + else: + print(f" โŒ Health check failed: {response.status_code}") + return + except Exception as e: + print(f" โŒ Health check error: {e}") + return + + # Test status endpoint + print("2. Testing status endpoint...") + try: + response = requests.get(f"{base_url}/status") + if response.status_code == 200: + data = response.json() + print(f" โœ… Status check passed") + print(f" ๐Ÿ“Š Model: {data['config']['model_name']}") + print(f" ๐Ÿ“Š Sample rate: {data['config']['sample_rate']}") + print(f" ๐Ÿ“Š Model loaded: {data['model_info']['is_loaded']}") + else: + print(f" โŒ Status check failed: {response.status_code}") + except Exception as e: + print(f" โŒ Status check error: {e}") + + # Test transcription endpoint + print("3. Testing transcription endpoint...") + try: + # Create test audio file + audio_file = create_test_audio() + print(f" ๐Ÿ“ Created test audio: {audio_file}") + + # Upload and transcribe + with open(audio_file, 'rb') as f: + files = {'audio': ('test.wav', f, 'audio/wav')} + + print(" ๐Ÿ”„ Uploading audio for transcription...") + start_time = time.time() + response = requests.post(f"{base_url}/transcribe", files=files) + end_time = time.time() + + if response.status_code == 200: + data = response.json() + if data['success']: + print(f" โœ… Transcription successful!") + print(f" ๐Ÿ“ Text: '{data['text']}'") + print(f" โฑ๏ธ Processing time: {data['transcription_time']:.2f}s") + print(f" โฑ๏ธ Total time: {end_time - start_time:.2f}s") + else: + print(f" โš ๏ธ Transcription returned no text: {data.get('error', 'Unknown error')}") + else: + print(f" โŒ Transcription failed: {response.status_code}") + print(f" ๐Ÿ“„ Response: {response.text}") + + # Clean up + import os + os.unlink(audio_file) + + except Exception as e: + print(f" โŒ Transcription test error: {e}") + + print("\n๐ŸŽ‰ API testing completed!") + +if __name__ == "__main__": + test_api() \ No newline at end of file diff --git a/voice_transcriber/api_server.py b/voice_transcriber/api_server.py new file mode 100644 index 0000000..52cc1b6 --- /dev/null +++ b/voice_transcriber/api_server.py @@ -0,0 +1,179 @@ +"""Web API server for mobile app integration.""" + +import logging +import os +import tempfile +import time +from typing import Optional +from flask import Flask, request, jsonify +from flask_cors import CORS +from werkzeug.utils import secure_filename +import threading + +from .transcriber import Transcriber +from .config import config + +logger = logging.getLogger(__name__) + +class VoiceTranscriberAPI: + """Web API server for voice transcription.""" + + def __init__(self, host='0.0.0.0', port=8000): + self.app = Flask(__name__) + CORS(self.app) # Enable CORS for mobile app + + self.host = host + self.port = port + self.transcriber = Transcriber() + + # Setup routes + self._setup_routes() + + def _setup_routes(self): + """Setup API routes.""" + + @self.app.route('/health', methods=['GET']) + def health_check(): + """Health check endpoint.""" + return jsonify({ + 'status': 'healthy', + 'service': 'voice-transcriber-api', + 'timestamp': time.time() + }) + + @self.app.route('/transcribe', methods=['POST']) + def transcribe_audio(): + """Transcribe uploaded audio file.""" + try: + # Check if audio file is present + if 'audio' not in request.files: + return jsonify({'error': 'No audio file provided'}), 400 + + audio_file = request.files['audio'] + if audio_file.filename == '': + return jsonify({'error': 'No audio file selected'}), 400 + + # Save uploaded file temporarily + temp_file = tempfile.NamedTemporaryFile( + suffix='.wav', + delete=False + ) + temp_filename = temp_file.name + temp_file.close() + + audio_file.save(temp_filename) + + logger.info(f"Received audio file for transcription: {temp_filename}") + + # Transcribe audio + start_time = time.time() + text = self.transcriber.transcribe(temp_filename) + transcription_time = time.time() - start_time + + # Clean up temp file (transcriber already does this, but just in case) + try: + if os.path.exists(temp_filename): + os.unlink(temp_filename) + except Exception as e: + logger.warning(f"Failed to clean up temp file: {e}") + + if text: + logger.info(f"Transcription successful: '{text}' (took {transcription_time:.2f}s)") + return jsonify({ + 'success': True, + 'text': text, + 'transcription_time': transcription_time, + 'timestamp': time.time() + }) + else: + logger.warning("No text transcribed") + return jsonify({ + 'success': False, + 'error': 'No speech detected or transcription failed', + 'transcription_time': transcription_time, + 'timestamp': time.time() + }), 400 + + except Exception as e: + logger.error(f"Transcription API error: {e}") + return jsonify({ + 'success': False, + 'error': str(e), + 'timestamp': time.time() + }), 500 + + @self.app.route('/status', methods=['GET']) + def get_status(): + """Get transcriber status.""" + try: + model_info = self.transcriber.get_model_info() + return jsonify({ + 'success': True, + 'model_info': model_info, + 'config': { + 'model_name': config.model_name, + 'sample_rate': config.sample_rate, + 'channels': config.channels + }, + 'timestamp': time.time() + }) + except Exception as e: + logger.error(f"Status API error: {e}") + return jsonify({ + 'success': False, + 'error': str(e), + 'timestamp': time.time() + }), 500 + + def run(self, debug=False): + """Run the API server.""" + logger.info(f"Starting Voice Transcriber API server on {self.host}:{self.port}") + self.app.run( + host=self.host, + port=self.port, + debug=debug, + threaded=True + ) + + def run_threaded(self, debug=False): + """Run the API server in a separate thread.""" + server_thread = threading.Thread( + target=self.run, + kwargs={'debug': debug}, + daemon=True + ) + server_thread.start() + return server_thread + + +def main(): + """Main entry point for API server.""" + import argparse + + parser = argparse.ArgumentParser(description='Voice Transcriber API Server') + parser.add_argument('--host', default='0.0.0.0', help='Host to bind to') + parser.add_argument('--port', type=int, default=8000, help='Port to bind to') + parser.add_argument('--debug', action='store_true', help='Enable debug mode') + parser.add_argument('--model', default='base', choices=['tiny', 'base', 'small', 'medium', 'large'], + help='Whisper model size') + + args = parser.parse_args() + + # Update config + config.model_name = args.model + config.debug = args.debug + + # Setup logging + level = logging.DEBUG if args.debug else logging.INFO + logging.basicConfig( + level=level, + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' + ) + + # Create and run API server + api_server = VoiceTranscriberAPI(host=args.host, port=args.port) + api_server.run(debug=args.debug) + + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/voice_transcriber/combined_service.py b/voice_transcriber/combined_service.py new file mode 100644 index 0000000..e7ddb4f --- /dev/null +++ b/voice_transcriber/combined_service.py @@ -0,0 +1,129 @@ +"""Combined service that runs both desktop hotkeys and API server.""" + +import logging +import time +import threading +from .service import VoiceTranscriberService +from .api_server import VoiceTranscriberAPI +from .config import config + +logger = logging.getLogger(__name__) + + +class CombinedVoiceService: + """Combined service running both desktop and API functionality.""" + + def __init__(self, api_host='0.0.0.0', api_port=8000): + self.desktop_service = VoiceTranscriberService() + self.api_server = VoiceTranscriberAPI(host=api_host, port=api_port) + self.api_thread = None + self.is_running = False + + def start(self): + """Start both services.""" + try: + logger.info("Starting Combined Voice Service") + + # Start desktop service + if not self.desktop_service.start(): + logger.error("Failed to start desktop service") + return False + + # Start API server in background thread + self.api_thread = self.api_server.run_threaded(debug=config.debug) + + self.is_running = True + logger.info("Combined Voice Service started successfully") + logger.info(f"Desktop hotkeys: {'+'.join(config.hotkey)} (STT), {'+'.join(config.tts_hotkey)} (TTS)") + logger.info(f"API server: http://{self.api_server.host}:{self.api_server.port}") + + return True + + except Exception as e: + logger.error(f"Failed to start combined service: {e}") + self.stop() + return False + + def stop(self): + """Stop both services.""" + if not self.is_running: + return + + logger.info("Stopping Combined Voice Service") + + # Stop desktop service + self.desktop_service.stop() + + # API server will stop when main thread exits + self.is_running = False + logger.info("Combined Voice Service stopped") + + def run_forever(self): + """Run combined service indefinitely.""" + if not self.start(): + return + + try: + logger.info("Combined service running. Press Ctrl+C to stop.") + while self.is_running: + time.sleep(1) + except KeyboardInterrupt: + logger.info("Received interrupt signal") + finally: + self.stop() + + def get_status(self): + """Get status of both services.""" + return { + 'desktop_service': self.desktop_service.get_status(), + 'api_server': { + 'host': self.api_server.host, + 'port': self.api_server.port, + 'is_running': self.is_running + } + } + + +def main(): + """Main entry point for combined service.""" + import argparse + + parser = argparse.ArgumentParser(description='Combined Voice Transcriber Service') + parser.add_argument('--api-host', default='0.0.0.0', help='API server host') + parser.add_argument('--api-port', type=int, default=8000, help='API server port') + parser.add_argument('--debug', action='store_true', help='Enable debug logging') + parser.add_argument('--model', default='base', choices=['tiny', 'base', 'small', 'medium', 'large'], + help='Whisper model size') + parser.add_argument('--hotkey', default='ctrl+f1', help='Recording hotkey') + parser.add_argument('--tts-hotkey', default='ctrl+f2', help='TTS hotkey') + parser.add_argument('--no-tray', action='store_true', help='Disable system tray icon') + parser.add_argument('--no-notifications', action='store_true', help='Disable desktop notifications') + + args = parser.parse_args() + + # Update config from arguments + config.debug = args.debug + config.model_name = args.model + config.show_system_tray = not args.no_tray + config.show_notifications = not args.no_notifications + + # Parse hotkeys + if args.hotkey: + config.hotkey = tuple(key.strip().lower() for key in args.hotkey.split('+')) + if args.tts_hotkey: + config.tts_hotkey = tuple(key.strip().lower() for key in args.tts_hotkey.split('+')) + + # Setup logging + level = logging.DEBUG if config.debug else logging.INFO + logging.basicConfig( + level=level, + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' + ) + + # Create and run combined service + service = CombinedVoiceService(api_host=args.api_host, api_port=args.api_port) + service.run_forever() + + +if __name__ == '__main__': + main() \ No newline at end of file