Skip to content

Commit 9600f1e

Browse files
authored
Merge pull request #11 from zSeriesGuy/beta
v1.2.3
2 parents 233fae3 + 010e80b commit 9600f1e

File tree

9 files changed

+80
-82
lines changed

9 files changed

+80
-82
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Changelog
22

3+
## v1.2.3 (2019-10-21)
4+
* Add support to ensure only one instance of SpeakReader is running.
5+
* Add exception handler for Google OutOfRange exception. Streaming limit is 5 minutes.
6+
* Run file cleanup if days changes in management console settings.
7+
* Upgrade packages.
8+
39
## v1.2.2 (2019-10-19)
410
* Add settings and process for deleting logs, transcripts, and recordings after a specified number of days.
511

requirements.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,4 @@ passlib
2020
wheel
2121
samplerate
2222
apscheduler
23+
psutil

requirements.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ apscheduler==3.6.1
88
azure-cognitiveservices-speech==1.7.0 ; sys_platform in "win32 macos" or (sys_platform == "linux" and platform_machine in "AMD64")
99
cachetools==3.1.1 # via google-auth
1010
certifi==2019.9.11 # via requests
11-
cffi==1.13.0 # via samplerate
11+
cffi==1.13.1 # via samplerate
1212
chardet==3.0.4 # via requests
1313
cheroot==8.2.1 # via cherrypy
1414
cherrypy==18.3.0
@@ -34,6 +34,7 @@ passlib==1.7.1
3434
pip-tools==4.2.0
3535
portend==2.5
3636
protobuf==3.10.0 # via google-api-core, googleapis-common-protos
37+
psutil==5.6.3
3738
pyasn1-modules==0.2.7 # via google-auth
3839
pyasn1==0.4.7 # via pyasn1-modules, rsa
3940
pyaudio==0.2.11

speakreader/googleTranscribe.py

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
try:
77
from google.cloud import speech
8+
from google.api_core import exceptions
89
is_supported = True
910
except ImportError:
1011
is_supported = False
@@ -51,26 +52,29 @@ def transcribe(self):
5152

5253
responses = self.client.streaming_recognize(self.streaming_config, requests)
5354

54-
for response in responses:
55-
if not response.results:
56-
continue
55+
try:
56+
for response in responses:
57+
if not response.results:
58+
continue
5759

58-
result = response.results[0]
60+
result = response.results[0]
5961

60-
if not result.is_final and not speakreader.CONFIG.SHOW_INTERIM_RESULTS:
61-
continue
62+
if not result.is_final and not speakreader.CONFIG.SHOW_INTERIM_RESULTS:
63+
continue
6264

63-
if not result.alternatives:
64-
continue
65+
if not result.alternatives:
66+
continue
6567

66-
if not result.is_final and result.stability < 0.75:
67-
continue
68+
if not result.is_final and result.stability < 0.75:
69+
continue
6870

69-
transcript = {
70-
'transcript': result.alternatives[0].transcript,
71-
'is_final': result.is_final,
72-
}
71+
transcript = {
72+
'transcript': result.alternatives[0].transcript,
73+
'is_final': result.is_final,
74+
}
7375

74-
yield transcript
75-
76-
logger.debug("googleTranscribe.transcribe Exiting")
76+
yield transcript
77+
logger.debug("googleTranscribe.transcribe Exiting")
78+
except exceptions.OutOfRange:
79+
logger.debug("googleTranscribe.transcribe OutOfRange Exception: Time Limit Exceeded. Restarting.")
80+
pass

speakreader/microphoneStream.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ def __enter__(self):
122122
stream_callback=self._fill_buff,
123123
)
124124
except OSError:
125-
print("microphone __enter__.OSError")
125+
logger.error("microphone __enter__.OSError")
126126
self.closed = True
127127
raise Exception("Microphone Not Functioning")
128128

@@ -132,6 +132,8 @@ def __enter__(self):
132132
return self
133133

134134
def __exit__(self, type, value, traceback):
135+
if self.closed:
136+
return
135137
logger.debug('MicrophoneStream.exit ENTER')
136138
self._audio_stream.stop_stream()
137139
self._audio_stream.close()

speakreader/transcribeEngine.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -95,11 +95,11 @@ def start(self):
9595

9696
def stop(self):
9797
if self._ONLINE:
98+
self._ONLINE = False
9899
self.microphoneStream.stop()
99100
self.transcriptQueue.put_nowait(self.OFFLINE_MESSAGE)
100101
self._transcribeThread.join()
101102
self.queueManager.transcriptHandler.setFileName(None)
102-
self._ONLINE = False
103103

104104
def shutdown(self):
105105
self.stop()
@@ -141,12 +141,14 @@ def run(self):
141141
self.transcriptQueue.put_nowait(self.ONLINE_MESSAGE)
142142
self._ONLINE = True
143143

