gpt4 book ai didi

java - 如何像其他网站一样解决 YouTube API 嵌入限制?

转载 作者:行者123 更新时间:2023-11-30 02:57:10 25 4
gpt4 key购买 nike

我正在构建一个 java 程序,该程序可以选择在嵌入式播放器中播放 YouTube 视频。问题是大多数音乐视频无法播放,并且出现以下错误:“该视频包含来自(媒体公司名称)的内容。该视频被限制在某些网站上播放。”

enter image description here

我尝试在 Chrome 中加载相同的网址并得到相同的结果。 https://www.youtube.com/embed/TMZi25Pq3T8

但是,经过一番研究后,我很快就通过安装一个 Chrome 扩展程序解决了这个问题,该扩展程序允许我添加 HTTP 请求 header ,并添加一个遵循此结构“https://www..com”的 Referer header ,并得到了它工作了。

enter image description here

所以我想一定是这样了。我添加了以下代码,以便将请求 header 添加到我的 JavaFX WebView/WebEngine:

URI uri = URI.create("https://www.youtube.com/embed/TMZi25Pq3T8");
List<String> cookies = new ArrayList<>();
cookies.add("User-Agent=BDM/v0.92");
cookies.add("Referer=https://www.youtube.com");
Map<String, List<String>> headers = new LinkedHashMap<String, List<String>>();
headers.put("Set-Cookie", cookies);
try {
CookieHandler.getDefault().put(uri, headers);
} catch (IOException ex) {
ex.printStackTrace();
}
System.out.println(webView.getEngine().getUserAgent());
webView.getEngine().load(uri.toString());

仍然没有成功,同样的错误消息。

我用来通过其 API Discogs 提取有关版本的数据的网站也能够播放“受限”视频。我在这里缺少什么?

稍后编辑:进一步说明:

我对我所犯的错误表示歉意:

  1. System.out.println(webView.getEngine().getUserAgent()); 不会打印“BDM/v0.92”,正如我首先所说的,它打印默认的 JavaFX用户代理,“Mozilla/5.0(Windows NT 10.0;Win64;x64)AppleWebKit/538.19(KHTML,如 Gecko)JavaFX/8.0 Safari/538.19”。这导致了数字 2
  2. 正如 Roman Nazarenko 指出的那样,我将 cookie 与请求 header 混淆了。

这引出了真正的问题,如何发送 JavaFX WebEngine 的 HTTP 请求 header ?唯一的选择是通过调用 webView.getEngine().setUserAgent("myUserAgent");

设置用户代理

我在这里发现了一个 hack,但这对我不起作用:https://twitter.com/codingfabian/status/524942996748652544

谢谢!

最佳答案

我设法使用javassist解决了这个问题和 this tutorial关于如何检测 Java 代码。

正如我在问题中所述,YouTube 播放器需要Referer header 来播放某些视频(例如 VEVO、Sony Music Enternatinment 等拥有的音乐视频)。

我所做的是拦截 prepareConnection 方法来自 JavaFX 的 WebEngine 使用的 URLLoader 类,并将我的指令插入到方法主体的顶部:

c.setRequestProperty("Referer", "https://www.discogs.com");

Code from the JDK source files

(再次,请按照 tutorial 获取所有说明)

(注意:尽管上面的教程很好地解释了概念,但它并没有真正触及 MANIFEST.MF 文件的作用和结构,因此请检查 this link有关这方面的更多信息)

这是我的两个类(class):

MyJavaAgent.java

package com.busytrack.discographymanager.headerfixagent;
import java.lang.instrument.Instrumentation;
public class MyJavaAgent {
public static void premain(String agentArgument, Instrumentation instrumentation) {
ClassTransformer transformer = new ClassTransformer();
instrumentation.addTransformer(transformer);
}
}

ClassTransformer.java

package com.busytrack.discographymanager.headerfixagent;
import java.io.ByteArrayInputStream;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;

public class ClassTransformer implements ClassFileTransformer {
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
byte[] byteCode = classfileBuffer;
if (className.equals("com/sun/webkit/network/URLLoader")) {
try {
ClassPool classPool = new ClassPool(true);
CtClass ctClass = classPool.makeClass(new ByteArrayInputStream(classfileBuffer));
CtMethod method = ctClass.getDeclaredMethod("prepareConnection");
String src = "$1.setRequestProperty(\"Referer\", \"https://www.discogs.com\");"; // Confused about there being "$1" instead of "c"? Please read below
method.insertBefore(src);
byteCode = ctClass.toBytecode();
ctClass.detach();
} catch (Exception e) {
e.printStackTrace();
}
}
return byteCode;
}
}

这就是为什么我使用“$1”来访问方法参数,而不是“c”:

The statement and the block can refer to fields and methods. They can also refer to the parameters to the method that they are inserted into if that method was compiled with the -g option (to include a local variable attribute in the class file). Otherwise, they must access the method parameters through the special variables $0, $1, $2, ... described below. Accessing local variables declared in the method is not allowed although declaring a new local variable in the block is allowed.

整个javassist教程可以找到here .

将两个类和 MANIFEST.MF 文件打包到单独的 JAR 中后,将其导入您的 IDE(我使用 Eclipse)并添加以下VM 参数:

-javaagent:./(your-jar-name).jar

在 Eclipse 中,您可以像这样添加 VM 参数:

right click on your project -> Run As -> Run Configurations... -> open the Arguments tab -> insert your VM argument -> Apply

我希望这对那里的人有帮助。我知道我在这个问题上花了几天时间。我不知道这是否是最好的方法,但它适合我。尽管如此,这让我想知道为什么没有一种简单的方法来为 JavaFX 的 WebEngine 设置请求 header ...

稍后编辑:

我发现了一种更干净更简单的方法来动态加载Java代理,而无需创建单独的JAR list 文件、导入它们、启动时传递 -javaagent VM 参数等。

我使用了ea-agent-loader (JAR download link)。

在 IDE 中导入 JAR,并将 MyJavaAgent 类(具有 premain 方法的类)更改为:

package com.busytrack.discographymanager.headerfixagent;
import java.lang.instrument.Instrumentation;
public class MyJavaAgent {
public static void agentmain(String agentArgument, Instrumentation instrumentation) {
ClassTransformer transformer = new ClassTransformer();
instrumentation.addTransformer(transformer);
}
}

我的 MainClass 中的 ma​​in 方法 如下所示:

public static void main(String[] args) {
AgentLoader.loadAgentClass(MyJavaAgent.class.getName(), null); // Load the MyJavaAgent class
launch(args); // Start the JavaFX application
}

我希望能够动态加载代理,因为使用静态方法需要我为所有平台创建单独的启动器并在启动时传递 -javaagent 参数。现在,我可以像平常一样从 eclipse 导出可运行的 JAR,并且代理将自动加载(不需要 VM 参数)。 感谢 BioWare 提供的这个工具!:D

关于java - 如何像其他网站一样解决 YouTube API 嵌入限制?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36917469/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com