Browse Source

Add changeOption and DownloadHandle::getOption API

In aria2c.rst, define section label for "Input File" section so that
it can be referenced from other document.
Tatsuhiro Tsujikawa 12 năm trước cách đây
mục cha
commit
c7c4d46672
6 tập tin đã thay đổi với 260 bổ sung79 xóa
  1. 2 0
      doc/manual-src/en/aria2c.rst
  2. 77 79
      src/RpcMethodImpl.cc
  3. 5 0
      src/RpcMethodImpl.h
  4. 83 0
      src/aria2api.cc
  5. 52 0
      src/includes/aria2/aria2.h
  6. 41 0
      test/Aria2ApiTest.cc

+ 2 - 0
doc/manual-src/en/aria2c.rst

@@ -1733,6 +1733,8 @@ if you have a torrent or metalink with chunk checksums for the file,
 you can resume the download without a control file by giving -V option
 to aria2c in command-line.
 
+.. _input-file:
+
 Input File
 ~~~~~~~~~~
 

+ 77 - 79
src/RpcMethodImpl.cc

@@ -1082,85 +1082,6 @@ SharedHandle<ValueBase> RemoveDownloadResultRpcMethod::process
   return VLB_OK;
 }
 
-namespace {
-void changeOption
-(const SharedHandle<RequestGroup>& group,
- const Option& option,
- DownloadEngine* e)
-{
-  const SharedHandle<DownloadContext>& dctx = group->getDownloadContext();
-  const SharedHandle<Option>& grOption = group->getOption();
-  grOption->merge(option);
-  if(option.defined(PREF_CHECKSUM)) {
-    const std::string& checksum = grOption->get(PREF_CHECKSUM);
-    std::pair<Scip, Scip> p;
-    util::divide(p, checksum.begin(), checksum.end(), '=');
-    std::string hashType(p.first.first, p.first.second);
-    util::lowercase(hashType);
-    dctx->setDigest(hashType, util::fromHex(p.second.first, p.second.second));
-  }
-  if(option.defined(PREF_SELECT_FILE)) {
-    SegList<int> sgl;
-    util::parseIntSegments(sgl, grOption->get(PREF_SELECT_FILE));
-    sgl.normalize();
-    dctx->setFileFilter(sgl);
-  }
-  if(option.defined(PREF_SPLIT)) {
-    group->setNumConcurrentCommand(grOption->getAsInt(PREF_SPLIT));
-  }
-  if(option.defined(PREF_MAX_CONNECTION_PER_SERVER)) {
-    int maxConn = grOption->getAsInt(PREF_MAX_CONNECTION_PER_SERVER);
-    const std::vector<SharedHandle<FileEntry> >& files = dctx->getFileEntries();
-    for(std::vector<SharedHandle<FileEntry> >::const_iterator i = files.begin(),
-          eoi = files.end(); i != eoi; ++i) {
-      (*i)->setMaxConnectionPerServer(maxConn);
-    }
-  }
-  if(option.defined(PREF_DIR) || option.defined(PREF_OUT)) {
-    if(dctx->getFileEntries().size() == 1
-#ifdef ENABLE_BITTORRENT
-       && !dctx->hasAttribute(CTX_ATTR_BT)
-#endif // ENABLE_BITTORRENT
-       ) {
-      dctx->getFirstFileEntry()->setPath
-        (grOption->blank(PREF_OUT) ? A2STR::NIL :
-         util::applyDir(grOption->get(PREF_DIR), grOption->get(PREF_OUT)));
-    }
-  }
-#ifdef ENABLE_BITTORRENT
-  if(option.defined(PREF_DIR) || option.defined(PREF_INDEX_OUT)) {
-    if(dctx->hasAttribute(CTX_ATTR_BT)) {
-      std::istringstream indexOutIn(grOption->get(PREF_INDEX_OUT));
-      std::vector<std::pair<size_t, std::string> > indexPaths =
-        util::createIndexPaths(indexOutIn);
-      for(std::vector<std::pair<size_t, std::string> >::const_iterator i =
-            indexPaths.begin(), eoi = indexPaths.end(); i != eoi; ++i) {
-        dctx->setFilePathWithIndex
-          ((*i).first,
-           util::applyDir(grOption->get(PREF_DIR), (*i).second));
-      }
-    }
-  }
-#endif // ENABLE_BITTORRENT
-  if(option.defined(PREF_MAX_DOWNLOAD_LIMIT)) {
-    group->setMaxDownloadSpeedLimit
-      (grOption->getAsInt(PREF_MAX_DOWNLOAD_LIMIT));
-  }
-  if(option.defined(PREF_MAX_UPLOAD_LIMIT)) {
-    group->setMaxUploadSpeedLimit(grOption->getAsInt(PREF_MAX_UPLOAD_LIMIT));
-  }
-#ifdef ENABLE_BITTORRENT
-  const SharedHandle<BtObject>& btObject =
-    e->getBtRegistry()->get(group->getGID());
-  if(btObject) {
-    if(option.defined(PREF_BT_MAX_PEERS)) {
-      btObject->btRuntime->setMaxPeers(grOption->getAsInt(PREF_BT_MAX_PEERS));
-    }
-  }
-#endif // ENABLE_BITTORRENT
-}
-} // namespace
-
 SharedHandle<ValueBase> ChangeOptionRpcMethod::process
 (const RpcRequest& req, DownloadEngine* e)
 {
@@ -1544,4 +1465,81 @@ bool pauseRequestGroup
   }
 }
 