144-
with self.microphoneStream as stream:
145-
while not stream.closed:
146-
responses = transcribeService.transcribe()
147-
self.process_responses(responses)
148-
149-
logger.info("Transcription Engine Stream Closed")
144+
try:
145+
with self.microphoneStream as stream:
146+
while self._ONLINE:
147+
responses = transcribeService.transcribe()
148+
self.process_responses(responses)
149+
logger.info("Transcription Engine Stream Closed")
150+
except Exception as e:
151+
logger.error(e)
150152

151153
self.transcriptFile.close()
152154
self.transcriptQueue.put_nowait(self.OFFLINE_MESSAGE)

speakreader/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
PRODUCT = 'SpeakReader'
2-
VERSION_RELEASE = 'v1.2.2'
2+
VERSION_RELEASE = 'v1.2.3'
33
GITHUB_BRANCH = 'master'

speakreader/webserve.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ def configUpdate(self, **kwargs):
150150
restartTranscribeEngine = False
151151
restartSpeakReader = False
152152
logout = False
153+
cleanup = False
153154

154155
checked_configs = [
155156
"start_transcribe_on_startup",
@@ -220,9 +221,17 @@ def configUpdate(self, **kwargs):
220221
if kwargs['http_username'] != speakreader.CONFIG.HTTP_USERNAME or set_http_password:
221222
logout = True
222223

224+
if kwargs.get('log_retention_days') != speakreader.CONFIG.LOG_RETENTION_DAYS \
225+
or kwargs.get('transcript_retention_days') != speakreader.CONFIG.TRANSCRIPT_RETENTION_DAYS \
226+
or kwargs.get('recording_retention_days') != speakreader.CONFIG.RECORDING_RETENTION_DAYS:
227+
cleanup = True
228+
223229
speakreader.CONFIG.process_kwargs(kwargs)
224230
speakreader.CONFIG.write()
225231

232+
if cleanup:
233+
self.SR.cleanup_files()
234+
226235
if restartSpeakReader:
227236
#self.restart()
228237
return {'portchanged': True}

start.py

Lines changed: 28 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import time
3636
import tzlocal
3737
import threading
38+
import psutil
3839

3940
import speakreader
4041
from speakreader import logger, config, SpeakReader
@@ -51,8 +52,6 @@ def main():
5152

5253
DAEMON = False
5354
NOFORK = False
54-
CREATEPID = False
55-
PIDFILE = None
5655

5756
QUIET = False
5857
VERBOSE = False
@@ -67,6 +66,7 @@ def main():
6766
PLATFORM_PROCESSOR = processor()
6867
PLATFORM_MACHINE = machine()
6968
PLATFORM_IS_64BITS = sys.maxsize > 2**32
69+
SYS_PLATFORM = sys.platform
7070

7171
# Fixed paths to application
7272
if hasattr(sys, 'frozen'):
@@ -76,19 +76,26 @@ def main():
7676

7777
PROG_DIR = os.path.dirname(FULL_PATH)
7878

79-
SYS_PLATFORM = sys.platform
80-
SYS_ENCODING = None
79+
# Ensure only one instance of SpeakReader running.
80+
PIDFILE = os.path.join(PROG_DIR, 'pidfile')
81+
myPid = os.getpid()
82+
if os.path.exists(PIDFILE) and os.path.isfile(PIDFILE):
83+
for p in psutil.process_iter():
84+
if 'python' in p.name() and p.pid != myPid:
85+
for f in p.open_files():
86+
if f.path == PIDFILE:
87+
logger.error("SpeakReader is already Running. Exiting.")
88+
sys.exit(0)
89+
90+
myPidFile = open(PIDFILE, 'w+')
91+
myPidFile.write(str(myPid))
92+
myPidFile.flush()
8193

8294
try:
8395
locale.setlocale(locale.LC_ALL, "")
84-
SYS_LANGUAGE, SYS_ENCODING = locale.getdefaultlocale()
8596
except (locale.Error, IOError):
8697
pass
8798

88-
# for OSes that are poorly configured I'll just force UTF-8
89-
if not SYS_ENCODING or SYS_ENCODING in ('ANSI_X3.4-1968', 'US-ASCII', 'ASCII'):
90-
SYS_ENCODING = 'UTF-8'
91-
9299
try:
93100
SYS_TIMEZONE = str(tzlocal.get_localzone())
94101
SYS_UTC_OFFSET = datetime.datetime.now(pytz.timezone(SYS_TIMEZONE)).strftime('%z')
@@ -116,8 +123,6 @@ def main():
116123
'--config', help='Specify a config file to use')
117124
parser.add_argument(
118125
'--nolaunch', action='store_true', help='Prevent browser from launching on startup')
119-
parser.add_argument(
120-
'--pidfile', help='Create a pid file (only relevant when running as a daemon)')
121126
parser.add_argument(
122127
'--nofork', action='store_true', help='Start SpeakReader as a service, do not fork when restarting')
123128

