ソースを参照

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 年 前
コミット
c7c4d46672
6 ファイル変更260 行追加79 行削除
  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