我如何处理YouTube API嵌入限制,如其他网站? [英] How can I work around YouTube API embed restrictions like other websites?

查看:326
本文介绍了我如何处理YouTube API嵌入限制,如其他网站?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在构建一个java程序,可以选择在嵌入式播放器中播放 YouTube 视频。
问题是大部分音乐视频都无法播放,我收到以下错误:
此视频包含来自(媒体公司名称)的内容。限制在某些网站上播放。





我尝试在Chrome中加载相同的网址并获得相同的结果。



所以我认为必须如此。
我添加了以下代码,以便将请求标头添加到我的 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与请求标题混淆。

这引出了一个真正的问题,我如何为JavaFX WebEngine发送HTTP请求标头?唯一的选择是通过调用 webView.getEngine()来设置用户代理.setUserAgent(myUserAgent);



<我在这里找到了一个黑客,但这对我不起作用:



(同样,请遵循 教程 所有说明)



(注意:尽管上面的教程很好地解释了这些概念,但它并没有真正涉及到这个角色和 MANIFEST.MF 文件的结构,请查看这个链接有关这方面的更多信息)



这是我的两个班级:



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(变压器);
}
}

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;

公共类ClassTransformer实现ClassFileTransformer {
public byte [] transform(ClassLoader loader,String className,Class<?> classBeingRedefined,ProtectionDomain protectionDomain,byte [] classfileBuffer)抛出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 \);; //对于$ 1而不是c感到困惑?请阅读以下
method.insertBefore(src);
byteCode = ctClass.toBytecode();
ctClass.detach();
} catch(例外e){
e.printStackTrace();
}
}
返回byteCode;
}
}

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


语句和块可以引用字段和方法。如果使用-g选项编译该方法(在类文件中包含局部变量属性),它们还可以引用插入它们的方法的参数。否则,他们必须通过下面描述的特殊变量$ 0,$ 1,$ 2,...来访问方法参数。虽然允许在块中声明新的局部变量,但不允许访问方法中声明的局部变量。


整个可以在这里找到javassist 教程。



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

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

在Eclipse中,您可以添加如下的VM参数:

 右键点击您的项目 - >运行方式 - >运行配置...  - >打开Arguments选项卡 - >插入您的VM参数 - >申请

我希望这可以帮助那里的人。我知道我在这个问题上花了几天时间。
我不知道这是否是最好的方法,但它能为我完成这项工作。
尽管如此,我还是想知道为什么没有为JavaFX的WebEngine设置请求标头的简单方法...



稍后编辑:<我强力加载Java代理,动态,无需创建单独的JAR,清单文件,导入它们,在启动时传递-javaagent VM参数等。



我使用了 ea-agent-loader JAR下载链接)。



在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(变压器);
}
}

我的主要方法来自MainClass看起来像这样:

  public static void main(String [] args){
AgentLoader.loadAgentClass(MyJavaAgent) .class.getName(),null); //加载MyJavaAgent类
launch(args); //启动JavaFX应用程序
}

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


I am building a java program that has the option to play YouTube videos in an embedded player. The problem is that most of the music videos won't play and I get the following error: "This video contains content from (Media Corporation Name). It is restricted from playback on certain sites."

I tried loading the same URL in Chrome and got the same results. https://www.youtube.com/embed/TMZi25Pq3T8

However, after some research, I quickly got it fixed by installing a Chrome Extension that allows me to add HTTP Request Headers and added a Referer header that follows this structure "https://www..com" and got it working.

So I thought that must be it. I added the following code in order to add request headers to my 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());

Still, no success, the same error message.

The website that I'm using to extract data about releases through their API, Discogs, is able to play "restricted" videos as well. What am I missing here?

LATER EDIT: Further clarifications:

I would like to apologize for the mistakes I made:

  1. The line System.out.println(webView.getEngine().getUserAgent()); doesn't print "BDM/v0.92" as I first stated, it prints the default JavaFX user agent, "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/538.19 (KHTML, like Gecko) JavaFX/8.0 Safari/538.19". And this leads to number 2
  2. As Roman Nazarenko pointed out, I was confusing cookies with request headers.

This leads to the real question, how can I send HTTP Request headers for JavaFX WebEngine? The only option is to set the user agent by calling webView.getEngine().setUserAgent("myUserAgent");

I found a hack here but this didin't work for me: https://twitter.com/codingfabian/status/524942996748652544

Thanks!

解决方案

I managed to solve the issue by using javassist and this tutorial on how to instrument Java code.

As I stated in my question, the YouTube player needs a Referer header to play some videos (like music videos owned by VEVO, Sony Music Enternatinment, etc.).

What I did is I intercepted prepareConnection method from the URLLoader class that is used by JavaFX's WebEngine and inserted my instruction at the top of the method body:

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

(Again, please follow the tutorial for all the instructions)

(Note: Even though the tutorial above is explains very well the concepts, it doesn't really touch much on the role and structure of a MANIFEST.MF file, so please check this link for more info about this aspect)

These are my two classes:

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;
    }
}

This is why I used "$1" to access the method parameter, instead of "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.

The entire javassist tutorial can be found here.

After packing the two classes and the MANIFEST.MF file in a separate JAR, import it in your IDE (I used Eclipse) and add the following VM argument:

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

In Eclipse, you can add VM arguments like this:

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

I hope this helps someone out there. I know I spent a few days on this issue. I don't know if this is the best approach but it does the job for me. Still, it makes me wonder why isn't there a straightforward way of setting Request Headers for JavaFX's WebEngine...

Later edit:

I found a much cleaner and easier approach for loading Java Agents, dynamically, without the need to create a separate JAR, manifest file, importing them, passing the -javaagent VM parameter at startup, etc.

I used the ea-agent-loader (JAR download link).

Import the JAR in your IDE and change the MyJavaAgent class (the one that had the premain method) to this:

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);
    }
}

My main method from the MainClass looks like this:

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

I wanted to be able to load the Agent dynamically because, using the static method required me to create separate launchers for all platforms and pass the -javaagent parameter on startup. Now, I can export a runnable JAR from eclipse like I usually do and the agent will load automatically (no VM parameters required). Thanks, BioWare for this tool! :D

这篇关于我如何处理YouTube API嵌入限制,如其他网站?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