@@ -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]\n Provide 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]' )
0 commit comments