From 2e8e926c1003a09663b1d16d7f4be8b45771cc9f Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Thu, 15 May 2008 14:37:02 +0000 Subject: [PATCH] 2008-05-15 Tatsuhiro Tsujikawa Print download progress summary of all parallel downloads in specified interval, default 60 secs. While aria2 could download several files in parallel (see -j option), it displayed just first download progress, and the other downloads are hidden from the users. This new feature prints all downloads currently in-progress to the console so that you can check the status of all downloads. The interval can be adjusted by --summary-interval option. The progress readout is now cut so that all fits in the one line of console. * src/ConsoleStatCalc.cc * src/ConsoleStatCalc.h * src/HelpItemFactory.cc * src/OptionHandlerFactory.cc * src/RequestGroupMan.cc * src/RequestGroupMan.h * src/main.cc * src/option_processing.cc * src/prefs.h * src/usage_text.h --- ChangeLog | 23 ++++ src/ConsoleStatCalc.cc | 254 +++++++++++++++++++++++------------- src/ConsoleStatCalc.h | 6 + src/HelpItemFactory.cc | 7 + src/OptionHandlerFactory.cc | 1 + src/RequestGroupMan.cc | 6 + src/RequestGroupMan.h | 4 +- src/main.cc | 2 +- src/option_processing.cc | 5 + src/prefs.h | 2 + src/usage_text.h | 3 + 11 files changed, 222 insertions(+), 91 deletions(-) diff --git a/ChangeLog b/ChangeLog index ec20053d..50d8f393 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,26 @@ +2008-05-15 Tatsuhiro Tsujikawa + + Print download progress summary of all parallel downloads in specified + interval, default 60 secs. While aria2 could download several files + in parallel (see -j option), it displayed just first download progress, + and the other downloads are hidden from the users. This new feature + prints all downloads currently in-progress to the console so that you + can check the status of all downloads. The interval can be adjusted + by --summary-interval option. + + The progress readout is now cut so that all fits in the one line of + console. + * src/ConsoleStatCalc.cc + * src/ConsoleStatCalc.h + * src/HelpItemFactory.cc + * src/OptionHandlerFactory.cc + * src/RequestGroupMan.cc + * src/RequestGroupMan.h + * src/main.cc + * src/option_processing.cc + * src/prefs.h + * src/usage_text.h + 2008-05-15 Tatsuhiro Tsujikawa Call updateFdSet() to initialize rfdset, wfdset fdmax. diff --git a/src/ConsoleStatCalc.cc b/src/ConsoleStatCalc.cc index 08a7dc59..fc2af9e6 100644 --- a/src/ConsoleStatCalc.cc +++ b/src/ConsoleStatCalc.cc @@ -48,9 +48,113 @@ #include #include #include +#include +#include +#include +#include namespace aria2 { +static void printProgress(std::ostream& o, const SharedHandle& rg) +{ + TransferStat stat = rg->calculateStat(); + unsigned int eta = 0; + if(rg->getTotalLength() > 0 && stat.getDownloadSpeed() > 0) { + eta = (rg->getTotalLength()-rg->getCompletedLength())/stat.getDownloadSpeed(); + } + + o << "[" + << "#" << rg->getGID() << " "; +#ifdef ENABLE_BITTORRENT + if(rg->downloadFinished() && + !dynamic_pointer_cast(rg->getDownloadContext()).isNull()) { + o << "SEEDING" << "(" << "ratio:" + << std::fixed << std::setprecision(1) + << ((stat.getAllTimeUploadLength()*10)/rg->getCompletedLength())/10.0 + << ")"; + } else +#endif // ENABLE_BITTORRENT + { + o << "SIZE:" + << Util::abbrevSize(rg->getCompletedLength()) + << "B" + << "/" + << Util::abbrevSize(rg->getTotalLength()) + << "B"; + if(rg->getTotalLength() > 0) { + o << "(" + << 100*rg->getCompletedLength()/rg->getTotalLength() + << "%)"; + } + } + o << " " + << "CN:" + << rg->getNumConnection(); + if(!rg->downloadFinished()) { + o << " " + << "SPD:" + << std::fixed << std::setprecision(2) << stat.getDownloadSpeed()/1024.0 << "KiB/s"; + } + if(stat.getSessionUploadLength() > 0) { + o << " " + << "UP:" + << std::fixed << std::setprecision(2) << stat.getUploadSpeed()/1024.0 << "KiB/s" + << "(" << Util::abbrevSize(stat.getAllTimeUploadLength()) << "B)"; + } + if(eta > 0) { + o << " " + << "ETA:" + << Util::secfmt(eta); + } + o << "]"; +} + +class PrintSummary +{ +private: + size_t _cols; +public: + PrintSummary(size_t cols):_cols(cols) {} + + void operator()(const SharedHandle& rg) + { + const char SEP_CHAR = '-'; + printProgress(std::cout, rg); + std::cout << "\n" + << "FILE: " << rg->getFilePath() << "\n" + << std::setfill(SEP_CHAR) << std::setw(_cols) << SEP_CHAR << "\n"; + } +}; + +static void printProgressSummary(const std::deque >& groups, size_t cols) +{ + const char SEP_CHAR = '='; + time_t now; + time(&now); + std::cout << " *** Download Progress Summary"; + { + time_t now; + struct tm* staticNowtmPtr; + char buf[26]; + if(time(&now) != (time_t)-1 && (staticNowtmPtr = localtime(&now)) != 0 && + asctime_r(staticNowtmPtr, buf) != 0) { + char* lfptr = strchr(buf, '\n'); + if(lfptr) { + *lfptr = '\0'; + } + std::cout << " as of " << buf; + } + } + std::cout << " *** " << "\n" + << std::setfill(SEP_CHAR) << std::setw(cols) << SEP_CHAR << "\n"; + std::for_each(groups.begin(), groups.end(), PrintSummary(cols)); +} + +ConsoleStatCalc::ConsoleStatCalc(time_t summaryInterval): + _summaryInterval(summaryInterval), + _summaryIntervalCount(0) +{} + void ConsoleStatCalc::calculateStat(const RequestGroupManHandle& requestGroupMan, const FileAllocationManHandle& fileAllocationMan, @@ -60,104 +164,67 @@ ConsoleStatCalc::calculateStat(const RequestGroupManHandle& requestGroupMan, return; } _cp.reset(); + ++_summaryIntervalCount; + bool isTTY = isatty(STDOUT_FILENO) == 1; + unsigned short int cols = 80; if(isTTY) { struct winsize size; - if(ioctl(STDOUT_FILENO, TIOCGWINSZ, &size) == -1) { - size.ws_col = 80; + if(ioctl(STDOUT_FILENO, TIOCGWINSZ, &size) == 0) { + cols = size.ws_col; } - std::cout << '\r' << std::setw(size.ws_col) << ' ' << '\r'; + std::cout << '\r' << std::setfill(' ') << std::setw(cols) << ' ' << '\r'; } + std::ostringstream o; if(requestGroupMan->countRequestGroup() > 0) { - RequestGroupHandle firstRequestGroup = requestGroupMan->getRequestGroup(0); - TransferStat stat = firstRequestGroup->calculateStat(); - unsigned int eta = 0; - if(firstRequestGroup->getTotalLength() > 0 && stat.getDownloadSpeed() > 0) { - eta = (firstRequestGroup->getTotalLength()-firstRequestGroup->getCompletedLength())/stat.getDownloadSpeed(); + if(_summaryInterval > 0 && _summaryIntervalCount%_summaryInterval == 0) { + printProgressSummary(requestGroupMan->getRequestGroups(), cols); + _summaryIntervalCount = 0; + std::cout << "\n"; } - std::cout << "[" - << "#" << firstRequestGroup->getGID() << " "; -#ifdef ENABLE_BITTORRENT - if(firstRequestGroup->downloadFinished() && - !dynamic_pointer_cast(firstRequestGroup->getDownloadContext()).isNull()) { - std::cout << "SEEDING" << "(" << "ratio:" - << std::fixed << std::setprecision(1) - << ((stat.getAllTimeUploadLength()*10)/firstRequestGroup->getCompletedLength())/10.0 - << ")"; - } else -#endif // ENABLE_BITTORRENT - { - std::cout << "SIZE:" - << Util::abbrevSize(firstRequestGroup->getCompletedLength()) - << "B" - << "/" - << Util::abbrevSize(firstRequestGroup->getTotalLength()) - << "B"; - if(firstRequestGroup->getTotalLength() > 0) { - std::cout << "(" - << 100*firstRequestGroup->getCompletedLength()/firstRequestGroup->getTotalLength() - << "%)"; - } - } - std::cout << " " - << "CN:" - << firstRequestGroup->getNumConnection(); - if(!firstRequestGroup->downloadFinished()) { - std::cout << " " - << "SPD:" - << std::fixed << std::setprecision(2) << stat.getDownloadSpeed()/1024.0 << "KiB/s"; - } - if(stat.getSessionUploadLength() > 0) { - std::cout << " " - << "UP:" - << std::fixed << std::setprecision(2) << stat.getUploadSpeed()/1024.0 << "KiB/s" - << "(" << Util::abbrevSize(stat.getAllTimeUploadLength()) << "B)"; - } - if(eta > 0) { - std::cout << " " - << "ETA:" - << Util::secfmt(eta); - } - std::cout << "]"; + RequestGroupHandle firstRequestGroup = requestGroupMan->getRequestGroup(0); + + printProgress(o, firstRequestGroup); + if(requestGroupMan->countRequestGroup() > 1) { - std::cout << "(" - << requestGroupMan->countRequestGroup()-1 - << "more...)"; + o << "(" + << requestGroupMan->countRequestGroup()-1 + << "more...)"; } } if(requestGroupMan->countRequestGroup() > 1 && !requestGroupMan->downloadFinished()) { TransferStat stat = requestGroupMan->calculateStat(); - std::cout << " " - << "[TOTAL SPD:" - << std::fixed << std::setprecision(2) << stat.getDownloadSpeed()/1024.0 << "KiB/s" << "]"; + o << " " + << "[TOTAL SPD:" + << std::fixed << std::setprecision(2) << stat.getDownloadSpeed()/1024.0 << "KiB/s" << "]"; } { FileAllocationEntryHandle entry = fileAllocationMan->getCurrentFileAllocationEntry(); if(!entry.isNull()) { - std::cout << " " - << "[FileAlloc:" - << "#" << entry->getRequestGroup()->getGID() << " " - << Util::abbrevSize(entry->getCurrentLength()) - << "B" - << "/" - << Util::abbrevSize(entry->getTotalLength()) - << "B" - << "("; + o << " " + << "[FileAlloc:" + << "#" << entry->getRequestGroup()->getGID() << " " + << Util::abbrevSize(entry->getCurrentLength()) + << "B" + << "/" + << Util::abbrevSize(entry->getTotalLength()) + << "B" + << "("; if(entry->getTotalLength() > 0) { - std::cout << 100*entry->getCurrentLength()/entry->getTotalLength(); + o << 100*entry->getCurrentLength()/entry->getTotalLength(); } else { - std::cout << "--"; + o << "--"; } - std::cout << "%)" - << "]"; + o << "%)" + << "]"; if(fileAllocationMan->countFileAllocationEntryInQueue() > 0) { - std::cout << "(" - << fileAllocationMan->countFileAllocationEntryInQueue() - << "waiting...)"; + o << "(" + << fileAllocationMan->countFileAllocationEntryInQueue() + << "waiting...)"; } } } @@ -165,29 +232,38 @@ ConsoleStatCalc::calculateStat(const RequestGroupManHandle& requestGroupMan, { CheckIntegrityEntryHandle entry = checkIntegrityMan->getFirstCheckIntegrityEntry(); if(!entry.isNull()) { - std::cout << " " - << "[Checksum:" - << "#" << entry->getRequestGroup()->getGID() << " " - << Util::abbrevSize(entry->getCurrentLength()) - << "B" - << "/" - << Util::abbrevSize(entry->getTotalLength()) - << "B" - << "(" - << 100*entry->getCurrentLength()/entry->getTotalLength() - << "%)"; - std::cout << "]"; + o << " " + << "[Checksum:" + << "#" << entry->getRequestGroup()->getGID() << " " + << Util::abbrevSize(entry->getCurrentLength()) + << "B" + << "/" + << Util::abbrevSize(entry->getTotalLength()) + << "B" + << "(" + << 100*entry->getCurrentLength()/entry->getTotalLength() + << "%)" + << "]"; if(checkIntegrityMan->countCheckIntegrityEntry() > 1) { - std::cout << "(" - << checkIntegrityMan->countCheckIntegrityEntry()-1 - << "more...)"; + o << "(" + << checkIntegrityMan->countCheckIntegrityEntry()-1 + << "more...)"; } } } #endif // ENABLE_MESSAGE_DIGEST + std::string readout = o.str(); if(isTTY) { + std::string::iterator last = readout.begin(); + if(readout.size() > cols) { + std::advance(last, cols); + } else { + last = readout.end(); + } + std::copy(readout.begin(), last, std::ostream_iterator(std::cout)); std::cout << std::flush; } else { + std::copy(readout.begin(), readout.end(), std::ostream_iterator(std::cout)); std::cout << std::endl; } } diff --git a/src/ConsoleStatCalc.h b/src/ConsoleStatCalc.h index c7f2f37f..014da63d 100644 --- a/src/ConsoleStatCalc.h +++ b/src/ConsoleStatCalc.h @@ -44,7 +44,13 @@ class ConsoleStatCalc:public StatCalc { private: Time _cp; + + time_t _summaryInterval; + + time_t _summaryIntervalCount; public: + ConsoleStatCalc(time_t summaryInterval); + virtual ~ConsoleStatCalc() {} virtual void diff --git a/src/HelpItemFactory.cc b/src/HelpItemFactory.cc index 6d1995d5..4d6d5e9d 100644 --- a/src/HelpItemFactory.cc +++ b/src/HelpItemFactory.cc @@ -470,6 +470,13 @@ TagContainerHandle HelpItemFactory::createHelpItems(const Option* op) item->addTag(TAG_FTP); tc->addItem(item); } + { + HelpItemHandle item(new HelpItem(PREF_SUMMARY_INTERVAL, + TEXT_SUMMARY_INTERVAL, + op->get(PREF_SUMMARY_INTERVAL))); + item->addTag(TAG_ADVANCED); + tc->addItem(item); + } { HelpItemHandle item(new HelpItem("help", TEXT_HELP, TAG_BASIC)); item->setAvailableValues diff --git a/src/OptionHandlerFactory.cc b/src/OptionHandlerFactory.cc index 16de9a2d..ad3fe84c 100644 --- a/src/OptionHandlerFactory.cc +++ b/src/OptionHandlerFactory.cc @@ -132,6 +132,7 @@ OptionHandlers OptionHandlerFactory::createOptionHandlers() handlers.push_back(SH(new BooleanOptionHandler(PREF_ASYNC_DNS))); #endif // ENABLE_ASYNC_DNS handlers.push_back(SH(new BooleanOptionHandler(PREF_FTP_REUSE_CONNECTION))); + handlers.push_back(SH(new NumberOptionHandler(PREF_SUMMARY_INTERVAL, 0, INT32_MAX))); return handlers; } diff --git a/src/RequestGroupMan.cc b/src/RequestGroupMan.cc index 506383ee..dee0e294 100644 --- a/src/RequestGroupMan.cc +++ b/src/RequestGroupMan.cc @@ -99,6 +99,12 @@ RequestGroupHandle RequestGroupMan::getRequestGroup(size_t index) const } } +const std::deque >& +RequestGroupMan::getRequestGroups() const +{ + return _requestGroups; +} + void RequestGroupMan::removeStoppedGroup() { unsigned int count = 0; diff --git a/src/RequestGroupMan.h b/src/RequestGroupMan.h index 3eb646ea..88a8908d 100644 --- a/src/RequestGroupMan.h +++ b/src/RequestGroupMan.h @@ -92,7 +92,9 @@ public: size_t countRequestGroup() const; SharedHandle getRequestGroup(size_t index) const; - + + const std::deque >& getRequestGroups() const; + void showDownloadResults(std::ostream& o) const; bool isSameFileBeingDownloaded(RequestGroup* requestGroup) const; diff --git a/src/main.cc b/src/main.cc index b5fb9cae..63e5dfd7 100644 --- a/src/main.cc +++ b/src/main.cc @@ -132,7 +132,7 @@ SharedHandle getStatCalc(const Option* op) if(op->getAsBool(PREF_QUIET)) { statCalc.reset(new NullStatCalc()); } else { - statCalc.reset(new ConsoleStatCalc()); + statCalc.reset(new ConsoleStatCalc(op->getAsInt(PREF_SUMMARY_INTERVAL))); } return statCalc; } diff --git a/src/option_processing.cc b/src/option_processing.cc index df203ab2..9e924f47 100644 --- a/src/option_processing.cc +++ b/src/option_processing.cc @@ -151,6 +151,7 @@ Option* createDefaultOption() op->put(PREF_ASYNC_DNS, V_FALSE); #endif // ENABLE_ASYNC_DNS op->put(PREF_FTP_REUSE_CONNECTION, V_TRUE); + op->put(PREF_SUMMARY_INTERVAL, "60"); return op; } @@ -226,6 +227,7 @@ Option* option_processing(int argc, char* const argv[]) { PREF_ASYNC_DNS, optional_argument, &lopt, 216 }, #endif // ENABLE_ASYNC_DNS { PREF_FTP_REUSE_CONNECTION, optional_argument, &lopt, 217 }, + { PREF_SUMMARY_INTERVAL, required_argument, &lopt, 218 }, #if defined ENABLE_BITTORRENT || ENABLE_METALINK { PREF_SHOW_FILES, no_argument, NULL, 'S' }, { PREF_SELECT_FILE, required_argument, &lopt, 21 }, @@ -435,6 +437,9 @@ Option* option_processing(int argc, char* const argv[]) case 217: cmdstream << PREF_FTP_REUSE_CONNECTION << "=" << toBoolArg(optarg) << "\n"; break; + case 218: + cmdstream << PREF_SUMMARY_INTERVAL << "=" << optarg << "\n"; + break; } break; } diff --git a/src/prefs.h b/src/prefs.h index f243107d..b881d80f 100644 --- a/src/prefs.h +++ b/src/prefs.h @@ -130,6 +130,8 @@ #define PREF_QUIET "quiet" // value: true | false #define PREF_ASYNC_DNS "async-dns" +// value: 1*digit +#define PREF_SUMMARY_INTERVAL "summary-interval" /** * FTP related preferences diff --git a/src/usage_text.h b/src/usage_text.h index 89564f55..fdb53f49 100644 --- a/src/usage_text.h +++ b/src/usage_text.h @@ -338,3 +338,6 @@ _(" -q, --quiet[=true|false] Make aria2 quite (no console output).") _(" --async-dns[=true|false] Enable asynchronous DNS.") #define TEXT_FTP_REUSE_CONNECTION \ _(" --ftp-reuse-connection[=true|false] Reuse connection in FTP.") +#define TEXT_SUMMARY_INTERVAL \ +_(" --summary-interval=SEC Set interval to output download progress summary.\n"\ + " Setting 0 suppresses the output.")