Трансляция проигрываемой в AIMP музыки в статус ВКонтакте
от aNNiMON
А также:
- Java Native Interface - взаимодействие Java c нативным кодом.
- Создание Dll-библиотеки, использующей AIMP Remote API.
- Создание модуля для OwnLang.
- Взаимодействие с VK API.
Генерирование заголовочного файла для Си
Для взаимодействия Java с Си, нужно определиться с сигнатурой нативных методов и расположением классов. Предположим, что класс для взаимодействия с Dll-библиотекой будет иметь имя AIMP, находиться в пакете aimpremote и иметь метод currentTrack, который возвращает массив. Напишем такой класс.
Скомпилируем AIMP.java
javac aimpremote\AIMP.java
И воспользуемся утилитой для создания заголовочных файлов javah
javah aimpremote.AIMP
В результате получим файл aimpremote_AIMP.h с таким содержимым:
Написание Dll-библиотеки
Создадим новый проект Win32 с именем LibAIMPRemote.
Добавим заголовочные файлы: сгенерированный aimpremote_AIMP.h и apiRemote.h из AIMP SDK (скачать можно с официального сайта).
Осталось в настройках проекта создать конфигурацию для компиляции 64-битной версии библиотеки и подключить заголовочные файлы из JDK.
Теперь реализуем функцию currentTrack. Создадим массив из трёх элементов (исполнитель, название, альбом) и заполним его данными из MemoryMappedFile.
Если ругается на тип AIMPRemoteAccessClass, смените его в apiRemote.h на const LPCWSTR AIMPRemoteAccessClass = L"AIMP2_RemoteInfo";
Собрав проект, получим LibAIMPRemote.dll для x86 и x64.
Создание модуля для OwnLang
Сначала проверим работоспособность библиотеки, модифицировав класс AIMP.
Для вызова нужной версии библиотеки пришлось переименовать Dll в LibAIMPRemote_x32.dll и LibAIMPRemote_x64.dll. Компилируем.
javac aimpremote\AIMP.java
Запустим с указанием пути к нативным библиотекам.
java -Djava.library.path=. aimpremote.AIMP
Работает! Теперь перейдём к созданию модуля. OwnLang берёт модули из пакета com.annimon.ownlang.lib.modules, поэтому создадим в этом пакете класс aimp, который реализует интерфейс Module и метод public void init(), в котором добавим одну единственную функцию currentTrack.
Для компиляции этого класса нужен OwnLang.jar.
javac -cp .;OwnLang.jar com\annimon\ownlang\lib\modules\aimp.java
Для удобства, соберём классы модуля в jar-файл.
jar cvf aimp.jar aimpremote\AIMP.class com\annimon\ownlang\lib\modules\aimp.class
Запустим OwnLang в режиме REPL.
java -Djava.library.path=. -cp ".;aimp.jar;OwnLang.jar" com.annimon.ownlang.Main -r
Трансляция статуса ВКонтакте
Алгоритм работы такой: каждые 2-3 минуты получаем данные о проигрываемом треке. Ищем этот трек в ВК методом audio.search. Если аудиозапись найдена, обновляем статус методом audio.setBroadcast.
Для работы нужен токен с правами на изменение статуса и поиск музыки.
Запускаем программу
java -Djava.library.path=. -cp ".;aimp.jar;OwnLang.jar" com.annimon.ownlang.Main -f vk_audio_broadcast.own
Скачать:
LibAIMPRemote.zip (VS 2012)
aimp_vk_status.zip
- Java Native Interface - взаимодействие Java c нативным кодом.
- Создание Dll-библиотеки, использующей AIMP Remote API.
- Создание модуля для OwnLang.
- Взаимодействие с VK API.
Генерирование заголовочного файла для Си
Для взаимодействия Java с Си, нужно определиться с сигнатурой нативных методов и расположением классов. Предположим, что класс для взаимодействия с Dll-библиотекой будет иметь имя AIMP, находиться в пакете aimpremote и иметь метод currentTrack, который возвращает массив. Напишем такой класс.
- package aimpremote;
- public final class AIMP {
- public static native String[] currentTrack();
- }
javac aimpremote\AIMP.java
И воспользуемся утилитой для создания заголовочных файлов javah
javah aimpremote.AIMP
В результате получим файл aimpremote_AIMP.h с таким содержимым:
- /* DO NOT EDIT THIS FILE - it is machine generated */
- #include <jni.h>
- /* Header for class aimpremote_AIMP */
- #ifndef _Included_aimpremote_AIMP
- #define _Included_aimpremote_AIMP
- #ifdef __cplusplus
- extern "C" {
- #endif
- /*
- * Class: aimpremote_AIMP
- * Method: currentTrack
- * Signature: ()[Ljava/lang/String;
- */
- JNIEXPORT jobjectArray JNICALL Java_aimpremote_AIMP_currentTrack
- (JNIEnv *, jclass);
- #ifdef __cplusplus
- }
- #endif
- #endif
Написание Dll-библиотеки
Создадим новый проект Win32 с именем LibAIMPRemote.
Добавим заголовочные файлы: сгенерированный aimpremote_AIMP.h и apiRemote.h из AIMP SDK (скачать можно с официального сайта).
Осталось в настройках проекта создать конфигурацию для компиляции 64-битной версии библиотеки и подключить заголовочные файлы из JDK.
Теперь реализуем функцию currentTrack. Создадим массив из трёх элементов (исполнитель, название, альбом) и заполним его данными из MemoryMappedFile.
- #include "stdafx.h"
- #include "aimpremote_AIMP.h"
- #include "apiRemote.h"
- #define WRITE_TO_STRING_ARRAY(i, x) \
- memcpy(charBuffer, pBuff, (x) * 2); \
- charBuffer[x] = '\0'; \
- pBuff += (x); \
- env->SetObjectArrayElement(info, (i), env->NewString(charBuffer, (x)));
- JNIEXPORT jobjectArray JNICALL Java_aimpremote_AIMP_currentTrack
- (JNIEnv *env, jclass jobj) {
- HANDLE hAIMP = OpenFileMapping(FILE_MAP_READ, false, AIMPRemoteAccessClass);
- if (!hAIMP) {
- return NULL;
- }
- jobjectArray info = env->NewObjectArray(3, env->FindClass("java/lang/String"), 0);
- PAIMPRemoteFileInfo aimpFileInfo = (PAIMPRemoteFileInfo)
- MapViewOfFile(hAIMP, FILE_MAP_READ, 0, 0, AIMPRemoteAccessMapFileSize);
- if (aimpFileInfo) {
- LPWSTR pBuff = (LPWSTR)((PBYTE)aimpFileInfo + sizeof(TAIMPRemoteFileInfo));
- jchar charBuffer[256];
- WRITE_TO_STRING_ARRAY(2, aimpFileInfo->AlbumLength);
- WRITE_TO_STRING_ARRAY(0, aimpFileInfo->ArtistLength);
- pBuff += aimpFileInfo->DateLength
- + aimpFileInfo->FileNameLength
- + aimpFileInfo->GenreLength;
- WRITE_TO_STRING_ARRAY(1, aimpFileInfo->TitleLength);
- }
- UnmapViewOfFile(aimpFileInfo);
- return info;
- }
Собрав проект, получим LibAIMPRemote.dll для x86 и x64.
Создание модуля для OwnLang
Сначала проверим работоспособность библиотеки, модифицировав класс AIMP.
- package aimpremote;
- public final class AIMP {
- static {
- System.loadLibrary("LibAIMPRemote_x" + System.getProperty("sun.arch.data.model"));
- }
- public static void main(String[] args) {
- final String[] track = currentTrack();
- System.out.println(track[0]);
- System.out.println(track[1]);
- System.out.println(track[2]);
- }
- public static native String[] currentTrack();
- }
javac aimpremote\AIMP.java
Запустим с указанием пути к нативным библиотекам.
java -Djava.library.path=. aimpremote.AIMP
Работает! Теперь перейдём к созданию модуля. OwnLang берёт модули из пакета com.annimon.ownlang.lib.modules, поэтому создадим в этом пакете класс aimp, который реализует интерфейс Module и метод public void init(), в котором добавим одну единственную функцию currentTrack.
- package com.annimon.ownlang.lib.modules;
- import aimpremote.AIMP;
- import com.annimon.ownlang.lib.ArrayValue;
- import com.annimon.ownlang.lib.Functions;
- public final class aimp implements Module {
- @Override
- public void init() {
- Functions.set("currentTrack", args -> {
- return ArrayValue.of(AIMP.currentTrack());
- });
- }
- }
javac -cp .;OwnLang.jar com\annimon\ownlang\lib\modules\aimp.java
Для удобства, соберём классы модуля в jar-файл.
jar cvf aimp.jar aimpremote\AIMP.class com\annimon\ownlang\lib\modules\aimp.class
Запустим OwnLang в режиме REPL.
java -Djava.library.path=. -cp ".;aimp.jar;OwnLang.jar" com.annimon.ownlang.Main -r
Трансляция статуса ВКонтакте
Алгоритм работы такой: каждые 2-3 минуты получаем данные о проигрываемом треке. Ищем этот трек в ВК методом audio.search. Если аудиозапись найдена, обновляем статус методом audio.setBroadcast.
- use "std"
- use "http"
- use "json"
- use "functional"
- use "aimp"
- access_token = "ваш токен"
- def toParams(obj) {
- str = ""
- for k, v : obj
- str += k + "=" + v + "&"
- return str
- }
- def createRawUrl(method, params, token = "") = "https://api.vk.com/method/"+method+"?v=5.52&"+params+"access_token="+token
- def createUrl(method, params, token = "") = createRawUrl(method, toParams(params), token)
- def invoke(method, params, callback) = http(createUrl(method, params, access_token), callback)
- def searchAudio(query, callback) = invoke("audio.search", {"q": query, "count": 1}, callback)
- def setBroadcast(audio) = invoke("audio.setBroadcast", {"audio": audio}, IDENTITY)
- def update() {
- extract(artist, title, ) = currentTrack()
- if (length(artist) == 0 || length(title) == 0) return 0
- println artist + " - " + title
- searchAudio(artist + " - " + title, def(r) {
- data = jsondecode(r)
- data = data.response
- if (data.count == 0) return 0
- audio = data.items[0]
- setBroadcast("" + audio.owner_id + "_" + audio.id)
- })
- }
- thread(def() {
- while(true) {
- update()
- sleep(2 * 60 * 1000)
- }
- })
Для работы нужен токен с правами на изменение статуса и поиск музыки.
Запускаем программу
java -Djava.library.path=. -cp ".;aimp.jar;OwnLang.jar" com.annimon.ownlang.Main -f vk_audio_broadcast.own
Скачать:
LibAIMPRemote.zip (VS 2012)
aimp_vk_status.zip