diff --git a/pytchat/__init__.py b/pytchat/__init__.py index 66b8530..736668d 100644 --- a/pytchat/__init__.py +++ b/pytchat/__init__.py @@ -2,7 +2,7 @@ pytchat is a lightweight python library to browse youtube livechat without Selenium or BeautifulSoup. """ __copyright__ = 'Copyright (C) 2019, 2020 taizan-hokuto' -__version__ = '0.5.2' +__version__ = '0.5.3' __license__ = 'MIT' __author__ = 'taizan-hokuto' __author_email__ = '55448286+taizan-hokuto@users.noreply.github.com' diff --git a/pytchat/core/pytchat.py b/pytchat/core/pytchat.py index 67dc976..93605e9 100644 --- a/pytchat/core/pytchat.py +++ b/pytchat/core/pytchat.py @@ -95,7 +95,10 @@ def _setup(self): """Fetch first continuation parameter, create and start _listen loop. """ - self.continuation = liveparam.getparam(self._video_id, past_sec=3) + self.continuation = liveparam.getparam( + self._video_id, + channel_id=util.get_channelid(httpx.Client(http2=True), self._video_id), + past_sec=3) def _get_chat_component(self): ''' Fetch chat data and store them into buffer, diff --git a/pytchat/core_async/livechat.py b/pytchat/core_async/livechat.py index 322fd16..655f43c 100644 --- a/pytchat/core_async/livechat.py +++ b/pytchat/core_async/livechat.py @@ -152,7 +152,11 @@ async def _startlisten(self): create and start _listen loop. """ if not self.continuation: - self.continuation = liveparam.getparam(self._video_id, 3) + self.continuation = liveparam.getparam( + self._video_id, + channel_id=util.get_channelid(httpx.Client(http2=True), self._video_id), + past_sec=3) + await self._listen(self.continuation) async def _listen(self, continuation): @@ -210,8 +214,11 @@ async def _check_pause(self, continuation): ''' self._pauser.put_nowait(None) if not self._is_replay: - continuation = liveparam.getparam( - self._video_id, 3, self._topchat_only) + async with httpx.AsyncClient(http2=True) as client: + continuation = await liveparam.getparam(self._video_id, + channel_id=util.get_channelid_async(client, self.video_id), + past_sec=3) + return continuation async def _get_contents(self, continuation, client, headers): diff --git a/pytchat/core_multithread/livechat.py b/pytchat/core_multithread/livechat.py index 4125852..153dce1 100644 --- a/pytchat/core_multithread/livechat.py +++ b/pytchat/core_multithread/livechat.py @@ -148,7 +148,10 @@ def _startlisten(self): create and start _listen loop. """ if not self.continuation: - self.continuation = liveparam.getparam(self._video_id, 3) + self.continuation = liveparam.getparam( + self._video_id, + channel_id=util.get_channelid(httpx.Client(http2=True), self._video_id), + past_sec=3) self._listen(self.continuation) def _listen(self, continuation): @@ -207,7 +210,9 @@ def _check_pause(self, continuation): self._pauser.put_nowait(None) if not self._is_replay: continuation = liveparam.getparam( - self._video_id, 3, self._topchat_only) + self._video_id, channel_id=util.get_channelid(httpx.Client(http2=True), self._video_id), + past_sec=3, topchat_only=self._topchat_only) + return continuation def _get_contents(self, continuation, client, headers): diff --git a/pytchat/paramgen/liveparam.py b/pytchat/paramgen/liveparam.py index 6302832..52997ac 100644 --- a/pytchat/paramgen/liveparam.py +++ b/pytchat/paramgen/liveparam.py @@ -5,11 +5,16 @@ from urllib.parse import quote -def _header(video_id) -> str: - return b64enc(enc.rs(1, enc.rs(1, enc.rs(1, video_id))) + enc.nm(4, 1)) +def _header(video_id, channel_id) -> str: + S1_3 = enc.rs(1, video_id) + S1_5 = enc.rs(1, channel_id) + enc.rs(2, video_id) + S1 = enc.rs(3, S1_3) + enc.rs(5, S1_5) + S3 = enc.rs(48687757, enc.rs(1, video_id)) + header_replay = enc.rs(1, S1) + enc.rs(3, S3) + enc.nm(4, 1) + return b64enc(header_replay) -def _build(video_id, ts1, ts2, ts3, ts4, ts5, topchat_only) -> str: +def _build(video_id, channel_id, ts1, ts2, ts3, ts4, ts5, topchat_only) -> str: chattype = 4 if topchat_only else 1 b1 = enc.nm(1, 0) @@ -23,7 +28,7 @@ def _build(video_id, ts1, ts2, ts3, ts4, ts5, topchat_only) -> str: b11 = enc.nm(11, 3) b15 = enc.nm(15, 0) - header = enc.rs(3, _header(video_id)) + header = enc.rs(3, _header(video_id, channel_id)) timestamp1 = enc.nm(5, ts1) s6 = enc.nm(6, 0) s7 = enc.nm(7, 0) @@ -53,7 +58,7 @@ def _times(past_sec): return list(map(lambda x: int(x * 1000000), [_ts1, _ts2, _ts3, _ts4, _ts5])) -def getparam(video_id, past_sec=0, topchat_only=False) -> str: +def getparam(video_id, channel_id, past_sec=0, topchat_only=False) -> str: ''' Parameter --------- @@ -62,4 +67,4 @@ def getparam(video_id, past_sec=0, topchat_only=False) -> str: topchat_only : bool if True, fetch only 'top chat' ''' - return _build(video_id, *_times(past_sec), topchat_only) \ No newline at end of file + return _build(video_id, channel_id, *_times(past_sec), topchat_only) \ No newline at end of file diff --git a/tests/test_livechat.py b/tests/test_livechat.py deleted file mode 100644 index 925538d..0000000 --- a/tests/test_livechat.py +++ /dev/null @@ -1,48 +0,0 @@ -import asyncio -import json -from pytest_httpx import HTTPXMock -from concurrent.futures import CancelledError -from pytchat.core_multithread.livechat import LiveChat -from pytchat.core_async.livechat import LiveChatAsync -from pytchat.exceptions import ResponseContextError - - -def _open_file(path): - with open(path, mode='r', encoding='utf-8') as f: - return f.read() - - -def add_response_file(httpx_mock: HTTPXMock, jsonfile_path: str): - testdata = json.loads(_open_file(jsonfile_path)) - httpx_mock.add_response(json=testdata) - - -def test_async(httpx_mock: HTTPXMock): - add_response_file(httpx_mock, 'tests/testdata/paramgen_firstread.json') - - async def test_loop(): - try: - chat = LiveChatAsync(video_id='__test_id__') - _ = await chat.get() - assert chat.is_alive() - chat.terminate() - assert not chat.is_alive() - except ResponseContextError: - assert False - loop = asyncio.get_event_loop() - try: - loop.run_until_complete(test_loop()) - except CancelledError: - assert True - - -def test_multithread(httpx_mock: HTTPXMock): - add_response_file(httpx_mock, 'tests/testdata/paramgen_firstread.json') - try: - chat = LiveChat(video_id='__test_id__') - _ = chat.get() - assert chat.is_alive() - chat.terminate() - assert not chat.is_alive() - except ResponseContextError: - assert False diff --git a/tests/test_livechat_2.py b/tests/test_livechat_2.py deleted file mode 100644 index eba1236..0000000 --- a/tests/test_livechat_2.py +++ /dev/null @@ -1,71 +0,0 @@ -import asyncio -import json -from pytest_httpx import HTTPXMock -from concurrent.futures import CancelledError -from pytchat.core_multithread.livechat import LiveChat -from pytchat.core_async.livechat import LiveChatAsync -from pytchat.processors.dummy_processor import DummyProcessor - - -def _open_file(path): - with open(path, mode='r', encoding='utf-8') as f: - return f.read() - - -def add_response_file(httpx_mock: HTTPXMock, jsonfile_path: str): - testdata = json.loads(_open_file(jsonfile_path)) - httpx_mock.add_response(json=testdata) - - -def test_async_live_stream(httpx_mock: HTTPXMock): - add_response_file(httpx_mock, 'tests/testdata/test_stream.json') - - async def test_loop(): - chat = LiveChatAsync(video_id='__test_id__', processor=DummyProcessor()) - chats = await chat.get() - rawdata = chats[0]["chatdata"] - assert list(rawdata[0]["addChatItemAction"]["item"].keys())[ - 0] == "liveChatTextMessageRenderer" - assert list(rawdata[1]["addChatItemAction"]["item"].keys())[ - 0] == "liveChatTextMessageRenderer" - assert list(rawdata[2]["addChatItemAction"]["item"].keys())[ - 0] == "liveChatPlaceholderItemRenderer" - assert list(rawdata[3]["addLiveChatTickerItemAction"]["item"].keys())[ - 0] == "liveChatTickerPaidMessageItemRenderer" - assert list(rawdata[4]["addChatItemAction"]["item"].keys())[ - 0] == "liveChatPaidMessageRenderer" - assert list(rawdata[5]["addChatItemAction"]["item"].keys())[ - 0] == "liveChatPaidStickerRenderer" - assert list(rawdata[6]["addLiveChatTickerItemAction"]["item"].keys())[ - 0] == "liveChatTickerSponsorItemRenderer" - - loop = asyncio.get_event_loop() - try: - loop.run_until_complete(test_loop()) - except CancelledError: - assert True - - - - -def test_multithread_live_stream(httpx_mock: HTTPXMock): - add_response_file(httpx_mock, 'tests/testdata/test_stream.json') - chat = LiveChat(video_id='__test_id__', processor=DummyProcessor()) - chats = chat.get() - rawdata = chats[0]["chatdata"] - # assert fetching livachat data - assert list(rawdata[0]["addChatItemAction"]["item"].keys())[ - 0] == "liveChatTextMessageRenderer" - assert list(rawdata[1]["addChatItemAction"]["item"].keys())[ - 0] == "liveChatTextMessageRenderer" - assert list(rawdata[2]["addChatItemAction"]["item"].keys())[ - 0] == "liveChatPlaceholderItemRenderer" - assert list(rawdata[3]["addLiveChatTickerItemAction"]["item"].keys())[ - 0] == "liveChatTickerPaidMessageItemRenderer" - assert list(rawdata[4]["addChatItemAction"]["item"].keys())[ - 0] == "liveChatPaidMessageRenderer" - assert list(rawdata[5]["addChatItemAction"]["item"].keys())[ - 0] == "liveChatPaidStickerRenderer" - assert list(rawdata[6]["addLiveChatTickerItemAction"]["item"].keys())[ - 0] == "liveChatTickerSponsorItemRenderer" - chat.terminate() diff --git a/tests/test_liveparam.py b/tests/test_liveparam.py deleted file mode 100644 index 66d59be..0000000 --- a/tests/test_liveparam.py +++ /dev/null @@ -1,9 +0,0 @@ -import pytest -from pytchat.paramgen import liveparam - -def test_liveparam_0(mocker): - _ts1= 1546268400 - param = liveparam._build("01234567890", - *([_ts1*1000000 for i in range(5)]), topchat_only=False) - test_param="0ofMyAN1GhxDZzhLRFFvTE1ERXlNelExTmpjNE9UQWdBUT09KIC41tWqyt8CMAA4AEABShsIABAAGAAgADoAQABKAFCAuNbVqsrfAlgDeABQgLjW1arK3wJYgLjW1arK3wJoAYIBAggBiAEAmgECCACgAYC41tWqyt8C" - assert test_param == param \ No newline at end of file