Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
385634b709 | ||
|
|
c1a78a2743 | ||
|
|
7961801e0c | ||
|
|
5fe4e7af04 | ||
|
|
892dfb8a91 | ||
|
|
fddab22a1f | ||
|
|
7194948066 | ||
|
|
a836d92194 | ||
|
|
c408cb2713 | ||
|
|
c3d2238ead | ||
|
|
6c8d390fc7 | ||
|
|
ff1ee70d7e | ||
|
|
404623546e | ||
|
|
3f9f64d19c | ||
|
|
7996c6adad | ||
|
|
50d55da7dc |
@@ -1,7 +1,5 @@
|
|||||||
include requirements.txt
|
include requirements.txt
|
||||||
include requirements_test.txt
|
include requirements_test.txt
|
||||||
prune testrun*.py
|
include README.MD
|
||||||
prune log.txt
|
global-exclude tests/*
|
||||||
prune quote.txt
|
global-exclude pytchat/testrun*.py
|
||||||
prune .gitignore
|
|
||||||
prun tests
|
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
pytchat is a python library for fetching youtube live chat without using yt api, Selenium, or BeautifulSoup.
|
pytchat is a python library for fetching youtube live chat without using yt api, Selenium, or BeautifulSoup.
|
||||||
"""
|
"""
|
||||||
__copyright__ = 'Copyright (C) 2019 taizan-hokuto'
|
__copyright__ = 'Copyright (C) 2019 taizan-hokuto'
|
||||||
__version__ = '0.0.4.3'
|
__version__ = '0.0.4.9'
|
||||||
__license__ = 'MIT'
|
__license__ = 'MIT'
|
||||||
__author__ = 'taizan-hokuto'
|
__author__ = 'taizan-hokuto'
|
||||||
__author_email__ = '55448286+taizan-hokuto@users.noreply.github.com'
|
__author_email__ = '55448286+taizan-hokuto@users.noreply.github.com'
|
||||||
|
|||||||
@@ -63,6 +63,9 @@ class LiveChatAsync:
|
|||||||
force_replay : bool
|
force_replay : bool
|
||||||
Trueの場合、ライブチャットが取得できる場合であっても
|
Trueの場合、ライブチャットが取得できる場合であっても
|
||||||
強制的にアーカイブ済みチャットを取得する。
|
強制的にアーカイブ済みチャットを取得する。
|
||||||
|
|
||||||
|
topchat_only : bool
|
||||||
|
Trueの場合、上位チャットのみ取得する。
|
||||||
|
|
||||||
Attributes
|
Attributes
|
||||||
---------
|
---------
|
||||||
@@ -81,7 +84,8 @@ class LiveChatAsync:
|
|||||||
done_callback = None,
|
done_callback = None,
|
||||||
exception_handler = None,
|
exception_handler = None,
|
||||||
direct_mode = False,
|
direct_mode = False,
|
||||||
force_replay = False
|
force_replay = False,
|
||||||
|
topchat_only = False
|
||||||
):
|
):
|
||||||
self.video_id = video_id
|
self.video_id = video_id
|
||||||
self.seektime = seektime
|
self.seektime = seektime
|
||||||
@@ -102,6 +106,7 @@ class LiveChatAsync:
|
|||||||
self._setup()
|
self._setup()
|
||||||
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
|
||||||
if not LiveChatAsync._setup_finished:
|
if not LiveChatAsync._setup_finished:
|
||||||
LiveChatAsync._setup_finished = True
|
LiveChatAsync._setup_finished = True
|
||||||
if exception_handler == None:
|
if exception_handler == None:
|
||||||
@@ -200,7 +205,8 @@ class LiveChatAsync:
|
|||||||
'''
|
'''
|
||||||
self._pauser.put_nowait(None)
|
self._pauser.put_nowait(None)
|
||||||
if not self._is_replay:
|
if not self._is_replay:
|
||||||
continuation = liveparam.getparam(self.video_id,3)
|
continuation = liveparam.getparam(
|
||||||
|
self.video_id, 3, self._topchat_only)
|
||||||
return continuation
|
return continuation
|
||||||
|
|
||||||
async def _get_contents(self, continuation, session, headers):
|
async def _get_contents(self, continuation, session, headers):
|
||||||
@@ -220,9 +226,9 @@ class LiveChatAsync:
|
|||||||
if contents is None or self._is_replay:
|
if contents is None or self._is_replay:
|
||||||
'''Try to fetch archive chat data.'''
|
'''Try to fetch archive chat data.'''
|
||||||
self._parser.is_replay = True
|
self._parser.is_replay = True
|
||||||
self._fetch_url = ("live_chat_replay/"
|
self._fetch_url = "live_chat_replay/get_live_chat_replay?continuation="
|
||||||
"get_live_chat_replay?continuation=")
|
continuation = arcparam.getparam(
|
||||||
continuation = arcparam.getparam(self.video_id, self.seektime)
|
self.video_id, self.seektime, self._topchat_only)
|
||||||
livechat_json = (await self._get_livechat_json(
|
livechat_json = (await self._get_livechat_json(
|
||||||
continuation, session, headers))
|
continuation, session, headers))
|
||||||
contents = self._parser.get_contents(livechat_json)
|
contents = self._parser.get_contents(livechat_json)
|
||||||
|
|||||||
@@ -60,6 +60,9 @@ class LiveChat:
|
|||||||
Trueの場合、ライブチャットが取得できる場合であっても
|
Trueの場合、ライブチャットが取得できる場合であっても
|
||||||
強制的にアーカイブ済みチャットを取得する。
|
強制的にアーカイブ済みチャットを取得する。
|
||||||
|
|
||||||
|
topchat_only : bool
|
||||||
|
Trueの場合、上位チャットのみ取得する。
|
||||||
|
|
||||||
Attributes
|
Attributes
|
||||||
---------
|
---------
|
||||||
_executor : ThreadPoolExecutor
|
_executor : ThreadPoolExecutor
|
||||||
@@ -80,7 +83,8 @@ class LiveChat:
|
|||||||
callback = None,
|
callback = None,
|
||||||
done_callback = None,
|
done_callback = None,
|
||||||
direct_mode = False,
|
direct_mode = False,
|
||||||
force_replay = False
|
force_replay = False,
|
||||||
|
topchat_only = False
|
||||||
):
|
):
|
||||||
self.video_id = video_id
|
self.video_id = video_id
|
||||||
self.seektime = seektime
|
self.seektime = seektime
|
||||||
@@ -101,7 +105,7 @@ class LiveChat:
|
|||||||
self._setup()
|
self._setup()
|
||||||
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
|
||||||
if not LiveChat._setup_finished:
|
if not LiveChat._setup_finished:
|
||||||
LiveChat._setup_finished = True
|
LiveChat._setup_finished = True
|
||||||
if interruptable:
|
if interruptable:
|
||||||
@@ -214,8 +218,7 @@ class LiveChat:
|
|||||||
if contents is None or self._is_replay:
|
if contents is None or self._is_replay:
|
||||||
'''Try to fetch archive chat data.'''
|
'''Try to fetch archive chat data.'''
|
||||||
self._parser.is_replay = True
|
self._parser.is_replay = True
|
||||||
self._fetch_url = ("live_chat_replay/"
|
self._fetch_url = "live_chat_replay/get_live_chat_replay?continuation="
|
||||||
"get_live_chat_replay?continuation=")
|
|
||||||
continuation = arcparam.getparam(self.video_id, self.seektime)
|
continuation = arcparam.getparam(self.video_id, self.seektime)
|
||||||
livechat_json = ( self._get_livechat_json(
|
livechat_json = ( self._get_livechat_json(
|
||||||
continuation, session, headers))
|
continuation, session, headers))
|
||||||
@@ -230,8 +233,7 @@ class LiveChat:
|
|||||||
continuation = urllib.parse.quote(continuation)
|
continuation = urllib.parse.quote(continuation)
|
||||||
livechat_json = None
|
livechat_json = None
|
||||||
status_code = 0
|
status_code = 0
|
||||||
url =(
|
url =f"https://www.youtube.com/{self._fetch_url}{continuation}&pbj=1"
|
||||||
f"https://www.youtube.com/{self._fetch_url}{continuation}&pbj=1")
|
|
||||||
for _ in range(MAX_RETRY + 1):
|
for _ in range(MAX_RETRY + 1):
|
||||||
with session.get(url ,headers = headers) as resp:
|
with session.get(url ,headers = headers) as resp:
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -1,31 +0,0 @@
|
|||||||
import logging
|
|
||||||
import datetime
|
|
||||||
|
|
||||||
|
|
||||||
def get_logger(modname,mode=logging.DEBUG):
|
|
||||||
logger = logging.getLogger(modname)
|
|
||||||
if mode == None:
|
|
||||||
logger.addHandler(logging.NullHandler())
|
|
||||||
return logger
|
|
||||||
logger.setLevel(mode)
|
|
||||||
#create handler1 for showing info
|
|
||||||
handler1 = logging.StreamHandler()
|
|
||||||
my_formatter = MyFormatter()
|
|
||||||
handler1.setFormatter(my_formatter)
|
|
||||||
|
|
||||||
handler1.setLevel(mode)
|
|
||||||
logger.addHandler(handler1)
|
|
||||||
#create handler2 for recording log file
|
|
||||||
if mode <= logging.DEBUG:
|
|
||||||
handler2 = logging.FileHandler(filename="log.txt", encoding='utf-8')
|
|
||||||
handler2.setLevel(logging.ERROR)
|
|
||||||
handler2.setFormatter(my_formatter)
|
|
||||||
|
|
||||||
|
|
||||||
logger.addHandler(handler2)
|
|
||||||
return logger
|
|
||||||
|
|
||||||
class MyFormatter(logging.Formatter):
|
|
||||||
def format(self, record):
|
|
||||||
s =(datetime.datetime.fromtimestamp(record.created)).strftime("%m-%d %H:%M:%S")+'| '+ (record.module).ljust(15)+(' { '+record.funcName).ljust(20) +":"+str(record.lineno).rjust(4)+'} - '+record.getMessage()
|
|
||||||
return s
|
|
||||||
@@ -52,8 +52,8 @@ def _nval(val):
|
|||||||
buf += val.to_bytes(1,'big')
|
buf += val.to_bytes(1,'big')
|
||||||
return buf
|
return buf
|
||||||
|
|
||||||
def _build(video_id, seektime, topchatonly = False):
|
def _build(video_id, seektime, topchat_only):
|
||||||
switch_01 = b'\x04' if topchatonly else b'\x01'
|
switch_01 = b'\x04' if topchat_only else b'\x01'
|
||||||
if seektime < 0:
|
if seektime < 0:
|
||||||
times =_nval(0)
|
times =_nval(0)
|
||||||
switch = b'\x04'
|
switch = b'\x04'
|
||||||
@@ -102,12 +102,14 @@ def _build(video_id, seektime, topchatonly = False):
|
|||||||
).decode()
|
).decode()
|
||||||
)
|
)
|
||||||
|
|
||||||
def getparam(video_id, seektime = 0):
|
def getparam(video_id, seektime = 0, topchat_only = False):
|
||||||
'''
|
'''
|
||||||
Parameter
|
Parameter
|
||||||
---------
|
---------
|
||||||
seektime : int
|
seektime : int
|
||||||
unit:seconds
|
unit:seconds
|
||||||
start position of fetching chat data.
|
start position of fetching chat data.
|
||||||
|
topchat_only : bool
|
||||||
|
if True, fetch only 'top chat'
|
||||||
'''
|
'''
|
||||||
return _build(video_id, seektime)
|
return _build(video_id, seektime, topchat_only)
|
||||||
|
|||||||
@@ -66,9 +66,9 @@ def _nval(val):
|
|||||||
buf += val.to_bytes(1,'big')
|
buf += val.to_bytes(1,'big')
|
||||||
return buf
|
return buf
|
||||||
|
|
||||||
def _build(video_id, _ts1, _ts2, _ts3, _ts4, _ts5, topchatonly = False):
|
def _build(video_id, _ts1, _ts2, _ts3, _ts4, _ts5, topchat_only):
|
||||||
#_short_type2
|
#_short_type2
|
||||||
switch_01 = b'\x04' if topchatonly else b'\x01'
|
switch_01 = b'\x04' if topchat_only else b'\x01'
|
||||||
parity = _tzparity(video_id, _ts1^_ts2^_ts3^_ts4^_ts5)
|
parity = _tzparity(video_id, _ts1^_ts2^_ts3^_ts4^_ts5)
|
||||||
|
|
||||||
header_magic= b'\xD2\x87\xCC\xC8\x03'
|
header_magic= b'\xD2\x87\xCC\xC8\x03'
|
||||||
@@ -155,12 +155,14 @@ def _times(past_sec):
|
|||||||
return list(map(lambda x:int(x*1000000),[_ts1,_ts2,_ts3,_ts4,_ts5]))
|
return list(map(lambda x:int(x*1000000),[_ts1,_ts2,_ts3,_ts4,_ts5]))
|
||||||
|
|
||||||
|
|
||||||
def getparam(video_id,past_sec = 0):
|
def getparam(video_id, past_sec = 0, topchat_only = False):
|
||||||
'''
|
'''
|
||||||
Parameter
|
Parameter
|
||||||
---------
|
---------
|
||||||
past_sec : int
|
past_sec : int
|
||||||
seconds to load past chat data
|
seconds to load past chat data
|
||||||
|
topchat_only : bool
|
||||||
|
if True, fetch only 'top chat'
|
||||||
'''
|
'''
|
||||||
return _build(video_id,*_times(past_sec))
|
return _build(video_id,*_times(past_sec),topchat_only)
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,8 @@ class Parser:
|
|||||||
if jsn is None:
|
if jsn is None:
|
||||||
raise ChatParseException('Called with none JSON object.')
|
raise ChatParseException('Called with none JSON object.')
|
||||||
if jsn['response']['responseContext'].get('errors'):
|
if jsn['response']['responseContext'].get('errors'):
|
||||||
raise ResponseContextError('The video_id would be wrong, or video is deleted or private.')
|
raise ResponseContextError('The video_id would be wrong,'
|
||||||
|
'or video is deleted or private.')
|
||||||
contents=jsn['response'].get('continuationContents')
|
contents=jsn['response'].get('continuationContents')
|
||||||
return contents
|
return contents
|
||||||
|
|
||||||
@@ -77,7 +78,8 @@ class Parser:
|
|||||||
metadata.setdefault("timeoutMs",interval)
|
metadata.setdefault("timeoutMs",interval)
|
||||||
"""Archived chat has different structures than live chat,
|
"""Archived chat has different structures than live chat,
|
||||||
so make it the same format."""
|
so make it the same format."""
|
||||||
chatdata = [action["replayChatItemAction"]["actions"][0] for action in actions]
|
chatdata = [action["replayChatItemAction"]["actions"][0]
|
||||||
|
for action in actions]
|
||||||
else:
|
else:
|
||||||
metadata.setdefault('timeoutMs', 10000)
|
metadata.setdefault('timeoutMs', 10000)
|
||||||
chatdata = actions
|
chatdata = actions
|
||||||
|
|||||||
@@ -33,5 +33,6 @@ symbols = {
|
|||||||
"ARS\xa0": {"fxtext": "ARS", "jptext": "アルゼンチン・ペソ"},
|
"ARS\xa0": {"fxtext": "ARS", "jptext": "アルゼンチン・ペソ"},
|
||||||
"CLP\xa0": {"fxtext": "CLP", "jptext": "チリ・ペソ"},
|
"CLP\xa0": {"fxtext": "CLP", "jptext": "チリ・ペソ"},
|
||||||
"NOK\xa0": {"fxtext": "NOK", "jptext": "ノルウェー・クローネ"},
|
"NOK\xa0": {"fxtext": "NOK", "jptext": "ノルウェー・クローネ"},
|
||||||
"BAM\xa0": {"fxtext": "BAM", "jptext": "ボスニア・兌換マルカ"}
|
"BAM\xa0": {"fxtext": "BAM", "jptext": "ボスニア・兌換マルカ"},
|
||||||
|
"SGD\xa0": {"fxtext": "SGD", "jptext": "シンガポール・ドル"}
|
||||||
}
|
}
|
||||||
@@ -33,5 +33,6 @@ symbols = {
|
|||||||
"ARS\xa0": {"fxtext": "ARS", "jptext": "アルゼンチン・ペソ"},
|
"ARS\xa0": {"fxtext": "ARS", "jptext": "アルゼンチン・ペソ"},
|
||||||
"CLP\xa0": {"fxtext": "CLP", "jptext": "チリ・ペソ"},
|
"CLP\xa0": {"fxtext": "CLP", "jptext": "チリ・ペソ"},
|
||||||
"NOK\xa0": {"fxtext": "NOK", "jptext": "ノルウェー・クローネ"},
|
"NOK\xa0": {"fxtext": "NOK", "jptext": "ノルウェー・クローネ"},
|
||||||
"BAM\xa0": {"fxtext": "BAM", "jptext": "ボスニア・兌換マルカ"}
|
"BAM\xa0": {"fxtext": "BAM", "jptext": "ボスニア・兌換マルカ"},
|
||||||
|
"SGD\xa0": {"fxtext": "SGD", "jptext": "シンガポール・ドル"}
|
||||||
}
|
}
|
||||||
22
setup.py
22
setup.py
@@ -1,6 +1,6 @@
|
|||||||
from setuptools import setup, find_packages, Command
|
from setuptools import setup, find_packages, Command
|
||||||
#from codecs import open as open_c
|
#from codecs import open as open_c
|
||||||
from os import path, system, remove, rename
|
from os import path, system, remove, rename, removedirs
|
||||||
import re
|
import re
|
||||||
|
|
||||||
package_name = "pytchat"
|
package_name = "pytchat"
|
||||||
@@ -13,6 +13,15 @@ def _requirements():
|
|||||||
def _test_requirements():
|
def _test_requirements():
|
||||||
return [name.rstrip() for name in open(path.join(root_dir, 'requirements_test.txt')).readlines()]
|
return [name.rstrip() for name in open(path.join(root_dir, 'requirements_test.txt')).readlines()]
|
||||||
|
|
||||||
|
txt= ''
|
||||||
|
with open('README.MD', 'r', encoding='utf-8') as f:
|
||||||
|
txt = f.read()
|
||||||
|
|
||||||
|
with open('README1.MD', 'w', encoding='utf-8', newline='\n') as f:
|
||||||
|
f.write(txt)
|
||||||
|
|
||||||
|
remove("README.MD")
|
||||||
|
rename("README1.MD","README.MD")
|
||||||
|
|
||||||
with open(path.join(root_dir, package_name, '__init__.py')) as f:
|
with open(path.join(root_dir, package_name, '__init__.py')) as f:
|
||||||
init_text = f.read()
|
init_text = f.read()
|
||||||
@@ -30,14 +39,7 @@ assert url
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
with open('README.MD', 'r', encoding='utf-8') as f:
|
|
||||||
txt = f.read()
|
|
||||||
|
|
||||||
with open('README1.MD', 'w', encoding='utf-8', newline='\n') as f:
|
|
||||||
f.write(txt)
|
|
||||||
|
|
||||||
remove("README.MD")
|
|
||||||
rename("README1.MD","README.MD")
|
|
||||||
with open('README.md', encoding='utf-8') as f:
|
with open('README.md', encoding='utf-8') as f:
|
||||||
long_description = f.read()
|
long_description = f.read()
|
||||||
|
|
||||||
@@ -45,7 +47,7 @@ with open('README.md', encoding='utf-8') as f:
|
|||||||
|
|
||||||
setup(
|
setup(
|
||||||
name=package_name,
|
name=package_name,
|
||||||
packages=find_packages(),
|
packages=find_packages(exclude=['*log.txt','*tests']),
|
||||||
version=version,
|
version=version,
|
||||||
url=url,
|
url=url,
|
||||||
author=author,
|
author=author,
|
||||||
@@ -54,7 +56,7 @@ setup(
|
|||||||
long_description_content_type='text/markdown',
|
long_description_content_type='text/markdown',
|
||||||
license=license,
|
license=license,
|
||||||
install_requires=_requirements(),
|
install_requires=_requirements(),
|
||||||
tests_require=_test_requirements(),
|
#tests_require=_test_requirements(),
|
||||||
description="a python library for fetching youtube live chat.",
|
description="a python library for fetching youtube live chat.",
|
||||||
classifiers=[
|
classifiers=[
|
||||||
'Natural Language :: Japanese',
|
'Natural Language :: Japanese',
|
||||||
|
|||||||
@@ -124,6 +124,21 @@ def test_superchat(mocker):
|
|||||||
assert "LCC." in ret["items"][0]["id"]
|
assert "LCC." in ret["items"][0]["id"]
|
||||||
assert ret["items"][0]["snippet"]["type"]=="superChatEvent"
|
assert ret["items"][0]["snippet"]["type"]=="superChatEvent"
|
||||||
|
|
||||||
|
def test_unregistered_currency(mocker):
|
||||||
|
processor = CompatibleProcessor()
|
||||||
|
|
||||||
|
_json = _open_file("tests/testdata/unregistered_currency.json")
|
||||||
|
|
||||||
|
_, chatdata = parser.parse(parser.get_contents(json.loads(_json)))
|
||||||
|
|
||||||
|
data = {
|
||||||
|
"video_id" : "",
|
||||||
|
"timeout" : 7,
|
||||||
|
"chatdata" : chatdata
|
||||||
|
}
|
||||||
|
ret = processor.process([data])
|
||||||
|
assert ret["items"][0]["snippet"]["superChatDetails"]["currency"] == "[UNREGISTERD]"
|
||||||
|
|
||||||
|
|
||||||
def _open_file(path):
|
def _open_file(path):
|
||||||
with open(path,mode ='r',encoding = 'utf-8') as f:
|
with open(path,mode ='r',encoding = 'utf-8') as f:
|
||||||
|
|||||||
@@ -4,6 +4,6 @@ from pytchat.paramgen import liveparam
|
|||||||
def test_liveparam_0(mocker):
|
def test_liveparam_0(mocker):
|
||||||
_ts1= 1546268400
|
_ts1= 1546268400
|
||||||
param = liveparam._build("01234567890",
|
param = liveparam._build("01234567890",
|
||||||
*([_ts1*1000000 for i in range(5)]))
|
*([_ts1*1000000 for i in range(5)]), topchat_only=False)
|
||||||
test_param="0ofMyAPiARp8Q2c4S0RRb0xNREV5TXpRMU5qYzRPVEFhUTZxNXdiMEJQUW83YUhSMGNITTZMeTkzZDNjdWVXOTFkSFZpWlM1amIyMHZiR2wyWlY5amFHRjBQM1k5TURFeU16UTFOamM0T1RBbWFYTmZjRzl3YjNWMFBURWdBZyUzRCUzRCiAuNbVqsrfAjAAOABAAkorCAEQABgAIAAqDnN0YXRpY2NoZWNrc3VtOgBAAEoCCAFQgLjW1arK3wJYA1CAuNbVqsrfAliAuNbVqsrfAmgBggEECAEQAIgBAKABgLjW1arK3wI%3D"
|
test_param="0ofMyAPiARp8Q2c4S0RRb0xNREV5TXpRMU5qYzRPVEFhUTZxNXdiMEJQUW83YUhSMGNITTZMeTkzZDNjdWVXOTFkSFZpWlM1amIyMHZiR2wyWlY5amFHRjBQM1k5TURFeU16UTFOamM0T1RBbWFYTmZjRzl3YjNWMFBURWdBZyUzRCUzRCiAuNbVqsrfAjAAOABAAkorCAEQABgAIAAqDnN0YXRpY2NoZWNrc3VtOgBAAEoCCAFQgLjW1arK3wJYA1CAuNbVqsrfAliAuNbVqsrfAmgBggEECAEQAIgBAKABgLjW1arK3wI%3D"
|
||||||
assert test_param == param
|
assert test_param == param
|
||||||
160
tests/testdata/unregistered_currency.json
vendored
Normal file
160
tests/testdata/unregistered_currency.json
vendored
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
{
|
||||||
|
"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": {
|
||||||
|
"liveChatPaidMessageRenderer": {
|
||||||
|
"id": "dummy_id",
|
||||||
|
"timestampUsec": "1576850000000000",
|
||||||
|
"authorName": {
|
||||||
|
"simpleText": "author_name"
|
||||||
|
},
|
||||||
|
"authorPhoto": {
|
||||||
|
"thumbnails": [
|
||||||
|
{
|
||||||
|
"url": "https://yt3.ggpht.com/------------/AAAAAAAAAAA/AAAAAAAAAAA/xxxxxxxxxxxx/s32-x-x-xx-xx-xx-c0xffffff/photo.jpg",
|
||||||
|
"width": 32,
|
||||||
|
"height": 32
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://yt3.ggpht.com/------------/AAAAAAAAAAA/AAAAAAAAAAA/xxxxxxxxxxxx/s32-x-x-xx-xx-xx-c0xffffff/photo.jpg",
|
||||||
|
"width": 64,
|
||||||
|
"height": 64
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"purchaseAmountText": {
|
||||||
|
"simpleText": "[UNREGISTERD]10,800"
|
||||||
|
},
|
||||||
|
"message": {
|
||||||
|
"runs": [
|
||||||
|
{
|
||||||
|
"text": "This is unregistered currency."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"headerBackgroundColor": 4291821568,
|
||||||
|
"headerTextColor": 4294967295,
|
||||||
|
"bodyBackgroundColor": 4293271831,
|
||||||
|
"bodyTextColor": 4294967295,
|
||||||
|
"authorExternalChannelId": "http://www.youtube.com/channel/author_channel_url",
|
||||||
|
"authorNameTextColor": 3019898879,
|
||||||
|
"contextMenuEndpoint": {
|
||||||
|
"commandMetadata": {
|
||||||
|
"webCommandMetadata": {
|
||||||
|
"ignoreNavigation": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"liveChatItemContextMenuEndpoint": {
|
||||||
|
"params": "___params___"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"timestampColor": 2164260863,
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user