Added --download-result option.

Added --download-result=OPT option.  This option changes the way
"Download Results" is formatted. If OPT is 'default', print GID,
status, average download speed and path/URI. If multiple files are
involved, path/URI of first requested file is printed and remaining
ones are omitted.  If OPT is 'full', print GID, status, average
download speed, percentage of progress and path/URI. The percentage of
progress and path/URI are printed for each requested file in each row.
pull/2/head
Tatsuhiro Tsujikawa 2011-08-22 23:05:06 +09:00
parent 205afd20fd
commit f7aeb86ccc
12 changed files with 208 additions and 31 deletions

View File

@ -60,7 +60,7 @@ BitfieldMan::BitfieldMan(size_t blockLength, uint64_t totalLength)
cachedFilteredTotalLength_(0)
{
if(blockLength_ > 0 && totalLength_ > 0) {
blocks_ = totalLength_/blockLength_+(totalLength_%blockLength_ ? 1 : 0);
blocks_ = (totalLength_+blockLength_-1)/blockLength_;
bitfieldLength_ = blocks_/8+(blocks_%8 ? 1 : 0);
bitfield_ = new unsigned char[bitfieldLength_];
useBitfield_ = new unsigned char[bitfieldLength_];
@ -772,6 +772,39 @@ bool BitfieldMan::isBitSetOffsetRange(uint64_t offset, uint64_t length) const
return true;
}
uint64_t BitfieldMan::getOffsetCompletedLength
(uint64_t offset,
uint64_t length) const
{
uint64_t res = 0;
if(length == 0 || totalLength_ <= offset) {
return 0;
}
if(totalLength_ < offset+length) {
length = totalLength_-offset;
}
size_t start = offset/blockLength_;
size_t end = (offset+length-1)/blockLength_;
if(start == end) {
if(isBitSet(start)) {
res = length;
}
} else {
if(isBitSet(start)) {
res += (start+1)*blockLength_-offset;
}
for(size_t i = start+1; i <= end-1; ++i) {
if(isBitSet(i)) {
res += blockLength_;
}
}
if(isBitSet(end)) {
res += offset+length-end*blockLength_;
}
}
return res;
}
uint64_t BitfieldMan::getMissingUnusedLength(size_t startingIndex) const
{
if(startingIndex < 0 || blocks_ <= startingIndex) {

View File

@ -264,6 +264,10 @@ public:
bool isBitSetOffsetRange(uint64_t offset, uint64_t length) const;
// Returns completed length in bytes in range [offset,
// offset+length). This function will not affected by filter.
uint64_t getOffsetCompletedLength(uint64_t offset, uint64_t length) const;
uint64_t getMissingUnusedLength(size_t startingIndex) const;
const unsigned char* getFilterBitfield() const

View File

@ -570,4 +570,26 @@ bool FileEntry::emptyRequestUri() const
return uris_.empty() && inFlightRequests_.empty() && requestPool_.empty();
}
void writeFilePath
(std::ostream& o,
const SharedHandle<FileEntry>& entry,
bool memory)
{
if(entry->getPath().empty()) {
std::vector<std::string> uris;
entry->getUris(uris);
if(uris.empty()) {
o << "n/a";
} else {
o << uris.front();
}
} else {
if(memory) {
o << "[MEMORY]" << File(entry->getPath()).getBasename();
} else {
o << entry->getPath();
}
}
}
} // namespace aria2

View File

@ -294,6 +294,14 @@ bool isUriSuppliedForRequsetFileEntry(InputIterator first, InputIterator last)
return false;
}
// Writes filename to given o. If memory is true, the output is
// "[MEMORY]" plus the basename of the filename. If there is no
// FileEntry, writes "n/a" to o.
void writeFilePath
(std::ostream& o,
const SharedHandle<FileEntry>& entry,
bool memory);
// Writes first filename to given o. If memory is true, the output is
// "[MEMORY]" plus the basename of the first filename. If there is no
// FileEntry, writes "n/a" to o. If more than 1 FileEntry are in the
@ -307,20 +315,8 @@ void writeFilePath
if(!e) {
o << "n/a";
} else {
if(e->getPath().empty()) {
std::vector<std::string> uris;
e->getUris(uris);
if(uris.empty()) {
o << "n/a";
} else {
o << uris.front();
}
} else {
if(memory) {
o << "[MEMORY]" << File(e->getPath()).getBasename();
} else {
o << e->getPath();
}
writeFilePath(o, e, memory);
if(!e->getPath().empty()) {
size_t count = countRequestedFileEntry(first, last);
if(count > 1) {
o << " (" << count-1 << "more)";

View File

@ -251,7 +251,8 @@ error_code::Value MultiUrlRequestInfo::execute()
if(!serverStatOf.empty()) {
e->getRequestGroupMan()->saveServerStat(serverStatOf);
}
e->getRequestGroupMan()->showDownloadResults(*summaryOut_);
e->getRequestGroupMan()->showDownloadResults
(*summaryOut_, option_->get(PREF_DOWNLOAD_RESULT) == A2_V_FULL);
summaryOut_->flush();
RequestGroupMan::DownloadStat s =

View File

@ -205,6 +205,16 @@ OptionHandlers OptionHandlerFactory::createOptionHandlers()
op->hide();
handlers.push_back(op);
}
{
SharedHandle<OptionHandler> op(new ParameterOptionHandler
(PREF_DOWNLOAD_RESULT,
TEXT_DOWNLOAD_RESULT,
A2_V_DEFAULT,
A2_V_DEFAULT,
A2_V_FULL));
op->addTag(TAG_ADVANCED);
handlers.push_back(op);
}
#ifdef ENABLE_ASYNC_DNS
{
SharedHandle<OptionHandler> op(new BooleanOptionHandler

View File

@ -588,25 +588,32 @@ RequestGroupMan::DownloadStat RequestGroupMan::getDownloadStat() const
lastError);
}
void RequestGroupMan::showDownloadResults(OutputFile& o) const
void RequestGroupMan::showDownloadResults(OutputFile& o, bool full) const
{
static const std::string MARK_OK("OK");
static const std::string MARK_ERR("ERR");
static const std::string MARK_INPR("INPR");
static const std::string MARK_RM("RM");
// Download Results:
// idx|stat|path/length
// ===+====+=======================================================================
o.printf("\n%s"
"\ngid|stat|avg speed |path/URI"
"\n===+====+===========+",
_("Download Results:"));
#ifdef __MINGW32__
int pathRowSize = 58;
#else // !__MINGW32__
int pathRowSize = 59;
#endif // !__MINGW32__
// Download Results:
// idx|stat|path/length
// ===+====+=======================================================================
o.printf("\n%s"
"\ngid|stat|avg speed |",
_("Download Results:"));
if(full) {
o.write(" %|path/URI"
"\n===+====+===========+===+");
pathRowSize -= 4;
} else {
o.write("path/URI"
"\n===+====+===========+");
}
std::string line(pathRowSize, '=');
o.printf("%s\n", line.c_str());
int ok = 0;
@ -633,8 +640,12 @@ void RequestGroupMan::showDownloadResults(OutputFile& o) const
status = MARK_ERR;
++err;
}
o.write(formatDownloadResult(status, *itr).c_str());
o.write("\n");
if(full) {
formatDownloadResultFull(o, status, *itr);
} else {
o.write(formatDownloadResult(status, *itr).c_str());
o.write("\n");
}
}
if(ok > 0 || err > 0 || inpr > 0 || rm > 0) {
o.printf("\n%s\n", _("Status Legend:"));
@ -654,11 +665,12 @@ void RequestGroupMan::showDownloadResults(OutputFile& o) const
}
}
std::string RequestGroupMan::formatDownloadResult
(const std::string& status,
const DownloadResultHandle& downloadResult) const
namespace {
void formatDownloadResultCommon
(std::ostream& o,
const std::string& status,
const DownloadResultHandle& downloadResult)
{
std::stringstream o;
o << std::setw(3) << downloadResult->gid << "|"
<< std::setw(4) << status << "|"
<< std::setw(11);
@ -670,6 +682,58 @@ std::string RequestGroupMan::formatDownloadResult
o << "n/a";
}
o << "|";
}
} // namespace
void RequestGroupMan::formatDownloadResultFull
(OutputFile& out,
const std::string& status,
const DownloadResultHandle& downloadResult) const
{
BitfieldMan bt(downloadResult->pieceLength, downloadResult->totalLength);
bt.setBitfield(reinterpret_cast<const unsigned char*>
(util::fromHex(downloadResult->bitfieldStr).data()),
downloadResult->bitfieldStr.size()/2);
bool head = true;
const std::vector<SharedHandle<FileEntry> >& fileEntries =
downloadResult->fileEntries;
for(std::vector<SharedHandle<FileEntry> >::const_iterator i =
fileEntries.begin(), eoi = fileEntries.end(); i != eoi; ++i) {
if(!(*i)->isRequested()) {
continue;
}
std::stringstream o;
if(head) {
formatDownloadResultCommon(o, status, downloadResult);
head = false;
} else {
o << " | | |";
}
if((*i)->getLength() == 0 || downloadResult->bitfieldStr.empty()) {
o << " -|";
} else {
uint64_t completedLength =
bt.getOffsetCompletedLength((*i)->getOffset(), (*i)->getLength());
o << std::setw(3) << 100*completedLength/(*i)->getLength() << "|";
}
writeFilePath(o, *i, downloadResult->inMemoryDownload);
o << "\n";
out.write(o.str().c_str());
}
if(head) {
std::stringstream o;
formatDownloadResultCommon(o, status, downloadResult);
o << " -|n/a\n";
out.write(o.str().c_str());
}
}
std::string RequestGroupMan::formatDownloadResult
(const std::string& status,
const DownloadResultHandle& downloadResult) const
{
std::stringstream o;
formatDownloadResultCommon(o, status, downloadResult);
const std::vector<SharedHandle<FileEntry> >& fileEntries =
downloadResult->fileEntries;
writeFilePath(fileEntries.begin(), fileEntries.end(), o,

View File

@ -85,6 +85,11 @@ private:
size_t maxDownloadResult_;
void formatDownloadResultFull
(OutputFile& out,
const std::string& status,
const DownloadResultHandle& downloadResult) const;
std::string formatDownloadResult
(const std::string& status,
const DownloadResultHandle& downloadResult) const;
@ -160,7 +165,7 @@ public:
bool removeReservedGroup(a2_gid_t gid);
void showDownloadResults(OutputFile& o) const;
void showDownloadResults(OutputFile& o, bool full) const;
bool isSameFileBeingDownloaded(RequestGroup* requestGroup) const;

View File

@ -45,6 +45,7 @@ const std::string A2_V_DEFAULT("default");
const std::string V_NONE("none");
const std::string V_MEM("mem");
const std::string V_ALL("all");
const std::string A2_V_FULL("full");
/**
* General preferences
@ -218,6 +219,8 @@ const std::string PREF_STREAM_PIECE_SELECTOR("stream-piece-selector");
const std::string PREF_TRUNCATE_CONSOLE_READOUT("truncate-console-readout");
// value: true | false
const std::string PREF_PAUSE("pause");
// value: default | full
const std::string PREF_DOWNLOAD_RESULT("download-result");
/**
* FTP related preferences

View File

@ -49,6 +49,7 @@ extern const std::string A2_V_DEFAULT;
extern const std::string V_NONE;
extern const std::string V_MEM;
extern const std::string V_ALL;
extern const std::string A2_V_FULL;
/**
* General preferences
*/
@ -221,6 +222,8 @@ extern const std::string PREF_STREAM_PIECE_SELECTOR;
extern const std::string PREF_TRUNCATE_CONSOLE_READOUT;
// value: true | false
extern const std::string PREF_PAUSE;
// value: default | full
extern const std::string PREF_DOWNLOAD_RESULT;
/**
* FTP related preferences

View File

@ -806,3 +806,15 @@
#define TEXT_RPC_ALLOW_ORIGIN_ALL \
_(" --rpc-allow-origin-all[=true|false] Add Access-Control-Allow-Origin header\n" \
" field with value '*' to the RPC response.")
#define TEXT_DOWNLOAD_RESULT \
_(" --download-result=OPT This option changes the way \"Download Results\"\n" \
" is formatted. If OPT is 'default', print GID,\n" \
" status, average download speed and path/URI. If\n" \
" multiple files are involved, path/URI of first\n" \
" requested file is printed and remaining ones are\n" \
" omitted.\n" \
" If OPT is 'full', print GID, status, average\n" \
" download speed, percentage of progress and\n" \
" path/URI. The percentage of progress and\n" \
" path/URI are printed for each requested file in\n" \
" each row.")

View File

@ -26,6 +26,7 @@ class BitfieldManTest:public CppUnit::TestFixture {
CPPUNIT_TEST(testGetSparseMissingUnusedIndex_setBit);
CPPUNIT_TEST(testGetSparseMissingUnusedIndex_withMinSplitSize);
CPPUNIT_TEST(testIsBitSetOffsetRange);
CPPUNIT_TEST(testGetOffsetCompletedLength);
CPPUNIT_TEST(testGetMissingUnusedLength);
CPPUNIT_TEST(testSetBitRange);
CPPUNIT_TEST(testGetAllMissingIndexes);
@ -57,6 +58,7 @@ public:
void testGetSparseMissingUnusedIndex_setBit();
void testGetSparseMissingUnusedIndex_withMinSplitSize();
void testIsBitSetOffsetRange();
void testGetOffsetCompletedLength();
void testGetMissingUnusedLength();
void testSetBitRange();
void testCountFilteredBlock();
@ -445,6 +447,28 @@ void BitfieldManTest::testIsBitSetOffsetRange()
CPPUNIT_ASSERT(!bitfield.isBitSetOffsetRange(pieceLength*100, pieceLength*3));
}
void BitfieldManTest::testGetOffsetCompletedLength()
{
BitfieldMan bt(1024, 1024*20);
// 00000|00000|00000|00000
CPPUNIT_ASSERT_EQUAL((uint64_t)0, bt.getOffsetCompletedLength(0, 1024));
CPPUNIT_ASSERT_EQUAL((uint64_t)0, bt.getOffsetCompletedLength(0, 0));
for(size_t i = 2; i <= 4; ++i) {
bt.setBit(i);
}
// 00111|00000|00000|00000
CPPUNIT_ASSERT_EQUAL((uint64_t)3072, bt.getOffsetCompletedLength(2048, 3072));
CPPUNIT_ASSERT_EQUAL((uint64_t)3071, bt.getOffsetCompletedLength(2047, 3072));
CPPUNIT_ASSERT_EQUAL((uint64_t)3071, bt.getOffsetCompletedLength(2049, 3072));
CPPUNIT_ASSERT_EQUAL((uint64_t)0, bt.getOffsetCompletedLength(2048, 0));
CPPUNIT_ASSERT_EQUAL((uint64_t)1, bt.getOffsetCompletedLength(2048, 1));
CPPUNIT_ASSERT_EQUAL((uint64_t)0, bt.getOffsetCompletedLength(2047, 1));
CPPUNIT_ASSERT_EQUAL((uint64_t)3072, bt.getOffsetCompletedLength(0, 1024*20));
CPPUNIT_ASSERT_EQUAL((uint64_t)3072,
bt.getOffsetCompletedLength(0, 1024*20+10));
CPPUNIT_ASSERT_EQUAL((uint64_t)0, bt.getOffsetCompletedLength(1024*20, 1));
}
void BitfieldManTest::testGetMissingUnusedLength()
{
uint64_t totalLength = 1024*10+10;