@@ -148,35 +153,6 @@ def main():
148153
NOFORK = True
149154
logger.info("SpeakReader is running as a service, it will not fork when restarted.")
150155

151-
if args.pidfile:
152-
PIDFILE = str(args.pidfile)
153-
154-
# If the pidfile already exists, SpeakReader may still be running, so EXIT
155-
if os.path.exists(PIDFILE):
156-
try:
157-
with open(PIDFILE, 'r') as fp:
158-
pid = int(fp.read())
159-
os.kill(pid, 0)
160-
except IOError as e:
161-
raise SystemExit("Unable to read PID file: %s", e)
162-
except OSError:
163-
logger.warn("PID file '%s' already exists, but PID %d is not running. Ignoring PID file." %
164-
(PIDFILE, pid))
165-
else:
166-
# The pidfile exists and points to a live PID. SpeakReader may still be running, so exit.
167-
raise SystemExit("PID file '%s' already exists. Exiting." % PIDFILE)
168-
169-
# The pidfile is only useful in daemon mode, make sure we can write the file properly
170-
if DAEMON:
171-
CREATEPID = True
172-
try:
173-
with open(PIDFILE, 'w') as fp:
174-
fp.write("pid\n")
175-
except IOError as e:
176-
raise SystemExit("Unable to write PID file: %s", e)
177-
else:
178-
logger.warn("Not running in daemon mode. PID file creation disabled.")
179-
180156
# Determine which data directory and config file to use
181157
if args.datadir:
182158
DATA_DIR = args.datadir
@@ -246,7 +222,7 @@ def main():
246222
CONFIG.RECORDINGS_FOLDER, os.path.join(DATA_DIR, 'recordings'), 'recordings')
247223

248224
if DAEMON:
249-
daemonize(CREATEPID, PIDFILE)
225+
daemonize(myPidFile)
250226

251227
# Store the original umask
252228
UMASK = os.umask(0)
@@ -295,9 +271,8 @@ def main():
295271

296272
SR.shutdown(restart=restart, update=update, checkout=checkout)
297273

298-
if CREATEPID:
299-
logger.info("Removing pidfile %s", PIDFILE)
300-
os.remove(PIDFILE)
274+
myPidFile.close()
275+
os.remove(PIDFILE)
301276

302277
if restart:
303278
logger.info("SpeakReader is restarting...")
@@ -332,7 +307,7 @@ def main():
332307
sys.exit(0)
333308

334309

335-
def daemonize(CREATEPID, PIDFILE):
310+
def daemonize(myPidFile):
336311

337312
if threading.activeCount() != 1:
338313
logger.warn(
@@ -377,12 +352,10 @@ def daemonize(CREATEPID, PIDFILE):
377352
os.dup2(se.fileno(), sys.stderr.fileno())
378353

379354
pid = os.getpid()
380-
logger.info(u"Daemonized to PID: %d", pid)
381-
382-
if CREATEPID:
383-
logger.info(u"Writing PID %d to %s", pid, PIDFILE)
384-
with open(PIDFILE, 'w') as fp:
385-
fp.write("%s\n" % pid)
355+
myPidFile.seek(0)
356+
myPidFile.write(str(pid))
357+
myPidFile.flush()
358+
logger.info("Daemonized to PID: %d", pid)
386359

387360

388361
def check_folder_writable(folder, fallback, name):
@@ -393,17 +366,17 @@ def check_folder_writable(folder, fallback, name):
393366
try:
394367
os.makedirs(folder)
395368
except OSError as e:
396-
logger.error(u"Could not create %s dir '%s': %s" % (name, folder, e))
369+
logger.error("Could not create %s dir '%s': %s" % (name, folder, e))
397370
if folder != fallback:
398-
logger.warn(u"Falling back to %s dir '%s'" % (name, fallback))
371+
logger.warn("Falling back to %s dir '%s'" % (name, fallback))
399372
return check_folder_writable(None, fallback, name)
400373
else:
401374
return folder, None
402375

403376
if not os.access(folder, os.W_OK):
404-
logger.error(u"Cannot write to %s dir '%s'" % (name, folder))
377+
logger.error("Cannot write to %s dir '%s'" % (name, folder))
405378
if folder != fallback:
406-
logger.warn(u"Falling back to %s dir '%s'" % (name, fallback))
379+
logger.warn("Falling back to %s dir '%s'" % (name, fallback))
407380
return check_folder_writable(None, fallback, name)
408381
else:
409382
return folder, False

0 commit comments

Comments
 (0)