Наверное, многие из вас слышали о торрентах и p2p-сетях, но мало кто знает, что с их помощью можно передавать и потоковое видео, в том числе и ТВ-сигнал. Для этого под ПК есть замечательная программа AceStream, и не менее замечательный сайт torrentstream.tv. На мобильных устройствах дела с этим обстоят куда печальнее. О том, как я пытался настроить торрент ТВ на Android и что из этого вышло пойдет речь в этой статье.
Как я уже говорил, дела с p2p-телевидением на Android обстоят довольно плохо: в Play Market я не смог найти ни одного вменяемого приложения, справляющегося с поставленной задачей. Пришлось гуглить в интернете. На официальном форуме AceStream нашел ссылку на бета-версию AcePlayer под Android (x86, ARMv7).
Установил, запустил:

О'кей, гугл, где мне найти «Ace Stream content id»? Недолго погуглив, нашел один не очень хороший способ:

Спасибо, как-то не очень хочется каждый раз при переключении канала лезть в компьютер за Content ID. Значит, решил я, нужно составить свой плей-лист.
Итак, цель — сделать программу-парсер, которая в автоматическом режиме обходит все страницы с каналами на torrentstream и вытягивает оттуда Content ID. Писать решил на java с помощью регулярных выражений, так как изучать что-либо более подходящее времени не было.
Для начала сделаем простой обходчик страниц с каналами:
Итак, данные со страниц каналов мы получили, теперь надо найти в исходном коде искомый Content ID. Немного покопавшись в исходниках торрентстрима, я выяснил, что плеер встраивается на сайт с помощью iframe с сайта 1ttv.net:
В коде самого iframe я нашел, в каком месте запускается плеер:
Видимо, f01532846f38f27ae29b532bb599e9ddb3578950 — и есть тот самый Content ID.
Ну что ж, достанем его:
Осталось только прикрутить компаратор и отсортировать каналы по алфавиту:
И вывести это в html-файл:
Теперь полученный файлик можно загрузить на android-устройство и наслаждаться списком из 260 каналов:

Устанавливаем AcePlayer
Как я уже говорил, дела с p2p-телевидением на Android обстоят довольно плохо: в Play Market я не смог найти ни одного вменяемого приложения, справляющегося с поставленной задачей. Пришлось гуглить в интернете. На официальном форуме AceStream нашел ссылку на бета-версию AcePlayer под Android (x86, ARMv7).
Установил, запустил:

О'кей, гугл, где мне найти «Ace Stream content id»? Недолго погуглив, нашел один не очень хороший способ:

