Implement exception holder
This commit is contained in:
@@ -42,6 +42,10 @@ class PytchatCore:
|
|||||||
topchat_only : bool
|
topchat_only : bool
|
||||||
If True, get only top chat.
|
If True, get only top chat.
|
||||||
|
|
||||||
|
hold_exception : bool [default:True]
|
||||||
|
If True, when exceptions occur, the exception is holded internally,
|
||||||
|
and can be raised by raise_for_status().
|
||||||
|
|
||||||
Attributes
|
Attributes
|
||||||
---------
|
---------
|
||||||
_is_alive : bool
|
_is_alive : bool
|
||||||
@@ -56,7 +60,8 @@ class PytchatCore:
|
|||||||
interruptable=True,
|
interruptable=True,
|
||||||
force_replay=False,
|
force_replay=False,
|
||||||
topchat_only=False,
|
topchat_only=False,
|
||||||
logger=config.logger(__name__)
|
hold_exception=True,
|
||||||
|
logger=config.logger(__name__),
|
||||||
):
|
):
|
||||||
self._video_id = extract_video_id(video_id)
|
self._video_id = extract_video_id(video_id)
|
||||||
self.seektime = seektime
|
self.seektime = seektime
|
||||||
@@ -66,12 +71,16 @@ class PytchatCore:
|
|||||||
self.processor = processor
|
self.processor = processor
|
||||||
self._is_alive = True
|
self._is_alive = True
|
||||||
self._is_replay = force_replay
|
self._is_replay = force_replay
|
||||||
self._parser = Parser(is_replay=self._is_replay)
|
self._hold_exception = hold_exception
|
||||||
|
self._exception_holder = None
|
||||||
|
self._parser = Parser(
|
||||||
|
is_replay=self._is_replay,
|
||||||
|
exception_holder=self._exception_holder
|
||||||
|
)
|
||||||
self._first_fetch = True
|
self._first_fetch = True
|
||||||
self._fetch_url = "live_chat/get_live_chat?continuation="
|
self._fetch_url = "live_chat/get_live_chat?continuation="
|
||||||
self._topchat_only = topchat_only
|
self._topchat_only = topchat_only
|
||||||
self._logger = logger
|
self._logger = logger
|
||||||
self.exception = None
|
|
||||||
if interruptable:
|
if interruptable:
|
||||||
signal.signal(signal.SIGINT, lambda a, b: self.terminate())
|
signal.signal(signal.SIGINT, lambda a, b: self.terminate())
|
||||||
self._setup()
|
self._setup()
|
||||||
@@ -108,13 +117,13 @@ class PytchatCore:
|
|||||||
return chat_component
|
return chat_component
|
||||||
except exceptions.ChatParseException as e:
|
except exceptions.ChatParseException as e:
|
||||||
self._logger.debug(f"[{self._video_id}]{str(e)}")
|
self._logger.debug(f"[{self._video_id}]{str(e)}")
|
||||||
raise
|
self._raise_exception(e)
|
||||||
except (TypeError, json.JSONDecodeError):
|
except (TypeError, json.JSONDecodeError) as e:
|
||||||
self._logger.error(f"{traceback.format_exc(limit=-1)}")
|
self._logger.error(f"{traceback.format_exc(limit=-1)}")
|
||||||
raise
|
self._raise_exception(e)
|
||||||
|
|
||||||
self._logger.debug(f"[{self._video_id}]finished fetching chat.")
|
self._logger.debug(f"[{self._video_id}]finished fetching chat.")
|
||||||
raise exceptions.ChatDataFinished
|
self._raise_exception(exceptions.ChatDataFinished)
|
||||||
|
|
||||||
def _get_contents(self, continuation, client, headers):
|
def _get_contents(self, continuation, client, headers):
|
||||||
'''Get 'continuationContents' from livechat json.
|
'''Get 'continuationContents' from livechat json.
|
||||||
@@ -167,7 +176,7 @@ class PytchatCore:
|
|||||||
else:
|
else:
|
||||||
self._logger.error(f"[{self._video_id}]"
|
self._logger.error(f"[{self._video_id}]"
|
||||||
f"Exceeded retry count. Last error: {str(err)}")
|
f"Exceeded retry count. Last error: {str(err)}")
|
||||||
raise exceptions.RetryExceedMaxCount()
|
self._raise_exception(exceptions.RetryExceedMaxCount())
|
||||||
return livechat_json
|
return livechat_json
|
||||||
|
|
||||||
def get(self):
|
def get(self):
|
||||||
@@ -188,5 +197,11 @@ class PytchatCore:
|
|||||||
self.processor.finalize()
|
self.processor.finalize()
|
||||||
|
|
||||||
def raise_for_status(self):
|
def raise_for_status(self):
|
||||||
if self.exception is not None:
|
if self._exception_holder is not None:
|
||||||
raise self.exception
|
raise self._exception_holder
|
||||||
|
|
||||||
|
def _raise_exception(self, exception: Exception = None):
|
||||||
|
self._is_alive = False
|
||||||
|
if self._hold_exception is False:
|
||||||
|
raise exception
|
||||||
|
self._exception_holder = exception
|
||||||
|
|||||||
@@ -8,15 +8,26 @@ from .. import exceptions
|
|||||||
|
|
||||||
|
|
||||||
class Parser:
|
class Parser:
|
||||||
|
'''
|
||||||
|
Parser of chat json.
|
||||||
|
|
||||||
|
Parameter
|
||||||
|
----------
|
||||||
|
is_replay : bool
|
||||||
|
|
||||||
__slots__ = ['is_replay']
|
exception_holder : Object [default:Npne]
|
||||||
|
The object holding exceptions.
|
||||||
|
This is passed from the parent livechat object.
|
||||||
|
'''
|
||||||
|
__slots__ = ['is_replay', 'exception_holder']
|
||||||
|
|
||||||
def __init__(self, is_replay):
|
def __init__(self, is_replay, exception_holder=None):
|
||||||
self.is_replay = is_replay
|
self.is_replay = is_replay
|
||||||
|
self.exception_holder = exception_holder
|
||||||
|
|
||||||
def get_contents(self, jsn):
|
def get_contents(self, jsn):
|
||||||
if jsn is None:
|
if jsn is None:
|
||||||
raise exceptions.IllegalFunctionCall('Called with none JSON object.')
|
self.raise_exception(exceptions.IllegalFunctionCall('Called with none JSON object.'))
|
||||||
if jsn['response']['responseContext'].get('errors'):
|
if jsn['response']['responseContext'].get('errors'):
|
||||||
raise exceptions.ResponseContextError(
|
raise exceptions.ResponseContextError(
|
||||||
'The video_id would be wrong, or video is deleted or private.')
|
'The video_id would be wrong, or video is deleted or private.')
|
||||||
@@ -42,11 +53,11 @@ class Parser:
|
|||||||
|
|
||||||
if contents is None:
|
if contents is None:
|
||||||
'''Broadcasting end or cannot fetch chat stream'''
|
'''Broadcasting end or cannot fetch chat stream'''
|
||||||
raise exceptions.NoContents('Chat data stream is empty.')
|
self.raise_exception(exceptions.NoContents('Chat data stream is empty.'))
|
||||||
|
|
||||||
cont = contents['liveChatContinuation']['continuations'][0]
|
cont = contents['liveChatContinuation']['continuations'][0]
|
||||||
if cont is None:
|
if cont is None:
|
||||||
raise exceptions.NoContinuation('No Continuation')
|
self.raise_exception(exceptions.NoContinuation('No Continuation'))
|
||||||
metadata = (cont.get('invalidationContinuationData')
|
metadata = (cont.get('invalidationContinuationData')
|
||||||
or cont.get('timedContinuationData')
|
or cont.get('timedContinuationData')
|
||||||
or cont.get('reloadContinuationData')
|
or cont.get('reloadContinuationData')
|
||||||
@@ -54,13 +65,13 @@ class Parser:
|
|||||||
)
|
)
|
||||||
if metadata is None:
|
if metadata is None:
|
||||||
if cont.get("playerSeekContinuationData"):
|
if cont.get("playerSeekContinuationData"):
|
||||||
raise exceptions.ChatDataFinished('Finished chat data')
|
self.raise_exception(exceptions.ChatDataFinished('Finished chat data'))
|
||||||
unknown = list(cont.keys())[0]
|
unknown = list(cont.keys())[0]
|
||||||
if unknown:
|
if unknown:
|
||||||
raise exceptions.ReceivedUnknownContinuation(
|
self.raise_exception(exceptions.ReceivedUnknownContinuation(
|
||||||
f"Received unknown continuation type:{unknown}")
|
f"Received unknown continuation type:{unknown}"))
|
||||||
else:
|
else:
|
||||||
raise exceptions.FailedExtractContinuation('Cannot extract continuation data')
|
self.raise_exception(exceptions.FailedExtractContinuation('Cannot extract continuation data'))
|
||||||
return self._create_data(metadata, contents)
|
return self._create_data(metadata, contents)
|
||||||
|
|
||||||
def reload_continuation(self, contents):
|
def reload_continuation(self, contents):
|
||||||
@@ -72,7 +83,7 @@ class Parser:
|
|||||||
"""
|
"""
|
||||||
if contents is None:
|
if contents is None:
|
||||||
'''Broadcasting end or cannot fetch chat stream'''
|
'''Broadcasting end or cannot fetch chat stream'''
|
||||||
raise exceptions.NoContents('Chat data stream is empty.')
|
self.raise_exception(exceptions.NoContents('Chat data stream is empty.'))
|
||||||
cont = contents['liveChatContinuation']['continuations'][0]
|
cont = contents['liveChatContinuation']['continuations'][0]
|
||||||
if cont.get("liveChatReplayContinuationData"):
|
if cont.get("liveChatReplayContinuationData"):
|
||||||
# chat data exist.
|
# chat data exist.
|
||||||
@@ -81,7 +92,7 @@ class Parser:
|
|||||||
init_cont = cont.get("playerSeekContinuationData")
|
init_cont = cont.get("playerSeekContinuationData")
|
||||||
if init_cont:
|
if init_cont:
|
||||||
return init_cont.get("continuation")
|
return init_cont.get("continuation")
|
||||||
raise exceptions.ChatDataFinished('Finished chat data')
|
self.raise_exception(exceptions.ChatDataFinished('Finished chat data'))
|
||||||
|
|
||||||
def _create_data(self, metadata, contents):
|
def _create_data(self, metadata, contents):
|
||||||
actions = contents['liveChatContinuation'].get('actions')
|
actions = contents['liveChatContinuation'].get('actions')
|
||||||
@@ -103,3 +114,8 @@ class Parser:
|
|||||||
start = int(actions[0]["replayChatItemAction"]["videoOffsetTimeMsec"])
|
start = int(actions[0]["replayChatItemAction"]["videoOffsetTimeMsec"])
|
||||||
last = int(actions[-1]["replayChatItemAction"]["videoOffsetTimeMsec"])
|
last = int(actions[-1]["replayChatItemAction"]["videoOffsetTimeMsec"])
|
||||||
return (last - start)
|
return (last - start)
|
||||||
|
|
||||||
|
def raise_exception(self, exception):
|
||||||
|
if self.exception_holder is None:
|
||||||
|
raise exception
|
||||||
|
self.exception_holder = exception
|
||||||
|
|||||||
Reference in New Issue
Block a user