From 9db8f1e9c0fd480418970e474b32ce2ac3e20562 Mon Sep 17 00:00:00 2001 From: laoyuyu <511455842@qq.com> Date: Wed, 9 Dec 2020 21:22:01 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E7=BB=84=E5=90=88=E4=BB=BB?= =?UTF-8?q?=E5=8A=A1=E5=9C=A8=E8=8E=B7=E5=8F=96=E5=AD=90=E4=BB=BB=E5=8A=A1?= =?UTF-8?q?=E4=BF=A1=E6=81=AF=E7=9A=84=E8=BF=87=E7=A8=8B=E4=B8=AD=EF=BC=8C?= =?UTF-8?q?=E6=89=8B=E5=8A=A8=E5=81=9C=E6=AD=A2=E6=88=96=E5=88=A0=E9=99=A4?= =?UTF-8?q?=EF=BC=8C=E6=B2=A1=E6=9C=89=E5=9B=9E=E8=B0=83=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../aria/core/download/CheckDGEntityUtil.java | 10 +++--- DEV_LOG.md | 1 + .../com/arialyy/aria/ftp/AbsFtpInfoTask.java | 35 ++++++++++++++----- .../aria/ftp/download/FtpDFileInfoTask.java | 12 +++---- .../aria/ftp/download/FtpDGInfoTask.java | 16 ++++----- .../aria/ftp/upload/FtpUFileInfoTask.java | 8 ++--- .../aria/http/download/HttpDFileInfoTask.java | 27 ++++++++++---- .../aria/http/download/HttpDGInfoTask.java | 24 +++++++++++++ .../com/arialyy/aria/m3u8/M3U8InfoTask.java | 21 +++++++++-- .../aria/core/group/AbsGroupLoader.java | 8 ++++- .../aria/core/group/SimpleSubQueue.java | 19 ++++++---- .../aria/core/loader/AbsNormalLoader.java | 2 ++ .../arialyy/aria/core/loader/IInfoTask.java | 10 ++++++ .../arialyy/aria/sftp/AbsSFtpInfoTask.java | 26 +++++++++++++- .../aria/sftp/download/SFtpDInfoTask.java | 7 ++-- .../aria/sftp/upload/SFtpUInfoTask.java | 2 +- 16 files changed, 174 insertions(+), 54 deletions(-) diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/CheckDGEntityUtil.java b/Aria/src/main/java/com/arialyy/aria/core/download/CheckDGEntityUtil.java index 5370f800..67c6e0a2 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/CheckDGEntityUtil.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/CheckDGEntityUtil.java @@ -69,7 +69,8 @@ public class CheckDGEntityUtil implements ICheckEntityUtil { ALog.e(TAG, "文件夹路径不能为null"); return false; } - if (!FileUtil.canWrite(dirPath)){ + File file = new File(dirPath); + if (!FileUtil.canWrite(file.getParent()) && !FileUtil.canWrite(dirPath)) { ALog.e(TAG, String.format("路径【%s】不可写", dirPath)); return false; } @@ -77,7 +78,7 @@ public class CheckDGEntityUtil implements ICheckEntityUtil { ALog.e(TAG, String.format("文件夹路径【%s】错误", dirPath)); return false; } - File file = new File(dirPath); + if (file.isFile()) { ALog.e(TAG, String.format("路径【%s】是文件,请设置文件夹路径", dirPath)); return false; @@ -131,8 +132,9 @@ public class CheckDGEntityUtil implements ICheckEntityUtil { * @return false 任务不再执行,true 任务继续执行 */ private boolean checkGroupHash(boolean isIgnoreTaskOccupy, String groupHash) { - DownloadGroupEntity dge = DbEntity.findFirst(DownloadGroupEntity.class, "groupHash=?", groupHash); - if (dge != null && dge.getGroupHash().equals(mEntity.getGroupHash())){ + DownloadGroupEntity dge = + DbEntity.findFirst(DownloadGroupEntity.class, "groupHash=?", groupHash); + if (dge != null && dge.getGroupHash().equals(mEntity.getGroupHash())) { mEntity.rowID = dge.rowID; return true; } diff --git a/DEV_LOG.md b/DEV_LOG.md index 57da165c..2a036a39 100644 --- a/DEV_LOG.md +++ b/DEV_LOG.md @@ -4,6 +4,7 @@ - 修复一个重新下载文件时,同名路径文件没有被被删除的问题 - fix bug https://github.com/AriaLyy/Aria/issues/807 - fix bug https://github.com/AriaLyy/Aria/issues/811 + - 修复组合任务在获取子任务信息的过程中,手动停止或删除,没有回调的问题 + v_3.8.15 (2020/11/9) - 修复不支持断点的连接下载失败问题,https://github.com/AriaLyy/Aria/issues/771 - 修复iv不存在时,索引文件异常的问题,https://github.com/AriaLyy/Aria/issues/780 diff --git a/FtpComponent/src/main/java/com/arialyy/aria/ftp/AbsFtpInfoTask.java b/FtpComponent/src/main/java/com/arialyy/aria/ftp/AbsFtpInfoTask.java index 3f09ac0d..35af8b43 100644 --- a/FtpComponent/src/main/java/com/arialyy/aria/ftp/AbsFtpInfoTask.java +++ b/FtpComponent/src/main/java/com/arialyy/aria/ftp/AbsFtpInfoTask.java @@ -27,6 +27,7 @@ import aria.apache.commons.net.ftp.FTPSClient; import com.arialyy.aria.core.AriaConfig; import com.arialyy.aria.core.FtpUrlEntity; import com.arialyy.aria.core.common.AbsEntity; +import com.arialyy.aria.core.common.CompleteInfo; import com.arialyy.aria.core.common.FtpConnectionMode; import com.arialyy.aria.core.loader.IInfoTask; import com.arialyy.aria.core.loader.ILoaderVisitor; @@ -55,7 +56,8 @@ public abstract class AbsFtpInfoTask 1 && checkSubOption()) { - callback.onSucceed(mEntity.getKey(), new CompleteInfo(200, mTaskWrapper)); + onSucceed(new CompleteInfo(200, mTaskWrapper)); } else { super.run(); } @@ -80,7 +80,7 @@ final class FtpDGInfoTask extends AbsFtpInfoTask return; } - handleFile(getRemotePath(), files.length == 0 ? null : files[0]); + handleFile(client, getRemotePath(), files.length == 0 ? null : files[0]); int reply = client.getReplyCode(); if (!FTPReply.isPositiveCompletion(reply)) { //服务器上没有该文件路径,表示该任务为新的上传任务 @@ -133,8 +133,8 @@ final class FtpUFileInfoTask extends AbsFtpInfoTask * @param remotePath ftp服务器文件夹路径 * @param ftpFile ftp服务器上对应的文件 */ - @Override protected void handleFile(String remotePath, FTPFile ftpFile) { - super.handleFile(remotePath, ftpFile); + @Override protected void handleFile(FTPClient client,String remotePath, FTPFile ftpFile) { + super.handleFile(client, remotePath, ftpFile); this.ftpFile = ftpFile; if (ftpFile != null && ftpFile.getSize() == mEntity.getFileSize()) { isComplete = true; @@ -145,6 +145,6 @@ final class FtpUFileInfoTask extends AbsFtpInfoTask super.onPreComplete(code); CompleteInfo info = new CompleteInfo(isComplete ? CODE_COMPLETE : code, mTaskWrapper); info.obj = ftpFile; - callback.onSucceed(mEntity.getKey(), info); + onSucceed(info); } } diff --git a/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDFileInfoTask.java b/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDFileInfoTask.java index 0ef1ea92..91423587 100644 --- a/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDFileInfoTask.java +++ b/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDFileInfoTask.java @@ -64,6 +64,7 @@ final class HttpDFileInfoTask implements IInfoTask, Runnable { private int mConnectTimeOut; private Callback callback; private HttpTaskOption taskOption; + private boolean isStop = false, isCancel = false; HttpDFileInfoTask(DTaskWrapper taskWrapper) { this.mTaskWrapper = taskWrapper; @@ -81,7 +82,7 @@ final class HttpDFileInfoTask implements IInfoTask, Runnable { conn = ConnectionHelp.handleConnection(url, taskOption); ConnectionHelp.setConnectParam(taskOption, conn); conn.setRequestProperty("Range", "bytes=" + 0 + "-"); - if (AriaConfig.getInstance().getDConfig().isUseHeadRequest()){ + if (AriaConfig.getInstance().getDConfig().isUseHeadRequest()) { ALog.d(TAG, "head请求"); conn.setRequestMethod("HEAD"); } @@ -111,6 +112,14 @@ final class HttpDFileInfoTask implements IInfoTask, Runnable { this.callback = callback; } + @Override public void stop() { + isStop = true; + } + + @Override public void cancel() { + isCancel = true; + } + private void handleConnect(HttpURLConnection conn) throws IOException { if (taskOption.getRequestEnum() == RequestEnum.POST) { Map params = taskOption.getParams(); @@ -232,7 +241,7 @@ final class HttpDFileInfoTask implements IInfoTask, Runnable { || code == HttpURLConnection.HTTP_CREATED // 201 跳转 || code == 307) { handleUrlReTurn(conn, conn.getHeaderField("Location")); - }else if (code == 416){ // 处理0k长度的文件的情况 + } else if (code == 416) { // 处理0k长度的文件的情况 ALog.w(TAG, "文件长度为0,不支持断点"); mTaskWrapper.setSupportBP(false); mTaskWrapper.setNewTask(true); @@ -245,12 +254,13 @@ final class HttpDFileInfoTask implements IInfoTask, Runnable { String.format("任务下载失败,errorCode:%s, errorMsg: %s, url: %s", code, conn.getResponseMessage(), mEntity.getUrl())), !CheckUtil.httpIsBadRequest(code)); } + if (isStop || isCancel) { + return; + } if (end) { taskOption.setChunked(isChunked); - if (callback != null) { - CompleteInfo info = new CompleteInfo(code, mTaskWrapper); - callback.onSucceed(mEntity.getUrl(), info); - } + CompleteInfo info = new CompleteInfo(code, mTaskWrapper); + callback.onSucceed(mEntity.getUrl(), info); mEntity.update(); } } @@ -343,7 +353,7 @@ final class HttpDFileInfoTask implements IInfoTask, Runnable { ConnectionHelp.setConnectParam(taskOption, conn); conn.setRequestProperty("Cookie", cookies); conn.setRequestProperty("Range", "bytes=" + 0 + "-"); - if (AriaConfig.getInstance().getDConfig().isUseHeadRequest()){ + if (AriaConfig.getInstance().getDConfig().isUseHeadRequest()) { conn.setRequestMethod("HEAD"); } conn.setConnectTimeout(mConnectTimeOut); @@ -367,6 +377,9 @@ final class HttpDFileInfoTask implements IInfoTask, Runnable { } private void failDownload(AriaHTTPException e, boolean needRetry) { + if (isStop || isCancel) { + return; + } if (callback != null) { callback.onFail(mEntity, e, needRetry); } diff --git a/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDGInfoTask.java b/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDGInfoTask.java index b02e1b44..50ffbe76 100644 --- a/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDGInfoTask.java +++ b/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDGInfoTask.java @@ -45,6 +45,7 @@ public final class HttpDGInfoTask implements IInfoTask { private AtomicInteger count = new AtomicInteger(); private AtomicInteger failCount = new AtomicInteger(); private DownloadGroupListener listener; + private boolean isStop = false, isCancel = false; /** * 子任务回调 @@ -71,6 +72,24 @@ public final class HttpDGInfoTask implements IInfoTask { this.listener = listener; } + /** + * 停止 + */ + @Override + public void stop() { + isStop = true; + if (mPool != null) { + mPool.shutdown(); + } + } + + @Override public void cancel() { + isCancel = true; + if (mPool != null) { + mPool.shutdown(); + } + } + @Override public void run() { // 如果是isUnknownSize()标志,并且获取大小没有完成,则直接回调onStop if (mPool != null && !getLenComplete) { @@ -131,6 +150,11 @@ public final class HttpDGInfoTask implements IInfoTask { * 检查组合任务大小是否获取完成,获取完成后取消阻塞,并设置组合任务大小 */ private void checkGetSizeComplete(int count, int failCount) { + if (isStop || isCancel){ + ALog.w(TAG, "任务已停止或已取消,isStop = " + isStop + ", isCancel = " + isCancel); + notifyLock(); + return; + } if (failCount == wrapper.getSubTaskWrapper().size()) { callback.onFail(wrapper.getEntity(), new AriaHTTPException("获取子任务长度失败"), false); notifyLock(); diff --git a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8InfoTask.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8InfoTask.java index 84a6b7e8..1970009f 100644 --- a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8InfoTask.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8InfoTask.java @@ -115,6 +115,14 @@ final public class M3U8InfoTask implements IInfoTask { mCallback = callback; } + @Override public void stop() { + this.isStop = true; + } + + @Override public void cancel() { + this.isStop = true; + } + private void handleConnect(String tsListUrl, HttpURLConnection conn) throws IOException { int code = conn.getResponseCode(); if (code == HttpURLConnection.HTTP_OK) { @@ -205,8 +213,7 @@ final public class M3U8InfoTask implements IInfoTask { } CompleteInfo info = new CompleteInfo(); info.obj = extInf; - - mCallback.onSucceed(mEntity.getKey(), info); + onSucceed(info); if (fos != null) { fos.close(); } @@ -223,6 +230,13 @@ final public class M3U8InfoTask implements IInfoTask { } } + private void onSucceed(CompleteInfo info) { + if (isStop) { + return; + } + mCallback.onSucceed(mEntity.getKey(), info); + } + /** * 添加切片信息到索引文件中 * 直播下载的索引只记录头部信息,不记录EXTINF中的信息,该信息在onGetPeer的方法中添加。 @@ -362,6 +376,9 @@ final public class M3U8InfoTask implements IInfoTask { } private void failDownload(String errorInfo, boolean needRetry) { + if (isStop) { + return; + } mCallback.onFail(mEntity, new AriaM3U8Exception(errorInfo), needRetry); } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsGroupLoader.java b/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsGroupLoader.java index 381cbefb..20c7a514 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsGroupLoader.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsGroupLoader.java @@ -217,14 +217,20 @@ public abstract class AbsGroupLoader implements ILoaderVisitor, ILoader { @Override public void cancel() { isCancel = true; + mInfoTask.cancel(); closeTimer(); mSubQueue.removeAllTask(); mListener.onCancel(); } @Override public void stop() { + mInfoTask.stop(); isStop = true; - mSubQueue.stopAllTask(); + if (mSubQueue.getExecSize() == 0) { + mListener.onStop(mGTWrapper.getEntity().getCurrentProgress()); + } else { + mSubQueue.stopAllTask(); + } closeTimer(); } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/group/SimpleSubQueue.java b/PublicComponent/src/main/java/com/arialyy/aria/core/group/SimpleSubQueue.java index 3ce35f9b..811df698 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/group/SimpleSubQueue.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/group/SimpleSubQueue.java @@ -43,7 +43,7 @@ final class SimpleSubQueue implements ISubQueue { /** * 最大执行任务数 */ - private int mExecSize; + private int mMaxExecSize; /** * 是否停止任务任务 @@ -51,7 +51,7 @@ final class SimpleSubQueue implements ISubQueue { private boolean isStopAll = false; private SimpleSubQueue() { - mExecSize = Configuration.getInstance().dGroupCfg.getSubMaxTaskNum(); + mMaxExecSize = Configuration.getInstance().dGroupCfg.getSubMaxTaskNum(); } static SimpleSubQueue newInstance() { @@ -73,6 +73,10 @@ final class SimpleSubQueue implements ISubQueue { return mCache.size(); } + public int getExecSize(){ + return mExec.size(); + } + boolean isStopAll() { return isStopAll; } @@ -82,7 +86,7 @@ final class SimpleSubQueue implements ISubQueue { } @Override public void startTask(AbsSubDLoadUtil fileer) { - if (mExec.size() < mExecSize) { + if (mExec.size() < mMaxExecSize) { mCache.remove(fileer.getKey()); mExec.put(fileer.getKey(), fileer); ALog.d(TAG, @@ -102,6 +106,7 @@ final class SimpleSubQueue implements ISubQueue { @Override public void stopAllTask() { isStopAll = true; ALog.d(TAG, "停止组合任务"); + mCache.clear(); Set keys = mExec.keySet(); for (String key : keys) { AbsSubDLoadUtil loader = mExec.get(key); @@ -117,12 +122,12 @@ final class SimpleSubQueue implements ISubQueue { ALog.e(TAG, String.format("修改组合任务子任务队列数失败,num: %s", num)); return; } - if (num == mExecSize) { - ALog.i(TAG, String.format("忽略此次修改,oldSize: %s, num: %s", mExecSize, num)); + if (num == mMaxExecSize) { + ALog.i(TAG, String.format("忽略此次修改,oldSize: %s, num: %s", mMaxExecSize, num)); return; } - int oldSize = mExecSize; - mExecSize = num; + int oldSize = mMaxExecSize; + mMaxExecSize = num; int diff = Math.abs(oldSize - num); if (oldSize < num) { // 处理队列变小的情况,该情况下将停止队尾任务,并将这些任务添加到缓存队列中 diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/loader/AbsNormalLoader.java b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/AbsNormalLoader.java index 6b6685f1..4282ef58 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/loader/AbsNormalLoader.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/AbsNormalLoader.java @@ -229,6 +229,7 @@ public abstract class AbsNormalLoader implements ILoad ALog.d(TAG, String.format("任务【%s】正在删除,删除任务失败", mTaskWrapper.getKey())); return; } + mInfoTask.cancel(); closeTimer(); isCancel = true; onCancel(); @@ -262,6 +263,7 @@ public abstract class AbsNormalLoader implements ILoad if (isStop) { return; } + mInfoTask.stop(); closeTimer(); isStop = true; onStop(); diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/loader/IInfoTask.java b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/IInfoTask.java index ee7da5ef..70a532e4 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/loader/IInfoTask.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/IInfoTask.java @@ -34,6 +34,16 @@ public interface IInfoTask extends ILoaderComponent { */ void setCallback(Callback callback); + /** + * 任务停止 + */ + void stop(); + + /** + * 任务取消 + */ + void cancel(); + interface Callback { /** * 处理完成 diff --git a/SFtpComponent/src/main/java/com/arialyy/aria/sftp/AbsSFtpInfoTask.java b/SFtpComponent/src/main/java/com/arialyy/aria/sftp/AbsSFtpInfoTask.java index 5e7bba77..e4f6d1c6 100644 --- a/SFtpComponent/src/main/java/com/arialyy/aria/sftp/AbsSFtpInfoTask.java +++ b/SFtpComponent/src/main/java/com/arialyy/aria/sftp/AbsSFtpInfoTask.java @@ -16,6 +16,7 @@ package com.arialyy.aria.sftp; import com.arialyy.aria.core.FtpUrlEntity; +import com.arialyy.aria.core.common.CompleteInfo; import com.arialyy.aria.core.loader.IInfoTask; import com.arialyy.aria.core.loader.ILoaderVisitor; import com.arialyy.aria.core.wrapper.AbsTaskWrapper; @@ -32,9 +33,10 @@ import java.io.UnsupportedEncodingException; */ public abstract class AbsSFtpInfoTask implements IInfoTask { protected String TAG = CommonUtil.getClassName(this); - protected Callback callback; + private Callback callback; private WP wrapper; private SFtpTaskOption option; + private boolean isStop = false, isCancel = false; public AbsSFtpInfoTask(WP wp) { this.wrapper = wp; @@ -44,6 +46,28 @@ public abstract class AbsSFtpInfoTask implements IInf protected abstract void getFileInfo(Session session) throws JSchException, UnsupportedEncodingException, SftpException; + @Override public void stop() { + isStop = true; + } + + @Override public void cancel() { + isCancel = true; + } + + protected void handleFail(AriaException e, boolean needRetry) { + if (isStop || isCancel){ + return; + } + callback.onFail(getWrapper().getEntity(), e, needRetry); + } + + protected void onSucceed(CompleteInfo info){ + if (isStop || isCancel){ + return; + } + callback.onSucceed(getWrapper().getKey(), info); + } + @Override public void run() { try { FtpUrlEntity entity = option.getUrlEntity(); diff --git a/SFtpComponent/src/main/java/com/arialyy/aria/sftp/download/SFtpDInfoTask.java b/SFtpComponent/src/main/java/com/arialyy/aria/sftp/download/SFtpDInfoTask.java index bfbe342f..97429f4f 100644 --- a/SFtpComponent/src/main/java/com/arialyy/aria/sftp/download/SFtpDInfoTask.java +++ b/SFtpComponent/src/main/java/com/arialyy/aria/sftp/download/SFtpDInfoTask.java @@ -39,7 +39,7 @@ final class SFtpDInfoTask extends AbsSFtpInfoTask { } @Override protected void getFileInfo(Session session) throws JSchException, - UnsupportedEncodingException, SftpException { + UnsupportedEncodingException { SFtpTaskOption option = (SFtpTaskOption) getWrapper().getTaskOption(); ChannelSftp channel = (ChannelSftp) session.openChannel("sftp"); channel.connect(1000); @@ -60,10 +60,9 @@ final class SFtpDInfoTask extends AbsSFtpInfoTask { getWrapper().getEntity().setFileSize(attr.getSize()); CompleteInfo info = new CompleteInfo(); info.code = 200; - callback.onSucceed(getWrapper().getKey(), info); + onSucceed(info); } else { - callback.onFail(getWrapper().getEntity(), - new AriaSFTPException(String.format("文件不存在,remotePath:%s", remotePath)), false); + handleFail(new AriaSFTPException(String.format("文件不存在,remotePath:%s", remotePath)), false); } channel.disconnect(); } diff --git a/SFtpComponent/src/main/java/com/arialyy/aria/sftp/upload/SFtpUInfoTask.java b/SFtpComponent/src/main/java/com/arialyy/aria/sftp/upload/SFtpUInfoTask.java index 138c38a2..cdac02d0 100644 --- a/SFtpComponent/src/main/java/com/arialyy/aria/sftp/upload/SFtpUInfoTask.java +++ b/SFtpComponent/src/main/java/com/arialyy/aria/sftp/upload/SFtpUInfoTask.java @@ -64,6 +64,6 @@ final class SFtpUInfoTask extends AbsSFtpInfoTask { info.code = isComplete ? ISCOMPLETE : 200; info.obj = attr; channel.disconnect(); - callback.onSucceed(getWrapper().getKey(), info); + onSucceed(info); } }