1 theshadow 1.1 #!/usr/bin/env python
2
3 # Written by Bram Cohen
4 # modified for multitracker by John Hoffman
5 # see LICENSE.txt for license information
6
|
7 theshadow 1.2 from BitTornado import PSYCO
|
8 theshadow 1.1 if PSYCO.psyco:
9 try:
10 import psyco
11 assert psyco.__version__ >= 0x010100f0
12 psyco.full()
13 except:
14 pass
15
|
16 theshadow 1.6 from sys import argv, platform, version
|
17 theshadow 1.5 assert version >= '2', "Install Python 2.0 or greater"
18 from BitTornado.BT1.makemetafile import make_meta_file
|
19 theshadow 1.1 from threading import Event, Thread, Lock
|
20 theshadow 1.2 from BitTornado.bencode import bencode,bdecode
|
21 theshadow 1.1 import sys, os, shutil
|
22 theshadow 1.4 from os import getcwd, listdir
|
23 theshadow 1.1 from os.path import join, isdir
24 from traceback import print_exc
|
25 theshadow 1.4 try:
26 from wxPython.wx import *
27 except:
28 print 'wxPython is either not installed or has not been installed properly.'
29 sys.exit(1)
30
|
31 theshadow 1.3 try:
32 True
33 except:
34 True = 1
35 False = 0
|
36 theshadow 1.1
37 basepath=os.path.abspath(os.path.dirname(sys.argv[0]))
38
39 if platform == 'win32':
40 DROP_HERE = '(drop here)'
41 else:
42 DROP_HERE = ''
43
44
45 wxEVT_INVOKE = wxNewEventType()
46
47 def EVT_INVOKE(win, func):
48 win.Connect(-1, -1, wxEVT_INVOKE, func)
49
50 class InvokeEvent(wxPyEvent):
51 def __init__(self, func, args, kwargs):
52 wxPyEvent.__init__(self)
53 self.SetEventType(wxEVT_INVOKE)
54 self.func = func
55 self.args = args
56 self.kwargs = kwargs
57 theshadow 1.1
58
59 class BasicDownloadInfo:
60 def __init__(self, config, calls):
61 self.config = config
62 self.calls = calls
63
64 self.uiflag = Event()
65 self.cancelflag = Event()
66 self.switchlock = Lock()
|
67 theshadow 1.3 self.working = False
|
68 theshadow 1.1 self.queue = []
69 wxInitAllImageHandlers()
70 self.thostselection = self.calls['getCurrentTHost']()
71 self.thostselectnum = 0
72 self.choices = None
73 self.choices1 = None
74 self.announce = ''
75 self.announce_list = None
76
77 self.windowStyle = wxSYSTEM_MENU|wxCAPTION|wxMINIMIZE_BOX
78 if self.config['stayontop']:
79 self.windowStyle |= wxSTAY_ON_TOP
80 frame = wxFrame(None, -1, 'T-Make',
81 size = wxSize(-1, -1),
82 style = self.windowStyle)
83 self.frame = frame
84 panel = wxPanel(frame, -1)
85 mainSizer = wxBoxSizer(wxVERTICAL)
86 groupSizer = wxFlexGridSizer(cols = 1, vgap = 0, hgap = 0)
87 # self.dropTarget = self.calls['newDropTarget']((200,200))
88 self.dropTarget = self.calls['newDropTarget']()
89 theshadow 1.1 self.dropTargetPtr = wxStaticBitmap(panel, -1, self.dropTarget)
90 self.calls['setDropTargetRefresh'](self.dropTargetPtr.Refresh)
91 self.dropTargetWidth = self.dropTarget.GetWidth()
92 EVT_LEFT_DOWN(self.dropTargetPtr,self.dropTargetClick)
93 EVT_ENTER_WINDOW(self.dropTargetPtr,self.calls['dropTargetHovered'])
94 EVT_LEAVE_WINDOW(self.dropTargetPtr,self.calls['dropTargetUnhovered'])
95 groupSizer.Add(self.dropTargetPtr,0,wxALIGN_CENTER)
96 lowerSizer1 = wxGridSizer(cols = 6)
97 dirlink = wxStaticText(panel, -1, 'dir')
|
98 theshadow 1.3 dirlink.SetFont(wxFont(7, wxDEFAULT, wxNORMAL, wxNORMAL, True))
|
99 theshadow 1.1 dirlink.SetForegroundColour('blue')
100 EVT_LEFT_UP(dirlink,self.selectdir)
101 lowerSizer1.Add(dirlink, -1, wxALIGN_LEFT)
102 lowerSizer1.Add(wxStaticText(panel, -1, ''), -1, wxALIGN_CENTER)
103 lowerSizer1.Add(wxStaticText(panel, -1, ''), -1, wxALIGN_CENTER)
104 lowerSizer1.Add(wxStaticText(panel, -1, ''), -1, wxALIGN_CENTER)
105 lowerSizer1.Add(wxStaticText(panel, -1, ''), -1, wxALIGN_CENTER)
106 filelink = wxStaticText(panel, -1, 'file')
|
107 theshadow 1.3 filelink.SetFont(wxFont(7, wxDEFAULT, wxNORMAL, wxNORMAL, True))
|
108 theshadow 1.1 filelink.SetForegroundColour('blue')
109 EVT_LEFT_UP(filelink,self.selectfile)
110 lowerSizer1.Add(filelink, -1, wxALIGN_RIGHT)
111
112 groupSizer.Add(lowerSizer1, -1, wxALIGN_CENTER)
113
114 self.gauge = wxGauge(panel, -1, range = 1000,
115 style = wxGA_HORIZONTAL, size = (-1,15))
116 groupSizer.Add(self.gauge, 0, wxEXPAND)
117 self.statustext = wxStaticText(panel, -1, 'ready',
118 style = wxALIGN_CENTER|wxST_NO_AUTORESIZE)
|
119 theshadow 1.3 self.statustext.SetFont(wxFont(7, wxDEFAULT, wxNORMAL, wxBOLD, False))
|
120 theshadow 1.1 groupSizer.Add(self.statustext, -1, wxEXPAND)
121 self.choices = wxChoice(panel, -1, (-1, -1), (self.dropTargetWidth, -1),
122 choices = [])
|
123 theshadow 1.3 self.choices.SetFont(wxFont(7, wxDEFAULT, wxNORMAL, wxNORMAL, False))
|
124 theshadow 1.1 EVT_CHOICE(self.choices, -1, self.set_thost)
125 groupSizer.Add(self.choices, 0, wxEXPAND)
126 cancellink = wxStaticText(panel, -1, 'cancel')
|
127 theshadow 1.3 cancellink.SetFont(wxFont(7, wxDEFAULT, wxNORMAL, wxNORMAL, True))
|
128 theshadow 1.1 cancellink.SetForegroundColour('red')
129 EVT_LEFT_UP(cancellink,self.cancel)
130 groupSizer.Add(cancellink, -1, wxALIGN_CENTER)
131 advlink = wxStaticText(panel, -1, 'advanced')
|
132 theshadow 1.3 advlink.SetFont(wxFont(7, wxDEFAULT, wxNORMAL, wxNORMAL, True))
|
133 theshadow 1.1 advlink.SetForegroundColour('blue')
134 EVT_LEFT_UP(advlink,self.calls['switchToAdvanced'])
135 groupSizer.Add(advlink, -1, wxALIGN_CENTER)
136 mainSizer.Add(groupSizer, 0, wxALIGN_CENTER)
137
138 self.refresh_thostlist()
139 self._set_thost()
140
141 if platform == 'win32':
|
142 theshadow 1.3 self.dropTargetPtr.DragAcceptFiles(True)
|
143 theshadow 1.1 EVT_DROP_FILES(self.dropTargetPtr, self.selectdrop)
144
145 # border = wxBoxSizer(wxHORIZONTAL)
146 # border.Add(mainSizer, 1, wxEXPAND | wxALL, 0)
147 panel.SetSizer(mainSizer)
|
148 theshadow 1.3 panel.SetAutoLayout(True)
|
149 theshadow 1.1 # border.Fit(panel)
150 mainSizer.Fit(panel)
151 frame.Fit()
|
152 theshadow 1.3 frame.Show(True)
|
153 theshadow 1.1
154 EVT_INVOKE(frame, self.onInvoke)
155 EVT_CLOSE(frame, self._close)
156
157
158 def selectdir(self, x = None):
159 self.calls['dropTargetHovered']()
160 dl = wxDirDialog(self.frame, style = wxDD_DEFAULT_STYLE | wxDD_NEW_DIR_BUTTON)
161 if dl.ShowModal() == wxID_OK:
162 self.calls['dropTargetDropped']()
163 self.complete(dl.GetPath())
164 else:
165 self.calls['dropTargetUnhovered']()
166
167 def selectfile(self, x = None):
168 self.calls['dropTargetHovered']()
169 dl = wxFileDialog (self.frame, 'Choose file to use', '', '', '', wxOPEN)
170 if dl.ShowModal() == wxID_OK:
171 self.calls['dropTargetDropped']()
172 self.complete(dl.GetPath())
173 else:
174 theshadow 1.1 self.calls['dropTargetUnhovered']()
175
176 def selectdrop(self, dat):
177 self.calls['dropTargetDropped']()
178 for f in dat.GetFiles():
179 self.complete(f)
180
181 def _announcecopy(self, f):
182 try:
183 h = open(f, 'rb')
184 metainfo = bdecode(h.read())
185 h.close()
186 self.announce = metainfo['announce']
187 if metainfo.has_key('announce-list'):
188 self.announce_list = metainfo['announce-list']
189 else:
190 self.announce_list = None
191 except:
192 return
193
194 def complete(self, x):
195 theshadow 1.1 params = {'piece_size_pow2': 0}
196 if self.announce_list:
197 params['real_announce_list'] = self.announce_list
198 self.queue.append((x, self.announce, params))
199 self.go_queue()
200
201 def go_queue(self):
202 self.switchlock.acquire()
203 if self.queue and not self.working:
|
204 theshadow 1.3 self.working = True
|
205 theshadow 1.1 self.statustext.SetLabel('working')
206 q = self.queue.pop(0)
207 MakeMetafile(q[0], q[1], q[2], self)
208 self.switchlock.release()
209
210 def cancel(self, x):
211 self.switchlock.acquire()
212 if self.working:
|
213 theshadow 1.3 self.working = False
|
214 theshadow 1.1 self.cancelflag.set()
215 self.cancelflag = Event()
216 self.queue = []
217 self.statustext.SetLabel('CANCELED')
218 self.calls['dropTargetError']()
219 self.switchlock.release()
220
221 def dropTargetClick(self, x):
222 if x.GetPosition()[0] < int(self.dropTargetWidth*0.4):
223 self.selectdir()
224 elif x.GetPosition()[0] > int(self.dropTargetWidth*0.6):
225 self.selectfile()
226
227 def refresh_thostlist(self):
228 l = []
229 d = 0
230 for f in listdir(join(basepath,'thosts')):
231 if f[-6:].lower() == '.thost':
232 l.append(f)
233 if f == self.thostselection:
234 d = len(l)
235 theshadow 1.1 self.choices.Clear()
236 if not d:
237 if l:
238 self.thostselection = l[0]
239 d = 1
240 else:
241 self.thostselection = ''
242 d = 1
243 self.config['thost'] = self.thostselection
244 self.calls['saveConfig']()
245 for f in l:
246 self.choices.Append(f[:-6])
247 self.thostselectnum = d-1
248 self.thostlist = l
249 self.choices.SetSelection(d-1)
250 return
251
252 def set_thost(self, x):
253 n = self.choices.GetSelection()
254 if n != self.thostselectnum:
255 self.thostselectnum = n
256 theshadow 1.1 if n:
257 self.thostselection = self.thostlist[n-1]
258
259 def _set_thost(self):
260 self._announcecopy(join(join(basepath,'thosts'),self.thostselection))
261 self.calls['setCurrentTHost'](self.thostselection)
262
263 def onInvoke(self, event):
264 if not self.uiflag.isSet():
265 apply(event.func, event.args, event.kwargs)
266
267 def invokeLater(self, func, args = [], kwargs = {}):
268 if not self.uiflag.isSet():
269 wxPostEvent(self.frame, InvokeEvent(func, args, kwargs))
270
271 def build_setgauge(self, x):
272 self.invokeLater(self.on_setgauge, [x])
273
274 def on_setgauge(self, x):
275 self.gauge.SetValue(int(x*1000))
276
277 theshadow 1.1 def build_done(self):
278 self.invokeLater(self.on_builddone)
279
280 def on_builddone(self):
281 self.gauge.SetValue(0)
282 self.statustext.SetLabel('done!')
283 self.calls['dropTargetSuccess']()
|
284 theshadow 1.3 self.working = False
|
285 theshadow 1.1 self.go_queue()
286
287 def build_failed(self, e):
288 self.invokeLater(self.on_buildfailed, [e])
289
290 def on_buildfailed(self, e):
291 self.gauge.SetValue(0)
292 self.statustext.SetLabel('ERROR')
293 self.calls['dropTargetError']()
|
294 theshadow 1.3 self.working = False
|
295 theshadow 1.1 self.go_queue()
296
297 def close(self):
298 self.cancelflag = None # this is a planned switch, don't cancel
299 self.uiflag.set()
300 self.frame.Close()
301
302 def _close(self, x = None):
303 self.uiflag.set()
304 try:
305 self.cancelflag.set()
306 except:
307 pass
308 self.frame.Destroy()
309
310
311
312 class AdvancedDownloadInfo:
313 def __init__(self, config, calls):
314 self.config = config
315 self.calls = calls
316 theshadow 1.1
317 self.uiflag = Event()
318 self.cancelflag = Event()
319 self.switchlock = Lock()
|
320 theshadow 1.3 self.working = False
|
321 theshadow 1.1 self.queue = []
322 wxInitAllImageHandlers()
323 self.thostselection = self.calls['getCurrentTHost']()
324 self.thostselectnum = 0
325 self.choices = None
326 self.choices1 = None
327
328 self.windowStyle = wxSYSTEM_MENU|wxCAPTION|wxMINIMIZE_BOX
329 if self.config['stayontop']:
330 self.windowStyle |= wxSTAY_ON_TOP
331 frame = wxFrame(None, -1, 'T-Make',
332 size = wxSize(-1, -1),
333 style = self.windowStyle)
334 self.frame = frame
335 panel = wxPanel(frame, -1)
336
337 fullSizer = wxFlexGridSizer(cols = 1, vgap = 0, hgap = 8)
338
339 colSizer = wxFlexGridSizer(cols = 2, vgap = 0, hgap = 8)
340 leftSizer = wxFlexGridSizer(cols = 1, vgap = 3)
341
342 theshadow 1.1 self.stayontop_checkbox = wxCheckBox(panel, -1, "stay on top")
343 self.stayontop_checkbox.SetValue(self.config['stayontop'])
344 EVT_CHECKBOX(frame, self.stayontop_checkbox.GetId(), self.setstayontop)
345 leftSizer.Add(self.stayontop_checkbox, -1, wxALIGN_CENTER)
346 leftSizer.Add(wxStaticText(panel, -1, ''))
347
348 button = wxButton(panel, -1, 'use image...')
349 EVT_BUTTON(frame, button.GetId(), self.selectDropTarget)
350 leftSizer.Add(button, -1, wxALIGN_CENTER)
351
352 self.groupSizer1Box = wxStaticBox(panel, -1, '')
353 groupSizer1 = wxStaticBoxSizer(self.groupSizer1Box, wxHORIZONTAL)
354 groupSizer = wxFlexGridSizer(cols = 1, vgap = 0)
355 self.dropTarget = self.calls['newDropTarget']((200,200))
356 # self.dropTarget = self.calls['newDropTarget']()
357 self.dropTargetPtr = wxStaticBitmap(panel, -1, self.dropTarget)
358 self.calls['setDropTargetRefresh'](self.dropTargetPtr.Refresh)
359 self.dropTargetWidth = self.dropTarget.GetWidth()
360 EVT_LEFT_DOWN(self.dropTargetPtr,self.dropTargetClick)
361 EVT_ENTER_WINDOW(self.dropTargetPtr,self.calls['dropTargetHovered'])
362 EVT_LEAVE_WINDOW(self.dropTargetPtr,self.calls['dropTargetUnhovered'])
363 theshadow 1.1 groupSizer.Add(self.dropTargetPtr,0,wxALIGN_CENTER)
364 lowerSizer1 = wxGridSizer(cols = 3)
365 dirlink = wxStaticText(panel, -1, 'dir')
|
366 theshadow 1.3 dirlink.SetFont(wxFont(7, wxDEFAULT, wxNORMAL, wxNORMAL, True))
|
367 theshadow 1.1 dirlink.SetForegroundColour('blue')
368 EVT_LEFT_UP(dirlink,self.selectdir)
369 lowerSizer1.Add(dirlink, -1, wxALIGN_LEFT)
370 lowerSizer1.Add(wxStaticText(panel, -1, ''), -1, wxALIGN_CENTER)
371 filelink = wxStaticText(panel, -1, 'file')
|
372 theshadow 1.3 filelink.SetFont(wxFont(7, wxDEFAULT, wxNORMAL, wxNORMAL, True))
|
373 theshadow 1.1 filelink.SetForegroundColour('blue')
374 EVT_LEFT_UP(filelink,self.selectfile)
375 lowerSizer1.Add(filelink, -1, wxALIGN_RIGHT)
376
377 groupSizer.Add(lowerSizer1, -1, wxALIGN_CENTER)
378
379 self.gauge = wxGauge(panel, -1, range = 1000,
380 style = wxGA_HORIZONTAL, size = (-1,15))
381 groupSizer.Add(self.gauge, 0, wxEXPAND)
382 self.statustext = wxStaticText(panel, -1, 'ready',
383 style = wxALIGN_CENTER|wxST_NO_AUTORESIZE)
|
384 theshadow 1.3 self.statustext.SetFont(wxFont(7, wxDEFAULT, wxNORMAL, wxBOLD, False))
|
385 theshadow 1.1 groupSizer.Add(self.statustext, -1, wxEXPAND)
386 self.choices = wxChoice(panel, -1, (-1, -1), (self.dropTargetWidth, -1),
387 choices = [])
|
388 theshadow 1.3 self.choices.SetFont(wxFont(7, wxDEFAULT, wxNORMAL, wxNORMAL, False))
|
389 theshadow 1.1 EVT_CHOICE(self.choices, -1, self.set_thost)
390 groupSizer.Add(self.choices, 0, wxEXPAND)
391 cancellink = wxStaticText(panel, -1, 'cancel')
|
392 theshadow 1.3 cancellink.SetFont(wxFont(7, wxDEFAULT, wxNORMAL, wxNORMAL, True))
|
393 theshadow 1.1 cancellink.SetForegroundColour('red')
394 EVT_LEFT_UP(cancellink,self.cancel)
395 groupSizer.Add(cancellink, -1, wxALIGN_CENTER)
396 dummyadvlink = wxStaticText(panel, -1, 'advanced')
|
397 theshadow 1.3 dummyadvlink.SetFont(wxFont(7, wxDEFAULT, wxNORMAL, wxNORMAL, False))
|
398 theshadow 1.1 dummyadvlink.SetForegroundColour('blue')
399 EVT_LEFT_UP(dirlink,self.selectdir)
400 groupSizer.Add(dummyadvlink, -1, wxALIGN_CENTER)
401 groupSizer1.Add(groupSizer)
402 leftSizer.Add(groupSizer1, -1, wxALIGN_CENTER)
403
404 leftSizer.Add(wxStaticText(panel, -1, 'make torrent of:'),0,wxALIGN_CENTER)
405
406 self.dirCtl = wxTextCtrl(panel, -1, '', size = (250,-1))
407 leftSizer.Add(self.dirCtl, 1, wxEXPAND)
408
409 b = wxBoxSizer(wxHORIZONTAL)
410 button = wxButton(panel, -1, 'dir')
411 EVT_BUTTON(frame, button.GetId(), self.selectdir)
412 b.Add(button, 0)
413
414 button2 = wxButton(panel, -1, 'file')
415 EVT_BUTTON(frame, button2.GetId(), self.selectfile)
416 b.Add(button2, 0)
417
418 leftSizer.Add(b, 0, wxALIGN_CENTER)
419 theshadow 1.1
420 leftSizer.Add(wxStaticText(panel, -1, ''))
421
422 simple_link = wxStaticText(panel, -1, 'back to basic mode')
|
423 theshadow 1.3 simple_link.SetFont(wxFont(-1, wxDEFAULT, wxNORMAL, wxNORMAL, True))
|
424 theshadow 1.1 simple_link.SetForegroundColour('blue')
425 EVT_LEFT_UP(simple_link,self.calls['switchToBasic'])
426 leftSizer.Add(simple_link, -1, wxALIGN_CENTER)
427
428 colSizer.Add(leftSizer, -1, wxALIGN_CENTER_VERTICAL)
429
430 gridSizer = wxFlexGridSizer(cols = 2, vgap = 6, hgap = 8)
431
432 gridSizer.Add(wxStaticText(panel, -1, 'Torrent host:'), -1,
433 wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL)
434
435 self.choices1 = wxChoice(panel, -1, (-1, -1), (-1, -1),
436 choices = [])
437 EVT_CHOICE(self.choices1, -1, self.set_thost1)
438 gridSizer.Add(self.choices1, 0, wxEXPAND)
439
440 b = wxBoxSizer(wxHORIZONTAL)
441 button1 = wxButton(panel, -1, 'set default')
442 EVT_BUTTON(frame, button1.GetId(), self.set_default_thost)
443 b.Add(button1, 0)
444 b.Add(wxStaticText(panel, -1, ' '))
445 theshadow 1.1 button2 = wxButton(panel, -1, 'delete')
446 EVT_BUTTON(frame, button2.GetId(), self.delete_thost)
447 b.Add(button2, 0)
448 b.Add(wxStaticText(panel, -1, ' '))
449 button3 = wxButton(panel, -1, 'save as...')
450 EVT_BUTTON(frame, button3.GetId(), self.save_thost)
451 b.Add(button3, 0)
452
453 gridSizer.Add(wxStaticText(panel, -1, ''))
454 gridSizer.Add(b, 0, wxALIGN_CENTER)
455
456 gridSizer.Add(wxStaticText(panel, -1, ''))
457 gridSizer.Add(wxStaticText(panel, -1, ''))
458
459 gridSizer.Add(wxStaticText(panel, -1, 'single tracker url:'),0,
460 wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL)
461 self.annCtl = wxTextCtrl(panel, -1, 'http://my.tracker:6969/announce')
462 gridSizer.Add(self.annCtl, 0, wxEXPAND)
463
464 a = wxFlexGridSizer(cols = 1, vgap = 3)
465 a.Add(wxStaticText(panel, -1, 'tracker list:'),0,wxALIGN_RIGHT)
466 theshadow 1.1 a.Add(wxStaticText(panel, -1, ''))
467 abutton = wxButton(panel, -1, 'copy\nannounces\nfrom\ntorrent', size = (70,70))
468 EVT_BUTTON(frame, abutton.GetId(), self.announcecopy)
469 a.Add(abutton, -1, wxALIGN_CENTER)
470 a.Add(wxStaticText(panel, -1, DROP_HERE), -1, wxALIGN_CENTER)
471 gridSizer.Add(a, -1, wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL)
472
473
474 self.annListCtl = wxTextCtrl(panel, -1, '\n\n\n\n\n', wxPoint(-1,-1), (300,120),
475 wxTE_MULTILINE|wxHSCROLL|wxTE_DONTWRAP)
476 gridSizer.Add(self.annListCtl, -1, wxEXPAND)
477
478 gridSizer.Add(wxStaticText(panel, -1, ''))
479 exptext = wxStaticText(panel, -1,
480 "a list of tracker urls separated by commas or whitespace\n" +
481 "and on several lines -trackers on the same line will be\n" +
482 "tried randomly, and all the trackers on one line\n" +
483 "will be tried before the trackers on the next line.")
|
484 theshadow 1.3 exptext.SetFont(wxFont(6, wxDEFAULT, wxNORMAL, wxNORMAL, False))
|
485 theshadow 1.1 gridSizer.Add(exptext, -1, wxALIGN_CENTER)
486
487 self.refresh_thostlist()
488 self._set_thost()
489
490 if platform == 'win32':
|
491 theshadow 1.3 self.dropTargetPtr.DragAcceptFiles(True)
|
492 theshadow 1.1 EVT_DROP_FILES(self.dropTargetPtr, self.selectdrop)
|
493 theshadow 1.3 self.groupSizer1Box.DragAcceptFiles(True)
|
494 theshadow 1.1 EVT_DROP_FILES(self.groupSizer1Box, self.selectdrop)
|
495 theshadow 1.3 abutton.DragAcceptFiles(True)
|
496 theshadow 1.1 EVT_DROP_FILES(abutton, self.announcedrop)
|
497 theshadow 1.3 self.annCtl.DragAcceptFiles(True)
|
498 theshadow 1.1 EVT_DROP_FILES(self.annCtl, self.announcedrop)
|
499 theshadow 1.3 self.annListCtl.DragAcceptFiles(True)
|
500 theshadow 1.1 EVT_DROP_FILES(self.annListCtl, self.announcedrop)
501
502 gridSizer.Add(wxStaticText(panel, -1, ''))
503 gridSizer.Add(wxStaticText(panel, -1, ''))
504
505 gridSizer.Add(wxStaticText(panel, -1, 'piece size:'),0,
506 wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL)
507 self.piece_length = wxChoice(panel, -1,
508 choices = ['automatic', '2MiB', '1MiB', '512KiB', '256KiB', '128KiB', '64KiB', '32KiB'])
509 self.piece_length_list = [0, 21, 20, 19, 18, 17, 16, 15]
510 self.piece_length.SetSelection(0)
511 gridSizer.Add(self.piece_length)
512
513 gridSizer.Add(wxStaticText(panel, -1, 'comment:'),0,
514 wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL)
515 self.commentCtl = wxTextCtrl(panel, -1, '')
516 gridSizer.Add(self.commentCtl, 0, wxEXPAND)
517
518 gridSizer.Add(wxStaticText(panel, -1, ''))
519 gridSizer.Add(wxStaticText(panel, -1, ''))
520
521 theshadow 1.1 b1 = wxButton(panel, -1, 'Cancel', size = (-1, 30))
522 EVT_BUTTON(frame, b1.GetId(), self.cancel)
523 gridSizer.Add(b1, 0, wxEXPAND)
524 b2 = wxButton(panel, -1, 'MAKE TORRENT', size = (-1, 30))
525 EVT_BUTTON(frame, b2.GetId(), self.complete)
526 gridSizer.Add(b2, 0, wxEXPAND)
527
528 gridSizer.AddGrowableCol(1)
529 colSizer.Add(gridSizer, -1, wxALIGN_CENTER_VERTICAL)
530 fullSizer.Add(colSizer)
531
532
533 border = wxBoxSizer(wxHORIZONTAL)
534 border.Add(fullSizer, 1, wxEXPAND | wxALL, 15)
535 panel.SetSizer(border)
|
536 theshadow 1.3 panel.SetAutoLayout(True)
|
537 theshadow 1.1 border.Fit(panel)
538 frame.Fit()
|
539 theshadow 1.3 frame.Show(True)
|
540 theshadow 1.1
541 EVT_INVOKE(frame, self.onInvoke)
542 EVT_CLOSE(frame, self._close)
543
544 def setstayontop(self, x):
545 if self.stayontop_checkbox.GetValue():
546 self.windowStyle |= wxSTAY_ON_TOP
547 else:
548 self.windowStyle &= ~wxSTAY_ON_TOP
549 self.frame.SetWindowStyle(self.windowStyle)
550 self.config['stayontop'] = self.stayontop_checkbox.GetValue()
551
552 def selectdir(self, x = None):
553 self.calls['dropTargetHovered']()
554 dl = wxDirDialog(self.frame, style = wxDD_DEFAULT_STYLE | wxDD_NEW_DIR_BUTTON)
555 if dl.ShowModal() == wxID_OK:
556 self.dirCtl.SetValue(dl.GetPath())
557 self.calls['dropTargetDropped']()
558 else:
559 self.calls['dropTargetUnhovered']()
560
561 theshadow 1.1 def selectfile(self, x = None):
562 self.calls['dropTargetHovered']()
563 dl = wxFileDialog (self.frame, 'Choose file to use', '', '', '', wxOPEN)
564 if dl.ShowModal() == wxID_OK:
565 self.dirCtl.SetValue(dl.GetPath())
566 self.calls['dropTargetDropped']()
567 else:
568 self.calls['dropTargetUnhovered']()
569
570 def selectdrop(self, dat):
571 self.calls['dropTargetDropped']()
572 for f in dat.GetFiles():
573 self.complete(f)
574
575 def announcecopy(self, x):
576 dl = wxFileDialog (self.frame, 'Choose .torrent file to use', '', '', '*.torrent', wxOPEN)
577 if dl.ShowModal() == wxID_OK:
|
578 theshadow 1.3 self._announcecopy(dl.GetPath(), True)
|
579 theshadow 1.1
580 def announcedrop(self, dat):
|
581 theshadow 1.3 self._announcecopy(dat.GetFiles()[0], True)
|
582 theshadow 1.1
|
583 theshadow 1.3 def _announcecopy(self, f, external = False):
|
584 theshadow 1.1 try:
585 h = open(f, 'rb')
586 metainfo = bdecode(h.read())
587 h.close()
588 self.annCtl.SetValue(metainfo['announce'])
589 if metainfo.has_key('announce-list'):
590 list = []
591 for tier in metainfo['announce-list']:
592 for tracker in tier:
593 list += [tracker, ', ']
594 del list[-1]
595 list += ['\n']
596 liststring = ''
597 for i in list:
598 liststring += i
599 self.annListCtl.SetValue(liststring+'\n\n')
600 else:
601 self.annListCtl.SetValue('')
602 if external:
603 self.choices.SetSelection(0)
604 self.choices1.SetSelection(0)
605 theshadow 1.1 except:
606 return
607
608 def getannouncelist(self):
609 list = []
610 for t in self.annListCtl.GetValue().split('\n'):
611 tier = []
612 t = t.replace(',',' ')
613 for tr in t.split(' '):
614 if tr != '':
615 tier += [tr]
616 if len(tier)>0:
617 list.append(tier)
618 return list
619
620 def complete(self, x):
621 if not self.dirCtl.GetValue():
622 dlg = wxMessageDialog(self.frame, message = 'You must select a\nfile or directory',
623 caption = 'Error', style = wxOK | wxICON_ERROR)
624 dlg.ShowModal()
625 dlg.Destroy()
626 theshadow 1.1 return
627 if not self.annCtl.GetValue():
628 dlg = wxMessageDialog(self.frame, message = 'You must specify a\nsingle tracker url',
629 caption = 'Error', style = wxOK | wxICON_ERROR)
630 dlg.ShowModal()
631 dlg.Destroy()
632 return
633 params = {'piece_size_pow2': self.piece_length_list[self.piece_length.GetSelection()]}
634 annlist = self.getannouncelist()
635 if len(annlist)>0:
636 warnings = ''
637 for tier in annlist:
638 if len(tier) > 1:
639 warnings += (
640 'WARNING: You should not specify multiple trackers\n' +
641 ' on the same line of the tracker list unless\n' +
642 ' you are certain they share peer data.\n')
643 break
644 if not self.annCtl.GetValue() in annlist[0]:
645 warnings += (
646 'WARNING: The single tracker url is not present in\n' +
647 theshadow 1.1 ' the first line of the tracker list. This\n' +
648 ' may produce a dysfunctional torrent.\n')
649 if warnings:
650 warnings += ('Are you sure you wish to produce a .torrent\n' +
651 'with these parameters?')
652 dlg = wxMessageDialog(self.frame,
653 message = warnings,
654 caption = 'Warning', style = wxYES_NO | wxICON_QUESTION)
655 if dlg.ShowModal() != wxID_YES:
656 dlg.Destroy()
657 return
658 params['real_announce_list'] = annlist
659 comment = self.commentCtl.GetValue()
660 if comment != '':
661 params['comment'] = comment
662 self.statustext.SetLabel('working')
663 self.queue.append((self.dirCtl.GetValue(), self.annCtl.GetValue(), params))
664 self.go_queue()
665
666 def go_queue(self):
667 self.switchlock.acquire()
668 theshadow 1.1 if self.queue and not self.working:
|
669 theshadow 1.3 self.working = True
|
670 theshadow 1.1 self.statustext.SetLabel('working')
671 q = self.queue.pop(0)
672 MakeMetafile(q[0], q[1], q[2], self)
673 self.switchlock.release()
674
675 def cancel(self, x):
676 self.switchlock.acquire()
677 if self.working:
|
678 theshadow 1.3 self.working = False
|
679 theshadow 1.1 self.cancelflag.set()
680 self.cancelflag = Event()
681 self.queue = []
682 self.statustext.SetLabel('CANCELED')
683 self.calls['dropTargetError']()
684 self.switchlock.release()
685
686 def selectDropTarget(self, x):
687 dl = wxFileDialog (self.frame, 'Choose image to use', join(basepath,'targets'),
688 join(join(basepath,'targets'), self.config['target']),
689 'Supported images (*.bmp,*.gif)|*.*', wxOPEN|wxHIDE_READONLY)
690 if dl.ShowModal() == wxID_OK:
691 try:
692 self.calls['changeDropTarget'](dl.GetPath())
693 self.config['target'] = dl.GetPath()
694 except:
695 pass
696
697 def dropTargetClick(self, x):
698 if x.GetPosition()[0] < int(self.dropTargetWidth*0.4):
699 self.selectdir()
700 theshadow 1.1 elif x.GetPosition()[0] > int(self.dropTargetWidth*0.6):
701 self.selectfile()
702
703 def refresh_thostlist(self):
704 l = []
705 d = 0
706 for f in listdir(join(basepath,'thosts')):
707 if f[-6:].lower() == '.thost':
708 l.append(f)
709 if f == self.thostselection:
710 d = len(l)
711 self.choices.Clear()
712 self.choices.Append(' ')
713 self.choices1.Clear()
714 self.choices1.Append('---')
715 if not d:
716 if l:
717 self.thostselection = l[0]
718 d = 1
719 else:
720 self.thostselection = ''
721 theshadow 1.1 d = 0
722 self.config['thost'] = self.thostselection
723 for f in l:
724 f1 = f[:-6]
725 self.choices.Append(f1)
726 if f == self.config['thost']:
727 f1 += ' (default)'
728 self.choices1.Append(f1)
729 self.thostselectnum = d
730 self.thostlist = l
731 self.choices.SetSelection(d)
732 self.choices1.SetSelection(d)
733
734 def set_thost(self, x):
735 n = self.choices.GetSelection()
736 if n != self.thostselectnum:
737 self.thostselectnum = n
738 self.choices1.SetSelection(n)
739 if n:
740 self.thostselection = self.thostlist[n-1]
741 self._set_thost()
742 theshadow 1.1
743 def set_thost1(self, x):
744 n = self.choices1.GetSelection()
745 if n != self.thostselectnum:
746 self.thostselectnum = n
747 self.choices.SetSelection(n)
748 if n:
749 self.thostselection = self.thostlist[n-1]
750 self._set_thost()
751
752 def _set_thost(self):
753 self._announcecopy(join(join(basepath,'thosts'),self.thostselection))
754 self.calls['setCurrentTHost'](self.thostselection)
755
756 def set_default_thost(self, x):
757 if self.thostlist:
758 self.config['thost'] = self.thostselection
759 self.refresh_thostlist()
760
761 def save_thost(self, x):
762 if not self.annCtl.GetValue():
763 theshadow 1.1 dlg = wxMessageDialog(self.frame, message = 'You must specify a\nsingle tracker url',
764 caption = 'Error', style = wxOK | wxICON_ERROR)
765 dlg.ShowModal()
766 dlg.Destroy()
767 return
768 try:
769 metainfo = {}
770 metainfo['announce'] = self.annCtl.GetValue()
771 annlist = self.getannouncelist()
772 if len(annlist)>0:
773 warnings = ''
774 for tier in annlist:
775 if len(tier) > 1:
776 warnings += (
777 'WARNING: You should not specify multiple trackers\n' +
778 ' on the same line of the tracker list unless\n' +
779 ' you are certain they share peer data.\n')
780 break
781 if not self.annCtl.GetValue() in annlist[0]:
782 warnings += (
783 'WARNING: The single tracker url is not present in\n' +
784 theshadow 1.1 ' the first line of the tracker list. This\n' +
785 ' may produce a dysfunctional torrent.\n')
786 if warnings:
787 warnings += ('Are you sure you wish to save a torrent host\n' +
788 'with these parameters?')
789 dlg = wxMessageDialog(self.frame,
790 message = warnings,
791 caption = 'Warning', style = wxYES_NO | wxICON_QUESTION)
792 if dlg.ShowModal() != wxID_YES:
793 dlg.Destroy()
794 return
795 metainfo['announce-list'] = annlist
796 metainfo = bencode(metainfo)
797 except:
798 return
799
800 if self.thostselectnum:
801 d = self.thostselection
802 else:
803 d = '.thost'
804 dl = wxFileDialog (self.frame, 'Save tracker data as',
805 theshadow 1.1 join(basepath,'thosts'), d, '*.thost',
806 wxSAVE|wxOVERWRITE_PROMPT)
807 if dl.ShowModal() != wxID_OK:
808 return
809 d = dl.GetPath()
810
811 try:
812 f = open(d,'wb')
813 f.write(metainfo)
814 f.close()
815 garbage, self.thostselection = os.path.split(d)
816 except:
817 pass
818 self.refresh_thostlist()
819
820 def delete_thost(self, x):
821 dlg = wxMessageDialog(self.frame,
822 message = 'Are you sure you want to delete\n'+self.thostselection[:-6]+'?',
823 caption = 'Warning', style = wxYES_NO | wxICON_EXCLAMATION)
824 if dlg.ShowModal() != wxID_YES:
825 dlg.Destroy()
826 theshadow 1.1 return
827 dlg.Destroy()
828 os.remove(join(join(basepath,'thosts'),self.thostselection))
829 self.thostselection = None
830 self.refresh_thostlist()
831
832 def onInvoke(self, event):
833 if not self.uiflag.isSet():
834 apply(event.func, event.args, event.kwargs)
835
836 def invokeLater(self, func, args = [], kwargs = {}):
837 if not self.uiflag.isSet():
838 wxPostEvent(self.frame, InvokeEvent(func, args, kwargs))
839
840 def build_setgauge(self, x):
841 self.invokeLater(self.on_setgauge, [x])
842
843 def on_setgauge(self, x):
844 self.gauge.SetValue(int(x*1000))
845
846 def build_done(self):
847 theshadow 1.1 self.invokeLater(self.on_builddone)
848
849 def on_builddone(self):
850 self.gauge.SetValue(0)
851 self.statustext.SetLabel('done!')
852 self.calls['dropTargetSuccess']()
|
853 theshadow 1.3 self.working = False
|
854 theshadow 1.1 self.go_queue()
855
856 def build_failed(self, e):
857 self.invokeLater(self.on_buildfailed, [e])
858
859 def on_buildfailed(self, e):
860 self.gauge.SetValue(0)
861 self.statustext.SetLabel('ERROR')
862 self.calls['dropTargetError']()
|
863 theshadow 1.3 self.working = False
|
864 theshadow 1.1 self.go_queue()
865
866 def close(self):
867 self.cancelflag = None # this is a planned switch, don't cancel
868 self.uiflag.set()
869 self.frame.Close()
870
871 def _close(self, x = None):
872 self.uiflag.set()
873 try:
874 self.cancelflag.set()
875 except:
876 pass
877 self.calls['saveConfig']()
878 self.frame.Destroy()
879
880
881 class MakeMetafile:
882 def __init__(self, d, a, params, external = None):
883 self.d = d
884 self.a = a
885 theshadow 1.1 self.params = params
886
887 self.call = external
888 # self.uiflag = external.uiflag
889 self.uiflag = external.cancelflag
890 Thread(target = self.complete).start()
891
892 def complete(self):
893 try:
894 make_meta_file(self.d, self.a, self.params, self.uiflag,
895 self.call.build_setgauge, progress_percent = 1)
896 if not self.uiflag.isSet():
897 self.call.build_done()
898 except (OSError, IOError), e:
899 self.failed(e)
900 except Exception, e:
901 print_exc()
902 self.failed(e)
903
904 def failed(self, e):
905 e = str(e)
906 theshadow 1.1 self.call.build_failed(e)
907 dlg = wxMessageDialog(self.frame, message = 'Error - ' + e,
908 caption = 'Error', style = wxOK | wxICON_ERROR)
909 dlg.ShowModal()
910 dlg.Destroy()
911
912
913 class T_make:
914 def __init__(self):
915 self.configobj = wxConfig('BitTorrent_T-make',style=wxCONFIG_USE_LOCAL_FILE)
916 self.getConfig()
917 self.currentTHost = self.config['thost']
918 # self.d = AdvancedDownloadInfo(self.config, self.getCalls())
919 self.d = BasicDownloadInfo(self.config, self.getCalls())
920
921 def getConfig(self):
922 config = {}
923 try:
|
924 theshadow 1.3 config['stayontop'] = self.configobj.ReadInt('stayontop',True)
|
925 theshadow 1.1 except:
|
926 theshadow 1.3 config['stayontop'] = True
927 self.configobj.WriteInt('stayontop',True)
|
928 theshadow 1.1 try:
929 config['target'] = self.configobj.Read('target','default.gif')
930 except:
931 config['target'] = 'default.gif'
932 self.configobj.Write('target','default.gif')
933 try:
934 config['thost'] = self.configobj.Read('thost','')
935 except:
936 config['thost'] = ''
937 self.configobj.Write('thost','')
938 self.configobj.Flush()
939 self.config = config
940
941 def saveConfig(self):
942 self.configobj.WriteInt('stayontop',self.config['stayontop'])
943 self.configobj.Write('target',self.config['target'])
944 self.configobj.Write('thost',self.config['thost'])
945 self.configobj.Flush()
946
947 def getCalls(self):
948 calls = {}
949 theshadow 1.1 calls['saveConfig'] = self.saveConfig
950 calls['newDropTarget'] = self.newDropTarget
951 calls['setDropTargetRefresh'] = self.setDropTargetRefresh
952 calls['changeDropTarget'] = self.changeDropTarget
953 calls['setCurrentTHost'] = self.setCurrentTHost
954 calls['getCurrentTHost'] = self.getCurrentTHost
955 calls['dropTargetHovered'] = self.dropTargetHovered
956 calls['dropTargetUnhovered'] = self.dropTargetUnhovered
957 calls['dropTargetDropped'] = self.dropTargetDropped
958 calls['dropTargetSuccess'] = self.dropTargetSuccess
959 calls['dropTargetError'] = self.dropTargetError
960 calls['switchToBasic'] = self.switchToBasic
961 calls['switchToAdvanced'] = self.switchToAdvanced
962 return calls
963
964 def setCurrentTHost(self, x):
965 self.currentTHost = x
966
967 def getCurrentTHost(self):
968 return self.currentTHost
969
970 theshadow 1.1 def newDropTarget(self, wh = None):
971 if wh:
972 self.dropTarget = wxEmptyBitmap(wh[0],wh[1])
973 try:
974 self.changeDropTarget(self.config['target'])
975 except:
976 pass
977 else:
978 try:
979 self.dropTarget = self._dropTargetRead(self.config['target'])
980 except:
981 try:
982 self.dropTarget = self._dropTargetRead('default.gif')
983 self.config['target'] = 'default.gif'
984 self.saveConfig()
985 except:
986 self.dropTarget = wxEmptyBitmap(100,100)
987 return self.dropTarget
988
989 def setDropTargetRefresh(self, refreshfunc):
990 self.dropTargetRefresh = refreshfunc
991 theshadow 1.1
992 def changeDropTarget(self, new):
993 bmp = self._dropTargetRead(new)
994 w1,h1 = self.dropTarget.GetWidth(),self.dropTarget.GetHeight()
995 w,h = bmp.GetWidth(),bmp.GetHeight()
996 x1,y1 = int((w1-w)/2.0),int((h1-h)/2.0)
997 bbdata = wxMemoryDC()
998 bbdata.SelectObject(self.dropTarget)
999 bbdata.SetPen(wxTRANSPARENT_PEN)
1000 bbdata.SetBrush(wxBrush(wx.wxSystemSettings_GetColour(wxSYS_COLOUR_MENU),wxSOLID))
1001 bbdata.DrawRectangle(0,0,w1,h1)
1002 bbdata.SetPen(wxBLACK_PEN)
1003 bbdata.SetBrush(wxTRANSPARENT_BRUSH)
1004 bbdata.DrawRectangle(x1-1,y1-1,w+2,h+2)
|
1005 theshadow 1.3 bbdata.DrawBitmap(bmp,x1,y1,True)
|
1006 theshadow 1.1 try:
1007 self.dropTargetRefresh()
1008 except:
1009 pass
1010
1011 def _dropTargetRead(self, new):
1012 a,b = os.path.split(new)
1013 if a and a != join(basepath,'targets'):
1014 if a != join(basepath,'targets'):
1015 b1,b2 = os.path.splitext(b)
1016 z = 0
1017 while os.path.isfile(join(join(basepath,'targets'),b)):
1018 z += 1
1019 b = b1+'('+str(z)+')'+b2
1020 shutil.copyfile(newname,join(join(basepath,'targets'),b))
1021 new = b
1022 name = join(join(basepath,'targets'),new)
1023 garbage, e = os.path.splitext(new.lower())
1024 if e == '.gif':
1025 bmp = wxBitmap(name, wxBITMAP_TYPE_GIF)
1026 elif e == '.bmp':
1027 theshadow 1.1 bmp = wxBitmap(name, wxBITMAP_TYPE_BMP)
1028 else:
|
1029 theshadow 1.3 assert False
|
1030 theshadow 1.1 return bmp
1031
1032 def dropTargetHovered(self, x = None):
1033 pass
1034
1035 def dropTargetUnhovered(self, x = None):
1036 pass
1037
1038 def dropTargetDropped(self, x = None):
1039 pass
1040
1041 def dropTargetSuccess(self, x = None):
1042 pass
1043
1044 def dropTargetError(self, x = None):
1045 pass
1046
1047 def switchToBasic(self, x = None):
1048 self.d.close()
1049 self.d = BasicDownloadInfo(self.config, self.getCalls())
1050
1051 theshadow 1.1 def switchToAdvanced(self, x = None):
1052 self.d.close()
1053 self.d = AdvancedDownloadInfo(self.config, self.getCalls())
1054
1055
1056
1057 class btWxApp(wxApp):
1058 def OnInit(self):
1059 self.APP = T_make()
|
1060 theshadow 1.3 return True
|
1061 theshadow 1.1
1062 if __name__ == '__main__':
1063 btWxApp().MainLoop()
|