Как стать автором
Обновить

Настройка Торрент ТВ на Android

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

Устанавливаем 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 каналов:

Теги:
Хабы:
Данная статья не подлежит комментированию, поскольку её автор ещё не является полноправным участником сообщества. Вы сможете связаться с автором только после того, как он получит приглашение от кого-либо из участников сообщества. До этого момента его username будет скрыт псевдонимом.