+void changeOption
+(const SharedHandle<RequestGroup>& group,
+ const Option& option,
+ DownloadEngine* e)
+{
+  const SharedHandle<DownloadContext>& dctx = group->getDownloadContext();
+  const SharedHandle<Option>& grOption = group->getOption();
+  grOption->merge(option);
+  if(option.defined(PREF_CHECKSUM)) {
+    const std::string& checksum = grOption->get(PREF_CHECKSUM);
+    std::pair<Scip, Scip> p;
+    util::divide(p, checksum.begin(), checksum.end(), '=');
+    std::string hashType(p.first.first, p.first.second);
+    util::lowercase(hashType);
+    dctx->setDigest(hashType, util::fromHex(p.second.first, p.second.second));
+  }
+  if(option.defined(PREF_SELECT_FILE)) {
+    SegList<int> sgl;
+    util::parseIntSegments(sgl, grOption->get(PREF_SELECT_FILE));
+    sgl.normalize();
+    dctx->setFileFilter(sgl);
+  }
+  if(option.defined(PREF_SPLIT)) {
+    group->setNumConcurrentCommand(grOption->getAsInt(PREF_SPLIT));
+  }
+  if(option.defined(PREF_MAX_CONNECTION_PER_SERVER)) {
+    int maxConn = grOption->getAsInt(PREF_MAX_CONNECTION_PER_SERVER);
+    const std::vector<SharedHandle<FileEntry> >& files = dctx->getFileEntries();
+    for(std::vector<SharedHandle<FileEntry> >::const_iterator i = files.begin(),
+          eoi = files.end(); i != eoi; ++i) {
+      (*i)->setMaxConnectionPerServer(maxConn);
+    }
+  }
+  if(option.defined(PREF_DIR) || option.defined(PREF_OUT)) {
+    if(dctx->getFileEntries().size() == 1
+#ifdef ENABLE_BITTORRENT
+       && !dctx->hasAttribute(CTX_ATTR_BT)
+#endif // ENABLE_BITTORRENT
+       ) {
+      dctx->getFirstFileEntry()->setPath
+        (grOption->blank(PREF_OUT) ? A2STR::NIL :
+         util::applyDir(grOption->get(PREF_DIR), grOption->get(PREF_OUT)));
+    }
+  }
+#ifdef ENABLE_BITTORRENT
+  if(option.defined(PREF_DIR) || option.defined(PREF_INDEX_OUT)) {
+    if(dctx->hasAttribute(CTX_ATTR_BT)) {
+      std::istringstream indexOutIn(grOption->get(PREF_INDEX_OUT));
+      std::vector<std::pair<size_t, std::string> > indexPaths =
+        util::createIndexPaths(indexOutIn);
+      for(std::vector<std::pair<size_t, std::string> >::const_iterator i =
+            indexPaths.begin(), eoi = indexPaths.end(); i != eoi; ++i) {
+        dctx->setFilePathWithIndex
+          ((*i).first,
+           util::applyDir(grOption->get(PREF_DIR), (*i).second));
+      }
+    }
+  }
+#endif // ENABLE_BITTORRENT
+  if(option.defined(PREF_MAX_DOWNLOAD_LIMIT)) {
+    group->setMaxDownloadSpeedLimit
+      (grOption->getAsInt(PREF_MAX_DOWNLOAD_LIMIT));
+  }
+  if(option.defined(PREF_MAX_UPLOAD_LIMIT)) {
+    group->setMaxUploadSpeedLimit(grOption->getAsInt(PREF_MAX_UPLOAD_LIMIT));
+  }
+#ifdef ENABLE_BITTORRENT
+  const SharedHandle<BtObject>& btObject =
+    e->getBtRegistry()->get(group->getGID());
+  if(btObject) {
+    if(option.defined(PREF_BT_MAX_PEERS)) {
+      btObject->btRuntime->setMaxPeers(grOption->getAsInt(PREF_BT_MAX_PEERS));
+    }
+  }
+#endif // ENABLE_BITTORRENT
+}
+
 } // namespace aria2

