Compare commits

...

31 Commits

Author SHA1 Message Date
taizan-hokuto
9de75788f2 Merge branch 'hotfix/cannot_fetch_at_0_multithread' 2020-02-01 01:41:40 +09:00
taizan-hokuto
76f0c0e658 Increment version 2020-02-01 01:36:35 +09:00
taizan-hokuto
0d8ecb778f Fix for #1 : core_multithread 2020-02-01 01:25:42 +09:00
taizan-hokuto
bbf7a2906a Merge branch 'hotfix/cannot_fetch_at_0' 2020-02-01 00:01:04 +09:00
taizan-hokuto
1862b83eac Increment version 2020-01-31 23:58:07 +09:00
taizan-hokuto
053ff5291f Modify url for japanese wiki 2020-01-31 23:54:03 +09:00
taizan-hokuto
4e47d4a262 Fix for #1 2020-01-31 23:38:51 +09:00
taizan-hokuto
956c7e2640 Update README.md 2020-01-18 17:50:31 +09:00
taizan-hokuto
f7d1830226 Merge branch 'release/v0.0.5.1.3' 2020-01-18 14:45:33 +09:00
taizan-hokuto
76b126faf2 Increment version 2020-01-18 14:43:06 +09:00
taizan-hokuto
bbd01d6523 Increment version 2020-01-18 13:12:52 +09:00
taizan-hokuto
f8fa0e394e Delete json_display_processor 2020-01-18 13:02:43 +09:00
taizan-hokuto
efdf07e3de Make it possible to set custom logger 2020-01-18 12:38:36 +09:00
taizan-hokuto
2573cc18de Fix setting exception_handler 2020-01-18 09:22:16 +09:00
taizan-hokuto
1c5852421b Undo _set_exception_handler 2020-01-18 09:18:20 +09:00
taizan-hokuto
970d111e1b Alert default processor attribute error
: delete default exception handler

Alert default processor attribute error
: delete default exception handler

Delete unnecessary lines

Delete unnecessary lines
2020-01-18 08:39:36 +09:00
taizan-hokuto
1643dd1ad1 Switch author type by icon type 2020-01-13 18:32:36 +09:00
taizan-hokuto
0272319fa6 Update README 2020-01-13 13:09:51 +09:00
taizan-hokuto
fb0edef136 Merge branch 'release/v0.0.5.0' 2020-01-13 09:38:10 +09:00
taizan-hokuto
260a2b35a9 Merge tag 'v0.0.5.0' into develop
v0.0.5.0
2020-01-13 09:38:10 +09:00
taizan-hokuto
e03d39475e Increment version 2020-01-13 09:37:34 +09:00
taizan-hokuto
2462b8aca0 Merge branch 'hotfix/fix_defaultprocessor_display_amount' 2020-01-13 08:19:42 +09:00
taizan-hokuto
a1024c8734 Merge tag 'fix_defaultprocessor_display_amount' into develop 2020-01-13 08:19:42 +09:00
taizan-hokuto
6b3ca00d35 Fix superchat/sticker display amount 2020-01-13 08:19:03 +09:00
taizan-hokuto
385634b709 Fix MANIFEST.in 2020-01-12 14:55:29 +09:00
taizan-hokuto
c1a78a2743 Fix setup.py 2020-01-12 14:40:26 +09:00
taizan-hokuto
7961801e0c Increment version 2020-01-12 14:38:21 +09:00
taizan-hokuto
5fe4e7af04 Fix description 2020-01-12 14:26:15 +09:00
taizan-hokuto
892dfb8a91 Fix setup.py 2020-01-11 14:23:32 +09:00
taizan-hokuto
fddab22a1f Delete unnecessary file 2020-01-11 13:29:34 +09:00
taizan-hokuto
7194948066 Modify setup.py 2020-01-11 13:23:55 +09:00
15 changed files with 153 additions and 172 deletions

View File

