3
3
import os
4
4
from signal import signal , SIGTERM , SIGINT , SIGQUIT , strsignal
5
5
from concurrent .futures import ThreadPoolExecutor
6
- from properties import YAMLPropertiesFile , EnvProperties , PropertyError
6
+ from properties import YAMLPropertiesFile , EnvProperties , PropertyError , DEFAULT_PROPERTIES
7
7
from grpc_reflection .v1alpha import reflection
8
- from grpc ._channel import _InactiveRpcError
9
8
from google .protobuf import empty_pb2
10
9
from model_loader import download_models , ModelLoadError
11
10
import grpc
12
11
import subtitles_pb2
13
12
import subtitles_pb2_grpc
13
+ from taskqueue import TaskQueue
14
14
from vosk_transcriber import VoskTranscriber
15
15
from whisper_transcriber import WhisperTranscriber
16
16
from transcriber import Transcriber
17
+ from tasks import GenerationTask
18
+ from worker import Worker
17
19
18
20
19
21
class SubtitleServerService (subtitles_pb2_grpc .SubtitleGeneratorServicer ):
20
22
"""grpc service for subtitles"""
21
23
22
- def __init__ (self , transcriber : Transcriber , receiver : str , executor : ThreadPoolExecutor ) -> None :
23
- """Initialize service.
24
-
25
- Args:
26
- transcriber: The transcriber used for subtitle generation.
27
- receiver: The address of the receiver service.
28
- executor: Threadpool for jobs.
29
- """
30
- self .__transcriber = transcriber
31
- self .__receiver = receiver
32
- self .__executor = executor
24
+ def __init__ (self , queue : TaskQueue ) -> None :
25
+ """Initialize service"""
26
+ self .__queue = queue
33
27
34
28
def Generate (self , req : subtitles_pb2 .GenerateRequest ,
35
29
context : grpc .ServicerContext ) -> empty_pb2 .Empty :
36
- """ Handler function for an incoming Generate request.
30
+ """Handler function for an incoming Generate request.
37
31
38
32
Args:
39
33
req: An object holding the grpc message data.
@@ -50,71 +44,50 @@ def Generate(self, req: subtitles_pb2.GenerateRequest,
50
44
logging .debug (f'checking if { source } exists' )
51
45
if not os .path .isfile (source ):
52
46
context .abort (grpc .StatusCode .NOT_FOUND , f'can not find source file: { source } ' )
53
- return empty_pb2 .Empty ()
54
-
55
- logging .debug ('starting thread to generate subtitles' )
56
- self .__executor .submit (self .__generate , self .__transcriber , source , stream_id , language )
57
- return empty_pb2 .Empty ()
47
+ return
58
48
59
- def __generate ( self , transcriber : Transcriber , source : str , stream_id : str , language : str ) -> None :
60
- subtitles , language = transcriber . generate ( source , language )
49
+ logging . debug ( 'enqueue request' )
50
+ self . __queue . put ( GenerationTask ( source , language , stream_id ) )
61
51
62
- logging .info (f'trying to connect to receiver @ { self .__receiver } ' )
63
- with grpc .insecure_channel (self .__receiver ) as channel :
64
- stub = subtitles_pb2_grpc .SubtitleReceiverStub (channel )
65
- request = subtitles_pb2 .ReceiveRequest (
66
- stream_id = stream_id ,
67
- subtitles = subtitles ,
68
- language = language )
69
-
70
- try :
71
- stub .Receive (request )
72
- logging .info ('subtitle-request sent' )
73
- except _InactiveRpcError as grpc_err :
74
- logging .error (grpc_err .details ())
75
- except Exception as err :
76
- logging .error (err )
52
+ return empty_pb2 .Empty ()
77
53
78
54
79
- def serve (transcriber : Transcriber ,
80
- receiver : str ,
55
+ def serve (executor : ThreadPoolExecutor ,
56
+ q : TaskQueue ,
81
57
port : int ,
82
- max_workers : int ,
83
58
debug : bool = False ) -> None :
84
59
"""Starts the grpc server.
85
60
86
61
Args:
87
- transcriber : The transcriber used.
88
- receiver: The network address of the receiver.
62
+ executor : The pool of threads
63
+ q: Queue of tasks
89
64
port: The port on which the voice service listens.
90
- max_workers: The maximum number of threads that can be used to execute the given calls.
91
65
debug: Whether the server should be started in debug mode or not.
92
66
"""
67
+ server = grpc .server (executor )
68
+ subtitles_pb2_grpc .add_SubtitleGeneratorServicer_to_server (
69
+ servicer = SubtitleServerService (q ),
70
+ server = server )
93
71
94
- with ThreadPoolExecutor (max_workers ) as executor :
95
- server = grpc .server (executor )
96
- subtitles_pb2_grpc .add_SubtitleGeneratorServicer_to_server (
97
- servicer = SubtitleServerService (transcriber , receiver , executor ),
98
- server = server )
72
+ if debug :
73
+ activate_reflection (server )
99
74
100
- if debug :
101
- activate_reflection (server )
75
+ logging .info (f'listening at :{ port } ' )
76
+ server .add_insecure_port (f'[::]:{ port } ' )
77
+ server .start ()
102
78
103
- logging .info (f'listening at :{ port } ' )
104
- server .add_insecure_port (f'[::]:{ port } ' )
105
- server .start ()
79
+ def handle_shutdown (signum , * _ ):
80
+ logging .info (f'received "{ strsignal (signum )} " signal' )
81
+ all_requests_done = server .stop (16 )
82
+ all_requests_done .wait (16 )
83
+ q .stop ()
84
+ executor .shutdown (wait = True )
85
+ logging .info ('shut down gracefully' )
106
86
107
- def handle_shutdown (signum , * _ ):
108
- logging .info (f'received "{ strsignal (signum )} " signal' )
109
- all_requests_done = server .stop (30 )
110
- all_requests_done .wait (30 )
111
- executor .shutdown (wait = True )
112
- logging .info ('shut down gracefully' )
113
-
114
- signal (SIGTERM , handle_shutdown )
115
- signal (SIGINT , handle_shutdown )
116
- signal (SIGQUIT , handle_shutdown )
117
- server .wait_for_termination ()
87
+ signal (SIGTERM , handle_shutdown )
88
+ signal (SIGINT , handle_shutdown )
89
+ signal (SIGQUIT , handle_shutdown )
90
+ server .wait_for_termination ()
118
91
119
92
120
93
def get_transcriber (properties : dict , debug : bool ) -> Transcriber :
@@ -141,18 +114,7 @@ def main():
141
114
debug = os .getenv ('DEBUG' , '' ) != ""
142
115
logging .basicConfig (level = (logging .INFO , logging .DEBUG )[debug ])
143
116
144
- properties = {
145
- 'api' : {'port' : 50055 },
146
- 'receiver' : {'host' : 'localhost' , 'port' : '50053' },
147
- 'transcriber' : 'whisper' ,
148
- 'vosk' : {
149
- 'model_dir' : '/tmp' ,
150
- 'download_urls' : [],
151
- 'models' : []
152
- },
153
- 'whisper' : {'model' : 'tiny' },
154
- 'max_workers' : None ,
155
- }
117
+ properties = DEFAULT_PROPERTIES
156
118
157
119
try :
158
120
config_file = os .getenv ("CONFIG_FILE" )
@@ -177,10 +139,15 @@ def main():
177
139
transcriber = get_transcriber (properties , debug )
178
140
receiver = f'{ properties ["receiver" ]["host" ]} :{ properties ["receiver" ]["port" ]} '
179
141
port = properties ['api' ]['port' ]
180
- max_workers = properties ['max_workers' ]
142
+ max_threads = properties ['max_threads' ]
143
+ cnt_workers = properties ['cnt_workers' ]
181
144
182
145
logging .debug (properties )
183
- serve (transcriber , receiver , port , max_workers , debug )
146
+
147
+ q = TaskQueue (cnt_workers )
148
+ with ThreadPoolExecutor (max_threads ) as executor :
149
+ [Worker (transcriber , receiver , executor , q ) for _ in range (cnt_workers )]
150
+ serve (executor , q , port , debug )
184
151
185
152
186
153
if __name__ == "__main__" :
0 commit comments