nICEnnnnnnnLee / AcFunDown

包含PC端UI界面的A站 视频下载器。支持收藏夹、UP主视频批量下载 😳仅供交流学习使用喔
GNU General Public License v3.0
71 stars 8 forks source link

视频分辨率问题 #17

Open MC-dusk opened 2 months ago

MC-dusk commented 2 months ago

A站视频如果投的是4k 120帧(也就是最高等级),根据app缓存选项,分辨率可以有2160P120,2160P60,2160P,1080P60,1080P+,1080P,720P60,720P,540P,360P,也就是说理论上视频会有这么多个流。(似乎网页端没有这么丰富的选项)

但是经常下载器搜到视频的分辨率只有4个:360p(360会奇怪地排在最前面)、1080p、720p、540p。

这个时候如果想下载高分辨率视频或1080高码率,就得使用最上面的批量下载,选最高的2160p来间接得到。(和稿件有关,不同稿件情况不同)

上面的当图一乐,多测试了几个稿件发现大部分稿件解析结果都是正常的,怎么我自己的投稿解析出来就有问题。

如果同一个投稿的不同分p分辨率不同,解析列表也可能会出问题,比如p1是4k的,p2只有1080p,这个时候解析结果可能每个分p都显示有2160p的选项。

而如果第一个分p只有1080p,那么即使后面的分p有4k也是显示不出来的。


另外有办法得到移动端app专属的4k120帧吗,现在网页端和下载器都最高只有4k30帧。

nICEnnnnnnnLee commented 2 months ago
MC-dusk commented 2 months ago

默认同一投稿的所有视频清晰度统一,故只取第一个视频的清晰度

但是如果批量下载选2160p,还是会取每个分p的最高分辨率。如果在下载的时候会自动fallback到真实存在的最高分辨率,那感觉是不是可以在每个分p最前面增加一个最佳画质的选项,或者默认所有视频都有全部的分辨率,也就是给足选项,让用户自己选。

nICEnnnnnnnLee commented 2 months ago

~但是经常下载器搜到视频的分辨率只有4个:360p(360会奇怪地排在最前面)、1080p、720p、540p。~

程序根据字符串描述硬编码了五个清晰度,2160P 1080P+ 1080P 720P 540P 360P,不在这里面的默认360P

目前,v1.3补充了清晰度1080P60720P60,如果有漏掉的清晰度欢迎补充(限网页端)

Q50("2160P", 50, "2160P"),      // ac30217383
Q40("1080P+", 40, "1080P+"),    // ac30217383
Q31("1080P60", 31, "1080P60"),  // ac46303302
Q30("1080P", 30, "1080P"),      // ac30217383
Q21("720P60", 21, "720P60"),    // ac46303302
Q20("720P", 20, "720P"),        // ac30217383
Q10("540P", 10, "540P"),        // ac30217383
Q00("360P", 0, "360P");         // ac30217383
GoFightNow commented 1 month ago

~但是经常下载器搜到视频的分辨率只有4个:360p(360会奇怪地排在最前面)、1080p、720p、540p。 ~

程序根据字符串描述硬编码了五个清晰度, 2160P 1080P+ 1080P 720P 540P 360P,不在这里面的默认 360P

目前, v1.3补充了清晰度 1080P60720P60,如果有漏掉的清晰度欢迎补充(限网页端)

Q50("2160P", 50, "2160P"),        // ac30217383
Q40("1080P+", 40, "1080P+"),  // ac30217383
Q31("1080P60", 31, "1080P60"),    // ac46303302
Q30("1080P", 30, "1080P"),        // ac30217383
Q21("720P60", 21, "720P60"),  // ac46303302
Q20("720P", 20, "720P"),      // ac30217383
Q10("540P", 10, "540P"),      // ac30217383
Q00("360P", 0, "360P");           // ac30217383

我在下载https://www.acfun.cn/v/ac13087641时,我选择了540P,但是不论是否登录,下载完成后的视频质量均是360P。 image

nICEnnnnnnnLee commented 1 month ago

我在下载https://www.acfun.cn/v/ac13087641时,我选择了540P,但是不论是否登录,下载完成后的视频质量均是360P。

大概只是给出一个解决方案的样子,暂不考虑正式支持。

这个视频540P只有hevc编码的,没有h264的。 如果你的电脑有JDK(不仅仅是JRE)的话,可以在jar包同级目录新建parsers文件夹,把下面两个.java文件放进去。
这样程序下载的都是hevc编码的视频。