@@ -1,6 +1,5 @@
include requirements.txt
include requirements_test.txt
prune testrun*.py
prune log.txt
prune quote.txt
prune .gitignore
include README.MD
global-exclude tests/*
global-exclude pytchat/testrun*.py

View File

@@ -7,13 +7,16 @@ pytchat is a python library for fetching youtube live chat.
pytchat is a python library for fetching youtube live chat
without using youtube api, Selenium or BeautifulSoup.
pytchatはAPIを使わずにYouTubeチャットを取得するための軽量pythonライブラリです。
Other features:
+ 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.
For more detailed information, see [wiki](https://github.com/taizan-hokuto/pytchat/wiki).
For more detailed information, see [wiki](https://github.com/taizan-hokuto/pytchat/wiki). <br>
より詳細な解説は[wiki](https://github.com/taizan-hokuto/pytchat/wiki/Home_jp)を参照してください。
## Install
```python
@@ -26,13 +29,17 @@ pip install pytchat
### on-demand mode
```python
from pytchat import LiveChat
livechat = LiveChat(video_id = "Zvp1pJpie4I")
chat = LiveChat("rsHWP7IjMiw")
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()
while livechat.is_alive():
try:
chatdata = livechat.get()
for c in chatdata.items:
print(f"{c.datetime} [{c.author.name}]- {c.message}")
chatdata.tick()
except KeyboardInterrupt:
livechat.terminate()
break
```
### callback mode
@@ -40,17 +47,21 @@ while chat.is_alive():
from pytchat import LiveChat
import time
#callback function is automatically called.
def display(data):
for c in data.items:
print(f"{c.datetime} [{c.author.name}]-{c.message} {c.amountString}")
data.tick()
def main():
livechat = LiveChat(video_id = "Zvp1pJpie4I", callback = disp)
while livechat.is_alive():
#other background operation.
time.sleep(1)
livechat.terminate()
#callback function (automatically called)
def disp(chatdata):
for c in chatdata.items:
print(f"{c.datetime} [{c.author.name}]- {c.message}")
chatdata.tick()
if __name__ == '__main__':
chat = LiveChat("rsHWP7IjMiw", callback = display)
while chat.is_alive():
#other background operation.
time.sleep(3)
main()
```
@@ -61,16 +72,16 @@ from concurrent.futures import CancelledError
import asyncio
async def main():
chat = LiveChatAsync("rsHWP7IjMiw", callback = func)
while chat.is_alive():
livechat = LiveChatAsync("Zvp1pJpie4I", callback = func)
while livechat.is_alive():
#other background operation.
await asyncio.sleep(3)
#callback function is automatically called.
async def func(data):
for c in data.items:
async def func(chatdata):
for c in chatdata.items:
print(f"{c.datetime} [{c.author.name}]-{c.message} {c.amountString}")
await data.tick_async()
await chatdata.tick_async()
if __name__ == '__main__':
try:
@@ -86,18 +97,20 @@ if __name__ == '__main__':
from pytchat import LiveChat, CompatibleProcessor
import time
chat = LiveChat("rsHWP7IjMiw",
chat = LiveChat("Zvp1pJpie4I",
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']))
try:
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']))
except KeyboardInterrupt:
chat.terminate()
```
### replay:
If specified video is not live,
@@ -108,19 +121,23 @@ from pytchat import LiveChat
def main():
#seektime (seconds): start position of chat.
chat = ReplayChat("ojes5ULOqhc", seektime = 60*30)
while chat.is_alive():
data = chat.get()
for c in data.items:
print(f"{c.elapsedTime} [{c.author.name}]-{c.message} {c.amountString}")
data.tick()
chat = LiveChat("ojes5ULOqhc", seektime = 60*30)
print('Replay from 30:00')
try:
while chat.is_alive():
data = chat.get()
for c in data.items:
print(f"{c.elapsedTime} [{c.author.name}]-{c.message} {c.amountString}")
data.tick()
except KeyboardInterrupt:
chat.terminate()
if __name__ == '__main__':
main()
```
## Structure of Default Processor
Each item can be got with items() function.
Each item can be got with `items` function.
<table>
<tr>
<th>name</th>
@@ -250,4 +267,4 @@ Structure of author object.
[taizan-hokuto](https://github.com/taizan-hokuto)
[twitter:@taizan205](https://twitter.com/taizan205)
[twitter:@taizan205](https://twitter.com/taizan205)

View File

@@ -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.4.6'
__version__ = '0.0.5.3'
__license__ = 'MIT'
__author__ = 'taizan-hokuto'
__author_email__ = '55448286+taizan-hokuto@users.noreply.github.com'

View File

@@ -1,13 +1,11 @@
import logging
from . import mylogger
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'}
def logger(module_name: str):
module_logger = mylogger.get_logger(module_name, mode = LOGGER_MODE)
def logger(module_name: str, loglevel = None):
module_logger = mylogger.get_logger(module_name, loglevel = loglevel)
return module_logger

View File

@@ -3,21 +3,21 @@ import logging
import datetime
def get_logger(modname,mode=logging.DEBUG):
def get_logger(modname,loglevel=logging.DEBUG):
logger = getLogger(modname)
if mode == None:
if loglevel == None:
logger.addHandler(NullHandler())
return logger
logger.setLevel(mode)
logger.setLevel(loglevel)
#create handler1 for showing info
handler1 = StreamHandler()
my_formatter = MyFormatter()
handler1.setFormatter(my_formatter)
handler1.setLevel(mode)
handler1.setLevel(loglevel)
logger.addHandler(handler1)
#create handler2 for recording log file
if mode <= logging.DEBUG:
if loglevel <= logging.DEBUG:
handler2 = FileHandler(filename="log.txt", encoding='utf-8')
handler2.setLevel(logging.ERROR)
handler2.setFormatter(my_formatter)

View File

@@ -17,7 +17,6 @@ from ..paramgen import liveparam, arcparam
from ..processors.default.processor import DefaultProcessor
from ..processors.combinator import Combinator
logger = config.logger(__name__)
headers = config.headers
MAX_RETRY = 10
@@ -85,7 +84,8 @@ class LiveChatAsync:
exception_handler = None,
direct_mode = False,
force_replay = False,
topchat_only = False
topchat_only = False,
logger = config.logger(__name__),
):
self.video_id = video_id
self.seektime = seektime
@@ -107,11 +107,12 @@ class LiveChatAsync:
self._first_fetch = True
self._fetch_url = "live_chat/get_live_chat?continuation="
self._topchat_only = topchat_only
self._logger = logger
LiveChatAsync._logger = logger
if not LiveChatAsync._setup_finished:
LiveChatAsync._setup_finished = True
if exception_handler == None:
self._set_exception_handler(self._handle_exception)
else:
if exception_handler:
self._set_exception_handler(exception_handler)
if interruptable:
signal.signal(signal.SIGINT,
@@ -187,14 +188,14 @@ class LiveChatAsync:
continuation = metadata.get('continuation')
except ChatParseException as e:
#self.terminate()
logger.debug(f"[{self.video_id}]{str(e)}")
self._logger.debug(f"[{self.video_id}]{str(e)}")
return
except (TypeError , json.JSONDecodeError) :
#self.terminate()
logger.error(f"{traceback.format_exc(limit = -1)}")
self._logger.error(f"{traceback.format_exc(limit = -1)}")
return
logger.debug(f"[{self.video_id}]finished fetching chat.")
self._logger.debug(f"[{self.video_id}]finished fetching chat.")
async def _check_pause(self, continuation):
if self._pauser.empty():
@@ -226,12 +227,16 @@ class LiveChatAsync:
if contents is None or self._is_replay:
'''Try to fetch archive chat data.'''
self._parser.is_replay = True
self._fetch_url = ("live_chat_replay/"
"get_live_chat_replay?continuation=")
self._fetch_url = "live_chat_replay/get_live_chat_replay?continuation="
continuation = arcparam.getparam(
self.video_id, self.seektime, self._topchat_only)
livechat_json = (await self._get_livechat_json(
continuation, session, headers))
reload_continuation = self._parser.reload_continuation(
self._parser.get_contents(livechat_json))
if reload_continuation:
livechat_json = (await self._get_livechat_json(
reload_continuation, session, headers))
contents = self._parser.get_contents(livechat_json)
self._first_fetch = False
return contents
@@ -243,8 +248,7 @@ class LiveChatAsync:
continuation = urllib.parse.quote(continuation)
livechat_json = None
status_code = 0
url =(
f"https://www.youtube.com/{self._fetch_url}{continuation}&pbj=1")
url =f"https://www.youtube.com/{self._fetch_url}{continuation}&pbj=1"
for _ in range(MAX_RETRY + 1):
async with session.get(url ,headers = headers) as resp:
try:
@@ -255,7 +259,7 @@ class LiveChatAsync:
await asyncio.sleep(1)
continue
else:
logger.error(f"[{self.video_id}]"
self._logger.error(f"[{self.video_id}]"
f"Exceeded retry count. status_code={status_code}")
return None
return livechat_json
@@ -310,7 +314,7 @@ class LiveChatAsync:
try:
self.terminate()
except CancelledError:
logger.debug(f'[{self.video_id}]cancelled:{sender}')
self._logger.debug(f'[{self.video_id}]cancelled:{sender}')
def terminate(self):
'''
@@ -320,28 +324,21 @@ class LiveChatAsync:
if self._direct_mode == False:
#bufferにダミーオブジェクトを入れてis_alive()を判定させる
self._buffer.put_nowait({'chatdata':'','timeout':0})
logger.info(f'[{self.video_id}]finished.')
self._logger.info(f'[{self.video_id}]finished.')
@classmethod
def _set_exception_handler(cls, handler):
loop = asyncio.get_event_loop()
loop.set_exception_handler(handler)
@classmethod
def _handle_exception(cls, loop, context):
if not isinstance(context["exception"],CancelledError):
logger.error(f"Caught exception: {context}")
loop= asyncio.get_event_loop()
loop.create_task(cls.shutdown(None,None,None))
@classmethod
async def shutdown(cls, event, sig = None, handler=None):
logger.debug("shutdown...")
cls._logger.debug("shutdown...")
tasks = [t for t in asyncio.all_tasks() if t is not
asyncio.current_task()]
[task.cancel() for task in tasks]
logger.debug(f"complete remaining tasks...")
cls._logger.debug(f"complete remaining tasks...")
await asyncio.gather(*tasks,return_exceptions=True)
loop = asyncio.get_event_loop()
loop.stop()

View File

@@ -16,7 +16,6 @@ from ..paramgen import liveparam, arcparam
from ..processors.default.processor import DefaultProcessor
from ..processors.combinator import Combinator
logger = config.logger(__name__)
headers = config.headers
MAX_RETRY = 10
@@ -74,7 +73,8 @@ class LiveChat:
_setup_finished = False
#チャット監視中のListenerのリスト
_listeners= []
_listeners = []
def __init__(self, video_id,
seektime = 0,
processor = DefaultProcessor(),
@@ -84,7 +84,8 @@ class LiveChat:
done_callback = None,
direct_mode = False,
force_replay = False,
topchat_only = False
topchat_only = False,
logger = config.logger(__name__)
):
self.video_id = video_id
self.seektime = seektime
@@ -104,8 +105,10 @@ class LiveChat:
self._pauser.put_nowait(None)
self._setup()
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._logger = logger
LiveChat._logger = logger
if not LiveChat._setup_finished:
LiveChat._setup_finished = True
if interruptable:
@@ -115,7 +118,6 @@ class LiveChat:
LiveChat._listeners.append(self)
def _setup(self):
#logger.debug("setup")
#direct modeがTrueでcallback未設定の場合例外発生。
if self._direct_mode:
if self._callback is None:
@@ -181,13 +183,13 @@ class LiveChat:
time.sleep(diff_time if diff_time > 0 else 0)
continuation = metadata.get('continuation')
except ChatParseException as e:
logger.debug(f"[{self.video_id}]{str(e)}")
self._logger.debug(f"[{self.video_id}]{str(e)}")
return
except (TypeError , json.JSONDecodeError) :
logger.error(f"{traceback.format_exc(limit = -1)}")
self._logger.error(f"{traceback.format_exc(limit = -1)}")
return
logger.debug(f"[{self.video_id}]finished fetching chat.")
self._logger.debug(f"[{self.video_id}]finished fetching chat.")
def _check_pause(self, continuation):
if self._pauser.empty():
@@ -218,11 +220,16 @@ class LiveChat:
if contents is None or self._is_replay:
'''Try to fetch archive chat data.'''
self._parser.is_replay = True
self._fetch_url = ("live_chat_replay/"
"get_live_chat_replay?continuation=")
continuation = arcparam.getparam(self.video_id, self.seektime)
self._fetch_url = "live_chat_replay/get_live_chat_replay?continuation="
continuation = arcparam.getparam(
self.video_id, self.seektime, self._topchat_only)
livechat_json = ( self._get_livechat_json(
continuation, session, headers))
reload_continuation = self._parser.reload_continuation(
self._parser.get_contents(livechat_json))
if reload_continuation:
livechat_json = (self._get_livechat_json(
reload_continuation, session, headers))
contents = self._parser.get_contents(livechat_json)
self._first_fetch = False
return contents
@@ -234,8 +241,7 @@ class LiveChat:
continuation = urllib.parse.quote(continuation)
livechat_json = None
status_code = 0
url =(
f"https://www.youtube.com/{self._fetch_url}{continuation}&pbj=1")
url =f"https://www.youtube.com/{self._fetch_url}{continuation}&pbj=1"
for _ in range(MAX_RETRY + 1):
with session.get(url ,headers = headers) as resp:
try:
@@ -246,7 +252,7 @@ class LiveChat:
time.sleep(1)
continue
else:
logger.error(f"[{self.video_id}]"
self._logger.error(f"[{self.video_id}]"
f"Exceeded retry count. status_code={status_code}")
return None
return livechat_json
@@ -301,7 +307,7 @@ class LiveChat:
try:
self.terminate()
except CancelledError:
logger.debug(f'[{self.video_id}]cancelled:{sender}')
self._logger.debug(f'[{self.video_id}]cancelled:{sender}')
def terminate(self):
'''
@@ -311,10 +317,10 @@ class LiveChat:
if self._direct_mode == False:
#bufferにダミーオブジェクトを入れてis_alive()を判定させる
self._buffer.put({'chatdata':'','timeout':0})
logger.info(f'[{self.video_id}]finished.')
self._logger.info(f'[{self.video_id}]finished.')
@classmethod
def shutdown(cls, event, sig = None, handler=None):
logger.debug("shutdown...")
cls._logger.debug("shutdown...")
for t in LiveChat._listeners:
t._is_alive = False

View File

@@ -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

View File

@@ -5,16 +5,12 @@ Parser of live chat JSON.
"""
import json
from .. import config
from .. exceptions import (
ResponseContextError,
NoContentsException,
NoContinuationsException,
ChatParseException )
logger = config.logger(__name__)
class Parser:
__slots__ = ['is_replay']
@@ -26,7 +22,8 @@ class Parser:
if jsn is None:
raise ChatParseException('Called with none JSON object.')
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')
return contents
@@ -64,12 +61,28 @@ class Parser:
raise ChatParseException('Finished chat data')
unknown = list(cont.keys())[0]
if unknown:
logger.debug(f"Received unknown continuation type:{unknown}")
metadata = cont.get(unknown)
raise ChatParseException(f"Received unknown continuation type:{unknown}")
else:
raise ChatParseException('Cannot extract continuation data')
return self._create_data(metadata, contents)
def reload_continuation(self, contents):
"""
When `seektime = 0` or seektime is abbreviated ,
check if fetched chat json has no chat data.
If so, try to fetch playerSeekContinuationData.
This function must be run only first fetching.
"""
cont = contents['liveChatContinuation']['continuations'][0]
if cont.get("liveChatReplayContinuationData"):
#chat data exist.
return None
#chat data do not exist, get playerSeekContinuationData.
init_cont = cont.get("playerSeekContinuationData")
if init_cont:
return init_cont.get("continuation")
raise ChatParseException('Finished chat data')
def _create_data(self, metadata, contents):
actions = contents['liveChatContinuation'].get('actions')
if self.is_replay:
@@ -77,7 +90,8 @@ class Parser:
metadata.setdefault("timeoutMs",interval)
"""Archived chat has different structures than live chat,
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:
metadata.setdefault('timeoutMs', 10000)
chatdata = actions

View File

@@ -61,7 +61,7 @@ class DefaultProcessor(ChatProcessor):
renderer.get_snippet()
renderer.get_authordetails()
except (KeyError,TypeError,AttributeError) as e:
except (KeyError,TypeError) as e:
logger.error(f"{str(type(e))}-{str(e)} sitem:{str(sitem)}")
return None
return renderer

View File

@@ -1,5 +1,4 @@
from datetime import datetime
class Author:
pass
class BaseRenderer:
@@ -67,16 +66,17 @@ class BaseRenderer:
badges=renderer.get("authorBadges")
if badges:
for badge in badges:
author_type = badge["liveChatAuthorBadgeRenderer"]["accessibility"]["accessibilityData"]["label"]
if author_type == '確認済み':
isVerified = True
if author_type == '所有者':
isChatOwner = True
if 'メンバー' in author_type:
if badge["liveChatAuthorBadgeRenderer"].get("icon"):
author_type = badge["liveChatAuthorBadgeRenderer"]["icon"]["iconType"]
if author_type == 'VERIFIED':
isVerified = True
if author_type == 'OWNER':
isChatOwner = True
if author_type == 'MODERATOR':
isChatModerator = True
if badge["liveChatAuthorBadgeRenderer"].get("customThumbnail"):
isChatSponsor = True
self.get_badgeurl(badge)
if author_type == 'モデレーター':
isChatModerator = True
return isVerified, isChatOwner, isChatSponsor, isChatModerator

View File

@@ -10,13 +10,9 @@ class LiveChatPaidMessageRenderer(BaseRenderer):
def get_snippet(self):
super().get_snippet()
self.author.name = self.renderer["authorName"]["simpleText"]
amountDisplayString, symbol, amount =(
self.get_amountdata(self.renderer)
)
self.message = self.get_message(self.renderer)
self.amountValue= amount
self.amountString = amountDisplayString
self.currency= currency.symbols[symbol]["fxtext"] if currency.symbols.get(symbol) else symbol

View File

@@ -10,13 +10,9 @@ class LiveChatPaidStickerRenderer(BaseRenderer):
def get_snippet(self):
super().get_snippet()
self.author.name = self.renderer["authorName"]["simpleText"]
amountDisplayString, symbol, amount =(
self.get_amountdata(self.renderer)
)
self.message = ""
self.amountValue = amount
self.amountString = amountDisplayString
self.currency = currency.symbols[symbol]["fxtext"] if currency.symbols.get(symbol) else symbol

View File

@@ -1,13 +0,0 @@
import json
from .chat_processor import ChatProcessor
class JsonDisplayProcessor(ChatProcessor):
def process(self,chat_components: list):
if chat_components:
for component in chat_components:
chatdata = component.get('chatdata')
if chatdata:
for chat in chatdata:
print(json.dumps(chat,ensure_ascii=False)[:200])

View File

@@ -1,6 +1,6 @@
from setuptools import setup, find_packages, Command
#from codecs import open as open_c
from os import path, system, remove, rename
from os import path, system, remove, rename, removedirs
import re
package_name = "pytchat"
@@ -13,6 +13,15 @@ def _requirements():
def _test_requirements():
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:
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:
long_description = f.read()
@@ -45,7 +47,7 @@ with open('README.md', encoding='utf-8') as f:
setup(
name=package_name,
packages=find_packages(),
packages=find_packages(exclude=['*log.txt','*tests']),
version=version,
url=url,
author=author,