|
@@ -38,7 +38,7 @@ else:
|
|
|
log.basicConfig(format='[%(asctime)s] [%(module)s] %(message)s', level=log.INFO)
|
|
|
|
|
|
AUDIOS_PATH = '/tmp'
|
|
|
-AHEAD_TIME_AUDIO_TOLERANCE = 2 # second
|
|
|
+AHEAD_TIME_AUDIO_TOLERANCE = 2 # second
|
|
|
MAX_SEGMENT_THREADS = 4
|
|
|
THRESHOLD = 10
|
|
|
SEGMENTS_TOLERANCE_RATE = 0.6
|
|
@@ -48,11 +48,11 @@ FALL_TOLERANCE_SEGMENTS = 1
|
|
|
THRESHOLD_FIXED = 1
|
|
|
THRESHOLD_AVERAGE = 2
|
|
|
|
|
|
-# Modos de procesamiento de queue
|
|
|
-# - QUEQUE_SINGLE: procesa solo un segmento a la vez
|
|
|
+# Modos de procesamiento de queue
|
|
|
+# - QUEQUE_SINGLE: procesa solo un segmento a la vez
|
|
|
# - QUEUE_THREAD: inicia un hilo para cada segmento
|
|
|
# Por default se usará el threaded.
|
|
|
-# TOOD: hacerlo configurable por medio de argumentos
|
|
|
+# TODO: hacerlo configurable por medio de argumentos
|
|
|
# de ejecución.
|
|
|
QUEUE_SINGLE = 1
|
|
|
QUEUE_THREAD = 2
|
|
@@ -65,8 +65,8 @@ config = parse_config()
|
|
|
queue = Queue()
|
|
|
client = Client(config['device_id'],
|
|
|
config['apiSecret'])
|
|
|
-cloud_base_url = 'https://storage.googleapis.com/{}'\
|
|
|
- .format(config['bucket'])
|
|
|
+cloud_base_url = 'https://storage.googleapis.com/{}' \
|
|
|
+ .format(config['bucket'])
|
|
|
base_path = config.get("basepath", "/var/fourier")
|
|
|
fb_credentials = credentials.Certificate('/etc/Fourier-key.json')
|
|
|
firebase_admin.initialize_app(fb_credentials, config['firebase'])
|
|
@@ -84,6 +84,7 @@ db_path = config.get('localDatabase', os.path.join(device_path, 'files.db'))
|
|
|
db = sqlite3.connect(db_path)
|
|
|
cloud_cache = {}
|
|
|
|
|
|
+
|
|
|
def feed_queue():
|
|
|
""" Search for pending scheduled work in
|
|
|
server and add them to a memory queue. """
|
|
@@ -95,9 +96,9 @@ def feed_queue():
|
|
|
|
|
|
if downloaded_counter:
|
|
|
log.info(('[feed_queue] {} new '
|
|
|
- + 'pending schedule items.')\
|
|
|
- .format(downloaded_counter)
|
|
|
- )
|
|
|
+ + 'pending schedule items.') \
|
|
|
+ .format(downloaded_counter)
|
|
|
+ )
|
|
|
|
|
|
if queue.qsize() > 0:
|
|
|
if queue_mode == QUEUE_THREAD:
|
|
@@ -117,6 +118,7 @@ def feed_queue():
|
|
|
loop.add_timeout(time.time() + 60, feed_queue)
|
|
|
raise ex
|
|
|
|
|
|
+
|
|
|
def process_queue():
|
|
|
""" Try to the next item in a queue and start
|
|
|
processing it accordingly. If success, repeat
|
|
@@ -131,6 +133,7 @@ def process_queue():
|
|
|
log.error(ex)
|
|
|
loop.add_callback(process_queue)
|
|
|
|
|
|
+
|
|
|
def process_queue_with_threads():
|
|
|
threads = [None] * MAX_SEGMENT_THREADS
|
|
|
is_drained = False
|
|
@@ -152,12 +155,12 @@ def process_queue_with_threads():
|
|
|
)]
|
|
|
|
|
|
thread = MultiAPI(target=process_segment,
|
|
|
- args=(item,),
|
|
|
- kwargs={
|
|
|
- 'audios': audios,
|
|
|
- 'calibration': calibration,
|
|
|
- }
|
|
|
- )
|
|
|
+ args=(item,),
|
|
|
+ kwargs={
|
|
|
+ 'audios': audios,
|
|
|
+ 'calibration': calibration,
|
|
|
+ }
|
|
|
+ )
|
|
|
threads[index] = thread
|
|
|
thread.start()
|
|
|
|
|
@@ -181,6 +184,7 @@ def process_queue_with_threads():
|
|
|
log.info('Finished thread processing')
|
|
|
loop.add_callback(feed_queue)
|
|
|
|
|
|
+
|
|
|
def process_segment(item, audios=None, calibration=None):
|
|
|
""" Procesa una hora de audio """
|
|
|
|
|
@@ -194,15 +198,15 @@ def process_segment(item, audios=None, calibration=None):
|
|
|
segment_size = calibration['segmentSize']
|
|
|
audio_length = 0
|
|
|
|
|
|
- log.info('[process_segment] (th: {}, tl: {}, ft: {}, ss: {}, ho: {}) {}'\
|
|
|
+ log.info('[process_segment] (th: {}, tl: {}, ft: {}, ss: {}, ho: {}) {}' \
|
|
|
.format(
|
|
|
- calibration['threshold'],
|
|
|
- calibration['tolerance'],
|
|
|
- calibration['fallTolerance'],
|
|
|
- calibration['segmentSize'],
|
|
|
- calibration['hourlyOffset'],
|
|
|
- item,
|
|
|
- )
|
|
|
+ calibration['threshold'],
|
|
|
+ calibration['tolerance'],
|
|
|
+ calibration['fallTolerance'],
|
|
|
+ calibration['segmentSize'],
|
|
|
+ calibration['hourlyOffset'],
|
|
|
+ item,
|
|
|
+ )
|
|
|
)
|
|
|
|
|
|
# 1. obtener el audio desde firebase
|
|
@@ -235,7 +239,7 @@ def process_segment(item, audios=None, calibration=None):
|
|
|
log.error(str(ex))
|
|
|
return
|
|
|
|
|
|
- dejavu = Dejavu({"database_type":"mem"})
|
|
|
+ dejavu = Dejavu({"database_type": "mem"})
|
|
|
try:
|
|
|
dejavu.fingerprint_file(filename)
|
|
|
except Exception as ex:
|
|
@@ -264,9 +268,7 @@ def process_segment(item, audios=None, calibration=None):
|
|
|
values = []
|
|
|
|
|
|
if not os.path.isfile(path):
|
|
|
- log.error('[process_segment] file not found: {}'\
|
|
|
- .format(short_path))
|
|
|
- continue
|
|
|
+ download_file(path)
|
|
|
|
|
|
try:
|
|
|
for match in dejavu.recognize(recognizer, path, segment_size,
|
|
@@ -289,18 +291,18 @@ def process_segment(item, audios=None, calibration=None):
|
|
|
|
|
|
except CouldntDecodeError as ex:
|
|
|
log.error('[process_segment] {}'.format(ex))
|
|
|
-
|
|
|
+
|
|
|
try:
|
|
|
response = client.put_schedule_results(
|
|
|
item['schedule'],
|
|
|
item['id'],
|
|
|
- None, # TODO: send results again
|
|
|
+ None, # TODO: send results again
|
|
|
found=find_repetitions(results,
|
|
|
- segments_needed=segments_needed,
|
|
|
- calibration=calibration,
|
|
|
- ),
|
|
|
+ segments_needed=segments_needed,
|
|
|
+ calibration=calibration,
|
|
|
+ ),
|
|
|
missing_files=(12 - audios_counter) \
|
|
|
- if audios_counter < 12 else 0
|
|
|
+ if audios_counter < 12 else 0
|
|
|
)
|
|
|
log.info('[{}] API response: {}'.format(station, response))
|
|
|
except ConnectionError as ex:
|
|
@@ -308,6 +310,7 @@ def process_segment(item, audios=None, calibration=None):
|
|
|
except UserWarning as warn:
|
|
|
log.warning(str(warn))
|
|
|
|
|
|
+
|
|
|
def find_repetitions(results, segments_needed=2, calibration=None):
|
|
|
found_counter = 0
|
|
|
found_down_counter = 0
|
|
@@ -373,6 +376,7 @@ def find_repetitions(results, segments_needed=2, calibration=None):
|
|
|
|
|
|
return found
|
|
|
|
|
|
+
|
|
|
def iterate_audios(dt, station, calibration=None):
|
|
|
""" Given a datetime object and an station,
|
|
|
iterate a list of files that are between
|
|
@@ -398,10 +402,10 @@ def iterate_audios(dt, station, calibration=None):
|
|
|
'select "filename", "timestamp" '
|
|
|
'from "file" '
|
|
|
'where "timestamp" between ? and ? '
|
|
|
- 'and "station" = ? '
|
|
|
+ 'and "station" = ? '
|
|
|
'order by "timestamp" asc'
|
|
|
- ),
|
|
|
- (from_time, to_time, station, ),
|
|
|
+ ),
|
|
|
+ (from_time, to_time, station,),
|
|
|
)
|
|
|
files = [file for file in cursor]
|
|
|
cursor.close()
|
|
@@ -429,7 +433,7 @@ def cloud_download(ad_key=None):
|
|
|
out_file = os.path.join(AUDIOS_PATH, filename)
|
|
|
url = '{}/{}'.format(cloud_base_url, ad['path'])
|
|
|
response = requests.get(url)
|
|
|
-
|
|
|
+
|
|
|
if response.status_code == 200:
|
|
|
hashes = response.headers['x-goog-hash']
|
|
|
hashes = hashes.split(',')
|
|
@@ -440,15 +444,31 @@ def cloud_download(ad_key=None):
|
|
|
with open(out_file, "wb") as fp:
|
|
|
fp.write(response.content)
|
|
|
tp = (out_file, md5sum,)
|
|
|
- p = Popen(['ffprobe', '-v', 'error', '-select_streams', 'a:0', '-show_entries', 'stream=codec_name', '-of', 'default=nokey=1:noprint_wrappers=1', out_file], stdin=PIPE, stdout=PIPE, stderr=PIPE)
|
|
|
+ p = Popen(['ffprobe', '-v', 'error', '-select_streams', 'a:0', '-show_entries', 'stream=codec_name', '-of',
|
|
|
+ 'default=nokey=1:noprint_wrappers=1', out_file], stdin=PIPE, stdout=PIPE, stderr=PIPE)
|
|
|
rc = p.returncode
|
|
|
if rc != 'mp3\n':
|
|
|
subprocess.call(['mv', out_file, out_file + '.old'])
|
|
|
- subprocess.call(['ffmpeg', '-hide_banner', '-loglevel', 'panic', '-i', out_file + '.old', '-f', 'mp3', out_file])
|
|
|
+ subprocess.call(
|
|
|
+ ['ffmpeg', '-hide_banner', '-loglevel', 'panic', '-i', out_file + '.old', '-f', 'mp3', out_file])
|
|
|
subprocess.call(['rm', '-rf', out_file + '.old'])
|
|
|
cloud_cache[ad_key] = tp
|
|
|
return tp
|
|
|
|
|
|
+
|
|
|
+def download_file(file_path=None):
|
|
|
+ file_path_cloud = file_path.replace("/var/fourier/", "")
|
|
|
+ url = '{}/{}'.format(cloud_base_url, file_path_cloud)
|
|
|
+ response = requests.get(url)
|
|
|
+
|
|
|
+ if response.status_code == 200:
|
|
|
+ with open(file_path, "wb") as fp:
|
|
|
+ fp.write(response.content)
|
|
|
+ cursor = db.cursor()
|
|
|
+ cursor.execute('update "file" set uploaded = 0 where filename = ?', (file_path,), )
|
|
|
+ cursor.close()
|
|
|
+
|
|
|
+
|
|
|
app = setup_endpoint(queue=queue)
|
|
|
loop = IOLoop.current()
|
|
|
loop.add_callback(feed_queue)
|
|
@@ -458,4 +478,4 @@ if __name__ == '__main__':
|
|
|
log.info('Starting ondemand service')
|
|
|
loop.start()
|
|
|
except KeyboardInterrupt:
|
|
|
- log.error('Process killed')
|
|
|
+ log.error('Process killed')
|