From f3cd99a471c0b3b21ee2c7dba5baff0b9ab69c30 Mon Sep 17 00:00:00 2001 From: hzrd149 <8001706+hzrd149@users.noreply.github.com> Date: Mon, 7 Apr 2025 11:47:27 +0100 Subject: [PATCH] Docker compose (#2) * Add dockerfile Add example docker compose Add docker build action Update extract.sh to use faster-whisper Update src/extract.py to check and download ollama models * remove multi-platform build * add vibeline-ui to docker compose * Fix VOICE_MEMOS_DIR variable being ignored in some files * remove requirement for faster-whisper since it comes with whisper-ctranslate2 --- .dockerignore | 7 ++++ .env.example | 8 ++++- .github/workflows/docker.yml | 51 +++++++++++++++++++++++++++ .gitignore | 7 +++- Dockerfile | 28 +++++++++++++++ README.md | 6 ++-- docker-compose.yml | 53 ++++++++++++++++++++++++++++ extract.sh | 4 +-- post_process.sh | 4 +-- process.sh | 6 ++-- requirements.txt | 3 +- run.sh | 4 +-- setup.sh | 4 +-- src/extract.py | 65 +++++++++++++++++++++++----------- src/post_process.py | 40 ++++++++++----------- src/watch_voice_memos.py | 68 +++++++++++++++++++----------------- transcribe.sh | 15 ++++---- watch.sh | 2 +- 18 files changed, 277 insertions(+), 98 deletions(-) create mode 100644 .dockerignore create mode 100644 .github/workflows/docker.yml create mode 100644 Dockerfile create mode 100644 docker-compose.yml diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..1a34863 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,7 @@ +ollama +VoiceMemos +vibenv +open-webui +docker-compose.yml +syncthing +.cursor diff --git a/.env.example b/.env.example index 35b9490..9852795 100644 --- a/.env.example +++ b/.env.example @@ -4,5 +4,11 @@ OLLAMA_EXTRACT_MODEL=llama2 OLLAMA_SUMMARIZE_MODEL=llama2 OLLAMA_DEFAULT_MODEL=llama2 +# Ollama connection configuration (optional) +# OLLAMA_HOST=http://localhost:11434 + # Path configuration -VOICE_MEMOS_DIR=VoiceMemos \ No newline at end of file +VOICE_MEMOS_DIR=VoiceMemos + +# Whisper model configuration +WHISPER_MODEL=base.en diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 0000000..1f762e6 --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,51 @@ +name: Docker image + +on: + push: + branches: + - "**" + tags: + - "v*.*.*" + +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} + +jobs: + build-and-push-image: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Log in to the Container registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + tags: | + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}} + type=ref,event=branch + type=ref,event=pr + type=sha + + - name: Build and push Docker image + uses: docker/build-push-action@v5 + with: + context: . + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} diff --git a/.gitignore b/.gitignore index 1ee54b9..4f331ea 100644 --- a/.gitignore +++ b/.gitignore @@ -7,7 +7,7 @@ VoiceMemos # Python cache files __pycache__/ *.py[cod] -*$py.class +*$py.class # macOS stuff .DS_Store @@ -15,3 +15,8 @@ __pycache__/ # Environment variables .env .env.local + +# docker compose volumes +ollama +open-webui +syncthing diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..5ceb8fb --- /dev/null +++ b/Dockerfile @@ -0,0 +1,28 @@ +FROM python:3.10-slim + +WORKDIR /app + +# Install ffmpeg +RUN apt-get update && apt-get install -y \ + ffmpeg bc \ + && rm -rf /var/lib/apt/lists/* + +# Install dependencies first +COPY requirements.txt . + +# Install dependencies +RUN pip install -r requirements.txt + +# Copy app files +COPY . . + +# Setup environment variables +ENV OLLAMA_EXTRACT_MODEL=llama2 +ENV OLLAMA_SUMMARIZE_MODEL=llama2 +ENV OLLAMA_DEFAULT_MODEL=llama2 +ENV WHISPER_MODEL=base.en +ENV VOICE_MEMOS_DIR=/app/VoiceMemos + +VOLUME [ "/app/VoiceMemos" ] + +CMD ["python", "src/watch_voice_memos.py"] diff --git a/README.md b/README.md index 13e6525..033bb60 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ cd vibeline 2. Create and activate a virtual environment: ```bash python -m venv vibenv -source vibenv/bin/activate # On Windows: vibenv\Scripts\activate +[ -d "vibenv" ] && source vibenv/bin/activate # On Windows: vibenv\Scripts\activate ``` 3. Install dependencies: @@ -112,7 +112,7 @@ run: matching output_extension: .md prompt: | Based on the following transcript, create a blog post... - + Transcript: {transcript} ``` @@ -129,4 +129,4 @@ Contributions are welcome! Please feel free to submit a Pull Request. ## License -This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. \ No newline at end of file +This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..6c0504f --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,53 @@ +services: + ollama: + image: ollama/ollama:latest + restart: unless-stopped + volumes: + - ./ollama:/root/.ollama + environment: + - OLLAMA_MODEL=llama2 + + vibeline: + build: . + # image: ghcr.io/dergigi/vibeline + restart: unless-stopped + environment: + OLLAMA_HOST: http://ollama:11434 + VOICE_MEMOS_DIR: /app/VoiceMemos + OLLAMA_EXTRACT_MODEL: llama2 + OLLAMA_SUMMARIZE_MODEL: llama2 + OLLAMA_DEFAULT_MODEL: llama2 + WHISPER_MODEL: base + depends_on: + - ollama + volumes: + - ./VoiceMemos:/app/VoiceMemos + + vibeline-ui: + image: ghcr.io/dergigi/vibeline-ui + restart: unless-stopped + ports: + - 3000:3000 + volumes: + - ./VoiceMemos:/app/VoiceMemos + + # Optionally run syncthing + syncthing: + image: syncthing/syncthing + restart: unless-stopped + environment: + PUID: 1000 + PGID: 1000 + volumes: + - ./syncthing:/var/syncthing/config + - ./VoiceMemos:/var/syncthing/VoiceMemos + ports: + - 8384:8384 # Web UI + - 22000:22000/tcp # TCP file transfers + - 22000:22000/udp # QUIC file transfers + - 21027:21027/udp # Receive local discovery broadcasts + healthcheck: + test: curl -fkLsS -m 2 127.0.0.1:8384/rest/noauth/health | grep -o --color=never OK || exit 1 + interval: 1m + timeout: 10s + retries: 3 diff --git a/extract.sh b/extract.sh index 4526b34..44cf9f6 100755 --- a/extract.sh +++ b/extract.sh @@ -25,10 +25,10 @@ if [ ! -f "$input_file" ]; then fi # Activate the virtual environment -source vibenv/bin/activate +[ -d "vibenv" ] && source vibenv/bin/activate # Run the Python script with force flag if provided python src/extract.py $force_flag "$input_file" # Deactivate the virtual environment -deactivate \ No newline at end of file +[ -d "vibenv" ] && deactivate diff --git a/post_process.sh b/post_process.sh index b1d90fa..6d49a7b 100755 --- a/post_process.sh +++ b/post_process.sh @@ -1,10 +1,10 @@ #!/bin/bash # Activate the virtual environment -source vibenv/bin/activate +[ -d "vibenv" ] && source vibenv/bin/activate # Run the Python script with any provided arguments python src/post_process.py "$@" # Deactivate the virtual environment -deactivate \ No newline at end of file +[ -d "vibenv" ] && deactivate diff --git a/process.sh b/process.sh index 2dcf8ab..1fa1ab7 100755 --- a/process.sh +++ b/process.sh @@ -25,8 +25,8 @@ if [ ! -f "$input_file" ]; then fi # Set the directory paths -VOICE_MEMO_DIR="VoiceMemos" -TRANSCRIPT_DIR="$VOICE_MEMO_DIR/transcripts" +VOICE_MEMOS_DIR="${VOICE_MEMOS_DIR:-VoiceMemos}" +TRANSCRIPT_DIR="$VOICE_MEMOS_DIR/transcripts" # Get the filename without the path and extension filename=$(basename "$input_file" .m4a) @@ -54,4 +54,4 @@ echo "Step 3: Post-processing action items..." ./post_process.sh echo "----------------------------------------" -echo "Processing complete!" \ No newline at end of file +echo "Processing complete!" diff --git a/requirements.txt b/requirements.txt index 9cda34c..ba8426b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,8 +6,9 @@ watchdog>=3.0.0 inflect>=7.5.0 pyyaml>=6.0.1 mcp>=1.6.0 +whisper-ctranslate2>=0.2.5 # Development dependencies pytest>=7.4.0 black>=23.7.0 -flake8>=6.1.0 +flake8>=6.1.0 diff --git a/run.sh b/run.sh index 3b6441b..d511798 100755 --- a/run.sh +++ b/run.sh @@ -10,7 +10,7 @@ done shift $((OPTIND-1)) # Activate virtual environment -source vibenv/bin/activate +[ -d "vibenv" ] && source vibenv/bin/activate echo "Processing all voice memos..." for file in VoiceMemos/*.m4a; do @@ -21,4 +21,4 @@ for file in VoiceMemos/*.m4a; do fi done -echo "Done! All voice memos have been processed." \ No newline at end of file +echo "Done! All voice memos have been processed." diff --git a/setup.sh b/setup.sh index b751068..97aef51 100755 --- a/setup.sh +++ b/setup.sh @@ -8,10 +8,10 @@ fi # Activate virtual environment echo "Activating virtual environment..." -source vibenv/bin/activate +[ -d "vibenv" ] && source vibenv/bin/activate # Install requirements echo "Installing requirements..." pip3 install -r requirements.txt -echo "Setup complete! Virtual environment is activated and requirements are installed." \ No newline at end of file +echo "Setup complete! Virtual environment is activated and requirements are installed." diff --git a/src/extract.py b/src/extract.py index 96a1b52..de737ec 100755 --- a/src/extract.py +++ b/src/extract.py @@ -21,16 +21,19 @@ p = inflect.engine() OLLAMA_MODEL = os.getenv("OLLAMA_EXTRACT_MODEL", "llama2") VOICE_MEMOS_DIR = os.getenv("VOICE_MEMOS_DIR", "VoiceMemos") +# Set a different host (default is http://localhost:11434) +ollama.host = os.getenv("OLLAMA_HOST", "http://localhost:11434") + def determine_active_plugins(text: str, plugins: Dict[str, Plugin]) -> List[str]: """Determine which plugins should be run on this transcript.""" active_plugins = set() # Use a set to avoid duplicates - + for plugin_name, plugin in plugins.items(): # Always include plugins with run: always if plugin.run == "always": active_plugins.add(plugin_name) continue - + # For matching plugins, check based on type (and/or) if plugin.run == "matching": words = plugin_name.split('_') @@ -40,16 +43,16 @@ def determine_active_plugins(text: str, plugins: Dict[str, Plugin]) -> List[str] else: # and if all(re.search(r'\b' + word + r'\b', text.lower()) for word in words): active_plugins.add(plugin_name) - + return list(active_plugins) def generate_additional_content(plugin: Plugin, transcript_text: str, summary_text: str) -> str: """Generate additional content using the specified plugin.""" prompt = plugin.prompt.format(transcript=transcript_text, summary=summary_text) - + # Use plugin-specific model if specified, otherwise use default model = plugin.model or OLLAMA_MODEL - + response = ollama.chat(model=model, messages=[ { 'role': 'user', @@ -58,76 +61,96 @@ def generate_additional_content(plugin: Plugin, transcript_text: str, summary_te ]) return response['message']['content'].strip() +def ensure_model_exists(model_name: str) -> None: + """ + Ensure the specified Ollama model is available locally. + If not, pull it before proceeding. + """ + try: + # Try to get model info - this will fail if model doesn't exist + ollama.show(model=model_name) + except Exception: + print(f"Model {model_name} not found locally. Pulling model...") + try: + ollama.pull(model=model_name) + print(f"Successfully pulled model {model_name}") + except Exception as e: + print(f"Error pulling model {model_name}: {e}") + sys.exit(1) + def main(): # Set up argument parser parser = argparse.ArgumentParser(description='Extract content from transcripts using plugins.') parser.add_argument('transcript_file', help='The transcript file to process') parser.add_argument('-f', '--force', action='store_true', help='Force overwrite existing output files') args = parser.parse_args() - + + # Ensure the default model exists + ensure_model_exists(OLLAMA_MODEL) + input_file = Path(args.transcript_file) if not input_file.exists(): print(f"Error: File {input_file} does not exist") sys.exit(1) - + # Load plugins plugin_manager = PluginManager(Path("plugins")) plugins = plugin_manager.get_all_plugins() if not plugins: print("Error: No plugins found in plugins directory") sys.exit(1) - + # Set up directory paths - voice_memo_dir = Path(VOICE_MEMOS_DIR) + voice_memos_dir = Path(VOICE_MEMOS_DIR) output_dirs = {} - + # Create output directories for each plugin for plugin_name in plugins.keys(): # Use inflect to properly pluralize the directory name plural_name = p.plural(plugin_name) - output_dir = voice_memo_dir / plural_name + output_dir = voice_memos_dir / plural_name output_dir.mkdir(parents=True, exist_ok=True) output_dirs[plugin_name] = output_dir - + print(f"Processing transcript: {input_file}") print("Extracting content...") - + # Read transcript with open(input_file, 'r', encoding='utf-8') as f: transcript_text = f.read() - + # Read summary if it exists summary_file = input_file.parent / f"{input_file.stem}_summary.txt" summary_text = "" if summary_file.exists(): with open(summary_file, 'r', encoding='utf-8') as f: summary_text = f.read() - + # Determine which plugins to run active_plugins = determine_active_plugins(transcript_text, plugins) if active_plugins: for plugin_name in active_plugins: plugin = plugins[plugin_name] print(f" Running {plugin_name} plugin...") - + # Check if output file exists filename = input_file.stem output_file = output_dirs[plugin_name] / f"{filename}{plugin.output_extension}" - + if output_file.exists() and not args.force: print(f" Skipping: {output_file} already exists (use -f to overwrite)") continue - + additional_content = generate_additional_content(plugin, transcript_text, summary_text) - + # Save to appropriate directory using base filename with open(output_file, 'w', encoding='utf-8') as f: f.write(additional_content) print(f" Content saved to: {output_file}") else: print(" No matching plugins found for this transcript") - + print("----------------------------------------") if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/src/post_process.py b/src/post_process.py index dad9d69..e009006 100644 --- a/src/post_process.py +++ b/src/post_process.py @@ -21,7 +21,7 @@ def extract_action_items(content: str) -> List[str]: # Skip empty lines and headers if not line.strip() or line.strip().startswith(('Here are', 'Rules were', 'No action items')): continue - + # Match lines that start with any list marker (-, *, +) if re.match(r'^\s*[-*+]', line): # Find the first letter in the line @@ -39,7 +39,7 @@ def format_action_items(items: List[str], filename: str) -> str: Uses the filename (timestamp) to create a human-readable header.""" if not items: return "# No items found\n" - + # Extract date and time from filename (format: YYYYMMDD_HHMMSS.txt) date_str = filename.split('.')[0] # Remove .txt extension year = int(date_str[:4]) @@ -47,11 +47,11 @@ def format_action_items(items: List[str], filename: str) -> str: day = int(date_str[6:8]) hour = int(date_str[9:11]) minute = int(date_str[11:13]) - + # Create datetime object and format it dt = datetime(year, month, day, hour, minute) formatted = f"# {dt.strftime('%a %b %d @ %I:%M %p')}\n\n" - + for item in items: # Ensure each item starts with a capital letter item = item[0].upper() + item[1:] if item else item @@ -59,7 +59,7 @@ def format_action_items(items: List[str], filename: str) -> str: if not item.endswith(('.', '!', '?')): item += '.' formatted += f"- [ ] {item}\n" - + return formatted def main(): @@ -67,48 +67,48 @@ def main(): parser = argparse.ArgumentParser(description='Post-process action items from voice memos.') parser.add_argument('-f', '--force', action='store_true', help='Force overwrite of existing files') args = parser.parse_args() - + # Set up directory paths - voice_memo_dir = Path(VOICE_MEMOS_DIR) - action_items_dir = voice_memo_dir / "action_items" - todos_dir = voice_memo_dir / "TODOs" - + voice_memos_dir = Path(VOICE_MEMOS_DIR) + action_items_dir = voice_memos_dir / "action_items" + todos_dir = voice_memos_dir / "TODOs" + # Create TODOs directory if it doesn't exist todos_dir.mkdir(parents=True, exist_ok=True) - + if not action_items_dir.exists(): print("No action items directory found") return - + # Process each action items file for action_file in action_items_dir.glob("*.txt"): print(f"Processing {action_file.name}...") - + # Check if output file already exists formatted_file = todos_dir / f"{action_file.stem}.md" if formatted_file.exists() and not args.force: print(f" Skipping: {formatted_file} already exists") continue - + # Read the action items with open(action_file, 'r', encoding='utf-8') as f: content = f.read() - + # Extract and format action items items = extract_action_items(content) - + # Skip if no items found if not items: print(f" No action items found in {action_file.name}") continue - + formatted_content = format_action_items(items, action_file.stem) - + # Save formatted content in TODOs directory with open(formatted_file, 'w', encoding='utf-8') as f: f.write(formatted_content) - + print(f" Formatted content saved to: {formatted_file}") if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/src/watch_voice_memos.py b/src/watch_voice_memos.py index 9e2006b..30df5b3 100755 --- a/src/watch_voice_memos.py +++ b/src/watch_voice_memos.py @@ -3,14 +3,18 @@ import os import time import subprocess -import re -import ollama import argparse from pathlib import Path from watchdog.observers import Observer from watchdog.events import FileSystemEventHandler import logging -from datetime import datetime +from dotenv import load_dotenv + +# Load environment variables +load_dotenv() + +# Configuration from environment variables +VOICE_MEMOS_DIR = os.getenv("VOICE_MEMOS_DIR", "VoiceMemos") # Set up logging with more detailed formatting logging.basicConfig( @@ -40,7 +44,7 @@ def process_voice_memo(file_path: Path, force: bool = False) -> None: if force: cmd.append('-f') cmd.append(str(file_path)) - + # Run subprocess without capturing output to show it in real-time result = subprocess.run( cmd, @@ -57,23 +61,23 @@ def process_voice_memo(file_path: Path, force: bool = False) -> None: def watch_voice_memos() -> None: """Watch the VoiceMemos directory for new or modified .m4a files.""" - voice_memos_dir = Path("VoiceMemos") + voice_memos_dir = Path(VOICE_MEMOS_DIR) processed_files = {} # Dictionary to store file paths and their last modification times - + # Set up argument parser parser = argparse.ArgumentParser(description='Watch for voice memos and process them') - parser.add_argument('-f', '--force', action='store_true', + parser.add_argument('-f', '--force', action='store_true', help='Force regeneration of existing files') args = parser.parse_args() - + # Verify the VoiceMemos directory exists if not voice_memos_dir.exists(): logger.error(f"VoiceMemos directory does not exist at: {voice_memos_dir.absolute()}") return - + logger.info("Starting voice memo watcher") logger.info(f"Watching directory: {voice_memos_dir.absolute()}") - + try: while True: # Recursively find all .m4a files @@ -82,7 +86,7 @@ def watch_voice_memos() -> None: current_files.add(str(file_path)) try: current_mtime = os.path.getmtime(file_path) - + # Check if file is new or modified if str(file_path) not in processed_files or processed_files[str(file_path)] != current_mtime: process_voice_memo(file_path, force=args.force) @@ -91,15 +95,15 @@ def watch_voice_memos() -> None: logger.debug(f"File disappeared while processing: {file_path}") except Exception as e: logger.error(f"Error checking file {file_path}: {e}") - + # Check for deleted files deleted_files = set(processed_files.keys()) - current_files for file_path in deleted_files: logger.info(f"File removed: {Path(file_path).name}") del processed_files[file_path] - + time.sleep(1) # Wait for 1 second before next check - + except KeyboardInterrupt: logger.info("Watcher stopped by user") except Exception as e: @@ -109,26 +113,26 @@ def watch_voice_memos() -> None: def main(): # Set up argument parser parser = argparse.ArgumentParser(description='Watch for voice memos and process them') - parser.add_argument('-f', '--force', action='store_true', + parser.add_argument('-f', '--force', action='store_true', help='Force regeneration of existing files') args = parser.parse_args() # Set up directory paths base_dir = Path(__file__).parent.parent - voice_memo_dir = base_dir / "VoiceMemos" - transcript_dir = voice_memo_dir / "transcripts" - summary_dir = voice_memo_dir / "summaries" + voice_memos_dir = base_dir / VOICE_MEMOS_DIR + transcript_dir = voice_memos_dir / "transcripts" + summary_dir = voice_memos_dir / "summaries" # Print debug info about symlinks print("\nDirectory information:") - print(f"Voice memo dir: {voice_memo_dir}") - print(f"Is symlink: {voice_memo_dir.is_symlink()}") - if voice_memo_dir.is_symlink(): - print(f"Resolves to: {voice_memo_dir.resolve()}") - + print(f"Voice memo dir: {voice_memos_dir}") + print(f"Is symlink: {voice_memos_dir.is_symlink()}") + if voice_memos_dir.is_symlink(): + print(f"Resolves to: {voice_memos_dir.resolve()}") + # Verify directories exist - if not voice_memo_dir.exists(): - print(f"Error: {voice_memo_dir} directory does not exist") + if not voice_memos_dir.exists(): + print(f"Error: {voice_memos_dir} directory does not exist") sys.exit(1) if not transcript_dir.exists(): print(f"Error: {transcript_dir} directory does not exist") @@ -138,16 +142,16 @@ def main(): sys.exit(1) # Set up event handler and observer - event_handler = VoiceMemoHandler(voice_memo_dir, transcript_dir, summary_dir, force=args.force) + event_handler = VoiceMemoHandler(voice_memos_dir, transcript_dir, summary_dir, force=args.force) observer = Observer() - + # Watch voice memo directory only (we'll handle transcripts immediately after creation) - observer.schedule(event_handler, str(voice_memo_dir.resolve()), recursive=False) - + observer.schedule(event_handler, str(voice_memos_dir.resolve()), recursive=False) + # Start the observer observer.start() print(f"\nWatching for changes in:") - print(f"- Voice memos: {voice_memo_dir.resolve()}") + print(f"- Voice memos: {voice_memos_dir.resolve()}") if args.force: print("Force mode enabled - will regenerate existing files") print("\nPress Ctrl+C to stop...") @@ -158,7 +162,7 @@ def main(): except KeyboardInterrupt: observer.stop() print("\nStopping file watcher...") - + observer.join() if __name__ == "__main__": @@ -166,4 +170,4 @@ if __name__ == "__main__": watch_voice_memos() except Exception as e: logger.critical(f"Fatal error: {e}") - raise \ No newline at end of file + raise diff --git a/transcribe.sh b/transcribe.sh index ece23d2..71aafc3 100755 --- a/transcribe.sh +++ b/transcribe.sh @@ -25,8 +25,8 @@ if [ ! -f "$input_file" ]; then fi # Set the directory paths -VOICE_MEMO_DIR="VoiceMemos" -TRANSCRIPT_DIR="$VOICE_MEMO_DIR/transcripts" +VOICE_MEMOS_DIR="${VOICE_MEMOS_DIR:-VoiceMemos}" +TRANSCRIPT_DIR="$VOICE_MEMOS_DIR/transcripts" # Create transcripts directory if it doesn't exist mkdir -p "$TRANSCRIPT_DIR" @@ -45,7 +45,7 @@ echo "Processing file: $input_file" echo "Transcribing audio..." # Activate the virtual environment -source vibenv/bin/activate +[ -d "vibenv" ] && source vibenv/bin/activate # Get the duration of the audio file in seconds using ffprobe duration=$(ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "$input_file") @@ -56,14 +56,15 @@ duration_minutes=$(echo "$duration / 60" | bc -l) # Set output format based on duration if (( $(echo "$duration_minutes > 21" | bc -l) )); then # For files longer than 21 minutes, produce all formats - whisper "$input_file" --model base.en --output_dir "$TRANSCRIPT_DIR" --output_format all + whisper-ctranslate2 "$input_file" --model $WHISPER_MODEL --output_dir "$TRANSCRIPT_DIR" --output_format all else # For shorter files, produce only txt format - whisper "$input_file" --model base.en --output_dir "$TRANSCRIPT_DIR" --output_format txt + whisper-ctranslate2 "$input_file" --model $WHISPER_MODEL --output_dir "$TRANSCRIPT_DIR" --output_format txt fi # Deactivate the virtual environment -deactivate +[ -d "vibenv" ] && deactivate echo "Transcription saved to: $transcript_file" -echo "----------------------------------------" \ No newline at end of file +echo "----------------------------------------" + diff --git a/watch.sh b/watch.sh index 32ecf0d..7178435 100755 --- a/watch.sh +++ b/watch.sh @@ -8,7 +8,7 @@ else fi # Activate virtual environment -source vibenv/bin/activate +[ -d "vibenv" ] && source vibenv/bin/activate # Run the watch script python src/watch_voice_memos.py