@ -3,16 +3,13 @@
import pluginInterfaces as pi
import utils
import video as vid
import os
import mimetypes
import json
import logging
import sys
import datetime
import pytz
from os.path import splitext , basename , abspath # TODO: remove me, we already import `os` or at least choose one
from os.path import splitext , basename , abspath
from tzlocal import get_localzone
from configparser import RawConfigParser
@ -20,16 +17,19 @@ from requests_oauthlib import OAuth2Session
from requests_toolbelt import MultipartEncoder , MultipartEncoderMonitor
from oauthlib.oauth2 import LegacyApplicationClient
from clint.textui.progress import Bar as ProgressBar
from yapsy.PluginManager import PluginManagerSingleton
logger = logging . getLogger ( ' Prismedia ' )
class Peertube ( pi . IPlatformPlugin ) :
"""
Plugin to upload to the Peertube platform .
The connetions files should be set as # TODO: EXPLAIN HOW TO SETUP THE SECRET FILES
The connec tions files should be set as # TODO: EXPLAIN HOW TO SETUP THE SECRET FILES
- `publish-at-peertube=DATE` : overrides the default `publish-at=DATE` for this platform . # TODO: Maybe we will use a [<plugin_name>] section on the config fire, explain that.
"""
SECRETS_FILE = ' peertube_secret '
NAME = " peertube " # TODO: find if it is possible to get the plugin’s name from inside the plugin
SECRETS_FILE = " peertube_secret "
PRIVACY = {
" public " : 1 ,
" unlisted " : 2 ,
@ -74,14 +74,17 @@ class Peertube(pi.IPlatformPlugin):
def __init__ ( self ) :
self . channelCreate = False
self . name = " peertube " # TODO: find if it is possible to get the plugin’s name from inside the plugin
self . oauth = { }
self . secret = { }
def prepare_options ( self , video , options ) :
pluginManager = PluginManagerSingleton . get ( )
# TODO: get the `publish-at-peertube=DATE` option
# TODO: get the `channel` and `channel-create` options
video . platform [ self . name ] . channel = " "
pluginManager . registerOptionFromPlugin ( " Platform " , self . NAME , " publish-at " , " 2034-05-07T19:00:00 " )
pluginManager . registerOptionFromPlugin ( " Platform " , self . NAME , " channel " , " toto " )
pluginManager . registerOptionFromPlugin ( " Platform " , self . NAME , " channel-create " , False )
video . platform [ self . NAME ] . channel = " "
self . secret = RawConfigParser ( )
self . secret . read ( self . SECRETS_FILE )
@ -105,32 +108,28 @@ class Peertube(pi.IPlatformPlugin):
client_secret = str ( self . secret . get ( ' peertube ' , ' client_secret ' ) )
)
def convert_peertube_date ( self , date ) :
date = datetime . datetime . strptime ( date , ' % Y- % m- %d T % H: % M: % S ' )
tz = get_localzone ( )
tz = pytz . timezone ( str ( tz ) )
return tz . localize ( date ) . isoformat ( )
def get_default_channel ( self , user_info ) :
return user_info [ ' videoChannels ' ] [ 0 ] [ ' id ' ]
def get_channel_by_name ( self , user_info , video ) :
for channel in user_info [ " videoChannels " ] :
if channel [ ' displayName ' ] == video . platform [ self . name ] . channel :
if channel [ ' displayName ' ] == video . platform [ self . NAME ] . channel :
return channel [ ' id ' ]
def create_channel ( self , instance_url , video ) :
template = ( ' Peertube: Channel %s does not exist, creating it. ' )
logger . info ( template % ( video . platform [ self . name ] . channel ) )
channel_name = utils . cleanString ( video . platform [ self . name ] . channel )
logger . info ( template % ( video . platform [ self . NAME ] . channel ) )
channel_name = utils . cleanString ( video . platform [ self . NAME ] . channel )
# Peertube allows 20 chars max for channel name
channel_name = channel_name [ : 19 ]
data = ' { " name " : " ' + channel_name + ' " , \
" displayName " : " ' + video.platform[self.name ].channel + ' " , \
" displayName " : " ' + video.platform[self.NAME ].channel + ' " , \
" description " : null , \
" support " : null } '
@ -139,8 +138,8 @@ class Peertube(pi.IPlatformPlugin):
}
try :
response = self . oauth . post ( instance_url + " /api/v1/video-channels/ " ,
data = data . encode ( ' utf-8 ' ) ,
headers = headers )
data = data . encode ( ' utf-8 ' ) ,
headers = headers )
except Exception as e :
logger . error ( " Peertube: " + utils . get_exception_string ( e ) )
@ -151,25 +150,24 @@ class Peertube(pi.IPlatformPlugin):
return jresponse [ ' id ' ]
if response . status_code == 409 :
logger . critical ( ' Peertube: It seems there is a conflict with an existing channel named '
+ channel_name + ' . '
' Please beware Peertube internal name is compiled from 20 firsts characters of channel name. '
' Also note that channel name are not case sensitive (no uppercase nor accent) '
' Please check your channel name and retry. ' )
+ channel_name + ' . '
' Please beware Peertube internal name is compiled from 20 firsts characters of channel name. '
' Also note that channel name are not case sensitive (no uppercase nor accent) '
' Please check your channel name and retry. ' )
exit ( 1 )
else :
logger . critical ( ( ' Peertube: Creating channel failed with an unexpected response: '
' %s ' ) % response )
' %s ' ) % response )
exit ( 1 )
def get_default_playlist ( user_info ) :
def get_default_playlist ( self , user_info ) :
return user_info [ ' videoChannels ' ] [ 0 ] [ ' id ' ]
def get_playlist_by_name ( instance_url , username , video ) :
def get_playlist_by_name ( self , instance_url , username , video ) :
start = 0
user_playlists = json . loads ( self . oauth . get (
instance_url + " /api/v1/accounts/ " + username + " /video-playlists?start= " + str ( start ) + " &count=100 " ) . content )
instance_url + " /api/v1/accounts/ " + username + " /video-playlists?start= " + str (
start ) + " &count=100 " ) . content )
total = user_playlists [ " total " ]
data = user_playlists [ " data " ]
# We need to iterate on pagination as peertube returns max 100 playlists (see #41)
@ -179,11 +177,11 @@ class Peertube(pi.IPlatformPlugin):
return playlist [ ' id ' ]
start = start + 100
user_playlists = json . loads ( self . oauth . get (
instance_url + " /api/v1/accounts/ " + username + " /video-playlists?start= " + str ( start ) + " &count=100 " ) . content )
instance_url + " /api/v1/accounts/ " + username + " /video-playlists?start= " + str (
start ) + " &count=100 " ) . content )
data = user_playlists [ " data " ]
def create_playlist ( instance_url , video , channel ) :
def create_playlist ( self , instance_url , video , channel ) :
template = ( ' Peertube: Playlist %s does not exist, creating it. ' )
logger . info ( template % ( str ( video . playlistName ) ) )
# We use files for form-data Content
@ -197,7 +195,7 @@ class Peertube(pi.IPlatformPlugin):
try :
response = self . oauth . post ( instance_url + " /api/v1/video-playlists/ " ,
files = files )
files = files )
except Exception as e :
logger . error ( " Peertube: " + utils . get_exception_string ( e ) )
@ -208,11 +206,10 @@ class Peertube(pi.IPlatformPlugin):
return jresponse [ ' id ' ]
else :
logger . critical ( ( ' Peertube: Creating the playlist failed with an unexpected response: '
' %s ' ) % response )
' %s ' ) % response )
exit ( 1 )
def set_playlist ( instance_url , video_id , playlist_id ) :
def set_playlist ( self , instance_url , video_id , playlist_id ) :
logger . info ( ' Peertube: add video to playlist. ' )
data = ' { " videoId " : " ' + str ( video_id ) + ' " } '
@ -221,9 +218,9 @@ class Peertube(pi.IPlatformPlugin):
}
try :
response = self . oauth . post ( instance_url + " /api/v1/video-playlists/ " + str ( playlist_id ) + " /videos " ,
data = data ,
headers = headers )
response = self . oauth . post ( instance_url + " /api/v1/video-playlists/ " + str ( playlist_id ) + " /videos " ,
data = data ,
headers = headers )
except Exception as e :
logger . error ( " Peertube: " + utils . get_exception_string ( e ) )
@ -232,19 +229,18 @@ class Peertube(pi.IPlatformPlugin):
logger . info ( ' Peertube: Video is successfully added to the playlist. ' )
else :
logger . critical ( ( ' Peertube: Configuring the playlist failed with an unexpected response: '
' %s ' ) % response )
' %s ' ) % response )
exit ( 1 )
def upload_video ( self , video , options ) :
def get_userinfo ( instanc e_url) :
return json . loads ( self . oauth . get ( instanc e_url + " /api/v1/users/me " ) . content )
def get_userinfo ( bas e_url) :
return json . loads ( self . oauth . get ( bas e_url + " /api/v1/users/me " ) . content )
def get_file ( path ) :
def get_file ( video_ path) :
mimetypes . init ( )
return ( basename ( path ) , open ( abspath ( path ) , ' rb ' ) ,
mimetypes . types_map [ splitext ( path ) [ 1 ] ] )
return ( basename ( video_ path) , open ( abspath ( video_ path) , ' rb ' ) ,
mimetypes . types_map [ splitext ( video_ path) [ 1 ] ] )
path = video . path
instance_url = str ( self . secret . get ( ' peertube ' , ' peertube_url ' ) ) . rstrip ( ' / ' )
@ -257,7 +253,7 @@ class Peertube(pi.IPlatformPlugin):
# https://github.com/requests/toolbelt/issues/205
fields = [
( " name " , video . name ) ,
( " licence " , " 1 " ) , # TODO: get licence from video object
( " licence " , " 1 " ) , # TODO: get licence from video object
( " description " , video . description ) ,
( " category " , str ( self . CATEGORY [ video . category ] ) ) ,
( " language " , str ( self . LANGUAGE [ video . language ] ) ) ,
@ -274,7 +270,8 @@ class Peertube(pi.IPlatformPlugin):
continue
# Tag more than 30 chars crashes Peertube, so skip tags
if len ( strtag ) > = 30 :
logger . warning ( " Peertube: Sorry, Peertube does not support tag with more than 30 characters, please reduce tag: " + strtag )
logger . warning (
" Peertube: Sorry, Peertube does not support tag with more than 30 characters, please reduce tag: " + strtag )
logger . warning ( " Peertube: Meanwhile, this tag will be skipped " )
continue
# Peertube supports only 5 tags at the moment
@ -285,8 +282,8 @@ class Peertube(pi.IPlatformPlugin):
fields . append ( ( " tags[] " , strtag ) )
# If peertubeAt exists, use instead of publishAt
if video . platform [ self . name ] . publishAt :
publishAt = video . platform [ self . name ] . publishAt
if video . platform [ self . NAME ] . publishAt :
publishAt = video . platform [ self . NAME ] . publishAt
elif video . publishAt :
publishAt = video . publishAt
@ -306,12 +303,13 @@ class Peertube(pi.IPlatformPlugin):
fields . append ( ( " thumbnailfile " , get_file ( video . thumbnail ) ) )
fields . append ( ( " previewfile " , get_file ( video . thumbnail ) ) )
if hasattr ( video . platform [ self . name ] , " channel " ) : # TODO: Should always be present
if hasattr ( video . platform [ self . NAME ] , " channel " ) : # TODO: Should always be present
channel_id = self . get_channel_by_name ( user_info , video )
if not channel_id and self . channelCreate :
channel_id = self . create_channel ( instance_url , video )
elif not channel_id :
logger . warning ( " Peertube: Channel ` " + video . platform [ self . name ] . channel + " ` is unknown, using default channel. " ) # TODO: debate if we should have the same message and behavior than playlist : "does not exist, please set --channelCreate"
logger . warning ( " Peertube: Channel ` " + video . platform [
self . NAME ] . channel + " ` is unknown, using default channel. " ) # TODO: debate if we should have the same message and behavior than playlist: "does not exist, please set --channelCreate"
channel_id = self . get_default_channel ( user_info )
else :
channel_id = self . get_default_channel ( user_info )
@ -323,8 +321,9 @@ class Peertube(pi.IPlatformPlugin):
if not playlist_id and video . playlistCreate :
playlist_id = create_playlist ( instance_url , video , channel_id )
elif not playlist_id :
logger . critical ( " Peertube: Playlist ` " + video . playlistName + " ` does not exist, please set --playlistCreate "
" if you want to create it " )
logger . critical (
" Peertube: Playlist ` " + video . playlistName + " ` does not exist, please set --playlistCreate "
" if you want to create it " )
exit ( 1 )
encoder = MultipartEncoder ( fields )
@ -338,8 +337,8 @@ class Peertube(pi.IPlatformPlugin):
' Content-Type ' : multipart_data . content_type
}
response = self . oauth . post ( instance_url + " /api/v1/videos/upload " ,
data = multipart_data ,
headers = headers )
data = multipart_data ,
headers = headers )
if response is not None :
if response . status_code == 200 :
@ -350,17 +349,16 @@ class Peertube(pi.IPlatformPlugin):
logger . info ( " Peertube: Video was successfully uploaded. " )
template_url = " %s /videos/watch/ %s "
video . platform [ self . name ] . url = template_url % ( instance_url , uuid )
logger . info ( " Peertube: Watch it at " + video . platform [ self . name ] . url + " . " )
video . platform [ self . NAME ] . url = template_url % ( instance_url , uuid )
logger . info ( " Peertube: Watch it at " + video . platform [ self . NAME ] . url + " . " )
# Upload is successful we may set playlist
if ' playlist_id ' in locals ( ) :
set_playlist ( instance_url , video_id , playlist_id )
else :
logger . critical ( ( ' Peertube: The upload failed with an unexpected response: '
' %s ' ) % response )
' %s ' ) % response )
exit ( 1 )
# upload_finished = False
# def create_callback(encoder, progress_type):
# upload_size_MB = encoder.len * (1 / (1024 * 1024))
@ -396,16 +394,14 @@ class Peertube(pi.IPlatformPlugin):
#
# return callback
def hearthbeat ( self ) :
def heartbeat ( self ) :
"""
If needed for your platform , use a bit of the api so the platform is aware the keys are still in use .
"""
print ( " Hearth beat for peertube (nothing to do)" )
print ( " heart beat for peertube (nothing to do)" )
pass
def upload ( self , video , options ) :
# def run(options):
def upload ( self , video , options ) :
logger . info ( ' Peertube: Uploading video... ' )
self . upload_video ( video , options )