Better auto-renaming

Up until now aria2 file auto renaming worked by just appending a new
unique number to the file path, behind the file name extension, if any,
changing what most other programs consider the file extension in the
process.
Now, aria2 will attempt to insert the number before the file extension,
leaving the extension intact, so that e.g. a ".jpg" still is a ".jpg"
file and opens in your configured image viewer when clicking it.
If a file has no extension (incl. so called "dot files"), the number
will be appended to the file name as usual.

Note: This is a potentially breaking change that might break third party
scripts that rely on aria2 auto file renaming producing a certain format
for renamed files. Please fix your scripts accordingly.

Closes GH-709
pull/709/merge
Nils Maier 2016-07-18 23:11:55 +02:00
parent b6d9e7a01f
commit 665a1d1b35
4 changed files with 78 additions and 4 deletions

View File

@ -1175,7 +1175,8 @@ Advanced Options
Rename file name if the same file already exists.
This option works only in HTTP(S)/FTP download.
The new file name has a dot and a number(1..9999) appended.
The new file name has a dot and a number(1..9999) appended after the
name, but before the file extension, if any.
Default: ``true``
.. option:: --auto-save-interval=<SEC>

View File

@ -778,8 +778,28 @@ void RequestGroup::tryAutoFileRenaming()
fmt("File renaming failed: %s", getFirstFilePath().c_str()),
error_code::FILE_RENAMING_FAILED);
}
auto fn = filepath;
std::string ext;
const auto idx = fn.find_last_of(".");
const auto slash = fn.find_last_of("\\/");
// Do extract the extension, as in "file.ext" = "file" and ".ext",
// but do not consider ".file" to be a file name without extension instead
// of a blank file name and an extension of ".file"
if (idx != std::string::npos &&
// fn has no path component and starts with a dot, but has no extension
// otherwise
idx != 0 &&
// has a file path component if we found a slash.
// if slash == idx - 1 this means a form of "*/.*", so the file name
// starts with a dot, has no extension otherwise, and therefore do not
// extract an extension either
(slash == std::string::npos || slash < idx - 1)
) {
ext = fn.substr(idx);
fn = fn.substr(0, idx);
}
for (int i = 1; i < 10000; ++i) {
auto newfilename = fmt("%s.%d", filepath.c_str(), i);
auto newfilename = fmt("%s.%d%s", fn.c_str(), i, ext.c_str());
File newfile(newfilename);
File ctrlfile(newfile.getPath() + DefaultBtProgressInfoFile::getSuffix());
if (!newfile.exists() || (newfile.exists() && ctrlfile.exists())) {

View File

@ -199,8 +199,6 @@ private:
void initializePostDownloadHandler();
void tryAutoFileRenaming();
// Returns the result code of this RequestGroup. If the download
// finished, then returns error_code::FINISHED. If the
// download didn't finish and error result is available in
@ -219,6 +217,8 @@ public:
bool isCheckIntegrityReady();
void tryAutoFileRenaming();
const std::shared_ptr<SegmentMan>& getSegmentMan() const
{
return segmentMan_;

View File

@ -14,6 +14,7 @@ class RequestGroupTest : public CppUnit::TestFixture {
CPPUNIT_TEST_SUITE(RequestGroupTest);
CPPUNIT_TEST(testGetFirstFilePath);
CPPUNIT_TEST(testTryAutoFileRenaming);
CPPUNIT_TEST(testCreateDownloadResult);
CPPUNIT_TEST_SUITE_END();
@ -24,6 +25,7 @@ public:
void setUp() { option_.reset(new Option()); }
void testGetFirstFilePath();
void testTryAutoFileRenaming();
void testCreateDownloadResult();
};
@ -44,6 +46,57 @@ void RequestGroupTest::testGetFirstFilePath()
CPPUNIT_ASSERT_EQUAL(std::string("[MEMORY]myfile"), group.getFirstFilePath());
}
void RequestGroupTest::testTryAutoFileRenaming()
{
std::shared_ptr<DownloadContext> ctx(
new DownloadContext(1_k, 1_k, "/tmp/myfile"));
RequestGroup group(GroupId::create(), option_);
group.setDownloadContext(ctx);
option_->put(PREF_AUTO_FILE_RENAMING, "false");
try {
group.tryAutoFileRenaming();
}
catch (const Exception& ex) {
CPPUNIT_ASSERT_EQUAL(error_code::FILE_ALREADY_EXISTS, ex.getErrorCode());
}
option_->put(PREF_AUTO_FILE_RENAMING, "true");
group.tryAutoFileRenaming();
CPPUNIT_ASSERT_EQUAL(std::string("/tmp/myfile.1"), group.getFirstFilePath());
ctx->getFirstFileEntry()->setPath("/tmp/myfile.txt");
group.tryAutoFileRenaming();
CPPUNIT_ASSERT_EQUAL(std::string("/tmp/myfile.1.txt"), group.getFirstFilePath());
ctx->getFirstFileEntry()->setPath("/tmp.txt/myfile");
group.tryAutoFileRenaming();
CPPUNIT_ASSERT_EQUAL(std::string("/tmp.txt/myfile.1"), group.getFirstFilePath());
ctx->getFirstFileEntry()->setPath("/tmp.txt/myfile.txt");
group.tryAutoFileRenaming();
CPPUNIT_ASSERT_EQUAL(std::string("/tmp.txt/myfile.1.txt"), group.getFirstFilePath());
ctx->getFirstFileEntry()->setPath(".bashrc");
group.tryAutoFileRenaming();
CPPUNIT_ASSERT_EQUAL(std::string(".bashrc.1"), group.getFirstFilePath());
ctx->getFirstFileEntry()->setPath(".bashrc.txt");
group.tryAutoFileRenaming();
CPPUNIT_ASSERT_EQUAL(std::string(".bashrc.1.txt"), group.getFirstFilePath());
ctx->getFirstFileEntry()->setPath("/tmp.txt/.bashrc");
group.tryAutoFileRenaming();
CPPUNIT_ASSERT_EQUAL(std::string("/tmp.txt/.bashrc.1"), group.getFirstFilePath());
ctx->getFirstFileEntry()->setPath("/tmp.txt/.bashrc.txt");
group.tryAutoFileRenaming();
CPPUNIT_ASSERT_EQUAL(std::string("/tmp.txt/.bashrc.1.txt"), group.getFirstFilePath());
}
void RequestGroupTest::testCreateDownloadResult()
{
std::shared_ptr<DownloadContext> ctx(