+ 5 - 0
src/RpcMethodImpl.h

@@ -608,6 +608,11 @@ void gatherBitTorrentMetadata
 bool pauseRequestGroup
 (const SharedHandle<RequestGroup>& group, bool reserved,  bool forcePause);
 
+void changeOption
+(const SharedHandle<RequestGroup>& group,
+ const Option& option,
+ DownloadEngine* e);
+
 } // namespace aria2
 
 #endif // D_RPC_METHOD_IMPL_H

+ 83 - 0
src/aria2api.cc

@@ -225,6 +225,27 @@ void apiGatherRequestOption(Option* option, const KeyVals& options,
 }
 } // namespace
 
+namespace {
+void apiGatherChangeableOption(Option* option, const KeyVals& options,
+                               const SharedHandle<OptionParser>& optionParser)
+{
+  apiGatherOption(options.begin(), options.end(),
+                  std::mem_fun(&OptionHandler::getChangeOption),
+                  option, optionParser);
+}
+} // namespace
+
+namespace {
+void apiGatherChangeableOptionForReserved
+(Option* option, const KeyVals& options,
+ const SharedHandle<OptionParser>& optionParser)
+{
+  apiGatherOption(options.begin(), options.end(),
+                  std::mem_fun(&OptionHandler::getChangeOptionForReserved),
+                  option, optionParser);
+}
+} // namespace
+
 namespace {
 void addRequestGroup(const SharedHandle<RequestGroup>& group,
                      const SharedHandle<DownloadEngine>& e,
@@ -419,6 +440,32 @@ int changePosition(Session* session, const A2Gid& gid, int pos, OffsetMode how)
   }
 }
 
+int changeOption(Session* session, const A2Gid& gid, const KeyVals& options)
+{
+  const SharedHandle<DownloadEngine>& e =
+    session->context->reqinfo->getDownloadEngine();
+  SharedHandle<RequestGroup> group = e->getRequestGroupMan()->findGroup(gid);
+  if(group) {
+    Option option;
+    try {
+      if(group->getState() == RequestGroup::STATE_ACTIVE) {
+        apiGatherChangeableOption(&option, options,
+                                  OptionParser::getInstance());
+      } else {
+        apiGatherChangeableOptionForReserved(&option, options,
+                                             OptionParser::getInstance());
+      }
+    } catch(RecoverableException& err) {
+      A2_LOG_INFO_EX(EX_EXCEPTION_CAUGHT, err);
+      return -1;
+    }
+    changeOption(group, option, e.get());
+    return 0;
+  } else {
+    return -1;
+  }
+}
+
 std::vector<A2Gid> getActiveDownload(Session* session)
 {
   const SharedHandle<DownloadEngine>& e =
@@ -527,6 +574,23 @@ void createFileEntry
 }
 } // namespace
 
