src/download.cpp

changeset 549
715d9a7f6598
parent 548
a99f41732cd2
child 552
454f8b730946
equal deleted inserted replaced
548:a99f41732cd2 549:715d9a7f6598
29 #include "file.h" 29 #include "file.h"
30 #include "gldraw.h" 30 #include "gldraw.h"
31 #include "configDialog.h" 31 #include "configDialog.h"
32 #include "moc_download.cpp" 32 #include "moc_download.cpp"
33 33
34 cfg (String, net_downloadpath, ""); 34 cfg (String, net_downloadpath, "");
35 cfg (Bool, net_guesspaths, true); 35 cfg (Bool, net_guesspaths, true);
36 cfg (Bool, net_autoclose, false); 36 cfg (Bool, net_autoclose, false);
37 37
38 constexpr const char *PartDownloader::k_UnofficialURL; 38 const str g_unofficialLibraryURL ("http://ldraw.org/library/unofficial/");
39 39
40 // ============================================================================= 40 // =============================================================================
41 // ----------------------------------------------------------------------------- 41 // -----------------------------------------------------------------------------
42 void PartDownloader::k_download() 42 void PartDownloader::staticBegin()
43 { str path = getDownloadPath(); 43 { str path = getDownloadPath();
44 44
45 if (path == "" || QDir (path).exists() == false) 45 if (path == "" || QDir (path).exists() == false)
46 { critical (PartDownloader::tr ("You need to specify a valid path for " 46 { critical (PartDownloader::tr ("You need to specify a valid path for "
47 "downloaded files in the configuration to download paths.")); 47 "downloaded files in the configuration to download paths."));
67 } 67 }
68 68
69 // ============================================================================= 69 // =============================================================================
70 // ----------------------------------------------------------------------------- 70 // -----------------------------------------------------------------------------
71 PartDownloader::PartDownloader (QWidget* parent) : QDialog (parent) 71 PartDownloader::PartDownloader (QWidget* parent) : QDialog (parent)
72 { ui = new Ui_DownloadFrom; 72 { setInterface (new Ui_DownloadFrom);
73 ui->setupUi (this); 73 getInterface()->setupUi (this);
74 ui->fname->setFocus(); 74 getInterface()->fname->setFocus();
75 ui->progress->horizontalHeader()->setResizeMode (PartLabelColumn, QHeaderView::Stretch); 75 getInterface()->progress->horizontalHeader()->setResizeMode (PartLabelColumn, QHeaderView::Stretch);
76 76
77 m_downloadButton = new QPushButton (tr ("Download")); 77 setDownloadButton (new QPushButton (tr ("Download")));
78 ui->buttonBox->addButton (m_downloadButton, QDialogButtonBox::ActionRole); 78 getInterface()->buttonBox->addButton (getDownloadButton(), QDialogButtonBox::ActionRole);
79 getButton (Abort)->setEnabled (false); 79 getButton (Abort)->setEnabled (false);
80 80
81 connect (ui->source, SIGNAL (currentIndexChanged (int)), this, SLOT (sourceChanged (int))); 81 connect (getInterface()->source, SIGNAL (currentIndexChanged (int)),
82 connect (ui->buttonBox, SIGNAL (clicked (QAbstractButton*)), this, SLOT (buttonClicked (QAbstractButton*))); 82 this, SLOT (sourceChanged (int)));
83 connect (getInterface()->buttonBox, SIGNAL (clicked (QAbstractButton*)),
84 this, SLOT (buttonClicked (QAbstractButton*)));
83 } 85 }
84 86
85 // ============================================================================= 87 // =============================================================================
86 // ----------------------------------------------------------------------------- 88 // -----------------------------------------------------------------------------
87 PartDownloader::~PartDownloader() 89 PartDownloader::~PartDownloader()
88 { delete ui; 90 { delete getInterface();
89 } 91 }
90 92
91 // ============================================================================= 93 // =============================================================================
92 // ----------------------------------------------------------------------------- 94 // -----------------------------------------------------------------------------
93 str PartDownloader::getURL() const 95 str PartDownloader::getURL() const
94 { const Source src = getSource(); 96 { const Source src = getSource();
95 str dest; 97 str dest;
96 98
97 switch (src) 99 switch (src)
98 { case PartsTracker: 100 { case PartsTracker:
99 dest = ui->fname->text(); 101 dest = getInterface()->fname->text();
100 modifyDestination (dest); 102 modifyDestination (dest);
101 return str (k_UnofficialURL) + dest; 103 return g_unofficialLibraryURL + dest;
102 104
103 case CustomURL: 105 case CustomURL:
104 return ui->fname->text(); 106 return getInterface()->fname->text();
105 } 107 }
106 108
107 // Shouldn't happen 109 // Shouldn't happen
108 return ""; 110 return "";
109 } 111 }
167 } 169 }
168 170
169 // ============================================================================= 171 // =============================================================================
170 // ----------------------------------------------------------------------------- 172 // -----------------------------------------------------------------------------
171 PartDownloader::Source PartDownloader::getSource() const 173 PartDownloader::Source PartDownloader::getSource() const
172 { return (Source) ui->source->currentIndex(); 174 { return (Source) getInterface()->source->currentIndex();
173 } 175 }
174 176
175 // ============================================================================= 177 // =============================================================================
176 // ----------------------------------------------------------------------------- 178 // -----------------------------------------------------------------------------
177 void PartDownloader::sourceChanged (int i) 179 void PartDownloader::sourceChanged (int i)
178 { if (i == CustomURL) 180 { if (i == CustomURL)
179 ui->fileNameLabel->setText (tr ("URL:")); 181 getInterface()->fileNameLabel->setText (tr ("URL:"));
180 else 182 else
181 ui->fileNameLabel->setText (tr ("File name:")); 183 getInterface()->fileNameLabel->setText (tr ("File name:"));
182 } 184 }
183 185
184 // ============================================================================= 186 // =============================================================================
185 // ----------------------------------------------------------------------------- 187 // -----------------------------------------------------------------------------
186 void PartDownloader::buttonClicked (QAbstractButton* btn) 188 void PartDownloader::buttonClicked (QAbstractButton* btn)
187 { if (btn == getButton (Close)) 189 { if (btn == getButton (Close))
188 { reject(); 190 { reject();
189 } elif (btn == getButton (Abort)) 191 }
192 elif (btn == getButton (Abort))
190 { setAborted (true); 193 { setAborted (true);
191 194
192 for (PartDownloadRequest* req : m_requests) 195 for (PartDownloadRequest* req : getRequests())
193 req->abort(); 196 req->abort();
194 } elif (btn == getButton (Download)) 197 }
195 { str dest = ui->fname->text(); 198 elif (btn == getButton (Download))
199 { str dest = getInterface()->fname->text();
196 setPrimaryFile (null); 200 setPrimaryFile (null);
197 setAborted (false); 201 setAborted (false);
198 202
199 if (getSource() == CustomURL) 203 if (getSource() == CustomURL)
200 dest = basename (getURL()); 204 dest = basename (getURL());
205 { const str overwritemsg = fmt (tr ("%1 already exists in download directory. Overwrite?"), dest); 209 { const str overwritemsg = fmt (tr ("%1 already exists in download directory. Overwrite?"), dest);
206 if (!confirm (tr ("Overwrite?"), overwritemsg)) 210 if (!confirm (tr ("Overwrite?"), overwritemsg))
207 return; 211 return;
208 } 212 }
209 213
210 m_downloadButton->setEnabled (false); 214 getDownloadButton()->setEnabled (false);
211 ui->progress->setEnabled (true); 215 getInterface()->progress->setEnabled (true);
212 ui->fname->setEnabled (false); 216 getInterface()->fname->setEnabled (false);
213 ui->source->setEnabled (false); 217 getInterface()->source->setEnabled (false);
214 downloadFile (dest, getURL(), true); 218 downloadFile (dest, getURL(), true);
215 getButton (Close)->setEnabled (false); 219 getButton (Close)->setEnabled (false);
216 getButton (Abort)->setEnabled (true); 220 getButton (Abort)->setEnabled (true);
217 getButton (Download)->setEnabled (false); 221 getButton (Download)->setEnabled (false);
218 } 222 }
219 } 223 }
220 224
221 // ============================================================================= 225 // =============================================================================
222 // ----------------------------------------------------------------------------- 226 // -----------------------------------------------------------------------------
223 void PartDownloader::downloadFile (str dest, str url, bool primary) 227 void PartDownloader::downloadFile (str dest, str url, bool primary)
224 { const int row = ui->progress->rowCount(); 228 { const int row = getInterface()->progress->rowCount();
225 229
226 // Don't download files repeadetly. 230 // Don't download files repeadetly.
227 if (m_filesToDownload.indexOf (dest) != -1) 231 if (getFilesToDownload().indexOf (dest) != -1)
228 return; 232 return;
229 233
230 modifyDestination (dest); 234 modifyDestination (dest);
231 log ("DOWNLOAD: %1 -> %2\n", url, PartDownloader::getDownloadPath() + DIRSLASH + dest); 235 log ("DOWNLOAD: %1 -> %2\n", url, PartDownloader::getDownloadPath() + DIRSLASH + dest);
232 PartDownloadRequest* req = new PartDownloadRequest (url, dest, primary, this); 236 PartDownloadRequest* req = new PartDownloadRequest (url, dest, primary, this);
233 237
234 m_filesToDownload << dest; 238 pushToFilesToDownload (dest);
235 m_requests << req; 239 pushToRequests (req);
236 ui->progress->insertRow (row); 240 getInterface()->progress->insertRow (row);
237 req->setTableRow (row); 241 req->setTableRow (row);
238 req->updateToTable(); 242 req->updateToTable();
239 } 243 }
240 244
241 // ============================================================================= 245 // =============================================================================
242 // ----------------------------------------------------------------------------- 246 // -----------------------------------------------------------------------------
243 void PartDownloader::checkIfFinished() 247 void PartDownloader::checkIfFinished()
244 { bool failed = isAborted(); 248 { bool failed = isAborted();
245 249
246 // If there is some download still working, we're not finished. 250 // If there is some download still working, we're not finished.
247 for (PartDownloadRequest* req : m_requests) 251 for (PartDownloadRequest* req : getRequests())
248 { if (!req->isFinished()) 252 { if (!req->isFinished())
249 return; 253 return;
250 254
251 if (req->state() == PartDownloadRequest::Failed) 255 if (req->getState() == PartDownloadRequest::EFailed)
252 failed = true; 256 failed = true;
253 } 257 }
254 258
255 for (PartDownloadRequest* req : m_requests) 259 for (PartDownloadRequest* req : getRequests())
256 delete req; 260 delete req;
257 261
258 m_requests.clear(); 262 clearRequests();
259 263
260 // Update everything now 264 // Update everything now
261 if (getPrimaryFile()) 265 if (getPrimaryFile())
262 { LDFile::setCurrent (getPrimaryFile()); 266 { LDFile::setCurrent (getPrimaryFile());
263 reloadAllSubfiles(); 267 reloadAllSubfiles();
279 // ============================================================================= 283 // =============================================================================
280 // ----------------------------------------------------------------------------- 284 // -----------------------------------------------------------------------------
281 QPushButton* PartDownloader::getButton (PartDownloader::Button i) 285 QPushButton* PartDownloader::getButton (PartDownloader::Button i)
282 { switch (i) 286 { switch (i)
283 { case Download: 287 { case Download:
284 return m_downloadButton; 288 return getDownloadButton();
285 289
286 case Abort: 290 case Abort:
287 return qobject_cast<QPushButton*> (ui->buttonBox->button (QDialogButtonBox::Abort)); 291 return qobject_cast<QPushButton*> (getInterface()->buttonBox->button (QDialogButtonBox::Abort));
288 292
289 case Close: 293 case Close:
290 return qobject_cast<QPushButton*> (ui->buttonBox->button (QDialogButtonBox::Close)); 294 return qobject_cast<QPushButton*> (getInterface()->buttonBox->button (QDialogButtonBox::Close));
291 } 295 }
292 296
293 return null; 297 return null;
294 } 298 }
295 299
296 // ============================================================================= 300 // =============================================================================
297 // ----------------------------------------------------------------------------- 301 // -----------------------------------------------------------------------------
298 PartDownloadRequest::PartDownloadRequest (str url, str dest, bool primary, PartDownloader* parent) : 302 PartDownloadRequest::PartDownloadRequest (str url, str dest, bool primary, PartDownloader* parent) :
299 QObject (parent), 303 QObject (parent),
300 m_prompt (parent), 304 m_State (ERequesting),
301 m_url (url), 305 m_Prompt (parent),
302 m_dest (dest), 306 m_URL (url),
303 m_fpath (PartDownloader::getDownloadPath() + DIRSLASH + dest), 307 m_Destinaton (dest),
304 m_nam (new QNetworkAccessManager), 308 m_FilePath (PartDownloader::getDownloadPath() + DIRSLASH + dest),
305 m_firstUpdate (true), 309 m_NAM (new QNetworkAccessManager),
306 m_state (Requesting), 310 m_FirstUpdate (true),
307 m_primary (primary), 311 m_Primary (primary),
308 m_fp (null) 312 m_FilePointer (null)
309 { 313 {
310 // Make sure that we have a valid destination. 314 // Make sure that we have a valid destination.
311 str dirpath = dirname (m_fpath); 315 str dirpath = dirname (getFilePath());
312 316
313 QDir dir (dirpath); 317 QDir dir (dirpath);
314 318
315 if (!dir.exists()) 319 if (!dir.exists())
316 { log ("Creating %1...\n", dirpath); 320 { log ("Creating %1...\n", dirpath);
317 321
318 if (!dir.mkpath (dirpath)) 322 if (!dir.mkpath (dirpath))
319 critical (fmt (tr ("Couldn't create the directory %1!"), dirpath)); 323 critical (fmt (tr ("Couldn't create the directory %1!"), dirpath));
320 } 324 }
321 325
322 m_reply = m_nam->get (QNetworkRequest (QUrl (url))); 326 setReply (getNAM()->get (QNetworkRequest (QUrl (url))));
323 connect (m_reply, SIGNAL (finished()), this, SLOT (downloadFinished())); 327 connect (getReply(), SIGNAL (finished()), this, SLOT (downloadFinished()));
324 connect (m_reply, SIGNAL (readyRead()), this, SLOT (readyRead())); 328 connect (getReply(), SIGNAL (readyRead()), this, SLOT (readyRead()));
325 connect (m_reply, SIGNAL (downloadProgress (qint64, qint64)), this, SLOT (downloadProgress (qint64, qint64))); 329 connect (getReply(), SIGNAL (downloadProgress (qint64, qint64)),
330 this, SLOT (downloadProgress (qint64, qint64)));
326 } 331 }
327 332
328 // ============================================================================= 333 // =============================================================================
329 // ----------------------------------------------------------------------------- 334 // -----------------------------------------------------------------------------
330 PartDownloadRequest::~PartDownloadRequest() {} 335 PartDownloadRequest::~PartDownloadRequest() {}
331 336
332 // ============================================================================= 337 // =============================================================================
333 // ----------------------------------------------------------------------------- 338 // -----------------------------------------------------------------------------
334 void PartDownloadRequest::updateToTable() 339 void PartDownloadRequest::updateToTable()
335 { const int labelcol = PartDownloader::PartLabelColumn, 340 { const int labelcol = PartDownloader::PartLabelColumn,
336 progcol = PartDownloader::ProgressColumn; 341 progcol = PartDownloader::ProgressColumn;
337 QTableWidget* table = m_prompt->ui->progress; 342 QTableWidget* table = getPrompt()->getInterface()->progress;
338 QProgressBar* prog; 343 QProgressBar* prog;
339 344
340 switch (m_state) 345 switch (getState())
341 { case Requesting: 346 { case ERequesting:
342 case Downloading: 347 case EDownloading:
343 { prog = qobject_cast<QProgressBar*> (table->cellWidget (getTableRow(), progcol)); 348 { prog = qobject_cast<QProgressBar*> (table->cellWidget (getTableRow(), progcol));
344 349
345 if (!prog) 350 if (!prog)
346 { prog = new QProgressBar; 351 { prog = new QProgressBar;
347 table->setCellWidget (getTableRow(), progcol, prog); 352 table->setCellWidget (getTableRow(), progcol, prog);
348 } 353 }
349 354
350 prog->setRange (0, m_bytesTotal); 355 prog->setRange (0, getBytesTotal());
351 prog->setValue (m_bytesRead); 356 prog->setValue (getBytesRead());
352 } break; 357 } break;
353 358
354 case Finished: 359 case EFinished:
355 case Failed: 360 case EFailed:
356 { QLabel* lb = new QLabel ((m_state == Finished) ? "<b><span style=\"color: #080\">FINISHED</span></b>" : 361 { const str text = (getState() == EFinished)
357 "<b><span style=\"color: #800\">FAILED</span></b>"); 362 ? "<b><span style=\"color: #080\">FINISHED</span></b>"
363 : "<b><span style=\"color: #800\">FAILED</span></b>";
364
365 QLabel* lb = new QLabel (text);
358 lb->setAlignment (Qt::AlignCenter); 366 lb->setAlignment (Qt::AlignCenter);
359 table->setCellWidget (getTableRow(), progcol, lb); 367 table->setCellWidget (getTableRow(), progcol, lb);
360 } break; 368 } break;
361 } 369 }
362 370
363 QLabel* lb = qobject_cast<QLabel*> (table->cellWidget (getTableRow(), labelcol)); 371 QLabel* lb = qobject_cast<QLabel*> (table->cellWidget (getTableRow(), labelcol));
364 372
365 if (m_firstUpdate) 373 if (isFirstUpdate())
366 { lb = new QLabel (fmt ("<b>%1</b>", m_dest), table); 374 { lb = new QLabel (fmt ("<b>%1</b>", getDestinaton()), table);
367 table->setCellWidget (getTableRow(), labelcol, lb); 375 table->setCellWidget (getTableRow(), labelcol, lb);
368 } 376 }
369 377
370 // Make sure that the cell is big enough to contain the label 378 // Make sure that the cell is big enough to contain the label
371 if (table->columnWidth (labelcol) < lb->width()) 379 if (table->columnWidth (labelcol) < lb->width())
372 table->setColumnWidth (labelcol, lb->width()); 380 table->setColumnWidth (labelcol, lb->width());
373 381
374 m_firstUpdate = false; 382 setFirstUpdate (true);
375 } 383 }
376 384
377 // ============================================================================= 385 // =============================================================================
378 // ----------------------------------------------------------------------------- 386 // -----------------------------------------------------------------------------
379 void PartDownloadRequest::downloadFinished() 387 void PartDownloadRequest::downloadFinished()
380 { if (m_reply->error() != QNetworkReply::NoError) 388 { if (getReply()->error() != QNetworkReply::NoError)
381 { if (m_primary && !m_prompt->isAborted()) 389 { if (isPrimary() && !getPrompt()->isAborted())
382 critical (m_reply->errorString()); 390 critical (getReply()->errorString());
383 391
384 m_state = Failed; 392 setState (EFailed);
385 } elif (state() != Failed) 393 }
386 m_state = Finished; 394 elif (getState() != EFailed)
387 395 setState (EFinished);
388 m_bytesRead = m_bytesTotal; 396
397 setBytesRead (getBytesTotal());
389 updateToTable(); 398 updateToTable();
390 399
391 if (m_fp) 400 if (getFilePointer())
392 { m_fp->close(); 401 { getFilePointer()->close();
393 delete m_fp; 402 delete getFilePointer();
394 m_fp = null; 403 setFilePointer (null);
395 404
396 if (m_state == Failed) 405 if (getState() == EFailed)
397 QFile::remove (m_fpath); 406 QFile::remove (getFilePath());
398 } 407 }
399 408
400 if (m_state != Finished) 409 if (getState() != EFinished)
401 { m_prompt->checkIfFinished(); 410 { getPrompt()->checkIfFinished();
402 return; 411 return;
403 } 412 }
404 413
405 // Try to load this file now. 414 // Try to load this file now.
406 LDFile* f = openDATFile (m_fpath, false); 415 LDFile* f = openDATFile (getFilePath(), false);
407 416
408 if (!f) 417 if (!f)
409 return; 418 return;
410 419
411 f->setImplicit (!m_primary); 420 f->setImplicit (!isPrimary());
412 421
413 // Iterate through this file and check for errors. If there's any that stems 422 // Iterate through this file and check for errors. If there's any that stems
414 // from unknown file references, try resolve that by downloading the reference. 423 // from unknown file references, try resolve that by downloading the reference.
415 // This is why downloading a part may end up downloading multiple files, as 424 // This is why downloading a part may end up downloading multiple files, as
416 // it resolves dependencies. 425 // it resolves dependencies.
419 428
420 if (!err || err->getFileReferenced().isEmpty()) 429 if (!err || err->getFileReferenced().isEmpty())
421 continue; 430 continue;
422 431
423 str dest = err->getFileReferenced(); 432 str dest = err->getFileReferenced();
424 m_prompt->modifyDestination (dest); 433 getPrompt()->modifyDestination (dest);
425 m_prompt->downloadFile (dest, str (PartDownloader::k_UnofficialURL) + dest, false); 434 getPrompt()->downloadFile (dest, g_unofficialLibraryURL + dest, false);
426 } 435 }
427 436
428 if (m_primary) 437 if (isPrimary())
429 { addRecentFile (m_fpath); 438 { addRecentFile (getFilePath());
430 m_prompt->setPrimaryFile (f); 439 getPrompt()->setPrimaryFile (f);
431 } 440 }
432 441
433 m_prompt->checkIfFinished(); 442 getPrompt()->checkIfFinished();
434 } 443 }
435 444
436 // ============================================================================= 445 // =============================================================================
437 // ----------------------------------------------------------------------------- 446 // -----------------------------------------------------------------------------
438 void PartDownloadRequest::downloadProgress (int64 recv, int64 total) 447 void PartDownloadRequest::downloadProgress (int64 recv, int64 total)
439 { m_bytesRead = recv; 448 { setBytesRead (recv);
440 m_bytesTotal = total; 449 setBytesTotal (total);
441 m_state = Downloading; 450 setState (EDownloading);
442 updateToTable(); 451 updateToTable();
443 } 452 }
444 453
445 // ============================================================================= 454 // =============================================================================
446 // ----------------------------------------------------------------------------- 455 // -----------------------------------------------------------------------------
447 void PartDownloadRequest::readyRead() 456 void PartDownloadRequest::readyRead()
448 { if (state() == Failed) 457 { if (getState() == EFailed)
449 return; 458 return;
450 459
451 if (m_fp == null) 460 if (getFilePointer() == null)
452 { m_fpath.replace ("\\", "/"); 461 { replaceInFilePath ("\\", "/");
453 462
454 // We have already asked the user whether we can overwrite so we're good 463 // We have already asked the user whether we can overwrite so we're good
455 // to go here. 464 // to go here.
456 m_fp = new QFile (m_fpath.toLocal8Bit()); 465 setFilePointer (new QFile (getFilePath().toLocal8Bit()));
457 466
458 if (!m_fp->open (QIODevice::WriteOnly)) 467 if (!getFilePointer()->open (QIODevice::WriteOnly))
459 { critical (fmt (tr ("Couldn't open %1 for writing: %2"), m_fpath, strerror (errno))); 468 { critical (fmt (tr ("Couldn't open %1 for writing: %2"), getFilePath(), strerror (errno)));
460 m_state = Failed; 469 setState (EFailed);
461 m_reply->abort(); 470 getReply()->abort();
462 updateToTable(); 471 updateToTable();
463 m_prompt->checkIfFinished(); 472 getPrompt()->checkIfFinished();
464 return; 473 return;
465 } 474 }
466 } 475 }
467 476
468 m_fp->write (m_reply->readAll()); 477 getFilePointer()->write (getReply()->readAll());
469 } 478 }
470 479
471 // ============================================================================= 480 // =============================================================================
472 // ----------------------------------------------------------------------------- 481 // -----------------------------------------------------------------------------
473 bool PartDownloadRequest::isFinished() const 482 bool PartDownloadRequest::isFinished() const
474 { return m_state == Finished || m_state == Failed; 483 { return getState() == EFinished || getState() == EFailed;
475 }
476
477 // =============================================================================
478 // -----------------------------------------------------------------------------
479 const PartDownloadRequest::State& PartDownloadRequest::state() const
480 { return m_state;
481 } 484 }
482 485
483 // ============================================================================= 486 // =============================================================================
484 // ----------------------------------------------------------------------------- 487 // -----------------------------------------------------------------------------
485 void PartDownloadRequest::abort() 488 void PartDownloadRequest::abort()
486 { m_reply->abort(); 489 { getReply()->abort();
487 } 490 }
488 491
489 // ============================================================================= 492 // =============================================================================
490 // ----------------------------------------------------------------------------- 493 // -----------------------------------------------------------------------------
491 DEFINE_ACTION (DownloadFrom, 0) 494 DEFINE_ACTION (DownloadFrom, 0)
492 { PartDownloader::k_download(); 495 { PartDownloader::staticBegin();
493 } 496 }

mercurial