Refactored code to use a downloader thread to download torrent in the background.
I haven't tested it yet.
This commit is contained in:
parent
5f590f48cd
commit
71e76f38cc
266
peertube.py
266
peertube.py
|
@ -14,139 +14,203 @@ import libtorrent
|
|||
import time, sys
|
||||
import urllib2, json
|
||||
from urlparse import parse_qsl
|
||||
import xbmcgui, xbmcplugin
|
||||
import AddonSignals
|
||||
import xbmcgui, xbmcplugin, xbmcvfs
|
||||
from threading import Thread
|
||||
|
||||
# Get the plugin url in plugin:// notation.
|
||||
__url__ = sys.argv[0]
|
||||
# Get the plugin handle as an integer number.
|
||||
__handle__ = int(sys.argv[1])
|
||||
|
||||
def list_videos():
|
||||
class PeertubeDownloader(Thread):
|
||||
"""
|
||||
Create the list of playable videos in the Kodi interface.
|
||||
:param: None
|
||||
:return: None
|
||||
A class to download peertube torrents in the background
|
||||
"""
|
||||
|
||||
# TODO: Make the instance configurable
|
||||
# Make it actuatly possible to use several instances
|
||||
inst = 'https://peertube.cpy.re'
|
||||
def init(self, magnet_f):
|
||||
"""
|
||||
:param magnet_f: str
|
||||
:return: None
|
||||
"""
|
||||
Thread.__init__(self)
|
||||
self.magnet_f
|
||||
|
||||
# Get the list of videos published by the instance
|
||||
# TODO: Handle failures
|
||||
resp = urllib2.urlopen(inst + '/api/v1/videos')
|
||||
videos = json.load(resp)
|
||||
def run(self):
|
||||
"""
|
||||
Download the torrent specified by self.magnet_f
|
||||
:param: None
|
||||
:return: None
|
||||
"""
|
||||
|
||||
# Return when no videos are found
|
||||
if videos['total'] == 0:
|
||||
# Open bitTorrent session
|
||||
ses = libtorrent.session()
|
||||
ses.listen_on(6881, 6891)
|
||||
|
||||
# Read magnet's data
|
||||
f = xbmcvfs.File(self.magnet_f, 'r')
|
||||
magnet = f.read()
|
||||
|
||||
# Add torrent
|
||||
fpath = xbmc.translatePath('special://temp')
|
||||
h = ses.add_torrent({'url': magnet, 'save_path': fpath})
|
||||
|
||||
# Set sequential mode to allow watching while downloading
|
||||
h.set_sequential_download(True)
|
||||
|
||||
# Download torrent
|
||||
signal_sent = 0
|
||||
while not h.is_seed():
|
||||
time.sleep(1)
|
||||
s = h.status(e)
|
||||
# Inform addon that all the metadata has been downloaded and that it may start playing the torrent
|
||||
if s.status >=3 and signal_sent == 0:
|
||||
AddonSignals.sendSignal('metadata_downloaded', {'name': h.name()} )
|
||||
signal_sent = 1
|
||||
|
||||
# Everything is done
|
||||
return
|
||||
|
||||
# Create a list for our items.
|
||||
listing = []
|
||||
for video in videos:
|
||||
class PeertubeAddon():
|
||||
"""
|
||||
Main class of the addon
|
||||
"""
|
||||
|
||||
# Create a list item with a text label
|
||||
list_item = xbmcgui.ListItem(label=video['name'])
|
||||
def __init__(self):
|
||||
"""
|
||||
"""
|
||||
|
||||
# Add thumbnail
|
||||
list_item.setArt({'thumb': inst + '/' + video['thumbnailPath'])
|
||||
# Nothing to play at initialisation
|
||||
self.play = 0
|
||||
|
||||
# Set a fanart image for the list item.
|
||||
#list_item.setProperty('fanart_image', video['thumb'])
|
||||
return None
|
||||
|
||||
# Compute media info from video's metadata
|
||||
info = {'title': video['name'],
|
||||
'playcount': video['views'],
|
||||
'plotoutline': video['description'],
|
||||
'duration': video['duration']
|
||||
}
|
||||
def list_videos(self):
|
||||
"""
|
||||
Create the list of playable videos in the Kodi interface.
|
||||
:param: None
|
||||
:return: None
|
||||
"""
|
||||
|
||||
# Add a rating based on likes and dislikes
|
||||
if video['likes'] > 0 or video['dislikes'] > 0):
|
||||
info['rating'] = video['likes']/(video['likes'] + video['dislikes']
|
||||
# TODO: Make the instance configurable
|
||||
# Make it actuatly possible to use several instances
|
||||
inst = 'https://peertube.cpy.re'
|
||||
|
||||
# Set additional info for the list item.
|
||||
list_item.setInfo('video', info)
|
||||
# Get the list of videos published by the instance
|
||||
# TODO: Handle failures
|
||||
resp = urllib2.urlopen(inst + '/api/v1/videos')
|
||||
videos = json.load(resp)
|
||||
|
||||
# This is mandatory for playable items!
|
||||
list_item.setProperty('IsPlayable', 'true')
|
||||
# Return when no videos are found
|
||||
if videos['total'] == 0:
|
||||
return
|
||||
|
||||
# Find smallest file's torrentUrl
|
||||
# TODO: Get the best quality torrent given settings and/or available bandwidth
|
||||
# See how they do that in the peerTube client's code
|
||||
min_size = -1
|
||||
resp = urllib2.urlopen(inst + '/api/v1/videos/' + video['uuid'])
|
||||
metadata = json.load(resp)
|
||||
for f in metadata['files']:
|
||||
if f['size'] < min_size or min_size == -1:
|
||||
# TODO: See if using magnet wouldn't be better
|
||||
# Atm, files are saved by their filename (<uuid>.mp4) while I think
|
||||
# that they are saved by their (dn) name when using magnet links
|
||||
#magnet = f['magnetUri']
|
||||
magnet = f['torrentUrl']
|
||||
# Create a list for our items.
|
||||
listing = []
|
||||
for video in videos:
|
||||
|
||||
# Create a list item with a text label
|
||||
list_item = xbmcgui.ListItem(label=video['name'])
|
||||
|
||||
# Add thumbnail
|
||||
list_item.setArt({'thumb': inst + '/' + video['thumbnailPath']})
|
||||
|
||||
# Set a fanart image for the list item.
|
||||
#list_item.setProperty('fanart_image', video['thumb'])
|
||||
|
||||
# Compute media info from video's metadata
|
||||
info = {'title': video['name'],
|
||||
'playcount': video['views'],
|
||||
'plotoutline': video['description'],
|
||||
'duration': video['duration']
|
||||
}
|
||||
|
||||
# Add a rating based on likes and dislikes
|
||||
if video['likes'] > 0 or video['dislikes'] > 0:
|
||||
info['rating'] = video['likes']/(video['likes'] + video['dislikes'])
|
||||
|
||||
# Set additional info for the list item.
|
||||
list_item.setInfo('video', info)
|
||||
|
||||
# This is mandatory for playable items!
|
||||
list_item.setProperty('IsPlayable', 'true')
|
||||
|
||||
# Find smallest file's torrentUrl
|
||||
# TODO: Get the best quality torrent given settings and/or available bandwidth
|
||||
# See how they do that in the peerTube client's code
|
||||
min_size = -1
|
||||
resp = urllib2.urlopen(inst + '/api/v1/videos/' + video['uuid'])
|
||||
metadata = json.load(resp)
|
||||
for f in metadata['files']:
|
||||
if f['size'] < min_size or min_size == -1:
|
||||
magnet = f['magnetUri']
|
||||
|
||||
# Save magnet link temporarily.
|
||||
tmp_f = xbmc.translatePath('special://temp') + '/plugin.video.peertube/todo'
|
||||
f = xbmcvfs.File(tmp_f, 'w')
|
||||
f.write(magnet)
|
||||
f.close()
|
||||
|
||||
# Add our item to the listing as a 3-element tuple.
|
||||
url = '{0}?action=play&url={1}'.format(__url__, magnet)
|
||||
url = '{0}?action=play&magnet={1}'.format(__url__, tmp_f)
|
||||
listing.append((url, list_item, False))
|
||||
|
||||
# Add our listing to Kodi.
|
||||
xbmcplugin.addDirectoryItems(__handle__, listing, len(listing))
|
||||
xbmcplugin.addSortMethod(__handle__, xbmcplugin.SORT_METHOD_LABEL_IGNORE_THE)
|
||||
xbmcplugin.endOfDirectory(__handle__)
|
||||
# Add our listing to Kodi.
|
||||
xbmcplugin.addDirectoryItems(__handle__, listing, len(listing))
|
||||
xbmcplugin.addSortMethod(__handle__, xbmcplugin.SORT_METHOD_LABEL_IGNORE_THE)
|
||||
xbmcplugin.endOfDirectory(__handle__)
|
||||
|
||||
def play_video(url):
|
||||
"""
|
||||
Play the video at the provided url
|
||||
:param url: str
|
||||
:return: None
|
||||
"""
|
||||
def play_video_continue():
|
||||
"""
|
||||
"""
|
||||
|
||||
# Open bitTorrent session
|
||||
ses = libtorrent.session()
|
||||
ses.listen_on(6881, 6891)
|
||||
self.play = 1
|
||||
return
|
||||
|
||||
# Add torrent
|
||||
fpath = xbmc.translatePath('special://temp')
|
||||
h = ses.add_torrent({'url': url, 'save_path': fpath})
|
||||
def play_video(self, magnet_f):
|
||||
"""
|
||||
Start the torrent's download and play it while being downloaded
|
||||
:param magnet_f: str
|
||||
:return: None
|
||||
"""
|
||||
|
||||
# Set sequential mode to allow watching while downloading
|
||||
h.set_sequential_download(True)
|
||||
# Start a downloader thread
|
||||
pd = PeertubeDownloader.start(magnet_f)
|
||||
|
||||
# Wait a little so that file is already partialy downloaded
|
||||
# TODO: The download should be run in the background
|
||||
# because at the moment it stops when kodi starts playing the video
|
||||
# This will probably need a service add on
|
||||
while True:
|
||||
time.sleep(1)
|
||||
s = h.status()
|
||||
if (s.state >= 3):
|
||||
break
|
||||
time.sleep(10)
|
||||
# Wait until the PeerTubeDownloader has downloaded all the torrent's metadata + a little bit more
|
||||
AddonSignals.RegisterSlot('plugin.video.peertube', 'metadata_downloaded', play_video_continue)
|
||||
while self.play == 0:
|
||||
xbmc.sleep(1000)
|
||||
xbmc.sleep(3000)
|
||||
|
||||
# Pass the item to the Kodi player.
|
||||
path = fpath + h.name()
|
||||
play_item = xbmcgui.ListItem(path=path)
|
||||
xbmcplugin.setResolvedUrl(__handle__, True, listitem=play_item)
|
||||
# Pass the item to the Kodi player for actual playback.
|
||||
path = fpath + h.name()
|
||||
play_item = xbmcgui.ListItem(path=path)
|
||||
xbmcplugin.setResolvedUrl(__handle__, True, listitem=play_item)
|
||||
|
||||
def router(paramstring):
|
||||
"""
|
||||
Router function that calls other functions
|
||||
depending on the provided paramstring
|
||||
:param paramstring:
|
||||
:return:
|
||||
"""
|
||||
# Parse a URL-encoded paramstring to the dictionary of
|
||||
# {<parameter>: <value>} elements
|
||||
params = dict(parse_qsl(paramstring[1:]))
|
||||
# Check the parameters passed to the plugin
|
||||
if params:
|
||||
# Play a video from a provided URL.
|
||||
play_video(params['url'])
|
||||
else:
|
||||
# If the plugin is called from Kodi UI without any parameters,
|
||||
# display the list of videos
|
||||
list_videos()
|
||||
def router(self, paramstring):
|
||||
"""
|
||||
Router function that calls other functions
|
||||
depending on the provided paramstring
|
||||
:param paramstring: dict
|
||||
:return: None
|
||||
"""
|
||||
|
||||
# Parse a URL-encoded paramstring to the dictionary of
|
||||
# {<parameter>: <value>} elements
|
||||
params = dict(parse_qsl(paramstring[1:]))
|
||||
|
||||
# Check the parameters passed to the plugin
|
||||
if params:
|
||||
# Play a video from a provided URL.
|
||||
self.play_video(params['magnet'])
|
||||
else:
|
||||
# Display the list of videos when the plugin is called from Kodi UI without any parameters
|
||||
self.list_videos()
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
# Initialise addon
|
||||
addon = PeertubeAddon()
|
||||
# Call the router function and pass the plugin call parameters to it.
|
||||
router(sys.argv[2])
|
||||
addon.router(sys.argv[2])
|
||||
|
|
Loading…
Reference in New Issue