parsers/AbstractBaseParser.java 找到所有`"ksPlayJson"`,替换为`"ksPlayJsonHevc"` ```java package nicelee.acfun.parsers.impl; import java.util.LinkedHashMap; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.json.JSONArray; import org.json.JSONObject; import nicelee.acfun.enums.VideoQualityEnum; import nicelee.acfun.model.ClipInfo; import nicelee.acfun.model.VideoInfo; import nicelee.acfun.parsers.IInputParser; import nicelee.acfun.parsers.IParamSetter; import nicelee.acfun.util.HttpCookies; import nicelee.acfun.util.HttpHeaders; import nicelee.acfun.util.HttpRequestUtil; import nicelee.acfun.util.Logger; import nicelee.ui.Global; public abstract class AbstractBaseParser implements IInputParser { protected Matcher matcher; protected HttpRequestUtil util; protected int pageSize = 20; protected IParamSetter paramSetter; public AbstractBaseParser(Object... obj) { this.util = (HttpRequestUtil) obj[0]; this.paramSetter = (IParamSetter) obj[1]; this.pageSize = (int) obj[2]; } @Override public abstract boolean matches(String input); @Override public abstract String validStr(String input); @Override public abstract VideoInfo result(String input, int videoFormat, boolean getVideoLink); /** * * @param avId 字符串带ac * @param videoFormat * @param getVideoLink * @return */ private final static Pattern pVideoInfo = Pattern.compile("window\\.videoInfo ?= ?(.*?});"); protected VideoInfo getAVDetail(String avId, int videoFormat, boolean getVideoLink) { VideoInfo viInfo = new VideoInfo(); viInfo.setVideoId(avId); // 获取json HttpHeaders headers = new HttpHeaders(); String basicInfoUrl = String.format("https://www.acfun.cn/v/%s", avId); String html = util.getContent(basicInfoUrl, headers.getCommonHeaders("www.acfun.cn"), HttpCookies.getGlobalCookies());// 请求1次 Matcher matcher = pVideoInfo.matcher(html); matcher.find(); String json = matcher.group(1); // System.out.println(json); // 获取ac总体信息 JSONObject jObj = new JSONObject(json); JSONObject jUser = jObj.getJSONObject("user"); viInfo.setVideoName(jObj.getString("title")); viInfo.setBrief(jObj.optString("description")); viInfo.setAuthor(jUser.getString("name")); viInfo.setAuthorId(jUser.getString("id")); viInfo.setVideoPreview(jObj.getString("coverUrl")); // 获取各P信息 JSONArray array = jObj.getJSONArray("videoList"); LinkedHashMap clipMap = new LinkedHashMap(); int qnList[] = null; for (int i = 0; i < array.length(); i++) { JSONObject clipObj = array.getJSONObject(i); ClipInfo clip = new ClipInfo(); clip.setAvTitle(viInfo.getVideoName()); clip.setAvId(avId); clip.setcId(clipObj.getLong("id")); clip.setPage(clipObj.getInt("priority")); clip.setTitle(clipObj.getString("title")); clip.setPicPreview(viInfo.getVideoPreview()); LinkedHashMap links = new LinkedHashMap(); try { if (qnList == null) { if(Global.noQualityRequest) { qnList = VideoQualityEnum.availableQNs(); } else { qnList = getVideoQNList(avId, String.valueOf(clip.getcId())); // 请求1次 } } for (int qn : qnList) { if (getVideoLink) { // String link = getVideoLink(avId, String.valueOf(clip.getcId()), qn, // videoFormat); String href = String.format("/v/%s_%d", avId, clip.getPage() + 1); String link = getVideoLinkByHref(headers, href, qn); // 请求1次 links.put(qn, link); } else { links.put(qn, ""); } } clip.setLinks(links); } catch (Exception e) { e.printStackTrace(); clip.setLinks(links); } clipMap.put(clip.getcId(), clip); } viInfo.setClips(clipMap); viInfo.print(); return viInfo; } /** * 0:标清 1:高清 2:超清 3:1080p * * @external input HttpRequestUtil util * @param avId * @param cid * @return 清晰度列表 */ public int[] getVideoQNList(String avId, String cid) { // 获取cid 对应的链接href HttpHeaders headers = new HttpHeaders(); String href = getHrefByIds(avId, cid, headers); // 获取Array String newhtml = util.getContent(String.format("https://www.acfun.cn%s", href), headers.getCommonHeaders("www.acfun.cn"), HttpCookies.getGlobalCookies()); Matcher matcher = pVideoInfo.matcher(newhtml); matcher.find(); String json = matcher.group(1); Logger.println(new JSONObject(json).getJSONObject("currentVideoInfo").getString("ksPlayJsonHevc")); JSONObject jObj = new JSONObject( new JSONObject(json).getJSONObject("currentVideoInfo").getString("ksPlayJsonHevc")); JSONArray jArr = jObj.getJSONArray("adaptationSet").getJSONObject(0).getJSONArray("representation"); int qnList[] = new int[jArr.length()]; Logger.println(qnList.length); for (int i = 0; i < qnList.length; i++) { qnList[i] = VideoQualityEnum.getQN(jArr.getJSONObject(i).getString("qualityLabel")); } return qnList; } /** * 查询视频链接(查询两次) * * @external input HttpRequestUtil util * @external input downFormat * @external output linkQN 保存返回链接的清晰度 * @param avId 视频的avid * @param cid av下面可能不只有一个视频, avId + cid才能确定一个真正的视频 * @param qn * @return 链接 */ @Override public String getVideoLink(String avId, String cid, int qn, int downFormat) { // 获取cid 对应的链接href HttpHeaders headers = new HttpHeaders(); String href = getHrefByIds(avId, cid, headers); // (查询一次) return getVideoLinkByHref(headers, href, qn);// (查询一次) } /** * 访问href页面,并提取下载链接 * * @param headers * @param href * @return 下载链接 */ private String getVideoLinkByHref(HttpHeaders headers, String href, int qn) { // 获取json String newhtml = util.getContent(String.format("https://www.acfun.cn%s", href), headers.getCommonHeaders("www.acfun.cn"), HttpCookies.getGlobalCookies()); Matcher matcher = pVideoInfo.matcher(newhtml); matcher.find(); String json = matcher.group(1); JSONObject jObj = new JSONObject( new JSONObject(json).getJSONObject("currentVideoInfo").getString("ksPlayJsonHevc")); JSONArray jArr = jObj.getJSONArray("adaptationSet").getJSONObject(0).getJSONArray("representation"); Integer realQn = null; for (int i = 0; i < jArr.length(); i++) { if (VideoQualityEnum.getQualityDescript(qn).equals(jArr.getJSONObject(i).getString("qualityLabel"))) { Logger.println("找到相应清晰度:" + VideoQualityEnum.getQualityDescript(qn)); realQn = i; break; } } if(realQn == null) { Logger.println("没有找到相应清晰度------------"); realQn = 0; if (qn <= realQn) { realQn = qn; } } JSONObject qnobj = jArr.getJSONObject(realQn); Logger.println(qnobj.getString("url")); paramSetter.setRealQN(VideoQualityEnum.getQN(qnobj.getString("qualityLabel"))); return qnobj.getString("url"); } /** * 由acid + 某p具体id, 获得访问链接 * * @param avId * @param cid * @param headers * @return href */ private String getHrefByIds(String avId, String cid, HttpHeaders headers) { String basicInfoUrl = String.format("https://www.acfun.cn/v/%s", avId); String html = util.getContent(basicInfoUrl, headers.getCommonHeaders("www.acfun.cn"), HttpCookies.getGlobalCookies()); try { // data-href='/v/ac10187818_5' title="05" data-id='10205818' String patt = String.format("data-href='([^>]*?)' title=\"[^>]*?\" data-id='%s'", cid); Pattern pCidInfo = Pattern.compile(patt); Matcher matcher = pCidInfo.matcher(html); matcher.find(); // System.out.println(href); String href = matcher.group(1); // System.out.println(href); return href; } catch (Exception e) { Logger.println("该ac为单p视频"); return "/v/" + avId; } } @Override public int getVideoLinkQN() { return paramSetter.getRealQN(); } } ```

parsers/ACParser.java

MC-dusk commented 1 month ago

我网页看也只有360p啊

image

GoFightNow commented 1 month ago

我在下载https://www.acfun.cn/v/ac13087641时,我选择了540P,但是不论是否登录,下载完成后的视频质量均是360P。

大概只是给出一个解决方案的样子,暂不考虑正式支持。

这个视频540P只有hevc编码的,没有h264的。 如果你的电脑有JDK(不仅仅是JRE)的话,可以在jar包同级目录新建parsers文件夹,把下面两个.java文件放进去。 这样程序下载的都是hevc编码的视频。

parsers/AbstractBaseParser.java parsers/ACParser.java

ok,搞定,谢谢老哥

GoFightNow commented 1 month ago

我网页看也只有360p啊

image

image 那可能是因为登陆问题。我的问题解决了,如果你的问题也已经结束了,请关闭问题