Skip to content

Commit 4df6801

Browse files
committed
nico: use NDGRClient to download comments
1 parent 5bfefe2 commit 4df6801

3 files changed

Lines changed: 35 additions & 75 deletions

File tree

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,11 @@ pip install browser-cookie3 websocket-client rich python-dateutil pytz requests
135135
npm i minyami -g
136136
```
137137

138+
If you want to download comments, you need to install [NDGRClient](https://github.com/tsukumijima/NDGRClient), too.
139+
```
140+
pip install git+https://github.com/tsukumijima/NDGRClient
141+
```
142+
138143
CLI:
139144

140145
```

nico.py

Lines changed: 29 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -86,63 +86,27 @@ def fetch_page(self, url):
8686
live_data = json.loads(soup.select_one('#embedded-data')['data-props'])
8787
return live_data
8888

89-
def download_comments(self, room_info, when, filename):
90-
chats_all = []
91-
url = room_info["data"]["messageServer"]["uri"]
92-
thread_id = room_info["data"]["threadId"]
93-
94-
ws = self.create_ws(url)
95-
print(f'Start download comments from thread {thread_id}')
96-
sending = True
97-
while sending:
98-
message = [
99-
{"ping": {"content": "rs:0"}},
100-
{"ping": {"content": "ps:0"}},
101-
{
102-
"thread": {
103-
"thread": thread_id,
104-
"version": "20090904",
105-
"res_from": -1000,
106-
"when": when + 10
107-
}
108-
},
109-
{"ping": {"content": "pf:0"}},
110-
{"ping": {"content": "rf:0"}}
111-
]
112-
ws.send(json.dumps(message))
113-
114-
first_chat = True
115-
chats = []
116-
while True:
117-
result = ws.recv()
118-
data = json.loads(result)
119-
if "chat" in data:
120-
if first_chat:
121-
first_chat = False
122-
# if the date of chat does not change from last batch,
123-
# we assume we have fetched all the comments.
124-
if data["chat"]["date"] == when:
125-
sending = False
126-
break
127-
when = data["chat"]["date"]
128-
chats.append(data)
129-
else:
130-
# reach the end of this batch
131-
if "ping" in data and 'rf' in data["ping"]["content"]:
132-
break
133-
chats_all.extend(chats)
134-
ws.close()
89+
def download_comments(self, video_id, filename):
90+
from ndgr_client import NDGRClient
91+
import asyncio
92+
93+
async def download(video_id, output, cookies, verbose=False):
94+
ndgr_client = NDGRClient(video_id, verbose=verbose, console_output=True)
95+
await ndgr_client.login(cookies=cookies)
96+
97+
comments = await ndgr_client.downloadBackwardComments()
98+
comments_count = len(comments)
13599

136-
# remove duplicate from chats_all
137-
_ = []
138-
for d in chats_all:
139-
if d not in _:
140-
_.append(d)
141-
chats_all = _
142-
chats_all.sort(key=lambda x: (x['chat']['date'], x['chat'].get('vpos', 0)))
100+
with output.open(mode='w', encoding='utf-8') as f:
101+
f.write('<?xml version="1.0" encoding="UTF-8"?>\n<packet>\n')
102+
f.write(NDGRClient.convertToXMLString(comments))
103+
f.write('\n</packet>\n')
104+
print(f'Total comments for {video_id}: {comments_count}')
105+
print(f'Saved to {output}.')
143106

144-
dump_json(chats_all, self.save_dir / f'{filename}.json')
145-
print(f'Total unique comments: {len(chats_all)}. Saved to "{filename}.json".')
107+
cookies = self.session.cookies.get_dict()
108+
output = self.save_dir / f'{filename}.xml'
109+
asyncio.run(download(video_id, output, cookies))
146110