+namespace {
+template<typename OutputIterator>
+void pushRequestOption
+(OutputIterator out,
+ const SharedHandle<Option>& option,
+ const SharedHandle<OptionParser>& oparser)
+{
+  for(size_t i = 1, len = option::countOption(); i < len; ++i) {
+    const Pref* pref = option::i2p(i);
+    const OptionHandler* h = oparser->find(pref);
+    if(h && h->getInitialOption() && option->defined(pref)) {
+      out++ = KeyVals::value_type(pref->k, option->get(pref));
+    }
+  }
+}
+} // namespace
+
 namespace {
 struct RequestGroupDH : public DownloadHandle {
   RequestGroupDH(const SharedHandle<RequestGroup>& group)
@@ -660,6 +724,17 @@ struct RequestGroupDH : public DownloadHandle {
 #endif // ENABLE_BITTORRENT
     return res;
   }
+  virtual const std::string& getOption(const std::string& name)
+  {
+    return group->getOption()->get(option::k2p(name));
+  }
+  virtual KeyVals getOption()
+  {
+    KeyVals res;
+    pushRequestOption(std::back_inserter(res), group->getOption(),
+                      OptionParser::getInstance());
+    return res;
+  }
   SharedHandle<RequestGroup> group;
   TransferStat ts;
 };
@@ -761,6 +836,14 @@ struct DownloadResultDH : public DownloadHandle {
   {
     return BtMetaInfoData();
   }
+  virtual const std::string& getOption(const std::string& name)
+  {
+    return A2STR::NIL;
+  }
+  virtual KeyVals getOption()
+  {
+    return KeyVals();
+  }
   SharedHandle<DownloadResult> dr;
 };
 } // namespace

+ 52 - 0
src/includes/aria2/aria2.h

@@ -404,6 +404,38 @@ int pauseDownload(Session* session, const A2Gid& gid, bool force = false);
  */
 int unpauseDownload(Session* session, const A2Gid& gid);
 
+/**
+ * @function
+ *
+ * Apply options in the |options| to the download denoted by the |gid|
+ * dynamically. The following options can be changed for downloads in
+ * :c:macro:`DOWNLOAD_ACTIVE` status:
+ *
+ * * :option:`bt-max-peers <--bt-max-peers>`
+ * * :option:`bt-request-peer-speed-limit <--bt-request-peer-speed-limit>`
+ * * :option:`bt-remove-unselected-file <--bt-remove-unselected-file>`
+ * * :option:`force-save <--force-save>`
+ * * :option:`max-download-limit <--max-download-limit>`
+ * * :option:`max-upload-limit <-u>`
+ *
+ * For downloads in :c:macro:`DOWNLOAD_WAITING` or
+ * :c:macro:`DOWNLOAD_PAUSED` status, in addition to the above
+ * options, options listed in :ref:`input-file` subsection are available,
+ * except for following options:
+ * :option:`dry-run <--dry-run>`,
+ * :option:`metalink-base-uri <--metalink-base-uri>`,
+ * :option:`parameterized-uri <-P>`,
+ * :option:`pause <--pause>`,
+ * :option:`piece-length <--piece-length>` and
+ * :option:`rpc-save-upload-metadata <--rpc-save-upload-metadata>` option.
+ *
+ * The options which are not applicable or unknown, they are just
+ * ignored.
+ *
+ * This function returns 0 if it succeeds, or negative error code.
+ */
+int changeOption(Session* session, const A2Gid& gid, const KeyVals& options);
+
 /**
  * @enum
  *
@@ -733,6 +765,26 @@ public:
    * stopped/completed.
    */
   virtual BtMetaInfoData getBtMetaInfo() = 0;
