|
|
@ -130,19 +130,9 @@ class Cutter(object):
|
|
|
|
# Shuffle the list so that (most of the time) we don't try to claim the same one as other nodes
|
|
|
|
# Shuffle the list so that (most of the time) we don't try to claim the same one as other nodes
|
|
|
|
random.shuffle(candidates)
|
|
|
|
random.shuffle(candidates)
|
|
|
|
for candidate in candidates:
|
|
|
|
for candidate in candidates:
|
|
|
|
try:
|
|
|
|
|
|
|
|
segments = self.check_candidate(candidate)
|
|
|
|
def set_error(error):
|
|
|
|
except ContainsHoles:
|
|
|
|
"""Common code for the two paths below, for setting an error on the row for humans to see"""
|
|
|
|
# TODO metric
|
|
|
|
|
|
|
|
self.logger.info("Ignoring candidate {} due to holes".format(format_job(candidate)))
|
|
|
|
|
|
|
|
continue # bad candidate, let someone else take it or just try again later
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
|
|
# Unknown error. This is either a problem with us, or a problem with the candidate
|
|
|
|
|
|
|
|
# (or most likely a problem with us that is only triggered by this candidate).
|
|
|
|
|
|
|
|
# In this case we would rather stay running so other jobs can continue to work if possible.
|
|
|
|
|
|
|
|
# But to give at least some feedback, we set the error message on the job
|
|
|
|
|
|
|
|
# if it isn't already.
|
|
|
|
|
|
|
|
self.logger.exception("Failed to check candidate {}, setting error on row".format(format_job(candidate)))
|
|
|
|
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
# Since this error message is just for humans, we don't go to too large
|
|
|
|
# Since this error message is just for humans, we don't go to too large
|
|
|
|
# a length to prevent it being put on the row if the row has changed.
|
|
|
|
# a length to prevent it being put on the row if the row has changed.
|
|
|
@ -152,7 +142,7 @@ class Cutter(object):
|
|
|
|
UPDATE events
|
|
|
|
UPDATE events
|
|
|
|
SET error = %s
|
|
|
|
SET error = %s
|
|
|
|
WHERE id = %s AND state = 'EDITED' AND error IS NULL
|
|
|
|
WHERE id = %s AND state = 'EDITED' AND error IS NULL
|
|
|
|
""", id=candidate.id, error='{}: Error while checking candidate: {}'.format(self.name, e))
|
|
|
|
""", id=candidate.id, error=error)
|
|
|
|
except Exception:
|
|
|
|
except Exception:
|
|
|
|
self.logger.exception("Failed to set error for candidate {}, ignoring".format(format_job(candidate)))
|
|
|
|
self.logger.exception("Failed to set error for candidate {}, ignoring".format(format_job(candidate)))
|
|
|
|
self.refresh_conn()
|
|
|
|
self.refresh_conn()
|
|
|
@ -160,12 +150,37 @@ class Cutter(object):
|
|
|
|
if result.rowcount > 0:
|
|
|
|
if result.rowcount > 0:
|
|
|
|
assert result.rowcount == 1
|
|
|
|
assert result.rowcount == 1
|
|
|
|
self.logger.info("Set error for candidate {}".format(format_job(candidate)))
|
|
|
|
self.logger.info("Set error for candidate {}".format(format_job(candidate)))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
|
|
segments = self.check_candidate(candidate)
|
|
|
|
|
|
|
|
except ContainsHoles:
|
|
|
|
|
|
|
|
# TODO metric
|
|
|
|
|
|
|
|
self.logger.info("Ignoring candidate {} due to holes".format(format_job(candidate)))
|
|
|
|
|
|
|
|
set_error(
|
|
|
|
|
|
|
|
"Node {} does not have all the video needed to cut this row. "
|
|
|
|
|
|
|
|
"This may just be because it's too recent and the video hasn't been downloaded yet. "
|
|
|
|
|
|
|
|
"However, it might also mean that there is a 'hole' of missing video, perhaps "
|
|
|
|
|
|
|
|
"because the stream went down or due to downloader issues. If you know why this "
|
|
|
|
|
|
|
|
"is happening and want to cut the video anyway, re-edit with the 'Allow Holes' option set."
|
|
|
|
|
|
|
|
.format(self.name))
|
|
|
|
|
|
|
|
continue # bad candidate, let someone else take it or just try again later
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
|
|
# Unknown error. This is either a problem with us, or a problem with the candidate
|
|
|
|
|
|
|
|
# (or most likely a problem with us that is only triggered by this candidate).
|
|
|
|
|
|
|
|
# In this case we would rather stay running so other jobs can continue to work if possible.
|
|
|
|
|
|
|
|
# But to give at least some feedback, we set the error message on the job
|
|
|
|
|
|
|
|
# if it isn't already.
|
|
|
|
|
|
|
|
self.logger.exception("Failed to check candidate {}, setting error on row".format(format_job(candidate)))
|
|
|
|
|
|
|
|
set_error('{}: Error while checking candidate: {}'.format(self.name, e))
|
|
|
|
self.wait(self.ERROR_RETRY_INTERVAL)
|
|
|
|
self.wait(self.ERROR_RETRY_INTERVAL)
|
|
|
|
continue
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
|
|
if all(segment is None for segment in segments):
|
|
|
|
if all(segment is None for segment in segments):
|
|
|
|
self.logger.info("Ignoring candidate {} as we have no segments".format(format_job(candidate)))
|
|
|
|
self.logger.info("Ignoring candidate {} as we have no segments".format(format_job(candidate)))
|
|
|
|
continue
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
|
|
return CutJob(segments=segments, **candidate._asdict())
|
|
|
|
return CutJob(segments=segments, **candidate._asdict())
|
|
|
|
|
|
|
|
|
|
|
|
# No candidates
|
|
|
|
# No candidates
|
|
|
|
self.wait(self.NO_CANDIDATES_RETRY_INTERVAL)
|
|
|
|
self.wait(self.NO_CANDIDATES_RETRY_INTERVAL)
|
|
|
|
|
|
|
|
|
|
|
|