Blog

  • Update Ollama without losing models

    , ,
    sudo docker pull ollama/ollama
    sudo docker stop ollama
    sudo docker rm ollama
    sudo docker run -d --gpus=all -v ollama:/root/.ollama -p 11434:11434 --name ollama ollama/ollama
  • AAX to MP3

    import subprocess
    import os
    import json
    import re
    import concurrent.futures
    
    def process_file(file_path, input_directory, verbose):
        """Processes a single .aax file."""
        filename = os.path.basename(file_path)
        filename_no_ext = os.path.splitext(filename)[0]
        output_dir = os.path.join(input_directory, filename_no_ext)
        output_mp3 = os.path.join(output_dir, f"{filename_no_ext}.mp3")
    
        if verbose:
            print(f"Processing file: {file_path}")
            print(f"Output directory: {output_dir}")
            print(f"Output MP3: {output_mp3}")
    
        os.makedirs(output_dir, exist_ok=True)
    
        if os.path.exists(output_mp3):
            if verbose:
                print(f"Output file {output_mp3} already exists. Skipping conversion.")
            return
    
        ffmpeg_command = ["ffmpeg", "-y", "-activation_bytes", "XXXXXXXXX", "-i", file_path, "-codec:a", "libmp3lame", output_mp3]
        try:
            subprocess.run(ffmpeg_command, check=True, capture_output=not verbose)
        except subprocess.CalledProcessError as e:
            print(f"Error: ffmpeg conversion failed for {file_path}")
            if verbose:
                print(e.stderr.decode())
            return
    
        if verbose:
            print(f"Conversion complete for {file_path}")
    
        try:
            ffprobe_output = subprocess.run(["ffprobe", "-i", output_mp3, "-show_chapters", "-print_format", "json"], capture_output=True, text=True, check=True).stdout
            chapters_data = json.loads(ffprobe_output)
            chapter_count = len(chapters_data.get("chapters", []))
        except (subprocess.CalledProcessError, json.JSONDecodeError):
            chapter_count = 0
    
        if chapter_count > 0:
            if verbose:
                print(f"Found {chapter_count} chapters. Splitting...")
    
            for i, chapter in enumerate(chapters_data["chapters"]):
                start_time = float(chapter["start_time"])
                end_time = float(chapter["end_time"])
                chapter_title = chapter.get("tags", {}).get("title", f"Chapter {i+1}")
                chapter_title = re.sub(r'[\\/*?:"<>|]', "", chapter_title)
                chapter_output = os.path.join(output_dir, f"{filename_no_ext}_{chapter_title}.mp3")
                duration = end_time - start_time
    
                if verbose:
                    print(f"Extracting chapter: {chapter_title}, Start: {start_time}, Duration: {duration}")
                
                ffmpeg_chapter_command = ["ffmpeg", "-ss", str(start_time), "-t", str(duration), "-i", output_mp3, "-c", "copy", chapter_output]
                subprocess.run(ffmpeg_chapter_command, capture_output=not verbose)
    
        elif verbose:
            print(f"No chapters found in {output_mp3}.")
    
    
    def convert_aax_to_mp3(input_directory=".", verbose=True, max_workers=None):
        """Converts .aax files to MP3 with multicore support."""
    
        try:
            subprocess.run(["ffmpeg", "-version"], check=True, capture_output=True)
            subprocess.run(["ffprobe", "-version"], check=True, capture_output=True)
            subprocess.run(["jq", "--version"], check=True, capture_output=True)
        except FileNotFoundError:
            print("Error: ffmpeg, ffprobe, and/or jq are not installed. Please install them.")
            return
    
        aax_files = [os.path.join(input_directory, filename) for filename in os.listdir(input_directory) if filename.endswith(".aax")]
    
        with concurrent.futures.ProcessPoolExecutor(max_workers=max_workers) as executor:
            futures = [executor.submit(process_file, file_path, input_directory, verbose) for file_path in aax_files]
            concurrent.futures.wait(futures)  # Wait for all processes to finish
    
        print("Script finished.")
    
    
    if __name__ == "__main__":
        import argparse
    
        parser = argparse.ArgumentParser(description="Convert AAX files to MP3 with multicore support.")
        parser.add_argument("-i", "--input", dest="input_directory", default=".", help="Input directory containing .aax files (default: current directory).")
        parser.add_argument("-v", "--verbose", action="store_true", help="Enable verbose output.")
        parser.add_argument("-w", "--workers", dest="max_workers", type=int, default=None, help="Maximum number of worker processes (default: number of CPUs).")
    
        args = parser.parse_args()
    
        convert_aax_to_mp3(input_directory=args.input_directory, verbose=args.verbose, max_workers=args.max_workers)
  • OpenWRT Update

    opkg update
    opkg list-upgradable | cut -f 1 -d ‚ ‚ | xargs -r opkg upgrade