2 # -*- coding: utf-8 -*-
4 # Copyright © 2014 dyknon
6 # This file is part of NicoNicoMemories GUI.
8 # NicoNicoMemories GUI is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation, either version 3 of the License, or
11 # (at your option) any later version.
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with this program. If not, see <http://www.gnu.org/licenses/>.
22 import tkinter.messagebox
24 import nicovideo.access
25 import nicovideo.comment
26 import nicovideo.tools
27 import nicovideo.logger
34 quelock = threading.Lock()
39 midfinder = re.compile(r"^\s*(?:(?:(?:(?:(?:http:)?//?)?www.nicovideo.jp)?/)?watch/)?([a-z]{2}[0-9]+)(?:\?.*)?\s*$")
40 midfinder2 = re.compile(r"^\s*(?:(?:(?:(?:http:)?//?)?www.nicovideo.jp)?/)?watch/((?:[a-z]{2})?[0-9]+)(?:\?.*)?\s*$")
42 def add_que(request, dl_video):
45 mo1 = midfinder.search(request)
46 mo2 = midfinder.search(request)
49 videoid = mo1.group(1)
51 videoid = mo2.group(1)
55 newque["videoid"] = request
56 newque["stat"] = "iderr"
59 newque["videoid"] = videoid
60 newque["dl_video"] = dl_video
61 m = nicovideo.access.Video(videoid)
64 except nicovideo.err.NotFound:
65 newque["stat"] = "notfound"
69 newque["stat"] = "wait"
70 newque["title"] = m.title
73 newque["id"] = next_queid
74 que_list.append(newque)
82 if que["stat"] == "wait":
86 except FileNotFoundError:
87 que["stat"] = "エラー:ファイルが見つかりませんでした"
96 que["stat"] = stat + " " + prog
101 fnf = nicovideo.tools.filename_fixer
103 stat = "ダウンロードを開始します"
107 m.get_standard_info()
108 filename = m.title + "[" + m.watch_id + "]"
109 system_encoding = locale.getpreferredencoding()
110 filename = filename.encode(system_encoding, "replace")
111 filename = filename.decode(system_encoding, "replace")
113 stat = "コメントにアクセスしています"
115 cmfn = fnf(filename + ".xml")
116 conn = m.generate_connection_to_coments()
117 res = conn.getresponse()
118 if res.status == 200:
120 filehandle = open(cmfn, mode="wb")
122 prog = "コメントファイルを作れません"
125 filesize = int(res.getheader("Content-Length", default=-1))
134 nowtime = int(time.time())
135 if lastprint != nowtime:
138 pst = int(sizedled / filesize * 100 + 0.5)
139 prog = "{}%".format(pst)
141 prog = "{}byte".format(sizedled)
145 filehandle.write(buf)
147 stat = "コメントのDLに失敗しました。"
152 stat = "動画ファイルにアクセスしています"
154 conn = m.generate_connection_to_video()
155 res = conn.getresponse()
156 if res.status == 200:
157 mimetype = res.getheader("Content-Type", default="unknown")
158 filename_ext = mimetypes.guess_extension(mimetype)
160 filename_ext = "." + mimetype[mimetype.find("/")+1:]
162 if m.info["play_video_url"][len(m.info["play_video_url"])-3:] \
165 filename_tail += filename_ext
167 filehandle = open(fnf(filename+filename_tail), mode="wb")
169 prog = "動画ファイルを作れません"
172 filesize = int(res.getheader("Content-Length", default=-1))
173 stat = "動画ファイルダウンロード中"
174 prog = "0%".format(filesize)
181 nowtime = int(time.time())
182 if lastprint != nowtime:
185 pst = int(sizedled / filesize * 100 + 0.5)
186 prog = "{}%".format(pst)
188 prog = "{}byte".format(sizedled)
192 filehandle.write(buf)
194 stat = "動画ファイルのDLに失敗しました。"
198 stat = "コメントを字幕化しています"
200 if not "playerinfo" in m.info_downloaded:
202 if "is_wide" in m.info and int(m.info["is_wide"]):
203 vs = nicovideo.comment.VirtualScreen("16:9")
205 vs = nicovideo.comment.VirtualScreen("4:3")
209 filehandle = open(cmfn, mode="rb")
211 prog = "コメントファイルのオープンに失敗しました。"
214 x = nicovideo.comment.interpret_xml(filehandle)
216 filehandle = open(fnf(filename + ".ass"), mode="wb")
219 filehandle.write(vs.get_ass().encode("UTF-8", "replace"))
222 que["stat"] = "finish"
225 class NamedInputBox(tkinter.Frame):
226 def __init__(self, master=None, name="", value="", width=20):
227 tkinter.Frame.__init__(self, master)
229 self.value = tkinter.StringVar()
230 self.value.set(value)
231 self.lab = tkinter.Label(self, text=name)
232 self.lab.pack(side="left", fill="both")
233 self.ent = tkinter.Entry(self, textvariable=self.value, width=width)
234 self.ent.pack(side="left", fill="both")
236 class PreviewItem(tkinter.Frame):
237 def __init__(self, master):
238 tkinter.Frame.__init__(self, master)
239 self.idlabel = tkinter.Label(self, text="")
240 self.messagelabel = tkinter.Label(self, text="")
241 self.titlelabel = tkinter.Label(self, text="")
242 self.idlabel.grid(column=0, row=0, sticky="w")
243 self.messagelabel.grid(column=1, row=0, sticky="e")
244 self.titlelabel.grid(column=0, row=1, columnspan=2)
245 self.columnconfigure(self.titlelabel, weight=1)
247 def update(self, status):
248 self.idlabel["text"] = status["id"]
249 self.messagelabel["text"] = status["message"]
250 self.titlelabel["text"] = status["title"]
252 def set_color(self, color):
253 self["background"] = color
254 self.idlabel["background"] = color
255 self.messagelabel["background"] = color
256 self.titlelabel["background"] = color
258 class PreviewArea(tkinter.LabelFrame):
259 def __init__(self, master, label):
260 tkinter.LabelFrame.__init__(self, master, text=label, pady=2, padx=2)
261 self.preview = tkinter.Canvas(self)
262 self.scr_y = tkinter.Scrollbar(self)
263 self.scr_x = tkinter.Scrollbar(self)
264 self.preview["width"] = 500
265 self.preview["height"] = 200
266 self.preview["bg"] = "white"
267 self.preview["xscrollcommand"] = self.scr_x.set
268 self.preview["yscrollcommand"] = self.scr_y.set
269 self.scr_x["orient"] = "horizontal"
270 self.scr_x["command"] = self.preview.xview
271 self.scr_y["orient"] = "vertical"
272 self.scr_y["command"] = self.preview.yview
273 self.preview.grid(row=0, column=0, sticky="we")
274 self.scr_x.grid(row=1, column=0, sticky="we")
275 self.scr_y.grid(row=0, column=1, sticky="ns")
276 self.preview["scrollregion"] = self.preview.bbox("all")
279 def update_state(self):
282 status = {"id": que["videoid"]}
283 status["title"] = que["title"]
284 if que["stat"] == "wait":
285 status["type"] = "wait"
286 status["message"] = "順番待ち中です。"
287 elif que["stat"] == "notfound":
288 status["type"] = "error"
289 status["message"] = "動画がありません。"
290 elif que["stat"] == "iderr":
291 status["type"] = "error"
292 status["message"] = "動画ID検出失敗"
293 elif que["stat"] == "finish":
294 status["type"] = "finish"
295 status["message"] = "完了。"
296 elif que["stat"] == "error":
297 status["type"] = "error"
298 status["message"] = "何らかの問題が発生しました。"
300 status["type"] = "prog"
301 status["message"] = que["stat"]
302 for disp in self.idlist:
303 if que["id"] == disp["queid"]:
304 disp["status"] = status
308 newdisp["queid"] = que["id"]
309 newdisp["status"] = status
310 newdisp["item"] = PreviewItem(self)
311 newdisp["id"] = self.preview.create_window(0, 0,
312 window=newdisp["item"],
317 self.idlist[0:0] = [newdisp]
320 colortable = ["#ddddff", "#ddffdd"]
322 for disp in self.idlist:
323 disp["item"].update(disp["status"])
324 disp["item"].set_color(colortable[colorindex])
325 self.preview.coords(disp["id"], (0, ktn))
326 ktn = self.preview.bbox(disp["id"])[3]
328 if len(colortable) <= colorindex:
331 self.preview["scrollregion"] = self.preview.bbox("all")
332 self.after(10, self.update_state)
334 class NnmGui(tkinter.Frame):
335 def __init__(self, master):
336 tkinter.Frame.__init__(self, master)
337 self.flag_dl_video = tkinter.BooleanVar()
338 self.id_box = NamedInputBox(self, "動画ID:", "", 80)
339 self.id_box.bind_all("<Key-Return>", self.do_conv)
340 self.id_box.pack(side="top", fill="both")
341 self.go_frame = tkinter.Frame(self)
342 self.go_frame.pack(side="top", fill="both")
343 self.do_button = tkinter.Button(self.go_frame)
344 self.do_button["text"] = "GO!"
345 self.do_button["command"] = self.do_conv
346 self.do_button.pack(side="left", fill="both")
347 self.videodl_check = tkinter.Checkbutton(self.go_frame)
348 self.videodl_check["text"] = "動画をDLする"
349 self.videodl_check["variable"] = self.flag_dl_video
350 self.videodl_check.pack(side="left", fill="both")
351 self.preview_area = PreviewArea(self, "一覧")
352 self.preview_area.pack(side="top")
353 self.preview_area.update_state()
355 def do_conv(self, event=None):
356 videoid = self.id_box.value.get()
357 dl_video = self.flag_dl_video.get()
358 self.subthread = threading.Thread( target=add_que,
359 args=(videoid, dl_video))
360 self.subthread.daemon = True
361 self.subthread.start()
364 self.do_button["text"] = "GO!"
368 root.title("NicoNicoMemories GUI")
369 do_thread_obj = threading.Thread(target=do_thread)
370 do_thread_obj.daemon = True
371 do_thread_obj.start()