147111
def download_timeshift(self, url_or_video_id, info_only=False, comments='yes', verbose=False, dump=False, auto_reserve=False):
148112
video_id, url, video_type = self._parse_url_or_video_id(url_or_video_id)
@@ -244,9 +208,6 @@ def download_timeshift(self, url_or_video_id, info_only=False, comments='yes', v
244208
room_info = None
245209
stream_info = None
246210

247-
#TODO: fix danmaku downloading with the new method
248-
# ex = concurrent.futures.ThreadPoolExecutor(max_workers=1)
249-
250211
while True:
251212
verbose and print("Receiving...")
252213
result = ws.recv()
@@ -255,31 +216,24 @@ def download_timeshift(self, url_or_video_id, info_only=False, comments='yes', v
255216
if data['type'] == 'stream':
256217
stream_info = data
257218
break
258-
#TODO: danmaku downloading is temporarily disabled until the new method is implemented
259-
# if data['type'] == 'room':
260-
# room_info = data
261-
# if comments in ['yes', 'only']:
262-
# ex.submit(self.download_comments, room_info, end_time_epoch, filename)
263-
# elif data['type'] == 'stream':
264-
# stream_info = data
265-
# # just grab all the info even if we don't need it, it doesn't save any time anyway.
266-
# if room_info and stream_info:
267-
# print('Got all the info we needed. Close WS.')
268-
# break
269219
ws.close()
270220

271221
if dump:
272222
dump_json(room_info, self.save_dir / f'{filename}.roominfo.json')
273223
dump_json(stream_info, self.save_dir / f'{filename}.streaminfo.json')
274-
275224
return_value.update({
276225
'room_info': room_info,
277226
'stream_info': stream_info
278227
})
279228

229+
ex = concurrent.futures.ThreadPoolExecutor(max_workers=1)
230+
if comments in ['yes', 'only']:
231+
print('Downloading comments...')
232+
ex.submit(self.download_comments, video_id, filename)
233+
280234
if comments == 'only':
281235
ex.shutdown(wait=True)
282-
return_value['danmaku'] = self.save_dir / f'{filename}.json'
236+
return_value['danmaku'] = self.save_dir / f'{filename}.xml'
283237
return return_value
284238

285239
master_m3u8_url = stream_info['data']['uri']
@@ -300,6 +254,8 @@ def download_timeshift(self, url_or_video_id, info_only=False, comments='yes', v
300254
# See: https://stackoverflow.com/questions/74700723/
301255
# Make sure to also use shell=True for *nix systems
302256
output = self.save_dir / f'{filename}.ts'
257+
258+
# print(f'"{playlist_url}", "--key", "{audience_token},{max_quality}"') # a format that can be copied to launch.json
303259
cmd = f'minyami -d "{playlist_url}" --key {audience_token},{max_quality} -o "{output}"'
304260
if self.proxy:
305261
cmd += f' --proxy "{self.proxy}"'
@@ -308,8 +264,7 @@ def download_timeshift(self, url_or_video_id, info_only=False, comments='yes', v
308264
print('CMD is:')
309265
print(cmd)
310266
run(cmd, shell=True)
311-
# TODO: fix danmaku downloading with the new method
312-
# ex.shutdown(wait=True) # ensure download_comments is finished
267+
ex.shutdown(wait=True) # ensure download_comments is finished
313268

314269
return_value.update({
315270
'master_m3u8_url': master_m3u8_url,
@@ -357,7 +312,7 @@ def _split_lines(self, text, width):
357312
parser.add_argument('--dump', action='store_true', help='Dump all the metadata to json files.')
358313
parser.add_argument('--thumb', action='store_true', help='Download thumbnail only. Only works for video type (not live type).')
359314
parser.add_argument('--cookies', '-c', default='chrome', help='R|Cookie source. [Default: chrome]\nProvide either:\n - A browser name to fetch from;\n - The value of "user_session";\n - A Netscape-style cookie file.')
360-
parser.add_argument('--comments', '-d', default='yes', choices=['yes', 'no', 'only'], help='Control if comments (danmaku) are downloaded. [Default: yes]')
315+
parser.add_argument('--comments', '-d', default='no', choices=['yes', 'no', 'only'], help='Control if comments (danmaku) are downloaded. [Default: yes]')
361316
parser.add_argument('--proxy', default='auto', help='Specify a proxy, "none", or "auto" (automatically detects system proxy settings). [Default: auto]')
362317
parser.add_argument('--save-dir', '-o', help='Specify the directory to save the downloaded files. [Default: current directory]')
363318
parser.add_argument('--reserve', action='store_true', help='Automatically reserve timeshift ticket if not reserved yet. [Default: no]')

util.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -831,7 +831,7 @@ def load_cookie(s):
831831
"""
832832
from http.cookiejar import MozillaCookieJar
833833
from requests.cookies import RequestsCookieJar, create_cookie
834-
import browser_cookie3
834+
# import browser_cookie3
835835
import rookiepy
836836
# pip install browser_cookie3 rookiepy
837837

0 commit comments

Comments
 (0)