File size: 5,148 Bytes
f021ebf
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
#!/usr/bin/env python3
"""
PocketTTS OpenAI-Compatible Server

A drop-in replacement for OpenAI's TTS API using the pocket-tts model.
Supports streaming, custom voices, and runs on CPU.

Usage:
    python server.py [OPTIONS]

    # Or with environment variables:
    POCKET_TTS_PORT=8080 python server.py
"""

import argparse
import os
import sys

from app import create_app, init_tts_service
from app.config import Config
from app.logging_config import get_logger


def parse_args():
    """Parse command line arguments."""
    parser = argparse.ArgumentParser(
        description='PocketTTS OpenAI-Compatible Server',
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
Examples:
    # Start with defaults
    python server.py

    # Custom port and voices directory
    python server.py --port 8080 --voices-dir ./my_voices

    # Load French model with quantization
    python server.py --language french_24l --quantize

Environment Variables:
    POCKET_TTS_HOST         Server host (default: 0.0.0.0)
    POCKET_TTS_PORT         Server port (default: 49112)
    POCKET_TTS_MODEL_PATH   Path to model config file
    POCKET_TTS_LANGUAGE     Model language (e.g., english, french_24l)
    POCKET_TTS_QUANTIZE     Enable int8 quantization (default: false)
    POCKET_TTS_VOICES_DIR   Path to voices directory
    POCKET_TTS_STREAM_DEFAULT Enable streaming by default
    POCKET_TTS_TEXT_PREPROCESS_DEFAULT Enable text preprocessing by default
    POCKET_TTS_LOG_DIR      Log directory path
        """,
    )

    parser.add_argument(
        '--host', type=str, default=Config.HOST, help=f'Host to bind to (default: {Config.HOST})'
    )
    parser.add_argument(
        '--port', type=int, default=Config.PORT, help=f'Port to listen on (default: {Config.PORT})'
    )
    parser.add_argument(
        '--model-path',
        type=str,
        default=Config.MODEL_PATH,
        dest='model_path',
        help='Path to model config file (.yaml) or variant name',
    )
    parser.add_argument(
        '--voices-dir',
        type=str,
        default=Config.VOICES_DIR,
        dest='voices_dir',
        help='Directory containing voice files',
    )
    parser.add_argument(
        '--stream',
        action='store_true',
        default=Config.STREAM_DEFAULT,
        help='Enable streaming by default for all requests',
    )
    parser.add_argument(
        '--text-preprocess',
        action='store_true',
        default=Config.TEXT_PREPROCESS_DEFAULT,
        help='Enable text preprocessing for all requests',
    )
    parser.add_argument(
        '--language',
        type=str,
        default=Config.LANGUAGE,
        dest='language',
        help='Model language (e.g., english, french_24l, german_24l, portuguese, italian, spanish_24l). Incompatible with --model-path.',
    )
    parser.add_argument(
        '--quantize',
        action='store_true',
        default=Config.QUANTIZE,
        help='Apply dynamic int8 quantization to reduce memory usage and improve speed.',
    )
    parser.add_argument(
        '--log-level',
        type=str,
        default=Config.LOG_LEVEL,
        choices=['DEBUG', 'INFO', 'WARNING', 'ERROR'],
        dest='log_level',
        help='Logging level',
    )

    return parser.parse_args()


def main():
    """Main entry point."""
    args = parse_args()

    # Update config from args (environment takes precedence via Config class)
    os.environ.setdefault('POCKET_TTS_LOG_LEVEL', args.log_level)

    # Create app
    app = create_app(
        {'STREAM_DEFAULT': args.stream, 'TEXT_PREPROCESS_DEFAULT': args.text_preprocess}
    )

    logger = get_logger()

    # Validate mutually exclusive options
    if args.language and args.model_path:
        logger.error('--language and --model-path are mutually exclusive. Use one or the other.')
        sys.exit(1)

    # Validate --language against supported list (prevents cryptic pocket-tts errors).
    if args.language and args.language not in Config.SUPPORTED_LANGUAGES:
        logger.error(
            f"Unknown language '{args.language}'. "
            f'Supported: {", ".join(Config.SUPPORTED_LANGUAGES)}'
        )
        sys.exit(1)

    # Initialize TTS service
    try:
        init_tts_service(
            model_path=args.model_path,
            voices_dir=args.voices_dir,
            language=args.language,
            quantize=args.quantize,
        )
    except Exception as e:
        logger.error(f'Failed to initialize TTS service: {e}')
        sys.exit(1)

    # Start server with Waitress (production WSGI server)
    try:
        from waitress import serve

        logger.info(f'Starting PocketTTS server on http://{args.host}:{args.port}')
        logger.info('Press Ctrl+C to stop')

        serve(app, host=args.host, port=args.port, threads=4, url_scheme='http')

    except ImportError:
        logger.warning('Waitress not installed, falling back to Flask dev server')
        logger.warning('Install waitress for production: pip install waitress')
        app.run(host=args.host, port=args.port, debug=False, threaded=True)


if __name__ == '__main__':
    main()