Refactored code to use a downloader thread to download torrent in the background.

I haven't tested it yet.
This commit is contained in:
Cyrille Bollu 2018-07-26 17:53:27 +02:00
parent 5f590f48cd
commit 71e76f38cc
1 changed files with 166 additions and 102 deletions

View File

@ -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):
"""
"""
# Nothing to play at initialisation
self.play = 0
# Add thumbnail
list_item.setArt({'thumb': inst + '/' + video['thumbnailPath'])
return None
# Set a fanart image for the list item.
#list_item.setProperty('fanart_image', video['thumb'])
def list_videos(self):
"""
Create the list of playable videos in the Kodi interface.
:param: None
:return: None
"""
# Compute media info from video's metadata
info = {'title': video['name'],
'playcount': video['views'],
'plotoutline': video['description'],
'duration': video['duration']
}
# TODO: Make the instance configurable
# Make it actuatly possible to use several instances
inst = 'https://peertube.cpy.re'
# Add a rating based on likes and dislikes
if video['likes'] > 0 or video['dislikes'] > 0):
info['rating'] = video['likes']/(video['likes'] + video['dislikes']
# Get the list of videos published by the instance
# TODO: Handle failures
resp = urllib2.urlopen(inst + '/api/v1/videos')
videos = json.load(resp)
# Set additional info for the list item.
list_item.setInfo('video', info)
# Return when no videos are found
if videos['total'] == 0:
return
# This is mandatory for playable items!
list_item.setProperty('IsPlayable', 'true')
# Create a list for our items.
listing = []
for video in videos:
# 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 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)
# 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)
# Start a downloader thread
pd = PeertubeDownloader.start(magnet_f)
# Pass the item to the Kodi player.
path = fpath + h.name()
play_item = xbmcgui.ListItem(path=path)
xbmcplugin.setResolvedUrl(__handle__, True, listitem=play_item)
# 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)
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()
# 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(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])