В YouTube роликах ThisIsHorosho с недавних пор стали появляться ключи к Diablo III Beta. В 7-ми минутном ролике на секунду показывается ключ, кто его первый активирует, то и выигрывает. Вот так на стоп кадре выглядит ключ:
Вы подумали о том же, о чем и я?
Всего 10 ключей.
Ключ 1
О первом ключе я не знал и, наверное, никто не знал. Кому-то повезло.
Ключ 2
Ко второму видео был готов самый простой бот, который раз в 10 секунд обновляет страницу с добавленным видео и, если появилось новое видео, то модальное окно в виде алерта сообщает об этом. Выяснилось, что через 4 часа youtube обнаруживает бота и просит его ввести recaptcha, а потом еще каждый час просит ее вводить. Очень не удобно, но я не особо хотел тратить время и писать по-нормальному, так как ожидал, что второй ключ появится как-нибудь по-другому, ну например, хотя бы зелеными буквами. Второй ключ я проморгал, но когда я увидел те же серые цифры и буквы, тем же шрифтом, того же размера, на том же белом фоне и даже размером в те же три строчки, я решил сделать нормального бота.
Ключ 3
К третьему видео бот использовал youtube api, квота на кол-во запросов к которому позволяла сканирование раз в 5 секунд на протяжении 12 часов.
Алгоритм был простой:
Итак, выскочил алерт, сообщающий о выходе нового видео, я залогинился к battle.net, через 10 секунд скачалось видео, еще через 10 появился скриншот с серийным номером:
Я его ввел и… он оказался уже использованым. Анализ ошибок показал, что данные через youtube api обновляются с задержкой до 5 минут! Когда бот обнаружил новое видео, уже было слишком поздно. Кому-то из других ботов или пользователей жмущих F5 повезло.
Ключ 4
К четвертому видео, бот был доработан: сейчас он использует порядка 100 прокси серверов (по 1 потоку на каждый прокси сервер), которые сканируют каждые 5 секунд gdata. Тесты показали, что около 20 прокси просто мгновенно сообщают о выходе нового видео, остальные подтягиваются в течении минуты, это было отлично. В отличии от gdata прямые ссылки появляются сразу для любого IP, поэтому тут без прокси бот просто качает видео в 20 потоков (работало даже в 1000 потоков, youtube оказывается позволяет). Скорость закачки возросла. Алгоритм сканирования был доработан: бот вначале в 4 потока сканирует вторую часть ролика (во всех предыдущих видео серийный ключ появлялся в конце ролика), а потом в 4 потока первую часть. Для надежности шаг сканирования был уменьшен до 500 мс, другие параметры так же были немного ослаблены.
Итак, выскочил алерт, сообщающий о выходе нового видео. Не успел я зайти в battle.net как уже скачалось видео (10 секунд) и появился скриншот с серийным номером:
Я порадовался, что учел возможность его появления в две строчки. Трясущимися руками я его кое-как написал и активировал! На все ушло секунд 20. Очень повезло со сканированием, алгоритм практически сразу показал серийный номер при том, что полное сканирование продолжалось 30 секунд. В этом ролике, как оказалось, было два ключа, которые показались последовательно, я ввел второй. Поздравляю того, кто активировал первый!
Осталось еще 5 ключей
Можно доработать алгоритм: запускать сканирование вместе с началом старта скачивания, алгоритм усложнится, но выигрышь будет секунд 10. Можно еще сделать распознование серийного номера и его автоматический ввод в battle.net. Тогда ключик можно будет ввести даже за 5 секунд.
Все писалось на Java, используя HttpComponents (http-протокол) и VLCj (обработка видео)
P.S. Diablo III клевая
UPDATE
Интересно было писать самого бота, поэтому затраченные 20 часов я расцениваю как время потраченное на развлечение, а не как 20 часов, за которые можно было бы заработать гораздо больше денег, чем стоит сам ключ. В свободное время я отдыхаю или изучаю что-то новое, а не работаю, а тут приятное с полезным. Игру еще не прошел.
Алгоритм
Алгоритм определения серийника я специально сразу не стал указывать по двум причинам. Узнав абсолютно точный алгоритм, авторы ThisIsHorosho быстро сделают ключ нераспознаваемым, и я окажу медвежью услугу тем, кто тоже пишет бота. Хотя допускаю, что таких людей нет, но как то же, судя по комментариям, за 3 минуты вводят серийники, неужели жмут F5 во время ожидания…
Ну раз много вопросов по алгоритму… Главное, алгоритм должен быть очень быстрый. По скриншоту с серийным номером сразу видна основная идея.
В итоге, появляются только картинки с текстом похожим на серийный номер:
На практике их не много появляется, так что найти из них нужный не составляет труда.
Работа с VLCj
Работа с VLCj очень простая. Вначале я прочитал документацию www.capricasoftware.co.uk/vlcj/tutorial1.php, потом немного поигрался с классом MediaPlayer, но он как-то глючил, в общем я остановился на вызове прямых функций из библиотеки LibVlc — оно и быстрее и безглючнее.
Вначале создаем библиотеку
Потом создаем массив из 8 штук AnalyzerThread (типа Runnable) (каждому 1/8 часть времени видео), которые передаем в Executors.newFixedThreadPool(4) в таком порядке: 4, 5, 6, 7, 0, 1, 2, 3. Т.е. вначале будет сканироваться вторая часть видео, а потом первая. В каждом AnalyzerThread такой код:
Функция analyzeImage как раз определяет, находится ли на скриншоте ключик или нет, если находится, то сохраняет его в специальную папку.
Вы подумали о том же, о чем и я?
Всего 10 ключей.
Ключ 1
О первом ключе я не знал и, наверное, никто не знал. Кому-то повезло.
Ключ 2
Ко второму видео был готов самый простой бот, который раз в 10 секунд обновляет страницу с добавленным видео и, если появилось новое видео, то модальное окно в виде алерта сообщает об этом. Выяснилось, что через 4 часа youtube обнаруживает бота и просит его ввести recaptcha, а потом еще каждый час просит ее вводить. Очень не удобно, но я не особо хотел тратить время и писать по-нормальному, так как ожидал, что второй ключ появится как-нибудь по-другому, ну например, хотя бы зелеными буквами. Второй ключ я проморгал, но когда я увидел те же серые цифры и буквы, тем же шрифтом, того же размера, на том же белом фоне и даже размером в те же три строчки, я решил сделать нормального бота.
Ключ 3
К третьему видео бот использовал youtube api, квота на кол-во запросов к которому позволяла сканирование раз в 5 секунд на протяжении 12 часов.
Алгоритм был простой:
- Каждые 5 секунд делаем GET к http://gdata.youtube.com/feeds/api/users/ThisIsHorosho/uploads?max-results=1&fields=openSearch:totalResults,entry/id и получаем общее количество видео пользователя и ссылку на последнее видео.
- Если количество видео изменилось, то из последней ссылки получаем videoId: 3J1CYzzZjNc
- Делаем GET к http://www.youtube.com/get_video_info?video_id=3J1CYzzZjNc и получаем url encoded параметры
- Берем параметр url_encoded_fmt_stream_map — это url encoded прямые ссылки на видео разных форматов и качества. Ссылка работает только для того IP, с которого пришел запрос. Пошаманив, можно получить красивую ссылку http://o-o.preferred.lhr14s07.v2.lscache8.c.youtube.com/videoplayback?sparams=cp,id,ip,ipbits,itag,ratebypass,source,expire&fexp=906929,907720,904821&itag=18&ip=88.0.0.0&signature=BA6D9C66CA9DF74931C899ABC3816E6FFB3AF2B5.326CFD03BDE8430990DEE9E8DC62046FAC43C62B&sver=3&ratebypass=yes&source=youtube&expire=1332983106&key=yt1&ipbits=8&cp=U0hSR1lTUV9FUENOMl9RTVVCOmNpbEFrX1hXTllx&id=dc9d42633cd98cd7
- Бот, скачав ролик, приступает к анализу видео с помощью vlc библиотеки, которая понимает из коробки mp4,flv,webm.
- Сканируя кадры через 1 секунду, находим и сохраняем те, на которых предположительно находится серийный номер. Сканируем в 4 потока, каждый свою 1/4 часть видео.
- Пользователю, т.е. мне, остается только посмотреть найденные скриншоты и ввести ключ
Итак, выскочил алерт, сообщающий о выходе нового видео, я залогинился к battle.net, через 10 секунд скачалось видео, еще через 10 появился скриншот с серийным номером:
Я его ввел и… он оказался уже использованым. Анализ ошибок показал, что данные через youtube api обновляются с задержкой до 5 минут! Когда бот обнаружил новое видео, уже было слишком поздно. Кому-то из других ботов или пользователей жмущих F5 повезло.
Ключ 4
К четвертому видео, бот был доработан: сейчас он использует порядка 100 прокси серверов (по 1 потоку на каждый прокси сервер), которые сканируют каждые 5 секунд gdata. Тесты показали, что около 20 прокси просто мгновенно сообщают о выходе нового видео, остальные подтягиваются в течении минуты, это было отлично. В отличии от gdata прямые ссылки появляются сразу для любого IP, поэтому тут без прокси бот просто качает видео в 20 потоков (работало даже в 1000 потоков, youtube оказывается позволяет). Скорость закачки возросла. Алгоритм сканирования был доработан: бот вначале в 4 потока сканирует вторую часть ролика (во всех предыдущих видео серийный ключ появлялся в конце ролика), а потом в 4 потока первую часть. Для надежности шаг сканирования был уменьшен до 500 мс, другие параметры так же были немного ослаблены.
Итак, выскочил алерт, сообщающий о выходе нового видео. Не успел я зайти в battle.net как уже скачалось видео (10 секунд) и появился скриншот с серийным номером:
Я порадовался, что учел возможность его появления в две строчки. Трясущимися руками я его кое-как написал и активировал! На все ушло секунд 20. Очень повезло со сканированием, алгоритм практически сразу показал серийный номер при том, что полное сканирование продолжалось 30 секунд. В этом ролике, как оказалось, было два ключа, которые показались последовательно, я ввел второй. Поздравляю того, кто активировал первый!
Осталось еще 5 ключей
Можно доработать алгоритм: запускать сканирование вместе с началом старта скачивания, алгоритм усложнится, но выигрышь будет секунд 10. Можно еще сделать распознование серийного номера и его автоматический ввод в battle.net. Тогда ключик можно будет ввести даже за 5 секунд.
Все писалось на Java, используя HttpComponents (http-протокол) и VLCj (обработка видео)
P.S. Diablo III клевая
UPDATE
Интересно было писать самого бота, поэтому затраченные 20 часов я расцениваю как время потраченное на развлечение, а не как 20 часов, за которые можно было бы заработать гораздо больше денег, чем стоит сам ключ. В свободное время я отдыхаю или изучаю что-то новое, а не работаю, а тут приятное с полезным. Игру еще не прошел.
Алгоритм
Алгоритм определения серийника я специально сразу не стал указывать по двум причинам. Узнав абсолютно точный алгоритм, авторы ThisIsHorosho быстро сделают ключ нераспознаваемым, и я окажу медвежью услугу тем, кто тоже пишет бота. Хотя допускаю, что таких людей нет, но как то же, судя по комментариям, за 3 минуты вводят серийники, неужели жмут F5 во время ожидания…
Ну раз много вопросов по алгоритму… Главное, алгоритм должен быть очень быстрый. По скриншоту с серийным номером сразу видна основная идея.
- Берем картинку из кадра и сохраняем ее с размером 640x320, VLCj позволяет сохранять картинки с любым разрешением, даже, если видео имеет другое. Все точки, близкие к цвету текста делаем черными, все остальное белое. В итоге получаем черно белые картинки. Пару таких картинок вставлены в эту статью.
- Для каждого скриншота считаем статистику белых и черных точек. Фоном объявляем те, где количество белых точек больше 92%, в тестах хватало и 94%, но это с запасом. На кадрах с фоном ищем серийник.
- Из кадра с краев отступаем по 30 пикселей, так как серийник появляется ближе к центру, а с края — никогда. Оставшееся поле разбиваем на квадраты 20x20, в каждом из которых считаем количество черных точек
- Квадраты с количеством черных точек от 10% до 60% объявляем квадратами с буквами — это уже с учетом того, что буква может только на половину попасть в квадрат и с некоторым запасом.
- Кадры, на которых есть непрерывная последовательность из как минимум 6 квадратов с буквами по горизонтали и в 3 квадрата по вертикали, объявляем кадрами с серийным номером. Сохраняем их в папку.
В итоге, появляются только картинки с текстом похожим на серийный номер:
На практике их не много появляется, так что найти из них нужный не составляет труда.
Работа с VLCj
Работа с VLCj очень простая. Вначале я прочитал документацию www.capricasoftware.co.uk/vlcj/tutorial1.php, потом немного поигрался с классом MediaPlayer, но он как-то глючил, в общем я остановился на вызове прямых функций из библиотеки LibVlc — оно и быстрее и безглючнее.
Вначале создаем библиотеку
LibVlc libvlc = LibVlcFactory.factory().create();
Потом создаем массив из 8 штук AnalyzerThread (типа Runnable) (каждому 1/8 часть времени видео), которые передаем в Executors.newFixedThreadPool(4) в таком порядке: 4, 5, 6, 7, 0, 1, 2, 3. Т.е. вначале будет сканироваться вторая часть видео, а потом первая. В каждом AnalyzerThread такой код:
System.out.println("Run section " + num);
libvlc_media_player_t p_mi = null;
libvlc_media_t media = null;
try
{
// prepare
//libvlc_instance_t instance = libvlc.libvlc_new(0, new String[0]);
libvlc_instance_t instance = libvlc.libvlc_new(2, new String[]{"--vout", "dummy"});
p_mi = libvlc.libvlc_media_player_new(instance);
libvlc.libvlc_audio_toggle_mute(p_mi);
media = libvlc.libvlc_media_new_path(instance, fileName);
libvlc.libvlc_media_player_set_media(p_mi, media);
libvlc.libvlc_media_player_play(p_mi);
Thread.sleep(msPlayerWait);
libvlc.libvlc_media_player_pause(p_mi);
// start snapshoting
int block = blockFrom;
for (long msTime = msFrom; msTime <= msTo; msTime += msInBlock, block++)
{
String path = snapshotPath + File.separator + "snap-" + String.format("%03d.png", block);
libvlc.libvlc_media_player_set_time(p_mi, msTime);
int r = libvlc.libvlc_video_take_snapshot(p_mi, 0, path, picWidth, picHeight);
if (r != 0)
System.out.println("SNAPSHOT FAILED: block=" + block + ", returnCode=" + r);
else
analyzeImage(path);
}
} finally
{
if (p_mi != null)
libvlc.libvlc_media_player_stop(p_mi);
if (media != null)
libvlc.libvlc_media_release(media);
if (p_mi != null)
libvlc.libvlc_media_player_release(p_mi);
System.out.println("Close section " + num);
}
Функция analyzeImage как раз определяет, находится ли на скриншоте ключик или нет, если находится, то сохраняет его в специальную папку.