From 289841a000e17608711e53c38b8ee2c1f6feac5a Mon Sep 17 00:00:00 2001 From: taizan-hokuto <55448286+taizan-hokuto@users.noreply.github.com> Date: Fri, 20 Dec 2019 01:13:50 +0900 Subject: [PATCH 01/11] Export api speed_calculator --- pytchat/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pytchat/api.py b/pytchat/api.py index 7fa873a..25c78e5 100644 --- a/pytchat/api.py +++ b/pytchat/api.py @@ -7,4 +7,4 @@ from .processors.default.processor import DefaultProcessor from .processors.compatible.processor import CompatibleProcessor from .processors.simple_display_processor import SimpleDisplayProcessor from .processors.jsonfile_archive_processor import JsonfileArchiveProcessor - +from .processors.speed_calculator import SpeedCalculator From 3c1f079d5f37858a5ce0d9b4ac43d6d146703a9d Mon Sep 17 00:00:00 2001 From: taizan-hokuto <55448286+taizan-hokuto@users.noreply.github.com> Date: Fri, 20 Dec 2019 21:33:32 +0900 Subject: [PATCH 02/11] Extends ChatProcessor explicitly --- pytchat/processors/chat_processor.py | 6 +++--- pytchat/processors/compatible/processor.py | 3 ++- pytchat/processors/default/processor.py | 3 ++- pytchat/processors/speed_calculator.py | 4 ++-- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/pytchat/processors/chat_processor.py b/pytchat/processors/chat_processor.py index 2b94221..44dda87 100644 --- a/pytchat/processors/chat_processor.py +++ b/pytchat/processors/chat_processor.py @@ -10,14 +10,14 @@ class ChatProcessor: Parameter ---------- - chat_components: [LIST:component] + chat_components: List[component] component : dict { "video_id" : str 動画ID "timeout" : int 次のチャットの再読み込みまでの時間(秒) - "chatdata" : list - チャットデータ(actions)のリスト + "chatdata" : List[dict] + チャットデータのリスト } ''' pass diff --git a/pytchat/processors/compatible/processor.py b/pytchat/processors/compatible/processor.py index 124af35..31e1b15 100644 --- a/pytchat/processors/compatible/processor.py +++ b/pytchat/processors/compatible/processor.py @@ -4,11 +4,12 @@ from .renderer.textmessage import LiveChatTextMessageRenderer from .renderer.paidmessage import LiveChatPaidMessageRenderer from .renderer.paidsticker import LiveChatPaidStickerRenderer from .renderer.legacypaid import LiveChatLegacyPaidMessageRenderer +from .. chat_processor import ChatProcessor from ... import mylogger from ... import config logger = mylogger.get_logger(__name__,mode=config.LOGGER_MODE) -class CompatibleProcessor: +class CompatibleProcessor(ChatProcessor): def process(self, chat_components: list): diff --git a/pytchat/processors/default/processor.py b/pytchat/processors/default/processor.py index 123157a..be3dfd6 100644 --- a/pytchat/processors/default/processor.py +++ b/pytchat/processors/default/processor.py @@ -4,6 +4,7 @@ from .renderer.textmessage import LiveChatTextMessageRenderer from .renderer.paidmessage import LiveChatPaidMessageRenderer from .renderer.paidsticker import LiveChatPaidStickerRenderer from .renderer.legacypaid import LiveChatLegacyPaidMessageRenderer +from .. chat_processor import ChatProcessor from ... import config from ... import mylogger logger = mylogger.get_logger(__name__,mode=config.LOGGER_MODE) @@ -25,7 +26,7 @@ class Chatdata: return await asyncio.sleep(self.interval/len(self.items)) -class DefaultProcessor: +class DefaultProcessor(ChatProcessor): def process(self, chat_components: list): chatlist = [] diff --git a/pytchat/processors/speed_calculator.py b/pytchat/processors/speed_calculator.py index fac7b73..361b32f 100644 --- a/pytchat/processors/speed_calculator.py +++ b/pytchat/processors/speed_calculator.py @@ -4,7 +4,7 @@ speedmeter.py Calculate speed of chat. """ import calendar, datetime, pytz - +from .chat_processor import ChatProcessor class RingQueue: """ リング型キュー @@ -77,7 +77,7 @@ class RingQueue: def item_count(self): return len(self.items) -class SpeedCalculator(RingQueue): +class SpeedCalculator(ChatProcessor, RingQueue): """ チャットの勢いを計算するクラス Parameter From b357bccb988400e2c53fb0da330790997f226579 Mon Sep 17 00:00:00 2001 From: taizan-hokuto <55448286+taizan-hokuto@users.noreply.github.com> Date: Fri, 20 Dec 2019 23:57:10 +0900 Subject: [PATCH 03/11] Add test json --- tests/testdata/chat.json | 164 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 164 insertions(+) create mode 100644 tests/testdata/chat.json diff --git a/tests/testdata/chat.json b/tests/testdata/chat.json new file mode 100644 index 0000000..1894dfa --- /dev/null +++ b/tests/testdata/chat.json @@ -0,0 +1,164 @@ +{ + "timing": { + "info": { + "st": 164 + } + }, + "csn": "", + "response": { + "responseContext": { + "serviceTrackingParams": [{ + "service": "CSI", + "params": [{ + "key": "GetLiveChat_rid", + "value": "" + }, { + "key": "c", + "value": "WEB" + }, { + "key": "cver", + "value": "2.20191219.03.01" + }, { + "key": "yt_li", + "value": "0" + }] + }, { + "service": "GFEEDBACK", + "params": [{ + "key": "e", + "value": "" + }, { + "key": "logged_in", + "value": "0" + }] + }, { + "service": "GUIDED_HELP", + "params": [{ + "key": "logged_in", + "value": "0" + }] + }, { + "service": "ECATCHER", + "params": [{ + "key": "client.name", + "value": "WEB" + }, { + "key": "client.version", + "value": "2.2" + }, { + "key": "innertube.build.changelist", + "value": "228" + }, { + "key": "innertube.build.experiments.source_version", + "value": "2858" + }, { + "key": "innertube.build.label", + "value": "youtube.ytfe.innertube_" + }, { + "key": "innertube.build.timestamp", + "value": "154" + }, { + "key": "innertube.build.variants.checksum", + "value": "e" + }, { + "key": "innertube.run.job", + "value": "ytfe-innertube-replica-only.ytfe" + }] + }], + "webResponseContextExtensionData": { + "ytConfigData": { + "csn": "ADw", + "visitorData": "%3D%3D" + } + } + }, + "continuationContents": { + "liveChatContinuation": { + "continuations": [{ + "timedContinuationData": { + "timeoutMs": 10000, + "continuation": "continuation" + } + }], + "actions": [{ + "addChatItemAction": { + "item": { + "liveChatTextMessageRenderer": { + "message": { + "runs": [{ + "text": "message" + }] + }, + "authorName": { + "simpleText": "authorName" + }, + "authorPhoto": { + "thumbnails": [{ + "url": "https://yt3.ggpht.com/photo.jpg", + "width": 32, + "height": 32 + }, { + "url": "https://yt3.ggpht.com/photo.jpg", + "width": 64, + "height": 64 + }] + }, + "contextMenuEndpoint": { + "commandMetadata": { + "webCommandMetadata": { + "ignoreNavigation": true + } + }, + "liveChatItemContextMenuEndpoint": { + "params": "params" + } + }, + "id": "id", + "timestampUsec": "1576851922945411", + "authorBadges": [{ + "liveChatAuthorBadgeRenderer": { + "customThumbnail": { + "thumbnails": [{ + "url": "https://yt3.ggpht.com/photo.jpg" + }, { + "url": "https://yt3.ggpht.com/photo.jpg" + }] + }, + "tooltip": "メンバー(6 か月)", + "accessibility": { + "accessibilityData": { + "label": "メンバー(6 か月)" + } + } + } + }], + "authorExternalChannelId": "UC", + "contextMenuAccessibility": { + "accessibilityData": { + "label": "コメントの操作" + } + } + } + }, + "clientId": "00000000000000000000" + } + } + ]} + }, + + "xsrf_token": "xsrf_token", + "url": "/live_chat/get_live_chat?continuation=0", + "endpoint": { + "commandMetadata": { + "webCommandMetadata": { + "url": "/live_chat/get_live_chat?continuation=0", + "rootVe": 0 + } + }, + "urlEndpoint": { + "url": "/live_chat/get_live_chat?continuation=0" + } + } + } +} + \ No newline at end of file From 18666199b780aedaed75d21ffeb1b081316537bb Mon Sep 17 00:00:00 2001 From: taizan-hokuto <55448286+taizan-hokuto@users.noreply.github.com> Date: Sat, 21 Dec 2019 02:13:15 +0900 Subject: [PATCH 04/11] Add test SpeedCalculator --- tests/test_speed_calculator.py | 54 +++++++ tests/testdata/speed/speedtest1.json | 188 +++++++++++++++++++++++ tests/testdata/speed/speedtest_none.json | 42 +++++ 3 files changed, 284 insertions(+) create mode 100644 tests/test_speed_calculator.py create mode 100644 tests/testdata/speed/speedtest1.json create mode 100644 tests/testdata/speed/speedtest_none.json diff --git a/tests/test_speed_calculator.py b/tests/test_speed_calculator.py new file mode 100644 index 0000000..69a902c --- /dev/null +++ b/tests/test_speed_calculator.py @@ -0,0 +1,54 @@ +import json +import pytest +import asyncio,aiohttp +from pytchat.parser.live import Parser +from pytchat.processors.compatible.processor import CompatibleProcessor +from pytchat.exceptions import ( + NoLivechatRendererException,NoYtinitialdataException, + ResponseContextError, NoContentsException) + +from pytchat.processors.speed_calculator import SpeedCalculator + +parser = Parser() + +def test_speed_1(mocker): + '''test speed normal + test json has 15 chatdata, duration is 30 seconds, + so the speed of chatdata is 30 chats/minute. + ''' + + processor = SpeedCalculator(capacity=30,video_id="") + + _json = _open_file("tests/testdata/speed/speedtest1.json") + + _, chatdata = parser.parse(json.loads(_json)) + data = { + "video_id" : "", + "timeout" : 10, + "chatdata" : chatdata + } + ret = processor.process([data]) + assert 30 == ret + +def test_speed_2(mocker): + '''test speed with no valid chat data + + ''' + + processor = SpeedCalculator(capacity=30,video_id="") + + _json = _open_file("tests/testdata/speed/speedtest_none.json") + + _, chatdata = parser.parse(json.loads(_json)) + data = { + "video_id" : "", + "timeout" : 10, + "chatdata" : chatdata + } + ret = processor.process([data]) + assert 0 == ret + + +def _open_file(path): + with open(path,mode ='r',encoding = 'utf-8') as f: + return f.read() diff --git a/tests/testdata/speed/speedtest1.json b/tests/testdata/speed/speedtest1.json new file mode 100644 index 0000000..f2e25a5 --- /dev/null +++ b/tests/testdata/speed/speedtest1.json @@ -0,0 +1,188 @@ +{ + "timing": { + "info": { + "st": 164 + } + }, + "csn": "", + "response": { + "responseContext": { + }, + "continuationContents": { + "liveChatContinuation": { + "continuations": [{ + "timedContinuationData": { + "timeoutMs": 10000, + "continuation": "continuation" + } + }], + "actions": [{ + "addChatItemAction": { + "item": { + "liveChatTextMessageRenderer": { + + "timestampUsec": "1500000000000000" + + } + } + } + }, + { + "addChatItemAction": { + "item": { + "liveChatTextMessageRenderer": { + + "timestampUsec": "1500000000000000" + + } + } + } + }, + { + "addChatItemAction": { + "item": { + "liveChatTextMessageRenderer": { + + "timestampUsec": "1500000000000000" + + } + } + } + }, + { + "addChatItemAction": { + "item": { + "liveChatTextMessageRenderer": { + + "timestampUsec": "1500000000000000" + + } + } + } + }, + { + "addChatItemAction": { + "item": { + "liveChatTextMessageRenderer": { + + "timestampUsec": "1500000000000000" + + } + } + } + }, + { + "addChatItemAction": { + "item": { + "liveChatTextMessageRenderer": { + + "timestampUsec": "1500000000000000" + + } + } + } + }, + { + "addChatItemAction": { + "item": { + "liveChatTextMessageRenderer": { + + "timestampUsec": "1500000000000000" + + } + } + } + }, + { + "addChatItemAction": { + "item": { + "liveChatTextMessageRenderer": { + + "timestampUsec": "1500000000000000" + + } + } + } + }, + { + "addChatItemAction": { + "item": { + "liveChatTextMessageRenderer": { + + "timestampUsec": "1500000000000000" + + } + } + } + }, + { + "addChatItemAction": { + "item": { + "liveChatTextMessageRenderer": { + + "timestampUsec": "1500000000000000" + + } + } + } + }, + { + "addChatItemAction": { + "item": { + "liveChatTextMessageRenderer": { + + "timestampUsec": "1500000000000000" + + } + } + } + }, + { + "addChatItemAction": { + "item": { + "liveChatTextMessageRenderer": { + + "timestampUsec": "1500000000000000" + + } + } + } + }, + { + "addChatItemAction": { + "item": { + "liveChatTextMessageRenderer": { + + "timestampUsec": "1500000000000000" + + } + } + } + }, + { + "addChatItemAction": { + "item": { + "liveChatTextMessageRenderer": { + + "timestampUsec": "1500000000000000" + + } + } + } + }, + { + "addChatItemAction": { + "item": { + "liveChatTextMessageRenderer": { + + "timestampUsec": "1500000030000000" + + } + } + } + } + ]} + } + } +} + \ No newline at end of file diff --git a/tests/testdata/speed/speedtest_none.json b/tests/testdata/speed/speedtest_none.json new file mode 100644 index 0000000..de6528d --- /dev/null +++ b/tests/testdata/speed/speedtest_none.json @@ -0,0 +1,42 @@ +{ + "timing": { + "info": { + "st": 164 + } + }, + "csn": "", + "response": { + "responseContext": { + }, + "continuationContents": { + "liveChatContinuation": { + "continuations": [{ + "timedContinuationData": { + "timeoutMs": 10000, + "continuation": "continuation" + } + }], + "actions": [{ + "addChatItemAction": { + "liveChatPlaceholderItemRenderer": { + "id": "", + "timestampUsec": "1500000000000000" + } + } + }, + { + "addChatItemAction": { + "item": { + "liveChatPlaceholderItemRenderer": { + "id": "", + "timestampUsec": "1500000030000000" + + } + } + } + } + ]} + } + } +} + \ No newline at end of file From 5a79f26fa73e1beb63769a2a99cf6e0b8e52b79b Mon Sep 17 00:00:00 2001 From: taizan-hokuto <55448286+taizan-hokuto@users.noreply.github.com> Date: Sat, 21 Dec 2019 20:06:55 +0900 Subject: [PATCH 05/11] Fix calculation algorithm --- pytchat/processors/speed_calculator.py | 74 ++++++++++--------- tests/test_speed_calculator.py | 29 ++++++-- tests/testdata/speed/speedtest_empty.json | 24 ++++++ ...{speedtest1.json => speedtest_normal.json} | 0 ...est_none.json => speedtest_undefined.json} | 0 5 files changed, 86 insertions(+), 41 deletions(-) create mode 100644 tests/testdata/speed/speedtest_empty.json rename tests/testdata/speed/{speedtest1.json => speedtest_normal.json} (100%) rename tests/testdata/speed/{speedtest_none.json => speedtest_undefined.json} (100%) diff --git a/pytchat/processors/speed_calculator.py b/pytchat/processors/speed_calculator.py index 361b32f..ff15517 100644 --- a/pytchat/processors/speed_calculator.py +++ b/pytchat/processors/speed_calculator.py @@ -21,7 +21,7 @@ class RingQueue: キュー内に余裕があるか。キュー内のアイテム個数が、キューの最大個数未満であればTrue。 """ - def __init__(self, capacity = 10): + def __init__(self, capacity): """ コンストラクタ @@ -82,29 +82,29 @@ class SpeedCalculator(ChatProcessor, RingQueue): チャットの勢いを計算するクラス Parameter ---------- + capacity : int 格納するチャットブロックの数 """ - def __init__(self, capacity, video_id): + def __init__(self, capacity = 10): super().__init__(capacity) - self.video_id=video_id self.speed = 0 def process(self, chat_components: list): + chatdata = [] if chat_components: for component in chat_components: - - chatdata = component.get('chatdata') - - if chatdata is None: - return self.speed - self.speed = self.calc(chatdata) - return self.speed + if component.get("chatdata"): + chatdata.extend(component.get("chatdata")) - def _value(self): - + self._put_chatdata(chatdata) + self.speed = self._calc_speed() + return self.speed + + + def _calc_speed(self): """ - ActionsQueue内のチャットデータリストから、 + RingQueue内のチャットデータリストから、 チャット速度を計算して返す Return @@ -123,24 +123,19 @@ class SpeedCalculator(ChatProcessor, RingQueue): except IndexError: return 0 - def _get_timestamp(self, action :dict): + def _put_chatdata(self,actions): """ - チャットデータのtimestampUsecを読み取る - liveChatTickerSponsorItemRenderer等のtickerデータは時刻格納位置が - 異なるため、時刻データなしとして扱う + チャットデータからタイムスタンプを読み取り、RingQueueに投入する。 + Parameter + --------- + actions : List[dict] + チャットデータ(addChatItemAction) のリスト """ - try: - item = action['addChatItemAction']['item'] - timestamp = int(item[list(item.keys())[0]]['timestampUsec']) - except (KeyError,TypeError): - return None - return timestamp - - def calc(self,actions): - - def empty_data(): + def _put_emptydata(): ''' - データがない場合にゼロのデータをリングキューに入れる + enqueue empty data when no chat data. + Return: int + speed value after enqueueing empty data ''' timestamp_now = calendar.timegm(datetime.datetime. now(pytz.utc).utctimetuple()) @@ -149,10 +144,21 @@ class SpeedCalculator(ChatProcessor, RingQueue): 'starttime':int(timestamp_now), 'endtime':int(timestamp_now) }) - return self._value() + + def _get_timestamp(action :dict): + """ + チャットデータのtimestampUsecを読み取る + """ + try: + item = action['addChatItemAction']['item'] + timestamp = int(item[list(item.keys())[0]]['timestampUsec']) + except (KeyError,TypeError): + return None + return timestamp if actions is None or len(actions)==0: - return empty_data + _put_emptydata() + return #actions内の時刻データを持つチャットデータの数(tickerは除く) counter=0 @@ -163,7 +169,7 @@ class SpeedCalculator(ChatProcessor, RingQueue): for action in actions: #チャットデータからtimestampUsecを読み取る - gettime = self._get_timestamp(action) + gettime = _get_timestamp(action) #時刻のないデータだった場合は次の行のデータで読み取り試行 if gettime is None: @@ -177,11 +183,12 @@ class SpeedCalculator(ChatProcessor, RingQueue): endtime = gettime #チャットの数をインクリメント - counter+=1 + counter += 1 #チャット速度用のデータをリングキューに送る if starttime is None or endtime is None: - return empty_data + _put_emptydata() + return self.put({ 'chat_count':counter, @@ -189,4 +196,3 @@ class SpeedCalculator(ChatProcessor, RingQueue): 'endtime':int(endtime/1000000) }) - return self._value() diff --git a/tests/test_speed_calculator.py b/tests/test_speed_calculator.py index 69a902c..b5964d6 100644 --- a/tests/test_speed_calculator.py +++ b/tests/test_speed_calculator.py @@ -12,14 +12,14 @@ from pytchat.processors.speed_calculator import SpeedCalculator parser = Parser() def test_speed_1(mocker): - '''test speed normal + '''test speed calculation with normal json. test json has 15 chatdata, duration is 30 seconds, so the speed of chatdata is 30 chats/minute. ''' - processor = SpeedCalculator(capacity=30,video_id="") + processor = SpeedCalculator(capacity=30) - _json = _open_file("tests/testdata/speed/speedtest1.json") + _json = _open_file("tests/testdata/speed/speedtest_normal.json") _, chatdata = parser.parse(json.loads(_json)) data = { @@ -31,13 +31,27 @@ def test_speed_1(mocker): assert 30 == ret def test_speed_2(mocker): - '''test speed with no valid chat data - + '''test speed calculation with no valid chat data. ''' + processor = SpeedCalculator(capacity=30) - processor = SpeedCalculator(capacity=30,video_id="") + _json = _open_file("tests/testdata/speed/speedtest_undefined.json") - _json = _open_file("tests/testdata/speed/speedtest_none.json") + _, chatdata = parser.parse(json.loads(_json)) + data = { + "video_id" : "", + "timeout" : 10, + "chatdata" : chatdata + } + ret = processor.process([data]) + assert 0 == ret + +def test_speed_3(mocker): + '''test speed calculation with empty data. + ''' + processor = SpeedCalculator(capacity=30) + + _json = _open_file("tests/testdata/speed/speedtest_empty.json") _, chatdata = parser.parse(json.loads(_json)) data = { @@ -52,3 +66,4 @@ def test_speed_2(mocker): def _open_file(path): with open(path,mode ='r',encoding = 'utf-8') as f: return f.read() +_put_chatdata \ No newline at end of file diff --git a/tests/testdata/speed/speedtest_empty.json b/tests/testdata/speed/speedtest_empty.json new file mode 100644 index 0000000..b116a66 --- /dev/null +++ b/tests/testdata/speed/speedtest_empty.json @@ -0,0 +1,24 @@ +{ + "timing": { + "info": { + "st": 164 + } + }, + "csn": "", + "response": { + "responseContext": { + }, + "continuationContents": { + "liveChatContinuation": { + "continuations": [{ + "timedContinuationData": { + "timeoutMs": 10000, + "continuation": "continuation" + } + }] + } + } + } +} + + diff --git a/tests/testdata/speed/speedtest1.json b/tests/testdata/speed/speedtest_normal.json similarity index 100% rename from tests/testdata/speed/speedtest1.json rename to tests/testdata/speed/speedtest_normal.json diff --git a/tests/testdata/speed/speedtest_none.json b/tests/testdata/speed/speedtest_undefined.json similarity index 100% rename from tests/testdata/speed/speedtest_none.json rename to tests/testdata/speed/speedtest_undefined.json From ab5a2a8df2658a2e63a67976881e6319a7ebc8b0 Mon Sep 17 00:00:00 2001 From: taizan-hokuto <55448286+taizan-hokuto@users.noreply.github.com> Date: Sat, 21 Dec 2019 20:12:30 +0900 Subject: [PATCH 06/11] Fix syntax error --- tests/test_speed_calculator.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_speed_calculator.py b/tests/test_speed_calculator.py index b5964d6..8a096c8 100644 --- a/tests/test_speed_calculator.py +++ b/tests/test_speed_calculator.py @@ -66,4 +66,3 @@ def test_speed_3(mocker): def _open_file(path): with open(path,mode ='r',encoding = 'utf-8') as f: return f.read() -_put_chatdata \ No newline at end of file From dc47f4debe6b56a2370564cfd2b2ca09fc44449e Mon Sep 17 00:00:00 2001 From: taizan-hokuto <55448286+taizan-hokuto@users.noreply.github.com> Date: Sat, 21 Dec 2019 22:40:08 +0900 Subject: [PATCH 07/11] Fix README --- README.md | 78 +++++++++++++++++++++++++++---------------------------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index d54040e..109550a 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ pytchat is a python library for fetching youtube live chat without using youtube api, Selenium or BeautifulSoup. Other features: -+ Customizable chat data processors including yt api compatible one. ++ Customizable chat data processors including youtube api compatible one. + Available on asyncio context. + Quick fetching of initial chat data by generating continuation params instead of web scraping. @@ -29,10 +29,10 @@ from pytchat import LiveChat chat = LiveChat("G1w62uEMZ74") while chat.is_alive(): - data = chat.get() - for c in data.items: - print(f"{c.datetime} [{c.author.name}]-{c.message} {c.amountString}") - data.tick() + data = chat.get() + for c in data.items: + print(f"{c.datetime} [{c.author.name}]-{c.message} {c.amountString}") + data.tick() ``` ### callback mode @@ -41,16 +41,16 @@ from pytchat import LiveChat import time def main() - chat = LiveChat("G1w62uEMZ74", callback = func) - while chat.is_alive(): - time.sleep(3) - #other background operation. + chat = LiveChat("G1w62uEMZ74", callback = func) + while chat.is_alive(): + time.sleep(3) + #other background operation. #callback function is automatically called periodically. def func(data): - for c in data.items: - print(f"{c.datetime} [{c.author.name}]-{c.message} {c.amountString}") - data.tick() + for c in data.items: + print(f"{c.datetime} [{c.author.name}]-{c.message} {c.amountString}") + data.tick() ``` ### asyncio context: @@ -59,16 +59,16 @@ from pytchat import LiveChatAsync import asyncio async def main(): - chat = LiveChatAsync("G1w62uEMZ74", callback = func) - while chat.is_alive(): - await asyncio.sleep(3) - #other background operation. + chat = LiveChatAsync("G1w62uEMZ74", callback = func) + while chat.is_alive(): + await asyncio.sleep(3) + #other background operation. #callback function is automatically called periodically. async def func(data): - for c in data.items: - print(f"{c.datetime} [{c.author.name}]-{c.message} {c.amountString}") - await data.tick_async() + for c in data.items: + print(f"{c.datetime} [{c.author.name}]-{c.message} {c.amountString}") + await data.tick_async() loop = asyncio.get_event_loop() loop.run_until_complete(main()) @@ -80,16 +80,16 @@ loop.run_until_complete(main()) from pytchat import LiveChat, CompatibleProcessor chat = LiveChat("G1w62uEMZ74", - processor = CompatibleProcessor() ) + processor = CompatibleProcessor() ) while chat.is_alive(): - data = chat.get() - polling = data["pollingIntervalMillis"]/1000 - for c in data["items"]: - if c.get("snippet"): - print(f"[{c['authorDetails']['displayName']}]" - f"-{c['snippet']['displayMessage']}") - time.sleep(polling/len(data["items"])) + data = chat.get() + polling = data["pollingIntervalMillis"]/1000 + for c in data["items"]: + if c.get("snippet"): + print(f"[{c['authorDetails']['displayName']}]" + f"-{c['snippet']['displayMessage']}") + time.sleep(polling/len(data["items"])) ``` ### replay: @@ -98,21 +98,21 @@ from pytchat import ReplayChatAsync import asyncio async def main(): - chat = ReplayChatAsync("G1w62uEMZ74", seektime = 1000, callback = func) - while chat.is_alive(): - await asyncio.sleep(3) - #other background operation here. + chat = ReplayChatAsync("G1w62uEMZ74", seektime = 1000, callback = func) + while chat.is_alive(): + await asyncio.sleep(3) + #other background operation here. #callback function is automatically called periodically. async def func(data): - for count in range(0,len(data.items)): - c= data.items[count] - if count!=len(data.items): - tick=data.items[count+1].timestamp -data.items[count].timestamp - else: - tick=0 - print(f"<{c.elapsedTime}> [{c.author.name}]-{c.message} {c.amountString}") - await asyncio.sleep(tick/1000) + for count in range(0,len(data.items)): + c= data.items[count] + if count!=len(data.items): + tick=data.items[count+1].timestamp -data.items[count].timestamp + else: + tick=0 + print(f"<{c.elapsedTime}> [{c.author.name}]-{c.message} {c.amountString}") + await asyncio.sleep(tick/1000) loop = asyncio.get_event_loop() loop.run_until_complete(main()) From a70efe8a67632cabf351aa3d42cdd54b5f634be7 Mon Sep 17 00:00:00 2001 From: taizan-hokuto <55448286+taizan-hokuto@users.noreply.github.com> Date: Sat, 21 Dec 2019 23:42:51 +0900 Subject: [PATCH 08/11] Fix comments --- pytchat/processors/speed_calculator.py | 32 +++++++++++++++----------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/pytchat/processors/speed_calculator.py b/pytchat/processors/speed_calculator.py index ff15517..fb01d24 100644 --- a/pytchat/processors/speed_calculator.py +++ b/pytchat/processors/speed_calculator.py @@ -79,11 +79,16 @@ class RingQueue: class SpeedCalculator(ChatProcessor, RingQueue): """ - チャットの勢いを計算するクラス + チャットの勢いを計算する。 + + 一定期間のチャットデータのうち、最初のチャットの投稿時刻と + 最後のチャットの投稿時刻の差を、チャット数で割り返し + 1分あたりの速度に換算する。 + Parameter ---------- capacity : int - 格納するチャットブロックの数 + RingQueueに格納するチャット勢い算出用データの最大数 """ def __init__(self, capacity = 10): @@ -104,15 +109,15 @@ class SpeedCalculator(ChatProcessor, RingQueue): def _calc_speed(self): """ - RingQueue内のチャットデータリストから、 + RingQueue内のチャット勢い算出用データリストを元に、 チャット速度を計算して返す Return --------------------------- - チャット速度(1分間で換算したチャット数) + チャット速度(1分間で換算したチャット数) """ try: - #キュー内のactionsの総チャット数 + #キュー内の総チャット数 total = sum(item['chat_count'] for item in self.items) #キュー内の最初と最後のチャットの時間差 duration = (self.items[self.last_pos]['endtime'] @@ -123,9 +128,12 @@ class SpeedCalculator(ChatProcessor, RingQueue): except IndexError: return 0 - def _put_chatdata(self,actions): + def _put_chatdata(self, actions): """ - チャットデータからタイムスタンプを読み取り、RingQueueに投入する。 + チャットデータからタイムスタンプを読み取り、勢い測定用のデータを組み立て、 + RingQueueに投入する。 + 200円以上のスパチャはtickerとmessageの2つのデータが生成されるが、 + tickerの方は時刻データの場所が異なることを利用し、勢いの集計から除外している。 Parameter --------- actions : List[dict] @@ -133,9 +141,7 @@ class SpeedCalculator(ChatProcessor, RingQueue): """ def _put_emptydata(): ''' - enqueue empty data when no chat data. - Return: int - speed value after enqueueing empty data + チャットデータがない場合に空のデータをキューに投入する。 ''' timestamp_now = calendar.timegm(datetime.datetime. now(pytz.utc).utctimetuple()) @@ -147,7 +153,7 @@ class SpeedCalculator(ChatProcessor, RingQueue): def _get_timestamp(action :dict): """ - チャットデータのtimestampUsecを読み取る + チャットデータから時刻データを取り出す。 """ try: item = action['addChatItemAction']['item'] @@ -160,7 +166,7 @@ class SpeedCalculator(ChatProcessor, RingQueue): _put_emptydata() return - #actions内の時刻データを持つチャットデータの数(tickerは除く) + #actions内の時刻データを持つチャットデータの数 counter=0 #actions内の最初のチャットデータの時刻 starttime= None @@ -185,7 +191,7 @@ class SpeedCalculator(ChatProcessor, RingQueue): #チャットの数をインクリメント counter += 1 - #チャット速度用のデータをリングキューに送る + #チャット速度用のデータをRingQueueに送る if starttime is None or endtime is None: _put_emptydata() return From fff3e0371fc9e6c4c595f0c36d8e8707dcc10465 Mon Sep 17 00:00:00 2001 From: taizan-hokuto <55448286+taizan-hokuto@users.noreply.github.com> Date: Sun, 22 Dec 2019 01:39:35 +0900 Subject: [PATCH 09/11] Export SpeedCalculator --- pytchat/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pytchat/__init__.py b/pytchat/__init__.py index 74ba1b2..f32d98f 100644 --- a/pytchat/__init__.py +++ b/pytchat/__init__.py @@ -18,5 +18,6 @@ from .api import ( ChatProcessor, CompatibleProcessor, SimpleDisplayProcessor, - JsonfileArchiveProcessor + JsonfileArchiveProcessor, + SpeedCalculator ) \ No newline at end of file From 6ac5191e8530d3d2e4f51b632e02ff55f2a382ef Mon Sep 17 00:00:00 2001 From: taizan-hokuto <55448286+taizan-hokuto@users.noreply.github.com> Date: Sun, 22 Dec 2019 01:58:42 +0900 Subject: [PATCH 10/11] Change debug mode --- pytchat/config/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pytchat/config/__init__.py b/pytchat/config/__init__.py index b17df93..eb109d2 100644 --- a/pytchat/config/__init__.py +++ b/pytchat/config/__init__.py @@ -1,4 +1,4 @@ import logging -LOGGER_MODE = logging.ERROR +LOGGER_MODE = None headers = { 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36'} From 91aa944df51fc17f07efad46bfb1b220a16e125e Mon Sep 17 00:00:00 2001 From: taizan-hokuto <55448286+taizan-hokuto@users.noreply.github.com> Date: Sun, 22 Dec 2019 02:54:20 +0900 Subject: [PATCH 11/11] Increment version --- pytchat/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pytchat/__init__.py b/pytchat/__init__.py index f32d98f..a013b74 100644 --- a/pytchat/__init__.py +++ b/pytchat/__init__.py @@ -2,7 +2,7 @@ pytchat is a python library for fetching youtube live chat without using yt api, Selenium, or BeautifulSoup. """ __copyright__ = 'Copyright (C) 2019 taizan-hokuto' -__version__ = '0.0.3.6' +__version__ = '0.0.3.7' __license__ = 'MIT' __author__ = 'taizan-hokuto' __author_email__ = '55448286+taizan-hokuto@users.noreply.github.com'