Как я могу обойти ограничения на встраивание API YouTube, как и на других веб-сайтах?

Я создаю Java-программу, которая может воспроизводить видео YouTube во встроенном проигрывателе. Проблема в том, что большинство музыкальных видеоклипов не воспроизводится, и я получаю следующую ошибку: «Это видео содержит контент из (название медиа-корпорации). Воспроизведение его на некоторых сайтах запрещено. . "

введите здесь описание изображения

Я попытался загрузить тот же URL-адрес в Chrome и получил те же результаты. https://www.youtube.com/embed/TMZi25Pq3T8

Однако после некоторого исследования я быстро исправил это, установив расширение Chrome, которое позволяет мне добавлять заголовки HTTP-запросов, и добавил заголовок Referer, который следует этой структуре "https://www..com" и все заработало.

введите здесь описание изображения

Так что я подумал, что это должно быть так. Я добавил следующий код, чтобы добавить заголовки запросов в свой 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. Как заметил Роман Назаренко, я путал файлы cookie с заголовками запросов.

Это приводит к реальному вопросу, как я могу отправить заголовки HTTP-запроса для JavaFX WebEngine? Единственный вариант - установить пользовательский агент, вызвав webView.getEngine().setUserAgent("myUserAgent");

Я нашел здесь взлом, но это не сработало для меня: https://twitter.com/codingfabian/status/524942996748652544

Спасибо!


person Community    schedule 28.04.2016    source источник
comment
Ошибка связана с вызовом API? Если это так, вам может потребоваться проверить, какие параметры вы должны отправлять при отправке запроса. Таким образом, вы сможете идентифицировать любой отсутствующий параметр в случае возникновения ошибки из API.   -  person Yogesh A Sakurikar    schedule 28.04.2016
comment
Вы говорите о заголовках, но здесь вы устанавливаете файлы cookie. Это не одно и то же.   -  person Roman Nazarenko    schedule 28.04.2016
comment
Вы пробовали это прочитать? stackoverflow.com/questions/13407482/. Кроме того, я нашел этот блог разработчиков и разработчиков YouTube: youtube-eng.blogspot.co.uk/2011/12/. Это может тебе помочь.   -  person Android Enthusiast    schedule 30.04.2016


Ответы (1)


Мне удалось решить проблему с помощью javassist и это руководство о том, как инструментировать код Java.

Как я уже сказал в своем вопросе, проигрывателю YouTube нужен заголовок Referer для воспроизведения некоторых видео (например, музыкальных клипов, принадлежащих VEVO, Sony Music Enternatinment и т. Д.).

Я перехватил prepareConnection из класса URLLoader, который является используется WebEngine JavaFX и вставил мою инструкцию в верхней части тела метода:

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

Код из исходных файлов JDK

(Опять же, следуйте руководству для всех инструкции)

(Примечание. Несмотря на то, что в приведенном выше руководстве очень хорошо объясняются концепции, в нем не особо много говорится о роли и структуре файла 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(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»:

Оператор и блок могут ссылаться на поля и методы. Они также могут ссылаться на параметры метода, в который они вставлены, если этот метод был скомпилирован с параметром -g (для включения атрибута локальной переменной в файл класса). В противном случае они должны получить доступ к параметрам метода через специальные переменные $ 0, $ 1, $ 2, ..., описанные ниже. Доступ к локальным переменным, объявленным в методе, не разрешен, хотя объявление новой локальной переменной в блоке разрешено.

Полное руководство по javassist можно найти здесь.

После упаковки двух классов и файла MANIFEST.MF в отдельный JAR импортируйте его в свою среду IDE (я использовал Eclipse) и добавьте следующий аргумент VM:

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

В Eclipse вы можете добавить аргументы виртуальной машины следующим образом:

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

Надеюсь, это кому-то поможет. Я знаю, что потратил несколько дней на эту проблему. Я не знаю, лучший ли это подход, но он выполняет свою работу за меня. Тем не менее, это заставляет меня задаться вопросом, почему нет простого способа установки заголовков запросов для JavaFX WebEngine ...

Позднее изменение:

Я нашел гораздо более чистый и более простой подход для загрузки агентов Java, динамически, без необходимости создавать отдельный JAR файл манифеста, импортируя их. , передача параметра виртуальной машины -javaagent при запуске и т. д.

Я использовал ea-agent-loader (ссылка для загрузки JAR ).

Импортируйте JAR в свою среду IDE и измените класс 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 выглядит так:

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

Я хотел иметь возможность загружать агент динамически, потому что использование статического метода требовало от меня создания отдельных модулей запуска для всех платформ и передачи параметра -javaagent при запуске. Теперь я могу экспортировать исполняемый JAR из eclipse, как я обычно это делаю, и агент загрузится автоматически (параметры виртуальной машины не требуются). Спасибо, BioWare за этот инструмент!: D

person Community    schedule 30.04.2016