mirror of https://github.com/aria2/aria2
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
parent
205afd20fd
commit
f7aeb86ccc
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)";
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.")
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue