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 # 全部任务完成后是否播放提示音