+  /**
+   * Returns the option value denoted by the |name|.  If the option
+   * denoted by the |name| is not available, returns empty string.
+   *
+   * Calling this function for the download which is not in
+   * :c:macro:`DOWNLOAD_ACTIVE`, :c:macro:`DOWNLOAD_PAUSED` or
+   * :c:macro:`DOWNLOAD_WAITING` will return empty string.
+   */
+  virtual const std::string& getOption(const std::string& name) = 0;
+  /**
+   * Returns options for this download. Note that this function does
+   * not return options which have no default value and have not been
+   * set by :func:`sessionNew()`, configuration files or API
+   * functions.
+   *
+   * Calling this function for the download which is not in
+   * :c:macro:`DOWNLOAD_ACTIVE`, :c:macro:`DOWNLOAD_PAUSED` or
+   * :c:macro:`DOWNLOAD_WAITING` will return empty array.
+   */
+  virtual KeyVals getOption() = 0;
 };
 
 /**

+ 41 - 0
test/Aria2ApiTest.cc

@@ -12,6 +12,7 @@ class Aria2ApiTest:public CppUnit::TestFixture {
   CPPUNIT_TEST(testAddTorrent);
   CPPUNIT_TEST(testRemovePause);
   CPPUNIT_TEST(testChangePosition);
+  CPPUNIT_TEST(testChangeOption);
   CPPUNIT_TEST_SUITE_END();
 
   Session* session_;
@@ -33,6 +34,7 @@ public:
   void testAddTorrent();
   void testRemovePause();
   void testChangePosition();
+  void testChangeOption();
 };
 
 CPPUNIT_TEST_SUITE_REGISTRATION(Aria2ApiTest);
@@ -149,4 +151,43 @@ void Aria2ApiTest::testChangePosition()
 
 }
 
+void Aria2ApiTest::testChangeOption()
+{
+  A2Gid gid;
+  std::vector<std::string> uris(1);
+  KeyVals options;
+  uris[0] = "http://localhost/1";
+  options.push_back(KeyVals::value_type("dir", "mydownload"));
+  CPPUNIT_ASSERT_EQUAL(0, addUri(session_, &gid, uris, options));
+
+  DownloadHandle* hd = getDownloadHandle(session_, gid);
+  CPPUNIT_ASSERT(hd);
+  CPPUNIT_ASSERT_EQUAL(1, hd->getNumFiles());
+  FileData file = hd->getFile(1);
+  CPPUNIT_ASSERT_EQUAL((size_t)1, file.uris.size());
+  CPPUNIT_ASSERT_EQUAL(uris[0], file.uris[0].uri);
+
+  CPPUNIT_ASSERT_EQUAL(std::string("mydownload"), hd->getOption("dir"));
+  CPPUNIT_ASSERT(hd->getOption("unknown").empty());
+  KeyVals retopts = hd->getOption();
+  CPPUNIT_ASSERT(std::find(retopts.begin(), retopts.end(),
+                           KeyVals::value_type("dir", "mydownload"))
+                 != retopts.end());
+  deleteDownloadHandle(hd);
+  // failure with null gid
+  CPPUNIT_ASSERT_EQUAL(-1, changeOption(session_, (A2Gid)0, options));
+  // change option
+  options.clear();
+  options.push_back(KeyVals::value_type("dir", "newlocation"));
+  CPPUNIT_ASSERT_EQUAL(0, changeOption(session_, gid, options));
+  hd = getDownloadHandle(session_, gid);
+  CPPUNIT_ASSERT_EQUAL(std::string("newlocation"), hd->getOption("dir"));
+  deleteDownloadHandle(hd);
+  // failure with bad option value
+  options.clear();
+  options.push_back(KeyVals::value_type("file-allocation", "foo"));
+  CPPUNIT_ASSERT_EQUAL(-1, changeOption(session_, gid, options));
+}
+
+
 } // namespace aria2