- PVSM.RU - https://www.pvsm.ru -
Всем привет. Речь в топике пойдёт о создании плагина (программного дополнения, аддона) к замечательной программе XBMC [1]. Уровень сложности: для начинающих. Понадобятся знания HTML и общее представление о работе сайтов; не помешает знать как выглядит Python [2]. Не ждите под катом уникальных алгоритмов и магического кода, это скорее отправная точка и общее объяснение механики работы плагинов. Код будет, надеюсь, наглядным.
Некоторые из вас могут спросить: «Ведь есть репозиторий seppius [3], который решает почти все проблемы с воспроизведением онлайн-контента в рунете (в контексте XBMC). Зачем велосипеды?». Я приведу свои доводы в небольшом предисловии.
Я занялся разработкой плагинов XBMC, когда у меня появился лишний ноутбук. Было принято решение, чтобы мёртвым грузом не валялся, сделать мультимедиа приставку для телевизора (TV был лишён связи с Всемирной паутиной). XBMC был шикарным решением проблемы, но вот незадача: живу-то я в Кыргызстане. Весь трафик с IP-адресов не относящихся к нашей стране очень дорог («внешка», не знаю есть ли у вас подобное? Лишь пару месяцев назад стали появляться доступные безлимитные тарифы). А вот внутренний трафик практически бесплатен (многие популярные сайты с медиа-контентом вообще не тарифицируются у многих провайдеров), а вот XBMC как-будто вообще никто до меня не пользовался. Придумано, сделано. К моменту написания статьи мной охвачены все нужные ресурсы. Теперь занимаюсь разработкой PVR [4] плагина.
Я уже не пишу на Python код такой, какой будет приведён в статье. Но это было моей отправной точкой и пониманием механизмов работы дополнений.
Нам понадобятся:
Устанавливаем вышеуказанные. Путь у Вас может отличаться – в скобках я указал свой: Python (C:Python, далее PathPython)
Не будет лишнем знать где лежит лог-файл XBMC:
Windows: %APPDATA%XBMCxbmc.log
Linux: $HOME/.xbmc/temp/xbmc.log
Mac OS X: /Users//Library/Logs/xbmc.log
Отмечу, что мои знания Python'а были практически нулевые. Поэтому, если и Вы с ним никогда не сталкивались, то пугаться не стóит. В изучении той области функционала, которая нам нужна, он очень прост. Ну, по крайней мере, мне так показалось.
В качестве подопытного кролика был выбран сайт LineCinema [8]. Почему? Потому что гладиолус [9] я зашёл в тему Запросы на создание плагинов [10] (XBMC Russia) и на одной из последних страниц (если хотите точности, то на 87-ой) увидел запрос для этого сайта. Далее просто Сайт.
Итак, создаём в текстовом редакторе новый файл и пишем следующий код:
# -*- coding: utf-8 -*-
# Импортируем нужные нам библиотеки
import urllib, urllib2, re, sys, os
# Функция для получения исходного кода web-страниц
def GetHTML(url):
headers = {'User-Agent':'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-GB; rv:1.9.0.3) Gecko/2008092417 Firefox/3.0.3', 'Content-Type':'application/x-www-form-urlencoded'}
conn = urllib2.urlopen(urllib2.Request(url, urlencode({}), headers))
html = conn.read()
conn.close()
return html
# Тест на работоспособность
html = GetHTML('http://www.linecinema.org/')
print html
Сохраняем PathPython/test.py
Открываем Командную строку и переходим в папку PathPython. Выполняем:
python test.py
Результатом её выполнения должен быть исходный код страницы Сайта.
Теперь, в браузере открываем Сайт и лезем в исходный код (различные веб-инспекторы и firebug нам не помогут). Здесь надо обратить внимание на строчку
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251" />
и не забыть, что кодировка страницы windows-1251.
Ищем ссылки раздела «Фильмы По Жанрам» и узнаём, что они имеют вид:
<a href="/newsz/drama-online/" title="" class="mainmenu">Драма</a><br />
Переключаемся (или открываем, если успели закрыть) на test.py. Удаляем строку
print html
И на её место пишем
genre_links = re.compile('<a href="(.+?)" title="" class="mainmenu">(.+?)</a><br />').findall(html.decode('windows-1251'))
# Сопоставляем результаты первого и второго (.+?) с переменными url и title (будут присвоены в порядке указания)
for url, title in genre_links:
print title + ' [' + url + ']'
Где (.+?) регулярное выражение, пропускающее все символы (про регулярные выражения Python можно почитать на хабре [11]).
Сохраняем файл и повторяем в Командной строке
python test.py
На этот раз результатом будут названия жанров и ссылка.
Но есть лишние ссылки, которые в XBMC будут бесполезны: «Главная», «Заказать фильм», «Тех. Поддержка». Что с ними делать? Мы их исключим. Для начала грубо, хотя можно было бы использовать BeautifulSoup [12].
Напишем функцию isLinkUseful()
, специально для Сайта, т. к. «лишние» ссылки будут объявлены внутри:
def isLinkUseful(needle):
haystack = ['/index.php', '/newsz/Televydenye/100432-2008-3-11-432.html', '/newsz/500183-tex-podderzhka.html'] # список “лишних” ссылок
for a in haystack:
if needle == a:
return False
return True
и изменим последние строчки:
for url, title in genre_links:
if isLinkUseful(url):
print title + ' [' + url + ']'
Проверяем. Всё как надо:
Ещё немного тренировки. Теперь проверим один из разделов. Я взял «Документальный» (/newsz/dokumentalnyij-online/). Изменим код:
#url = 'http://www.linecinema.org/'
#html = GetHTML(url)
#genre_links = re.compile('<a href="(.+?)">(.+?)</a>').findall(html.decode('windows-1251'))
#for url, title in genre_links:
#if isLinkUseful(url):
#print title + ' [' + url + ']'
url = 'http://www.linecinema.org/newsz/dokumentalnyij-online/'
html = GetHTML(url)
genre_links = re.compile('<h1> <a href="(.+?)">(.+?)</a> </h1>').findall(html.decode('windows-1251'))
for url, title in genre_links:
print title
Проверяем и видим, что здесь исключать ничего не надо:
Тут мы использовали <h1> <a href="(.+?)">(.+?)</a> </h1>
(да-да, с пробелами), т. к. в исходном коде тоже с пробелами и если бы мы использовали просто <a href="(.+?)">(.+?)</a>
, то получили бы все ссылки со страницы. А оно нам надо?
Для начала было бы неплохо создать папку (где Вам больше всего нравится). И название можно было бы оставить «Новая папка (∞)», но не в этот раз. Прочитав Рекомендации к разработке дополнений [13] можно узнать, что название папки составляется по следующему шаблону:
<addonType>[.<mediaType>].<yourPluginName>
Следуя этому шаблону (я надеюсь, Вы всё-таки прочтёте рекомендации [14]) название будет следующим:
plugin.video.linecinema
Зайдем внутрь папки и заполним пустоту. Официальные требования [13] к видео/аудио/некоторым другим add-on’ам (если Вы до сих пор не в курсе, мы делаем именно видео-плагин) гласят, что структура должна быть следующей:
addon.py
addon.xml
changelog.txt
fanart.jpg
icon.png
LICENSE.txt
/resources
settings.xml
/language/
/lib/
/media/
Из всего вышеперечисленного мы задействуем: addon.py, addon.xml и… всё. Но в конце статьи можно найти архив, в котором сохранена вся структура.
Код файла addon.xml:
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<addon id="plugin.video.linecinema"
name="LineCinema"
version="0.0.1"
provider-name="noname">
<requires>
<import addon="xbmc.python" version="1.0"/>
</requires>
<extension point="xbmc.python.pluginsource" provides="video" library="addon.py">
<provides>video</provides>
</extension>
<extension point="xbmc.addon.metadata">
<summary>LineCinema.org</summary>
<description>
Привет, Хабрахабр.
Это тестовый плагин для сайта LineCinema.org
</description>
<platform>all</platform>
</extension>
</addon>
Остановимся подробнее. Из строки <addon id="plugin.video.linecinema" name="LineCinema" version="0.0.1" provider-name="noname">
XBMC узнает, что директория с плагином называется «plugin.video.linecinema», отображаемое название «LineCinema», версия 0.0.1, имя автора «noname».
Из строки <requires><import addon="xbmc.python" version="1.0"/></requires>
, что для работы просто необходима библиотека xbmc.python [21] версии 1.0.
Далее <extension point="xbmc.python.pluginsource" provides="video" library="addon.py"><provides>video</provides></extension>
точка входа – файл «addon.py». А сам плагин надо разместить во вкладке «Видео дополнения».
Если остались непонятные места, то попробуйте перечитать формат файла addon.xml [16].
Перейдём к файлу addon.py:
# -*- coding: utf-8 -*-
import urllib, urllib2, re, sys
import xbmcplugin, xbmcgui
def get_params():
param=[]
paramstring=sys.argv[2]
if len(paramstring)>=2:
params=sys.argv[2]
cleanedparams=params.replace('?','')
if (params[len(params)-1]=='/'):
params=params[0:len(params)-2]
pairsofparams=cleanedparams.split('&')
param={}
for i in range(len(pairsofparams)):
splitparams={}
splitparams=pairsofparams[i].split('=')
if (len(splitparams))==2:
param[splitparams[0]]=splitparams[1]
return param
xbmcplugin и xbmcgui – модули из библиотеки xbmc.python.
Функция get_params()
– честно говоря, даже не пытался разобраться в том, что она делает. Шестым чувством догадываюсь, что разбивает строку обращения плагина на параметры, позволяющее сохранять переменные при переходе по директориям плагина. Если непонятно, то не кидайте тухлое яйцо в меня. Это функция, которая очень часто встречается в плагинах, взята из одного из них.
Пишем дальше:
def addLink(title, url):
item = xbmcgui.ListItem(title, iconImage='DefaultVideo.png', thumbnailImage='')
item.setInfo( type='Video', infoLabels={'Title': title} )
xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]), url=url, listitem=item)
def addDir(title, url, mode):
sys_url = sys.argv[0] + '?title=' + urllib.quote_plus(title) + '&url=' + urllib.quote_plus(url) + '&mode=' + urllib.quote_plus(str(mode))
item = xbmcgui.ListItem(title, iconImage='DefaultFolder.png', thumbnailImage='')
item.setInfo( type='Video', infoLabels={'Title': title} )
xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]), url=sys_url, listitem=item, isFolder=True)
addDir(title, url, mode)
– будет создавать пункт для перехода по ссылке и отображения новых пунктов. Mode
– переменная, хранящая номер функции для обработки ссылки (ниже будет понятно что к чему).
addLink(title, url)
– будет создавать пункт, при переходе на который начнётся воспроизведение url
. В url
должна храниться прямая ссылка на видео/аудио/изображение и т.п.
Добавляем ниже:
params = get_params()
url = None
title = None
mode = None
try: title = urllib.unquote_plus(params['title'])
except: pass
try: url = urllib.unquote_plus(params['url'])
except: pass
try: mode = int(params['mode'])
except: pass
xbmcplugin.endOfDirectory(int(sys.argv[1]))
Это будут наши «глобальные переменные». А xbmcplugin.endOfDirectory(int(sys.argv[1]))
сообщит XBMC, что это конец «директории» и больше пунктов не будет.
Осталось совсем немного. Добавляем наши функции getHTML(url)
и isLinkUseful(needle)
из первой части и теперь addon.py должен выглядеть следующим образом:
# -*- coding: utf-8 -*-
import urllib, urllib2, re, sys
import xbmcplugin, xbmcgui
def getHTML(url):
headers = {'User-Agent':'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-GB; rv:1.9.0.3) Gecko/2008092417 Firefox/3.0.3', 'Content-Type':'application/x-www-form-urlencoded'}
conn = urllib2.urlopen(urllib2.Request(url, urllib.urlencode({}), headers))
html = conn.read()
conn.close()
return html
def isLinkUseful(needle):
haystack = ['/index.php', '/newsz/Televydenye/100432-2008-3-11-432.html', '/newsz/500183-tex-podderzhka.html']
for a in haystack:
if needle == a:
return False
return True
def get_params():
param=[]
paramstring=sys.argv[2]
if len(paramstring)>=2:
params=sys.argv[2]
cleanedparams=params.replace('?','')
if (params[len(params)-1]=='/'):
params=params[0:len(params)-2]
pairsofparams=cleanedparams.split('&')
param={}
for i in range(len(pairsofparams)):
splitparams={}
splitparams=pairsofparams[i].split('=')
if (len(splitparams))==2:
param[splitparams[0]]=splitparams[1]
return param
def addLink(title, url):
item = xbmcgui.ListItem(title, iconImage='DefaultVideo.png', thumbnailImage='')
item.setInfo( type='Video', infoLabels={'Title': title} )
xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]), url=url, listitem=item)
def addDir(title, url, mode):
sys_url = sys.argv[0] + '?title=' + urllib.quote_plus(title) + '&url=' + urllib.quote_plus(url) + '&mode=' + urllib.quote_plus(str(mode))
item = xbmcgui.ListItem(title, iconImage='DefaultFolder.png', thumbnailImage='')
item.setInfo( type='Video', infoLabels={'Title': title} )
xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]), url=sys_url, listitem=item, isFolder=True)
params = get_params()
url = None
title = None
mode = None
try: title = urllib.unquote_plus(params['title'])
except: pass
try: url = urllib.unquote_plus(params['url'])
except: pass
try: mode = int(params['mode'])
except: pass
xbmcplugin.endOfDirectory(int(sys.argv[1]))
У Вас так же? Если да, то отлично.
Теперь опять вспомним первую часть и проанализируем Сайт: у нас будет список жанров; было бы неплохо найти и показать какие фильмы есть в каждой из категорий; отобразить ссылку на просмотр фильма. Получается нам нужны три функции. Назовём их так: Categories() – список жанров, Movies() – список доступных фильмов, Videos() – ссылки на видео. Приступим:
def Categories():
url = 'http://www.linecinema.org'
html = getHTML(url)
genre_links = re.compile('<a href="(.+?)" title="" class="mainmenu">(.+?)</a><br />').findall(html.decode('windows-1251').encode('utf-8'))
for link, title in genre_links:
if isLinkUseful(link):
addDir(title, url + link, 20)
addDir(title, url + link, 20)
заменил нам print title + ' [' + url + ']'
. Categories()
не будет иметь входных параметров — url
нам надо объявить самим. Обратите внимание, что последний слеш убран – он будет компенсирован переменной link
.
Цифра 20
– сообщит, что следующая функция будет Movies(url)
. Но ведь магии не бывает и нам надо написать для этого в конец файла, перед xbmcplugin.endOfDirectory(int(sys.argv[1]))
, проверку:
if mode == None:
Categories()
elif mode == 20:
Movies(url)
elif mode == 30:
Videos(url, title)
Чем это попахивает тут? Не говнокодом ли? Ну точно, им самым. Но такая модель помогла мне разобраться что к чему, сейчас я её, конечно же, не использую. А вот мой первый плагин был точно таким же.
Не отвлекаемся и парсим список фильмов:
def Movies(url):
html = getHTML(url)
movie_links = re.compile('<h1> <a href="(.+?)">(.+?)</a> </h1>').findall(html.decode('windows-1251').encode('utf-8'))
for link, title in movie_links:
addDir(title[:-12], link, 30)
Опять код из первой части. title[:-12]
что это за страшный смайлик? Если убрать [:-12], то вы увидите, что название будет иметь вид «Умопомрачительный фильмец / Magnificent movie (2013) HDRip онлайн». И зачем нам «онлайн»? Убрать его!
Теперь надо выдернуть прямую ссылку на видео. Открываем любую ссылку фильма с Сайта и смотрим его исходный код. Ого, да тут нас ждали! Видим вот такой блок:
<script language="javascript">
flashvars = {
uid: "player_uppod",
bufferproc2 :1,
bufferproc_reloadsec :10,
st: "/templates/linecinema-dle90/swf/video30-365.txt",
file: "http://st7.linecinema.org/s/820e31e7cbe3e8c362785b733db56c57/film10/Druzea.navek.DVDRip.flv"
};
params = {bgcolor:"#ffffff", allowFullScreen:"true", allowScriptAccess:"always",wmode:"opaque"};
attributes = {
id: "player_uppod",
name: "player_uppod"
};
swfobject.embedSWF("/templates/linecinema-dle90/swf/uppod.swf", "player_uppod", "570", "440", "10.0.0",false,flashvars,params,attributes);
</script>
А вот и ссылка: http://st7.linecinema.org/s/820e31e7cbe3e8c362785b733db56c57/film10/Druzea.navek.DVDRip.flv
(у Вас может отличаться). Исходя из этого делаем функцию:
def Videos(url, title):
html = getHTML(url)
link = re.compile('file: "(.+?)"').findall(html.decode('windows-1251').encode('utf-8'))[0]
addLink(title, link)
С кодом закончили. Сравните свой результат с моим:
# -*- coding: utf-8 -*-
import urllib, urllib2, re, sys
import xbmcplugin, xbmcgui
def getHTML(url):
headers = {'User-Agent':'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-GB; rv:1.9.0.3) Gecko/2008092417 Firefox/3.0.3', 'Content-Type':'application/x-www-form-urlencoded'}
conn = urllib2.urlopen(urllib2.Request(url, urllib.urlencode({}), headers))
html = conn.read()
conn.close()
return html
def isLinkUseful(needle):
haystack = ['/index.php', '/newsz/Televydenye/100432-2008-3-11-432.html', '/newsz/500183-tex-podderzhka.html']
for a in haystack:
if needle == a:
return False
return True
def Categories():
url = 'http://www.linecinema.org'
html = getHTML(url)
genre_links = re.compile('<a href="(.+?)" title="" class="mainmenu">(.+?)</a><br />').findall(html.decode('windows-1251').encode('utf-8'))
for link, title in genre_links:
if isLinkUseful(link):
addDir(title, url + link, 20)
def Movies(url):
html = getHTML(url)
movie_links = re.compile('<h1> <a href="(.+?)">(.+?)</a> </h1>').findall(html.decode('windows-1251').encode('utf-8'))
for link, title in movie_links:
addDir(title[:-12], link, 30)
def Videos(url, title):
html = getHTML(url)
link = re.compile('file: "(.+?)"').findall(html.decode('windows-1251').encode('utf-8'))[0]
addLink(title, link)
def get_params():
param=[]
paramstring=sys.argv[2]
if len(paramstring)>=2:
params=sys.argv[2]
cleanedparams=params.replace('?','')
if (params[len(params)-1]=='/'):
params=params[0:len(params)-2]
pairsofparams=cleanedparams.split('&')
param={}
for i in range(len(pairsofparams)):
splitparams={}
splitparams=pairsofparams[i].split('=')
if (len(splitparams))==2:
param[splitparams[0]]=splitparams[1]
return param
def addLink(title, url):
item = xbmcgui.ListItem(title, iconImage='DefaultVideo.png', thumbnailImage='')
item.setInfo( type='Video', infoLabels={'Title': title} )
xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]), url=url, listitem=item)
def addDir(title, url, mode):
sys_url = sys.argv[0] + '?title=' + urllib.quote_plus(title) + '&url=' + urllib.quote_plus(url) + '&mode=' + urllib.quote_plus(str(mode))
item = xbmcgui.ListItem(title, iconImage='DefaultFolder.png', thumbnailImage='')
item.setInfo( type='Video', infoLabels={'Title': title} )
xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]), url=sys_url, listitem=item, isFolder=True)
params = get_params()
url = None
title = None
mode = None
try: title = urllib.unquote_plus(params['title'])
except: pass
try: url = urllib.unquote_plus(params['url'])
except: pass
try: mode = int(params['mode'])
except: pass
if mode == None:
Categories()
elif mode == 20:
Movies(url)
elif mode == 30:
Videos(url, title)
xbmcplugin.endOfDirectory(int(sys.argv[1]))
Наконец-то, перейдём к установке плагина. Существует два простых способа: установка из репозитория и установка из файла ZIP. Нам подходит второй. Запаковываем в ZIP нашу папку (папку!) любым архиватором. Затем запускаем XBMC, переходим Система (Настройки) > Дополнения > Установить из файла ZIP и указываем путь до нашего архива. После сообщения о том, что «Дополнение успешно включено», идём в главное меню Видео > Дополнения > LineCinema.
Спасибо за внимание. Надеюсь было интересно… или полезно. Не исключаю, что у меня ужасная манера изложения, но это первый опыт в написании статей.
P.S. Пожалуйста, не кидайте в меня тухлые помидоры.
Автор: delletenebre
Источник [28]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/htpc/25035
Ссылки в тексте:
[1] XBMC: http://xbmc.org/
[2] Python: http://ru.wikipedia.org/wiki/Python
[3] репозиторий seppius: http://code.google.com/p/seppius-xbmc-repo/
[4] PVR: http://wiki.xbmc.org/index.php?title=PVR
[5] Python 2.x: http://www.python.org/download/
[6] здесь: http://wiki.xbmc.org/index.php?title=Python_development
[7] XBMC 11 Eden: http://xbmc.org/download/
[8] LineCinema: http://www.linecinema.org/
[9] гладиолус: http://lurkmore.to/%D0%9F%D0%BE%D1%82%D0%BE%D0%BC%D1%83_%D1%87%D1%82%D0%BE_%D0%B3%D0%BB%D0%B0%D0%B4%D0%B8%D0%BE%D0%BB%D1%83%D1%81
[10] Запросы на создание плагинов: http://xbmc.ru/forum/showthread.php?t=428
[11] хабре: http://habrahabr.ru/post/115825/
[12] BeautifulSoup: http://habrahabr.ru/post/114503/
[13] Рекомендации к разработке дополнений: http://wiki.xbmc.org/index.php?title=Add-on_development
[14] прочтёте рекомендации: http://wiki.xbmc.org/index.php?title=Add-on_development#Directory_Name
[15] так далее: http://wiki.xbmc.org/index.php?title=Addon.xml#The_.3Cextension.3E_elements
[16] здесь: http://wiki.xbmc.org/index.php?title=Addon.xml
[17] Ссылка на рекомендации: http://wiki.xbmc.org/index.php?title=Add-on_development#fanart.jpg
[18] Рекомендации: http://wiki.xbmc.org/index.php?title=Add-on_development#icon.png
[19] Описание и формат: http://wiki.xbmc.org/index.php?title=Addon_Settings
[20] надо переводить: http://wiki.xbmc.org/index.php?title=Language_support
[21] xbmc.python: http://mirrors.xbmc.org/docs/python-docs/
[22] ZIP с исходниками: http://code.google.com/p/xbmc-kg-ontv/downloads/detail?name=plugin.video.linecinema.zip
[23] HOW-TO:Write plugins for XBMC: http://wiki.xbmc.org/index.php?title=HOW-TO:Write_plugins_for_XBMC
[24] XBMC Addon Developers Guide: https://www.google.kg/url?sa=t&rct=j&q=&esrc=s&source=web&cd=1&cad=rja&ved=0CC0QFjAA&url=http%3A%2F%2Fxbmc-gpodder-integration.googlecode.com%2Ffiles%2FXBMC%2520Addon%2520Developers%2520Guide%2520-R7.pdf&ei=_Uf2UJ7TKPHU4QS84YDICQ&usg=AFQjCNGqm1zWNmZPk7KLDJKOPW0gCwsevQ&bvm=bv.41018144,d.bGE
[25] 3rd party add-on repositories: http://wiki.xbmc.org/index.php?title=3rd_party_add-on_repositories
[26] Форум XBMC Russia: http://xbmc.ru
[27] XBMC Community Forum: http://forum.xbmc.org/
[28] Источник: http://habrahabr.ru/post/166097/
Нажмите здесь для печати.