Спасибо, как-то не очень хочется каждый раз при переключении канала лезть в компьютер за Content ID. Значит, решил я, нужно составить свой плей-лист.
Составляем плей-лист
Итак, цель — сделать программу-парсер, которая в автоматическом режиме обходит все страницы с каналами на torrentstream и вытягивает оттуда Content ID. Писать решил на java с помощью регулярных выражений, так как изучать что-либо более подходящее времени не было.
Для начала сделаем простой обходчик страниц с каналами:
public class ChannelParser {
private static Pattern nextPagePattern = Pattern.compile("<a href=\"[^\"]+\">»</a>");
private static Pattern chPattern = Pattern.compile("<a href=\"[^\"]+\" class=\"pm-title-link \" title=\"[^\"]+\">");
public static void main(String[] args) throws IOException {
List<Channel> channels = new ArrayList<>();
URL cPage = new URL("http://torrentstream.tv/browse-vse-videos-1-date.html");
while (cPage != null) {
String cPageContent = urlContent(cPage);
Matcher chMatcher = chPattern.matcher(cPageContent);
while (chMatcher.find()) {
channels.add(new Channel(chMatcher.group()));
}
cPage = nextPage(cPageContent);
}
//todo
}
private static String urlContent(URL page) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(page.openStream()));
String inputLine;
StringBuilder response = new StringBuilder();
while ((inputLine = reader.readLine()) != null) {
response.append(inputLine);
}
return new String(response.toString().getBytes("cp1251"), "utf8"); //Костыль, чтобы не было проблем с кодировкой
}
private static URL nextPage(String content) throws MalformedURLException {
Matcher nextPageMatcher = nextPagePattern.matcher(content);
if (nextPageMatcher.find()) {
Matcher link = Pattern.compile("\"\\S+\"").matcher(nextPageMatcher.group());
if (link.find()) {
return new URL(nextPageMatcher.group().substring(link.start() + 1, link.end() - 1));
}
}
return null;
}
private static class Channel {
private static Pattern titlePattern = Pattern.compile("title=\"[^\"]+\"");
private static Pattern linkPattern = Pattern.compile("href=\"[^\"]+\"");
private String name;
private String id;
public Channel(String linkTag) throws IOException {
Matcher titleMatcher = titlePattern.matcher(linkTag);
if (titleMatcher.find()) {
name = linkTag.substring(titleMatcher.start() + 7, titleMatcher.end() - 1);
}
Matcher chLinkMatcher = linkPattern.matcher(linkTag);
if (chLinkMatcher.find()) {
String content = urlContent(new URL(linkTag.substring(chLinkMatcher.start() + 6, chLinkMatcher.end() - 1)));
//todo
}
}
}
}
Итак, данные со страниц каналов мы получили, теперь надо найти в исходном коде искомый Content ID. Немного покопавшись в исходниках торрентстрима, я выяснил, что плеер встраивается на сайт с помощью iframe с сайта 1ttv.net:
<iframe src="http://1ttv.net/iframe.php?site=286&channel=388" style="width:650px; height: 586px; border: none; background-color: #000;" scrolling="no" frameborder="0"></iframe>
В коде самого iframe я нашел, в каком месте запускается плеер:
try {
var p = this;
player_context = this;
this.loadPlayer("f01532846f38f27ae29b532bb599e9ddb3578950",{autoplay: true});
}
Видимо, f01532846f38f27ae29b532bb599e9ddb3578950 — и есть тот самый Content ID.
Ну что ж, достанем его:
//...
private static Pattern framePattern = Pattern.compile("http://1ttv\\.net/iframe\\.php[^\"]+");
private static Pattern idPattern = Pattern.compile("loadPlayer\\(\"[0-9a-f]+\"");
//...
public Channel(String linkTag) throws IOException {
//...
Matcher frameMatcher = framePattern.matcher(content);
if (frameMatcher.find()) {
String frameContent = urlContent(new URL(content.substring(frameMatcher.start(), frameMatcher.end())));
Matcher idMatcher = idPattern.matcher(frameContent);
if (idMatcher.find()) {
id = frameContent.substring(idMatcher.start() + 12, idMatcher.end() - 1);
}
}
}
Осталось только прикрутить компаратор и отсортировать каналы по алфавиту:
public static void main(String[] args) throws IOException {
//...
Collections.sort(channels);
}
//...
private static class Channel implements Comparable<Channel> {
//...
@Override
public int compareTo(Channel ch) {
return name.compareToIgnoreCase(ch.name);
}
}
И вывести это в html-файл:
public static void main(String[] args) throws IOException {
//...
File file = new File("list.html");
if (!file.exists()) {
file.createNewFile();
}
FileWriter writer = new FileWriter(file);
writer.write("<!DOCTYPE html><html><head><title>Список каналов</title></head><body><table>");
for (Channel channel : channels) {
if (channel.id == null || channel.id.length() < 40) {
continue;
}
writer.write("<tr><td>" + channel.name + "</td><td>" + channel.id + "</td></tr>");
}
writer.write("</table></body></html>");
writer.flush();
writer.close();
}
Теперь полученный файлик можно загрузить на android-устройство и наслаждаться списком из 260 каналов:
