diff --git a/.github/release.info b/.github/release.info
index 0da22b98..a5d69694 100644
--- a/.github/release.info
+++ b/.github/release.info
@@ -1,10 +1,9 @@
-* 新增: 添加功能,可以周期性地进行“一键下载”,并通报结果。
-* 优化: 现在按平台和架构编译了四个版本ffmpeg,缺省时符合条件的会提示进行下载:`win_amd64`、`linux_amd64`、`win_arm64`、`linux_arm64`
-* 优化: 现在补充完善了浏览器指纹等方面的cookie,期望是预防风控[#177](https://github.com/nICEnnnnnnnLee/BilibiliDown/issues/177), [#180](https://github.com/nICEnnnnnnnLee/BilibiliDown/issues/180)
- + 因为尚不清楚相关机制,目前`通过API上传指纹`这一动作只在`刷新cookie`时才会进行。在遇到风控时,不妨先试一试菜单栏里的`刷新cookie`选项。
- + 现在最好不要随意修改配置的UA,如果必要,需要在隐私模式下抓取cookie并抓包相应API的payload。详见配置页。
-* 修复: [#182](https://github.com/nICEnnnnnnnLee/BilibiliDown/issues/182) 考虑在`UP主所有视频`类型的链接解析时,keyword中含有空格的情况。
-* 删除: 移除解析分页链接时`promptAll`模式相关代码。
+* 新增: 重命名文件失败时,尝试添加序号继续重命名。[#185](https://github.com/nICEnnnnnnnLee/BilibiliDown/issues/185)
+ + 目的地址存在文件时,会在约定的名称末尾尝试添加`(01)、(02)...`这样的序号
+ + 通过配置`bilibili.name.autoNumber`可以开启/关闭该功能
+* 优化: 查询UP主所有链接时添加`dm_img`系列参数,防止返回352。(不登录也能用了)
+* 修复: 查询UP主所有链接时相关请求添加`referer`,防止返回412。[#192](https://github.com/nICEnnnnnnnLee/BilibiliDown/issues/192)
+* 其它常规优化,详见[V6.30...V6.31](https://github.com/nICEnnnnnnnLee/BilibiliDown/compare/V6.30...V6.31)
如果你是Win64用户,且没有java环境,请下载附件`*.win_x64_jre11.release.zip`
\ No newline at end of file
diff --git a/.github/workflows/document.yml b/.github/workflows/document.yml
index 535da168..670abbb8 100644
--- a/.github/workflows/document.yml
+++ b/.github/workflows/document.yml
@@ -31,7 +31,7 @@ jobs:
run: npm run docs:build
- name: Deploy
- uses: peaceiris/actions-gh-pages@main
+ uses: peaceiris/actions-gh-pages@v4
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: docs/.vitepress/dist
diff --git a/UPDATE.md b/UPDATE.md
index 1dbd82f4..15ce7ba2 100644
--- a/UPDATE.md
+++ b/UPDATE.md
@@ -1,4 +1,12 @@
## UPDATE
+* V6.31 `2024-05-08`
+ * 新增: 重命名文件失败时,尝试添加序号继续重命名。[#185](https://github.com/nICEnnnnnnnLee/BilibiliDown/issues/185)
+ + 目的地址存在文件时,会在约定的名称末尾尝试添加`(01)、(02)...`这样的序号
+ + 通过配置`bilibili.name.autoNumber`可以开启/关闭该功能
+ * 优化: 查询UP主所有链接时添加`dm_img`系列参数,防止返回352。(不登录也能用了)
+ * 修复: 查询UP主所有链接时相关请求添加`referer`,防止返回412。[#192](https://github.com/nICEnnnnnnnLee/BilibiliDown/issues/192)
+ * 其它常规优化,详见[V6.30...V6.31](https://github.com/nICEnnnnnnnLee/BilibiliDown/compare/V6.30...V6.31)
+
* V6.30 `2024-02-23`
* 新增: 添加功能,可以周期性地进行“一键下载”,并通报结果。
* 优化: 现在按平台和架构编译了四个版本ffmpeg,缺省时符合条件的会提示进行下载:`win_amd64`、`linux_amd64`、`win_arm64`、`linux_arm64`
diff --git a/src/nicelee/bilibili/API.java b/src/nicelee/bilibili/API.java
index f1a29b98..740a7335 100644
--- a/src/nicelee/bilibili/API.java
+++ b/src/nicelee/bilibili/API.java
@@ -5,13 +5,14 @@
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
-import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.Arrays;
+import java.util.Base64;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Random;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
@@ -235,7 +236,12 @@ public static void uploadFingerprint() {
String b_lsid = ResourcesUtil.randomHex(8) + "_" + Long.toHexString(currentTime).toUpperCase();
HttpCookies.set("b_lsid", b_lsid);
}
-
+ // 设置 browser_resolution
+ Matcher mResolution = pResolution.matcher(Global.userAgentPayload);
+ if(mResolution.find()) {
+ String resolution = String.format("%s-%s", mResolution.group(1), mResolution.group(2));
+ HttpCookies.set("browser_resolution", resolution);
+ }
// TODO payload
String payload = Global.userAgentPayload;
payload = payload.replaceFirst("\"5062\":\"[^\"]+\"", "\"5062\":\"" + currentTime + "\"");
@@ -293,10 +299,67 @@ public static String genNewFingerprint() {
String buvid_fp = Global.userAgentFingerprint;
kvMap.put("buvid_fp", buvid_fp);
kvMap.put("fingerprint", buvid_fp);
+ // 获取 browser_resolution
+ Matcher mResolution = pResolution.matcher(Global.userAgentPayload);
+ mResolution.find();
+ String resolution = String.format("%s-%s", mResolution.group(1), mResolution.group(2));
+ kvMap.put("browser_resolution", resolution);
return HttpCookies.map2CookieStr(kvMap);
} catch (IOException e) {
e.printStackTrace();
}
return "";
}
+
+ static String b64Sub2(String data) {
+ try {
+ String result = new String(Base64.getEncoder().encode(data.getBytes("UTF-8")), "UTF-8");
+ result = result.substring(0, result.length() - 2);
+ return result;
+ } catch (UnsupportedEncodingException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ static Random random = new Random();
+
+ static String f114(int a, int b) {
+ int t = random.nextInt(114);
+ return String.format("[%d,%d,%d]", 2 * a + 2 * b + 3 * t, 4 * a - b + t, t);
+ }
+
+ static String f514(int a, int b) {
+ int t = random.nextInt(514);
+ return String.format("[%d,%d,%d]", 3 * a + 2 * b + t, 4 * a - 4 * b + 2 * t, t);
+ }
+
+ static Pattern pWebglVersion = Pattern.compile("\"webgl version:([^\"]+)\"");
+ static Pattern pResolution = Pattern.compile("\"6e7c\":\"(\\d+)x(\\d+)\"");
+ static Pattern pWebglUnRenderer = Pattern.compile("\"webgl unmasked renderer:([^\"]+)\"");
+ static Pattern pWebglUnVendor = Pattern.compile("\"webgl unmasked vendor:([^\"]+)\"");
+
+ public static String genDmImgParams() {
+ // TODO dm_img_list 浏览器加载完毕后,如果没什么动作,请求始终为[]
+ String dm_img_list = "[]";
+ // dm_img_str
+ Matcher mWebglVersion = pWebglVersion.matcher(Global.userAgentPayload);
+ mWebglVersion.find();
+ String dm_img_str = b64Sub2(mWebglVersion.group(1).trim());
+ // TODO dm_img_inter 浏览器加载完毕后第一个请求始终为
+ // {"ds":[],"wh":f114(width, height),"of":f514(0,0)}
+ Matcher mResolution = pResolution.matcher(Global.userAgentPayload);
+ mResolution.find();
+ String _wh = f114(Integer.parseInt(mResolution.group(1)), Integer.parseInt(mResolution.group(2)));
+ String _of = f514(0, 0);
+ String dm_img_inter = String.format("{\"ds\":[],\"wh\":%s,\"of\":%s}", _wh, _of);
+ // dm_cover_img_str
+ Matcher mWebglUnRenderer = pWebglUnRenderer.matcher(Global.userAgentPayload);
+ mWebglUnRenderer.find();
+ Matcher mWebglUnVendor = pWebglUnVendor.matcher(Global.userAgentPayload);
+ mWebglUnVendor.find();
+ String dm_cover_img_str = mWebglUnRenderer.group(1).trim() + mWebglUnVendor.group(1).trim();
+ dm_cover_img_str = b64Sub2(dm_cover_img_str);
+ return String.format("&dm_img_list=%s&dm_img_str=%s&dm_img_inter=%s&dm_cover_img_str=%s", dm_img_list,
+ dm_img_str, dm_img_inter, dm_cover_img_str);
+ }
}
diff --git a/src/nicelee/bilibili/downloaders/Downloader.java b/src/nicelee/bilibili/downloaders/Downloader.java
index 23c82c9b..f8d890b9 100644
--- a/src/nicelee/bilibili/downloaders/Downloader.java
+++ b/src/nicelee/bilibili/downloaders/Downloader.java
@@ -51,6 +51,7 @@ public boolean download(String url, String avId, int qn, int page) {
try {
this.downloader = downloader.getClass().newInstance();
this.downloader.matches(url);
+ break;
} catch (InstantiationException | IllegalAccessException e) {
}
}
diff --git a/src/nicelee/bilibili/parsers/impl/URL4UPAllMedialistParser.java b/src/nicelee/bilibili/parsers/impl/URL4UPAllMedialistParser.java
index 9e2c523f..b56b597a 100644
--- a/src/nicelee/bilibili/parsers/impl/URL4UPAllMedialistParser.java
+++ b/src/nicelee/bilibili/parsers/impl/URL4UPAllMedialistParser.java
@@ -142,6 +142,9 @@ public VideoInfo result(int pageSize, int page, Object... obj) {
try {
// 先获取合集信息
HashMap headers = new HttpHeaders().getCommonHeaders("api.bilibili.com");
+ HashMap headersRefer = new HashMap<>(headers);
+ headersRefer.put("Referer", "https://space.bilibili.com/");
+ headersRefer.put("Origin", "https://space.bilibili.com/");
if (pageQueryResult.getVideoName() == null) {
String url = "https://api.bilibili.com/x/v1/medialist/info?type=1&tid=0&biz_id=" + spaceID;
Logger.println(url);
@@ -162,10 +165,10 @@ public VideoInfo result(int pageSize, int page, Object... obj) {
break;
}
}
- String firstOid = position2Oid((page - 1) * pageSize + 1, headers, sortFieldParam);
+ String firstOid = position2Oid((page - 1) * pageSize + 1, headersRefer, sortFieldParam);
if(firstOid.equals("end"))
return pageQueryResult;
- String lastOidPlus1 = position2Oid(page * pageSize + 1, headers, sortFieldParam);
+ String lastOidPlus1 = position2Oid(page * pageSize + 1, headersRefer, sortFieldParam);
// 根据oid查询分页的详细信息
String urlFormat = "https://api.bilibili.com/x/v2/medialist/resource/list?type=1&oid=%s&otype=2&biz_id=%s&bvid=&with_current=%s&mobi_app=web&ps=%d&direction=false&sort_field=%d&tid=%s&desc=true";
@@ -267,6 +270,7 @@ private String position2Oid(int pageNumber, HashMap headers, Str
// String urlFormat = "https://api.bilibili.com/x/space/arc/search?mid=%s&ps=%d&tid=%s&pn=%d&keyword=&order=%s&jsonp=jsonp";
String urlFormat = "https://api.bilibili.com/x/space/wbi/arc/search?mid=%s&ps=%d&tid=%s&special_type=&pn=%d&keyword=&order=%s&platform=web"; // &web_location=1550101&order_avoided=true
String url = String.format(urlFormat, spaceID, 1, params.get("tid"), pageNumber, sortFieldParam);
+ url += API.genDmImgParams();
url = API.encWbi(url);
String json = util.getContent(url, headers, HttpCookies.globalCookiesWithFingerprint());
Logger.println(url);
diff --git a/src/nicelee/bilibili/parsers/impl/URL4UPAllParser.java b/src/nicelee/bilibili/parsers/impl/URL4UPAllParser.java
index 0d578e90..0c0c3548 100644
--- a/src/nicelee/bilibili/parsers/impl/URL4UPAllParser.java
+++ b/src/nicelee/bilibili/parsers/impl/URL4UPAllParser.java
@@ -88,8 +88,12 @@ protected boolean query(int page, int min, int max, Object... obj) {
String urlFormat = "https://api.bilibili.com/x/space/wbi/arc/search?mid=%s&ps=%d&tid=%s&pn=%d&keyword=%s&order=%s&platform=web"; // &web_location=1550101&order_avoided=true
String keyword = API.encodeURL(params.get("keyword"));
String url = String.format(urlFormat, spaceID, API_PMAX, params.get("tid"), page, keyword, params.get("order"));
+ url += API.genDmImgParams();
url = API.encWbi(url);
- String json = util.getContent(url, new HttpHeaders().getCommonHeaders("api.bilibili.com"), HttpCookies.globalCookiesWithFingerprint());
+ HashMap headersRefer = new HttpHeaders().getCommonHeaders("api.bilibili.com");
+ headersRefer.put("Referer", "https://space.bilibili.com/");
+ headersRefer.put("Origin", "https://space.bilibili.com/");
+ String json = util.getContent(url, headersRefer, HttpCookies.globalCookiesWithFingerprint());
Logger.println(url);
Logger.println(json);
JSONObject jobj = new JSONObject(json);
diff --git a/src/nicelee/bilibili/util/CmdUtil.java b/src/nicelee/bilibili/util/CmdUtil.java
index 424fd313..375645c6 100644
--- a/src/nicelee/bilibili/util/CmdUtil.java
+++ b/src/nicelee/bilibili/util/CmdUtil.java
@@ -310,7 +310,17 @@ public synchronized static void convertOrAppendCmdToRenameBat(final String avid_
File folder = file.getParentFile();
if (!folder.exists())
folder.mkdirs();
- originFile.renameTo(file);
+ if((!originFile.renameTo(file)) && Global.autoNumberWhenFileExists) {// 如果不成功,大概率是文件名重复,在后面加上序号,类似于(01)
+ for(int i = 1; i < 100; i++) {
+ File f = new File(Global.savePath,
+ String.format("%s(%02d)%s", formattedTitle, i, tail));
+ Logger.println(f.getAbsolutePath());
+ if(!f.exists()) {
+ originFile.renameTo(f);
+ break;
+ }
+ }
+ }
} else {
File f = new File(Global.savePath, "rename.bat");
boolean isExist = f.exists();
diff --git a/src/nicelee/bilibili/util/HttpCookies.java b/src/nicelee/bilibili/util/HttpCookies.java
index a367acb7..f501ce2a 100644
--- a/src/nicelee/bilibili/util/HttpCookies.java
+++ b/src/nicelee/bilibili/util/HttpCookies.java
@@ -99,6 +99,8 @@ public static boolean set(String key, String value) {
return true;
}
}
+ HttpCookie cCookie = new HttpCookie(key, value);
+ globalCookiesWithFingerprint().add(cCookie);
return false;
}
}
diff --git a/src/nicelee/ui/Global.java b/src/nicelee/ui/Global.java
index 4673836b..0364fa43 100644
--- a/src/nicelee/ui/Global.java
+++ b/src/nicelee/ui/Global.java
@@ -24,7 +24,7 @@
public class Global {
// 界面显示相关
- @Config(key = "bilibili.version", defaultValue = "v6.30", warning = false)
+ @Config(key = "bilibili.version", defaultValue = "v6.31", warning = false)
public static String version; // 一般情况下,我们不会设置这个标签,这个用于测试
@Config(key = "bilibili.theme", note = "界面主题", defaultValue = "true", eq_true = "default", valids = { "default", "system" })
public static boolean themeDefault;
@@ -109,6 +109,8 @@ public class Global {
public static String cTimeFormat;
@Config(key = "bilibili.name.doAfterComplete", note = "下载完成后自动重命名", defaultValue = "true", valids = { "true", "false" })
public static boolean doRenameAfterComplete = true;
+ @Config(key = "bilibili.name.autoNumber", note = "遇到同名文件自动添加序号", defaultValue = "true", valids = { "true", "false" })
+ public static boolean autoNumberWhenFileExists = true;
@Config(key = "bilibili.repo.definitionStrictMode", note = "是否将同一视频不同清晰度看作不同任务", defaultValue = "false", eq_true = "on", valids = { "on",
"off" }) /* 存在某一清晰度后, 在下载另一种清晰度时是否判断已完成 */
public static boolean repoInDefinitionStrictMode; //
diff --git a/src/nicelee/ui/thread/DownloadRunnable.java b/src/nicelee/ui/thread/DownloadRunnable.java
index d90764a7..6e834aec 100644
--- a/src/nicelee/ui/thread/DownloadRunnable.java
+++ b/src/nicelee/ui/thread/DownloadRunnable.java
@@ -31,6 +31,9 @@ public class DownloadRunnable implements Runnable {
String record;
int qn; //想要申请的链接视频质量
+ final static String MSG_VIDEO_DOWNLOADED = "您已经下载过视频 %s\n如果想继续下载:\n"
+ + "临时方案: 右上角[配置] -> [下载前先查询记录?] -> [不查询]\n"
+ + "持久化方案: 在配置页搜索并修改配置 bilibili.repo";
// public DownloadRunnable(ClipInfo clip, int qn) {
// this.displayName = clip.getAvTitle() + "p" + clip.getRemark() + "-" +clip.getTitle();
// this.clip = clip;
@@ -75,7 +78,7 @@ public void download() {
}
//判断是否已经下载过
if(Global.useRepo && RepoUtil.isInRepo(record)) {
- JOptionPaneManager.showMsgWithNewThread("提示", "您已经下载过视频" + record);
+ JOptionPaneManager.showMsgWithNewThread("提示", String.format(MSG_VIDEO_DOWNLOADED, record));
System.out.println("已经下载过 " + record);
BatchDownloadRbyRThread.taskFail(clip, "already downloaded");
return;
@@ -107,7 +110,7 @@ public void download() {
//如果清晰度不符合预期,再判断一次记录
//判断是否已经下载过
if (qn != realQN && Global.useRepo && RepoUtil.isInRepo(record)) {
- JOptionPaneManager.showMsgWithNewThread("提示", "您已经下载过视频" + record);
+ JOptionPaneManager.showMsgWithNewThread("提示", String.format(MSG_VIDEO_DOWNLOADED, record));
System.out.println("已经下载过 " + record);
BatchDownloadRbyRThread.taskFail(clip, "already downloaded2");
return;
diff --git a/src/resources/app.config b/src/resources/app.config
index 7bb6321c..3b98fb2a 100644
--- a/src/resources/app.config
+++ b/src/resources/app.config
@@ -33,6 +33,8 @@ bilibili.name.format = (:listName listName-)avTitle-pAv2-qn
# 下载完成后是否马上重命名
# 若为false, 那么会追加到重命名文件, 可以人工运行rename.bat 重命名
bilibili.name.doAfterComplete = true
+# 遇到同名文件自动添加序号 (01)、(02)...
+bilibili.name.autoNumber = true
# 下载完成后是否尝试给相关作品点赞👍(已经点赞的不会取消)
bilibili.download.thumbUp = false
# 全部任务完成后是否播放提示音