Scripting way to upload videos to peertube and youtube
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

263 lines
8.4 KiB

  1. #!/usr/bin/python
  2. # coding: utf-8
  3. from ConfigParser import RawConfigParser, NoOptionError, NoSectionError
  4. from os.path import dirname, splitext, basename, isfile
  5. from os import devnull
  6. from subprocess import check_call, CalledProcessError, STDOUT
  7. import unicodedata
  8. import logging
  9. ### CATEGORIES ###
  10. YOUTUBE_CATEGORY = {
  11. "music": 10,
  12. "films": 1,
  13. "vehicles": 2,
  14. "sport": 17,
  15. "travels": 19,
  16. "gaming": 20,
  17. "people": 22,
  18. "comedy": 23,
  19. "entertainment": 24,
  20. "news": 25,
  21. "how to": 26,
  22. "education": 27,
  23. "activism": 29,
  24. "science & technology": 28,
  25. "science": 28,
  26. "technology": 28,
  27. "animals": 15
  28. }
  29. PEERTUBE_CATEGORY = {
  30. "music": 1,
  31. "films": 2,
  32. "vehicles": 3,
  33. "sport": 5,
  34. "travels": 6,
  35. "gaming": 7,
  36. "people": 8,
  37. "comedy": 9,
  38. "entertainment": 10,
  39. "news": 11,
  40. "how to": 12,
  41. "education": 13,
  42. "activism": 14,
  43. "science & technology": 15,
  44. "science": 15,
  45. "technology": 15,
  46. "animals": 16
  47. }
  48. ### LANGUAGES ###
  49. YOUTUBE_LANGUAGE = {
  50. "arabic": 'ar',
  51. "english": 'en',
  52. "french": 'fr',
  53. "german": 'de',
  54. "hindi": 'hi',
  55. "italian": 'it',
  56. "japanese": 'ja',
  57. "korean": 'ko',
  58. "mandarin": 'zh-CN',
  59. "portuguese": 'pt-PT',
  60. "punjabi": 'pa',
  61. "russian": 'ru',
  62. "spanish": 'es'
  63. }
  64. PEERTUBE_LANGUAGE = {
  65. "arabic": "ar",
  66. "english": "en",
  67. "french": "fr",
  68. "german": "de",
  69. "hindi": "hi",
  70. "italian": "it",
  71. "japanese": "ja",
  72. "korean": "ko",
  73. "mandarin": "zh",
  74. "portuguese": "pt",
  75. "punjabi": "pa",
  76. "russian": "ru",
  77. "spanish": "es"
  78. }
  79. ######################
  80. def getCategory(category, platform):
  81. if platform == "youtube":
  82. return YOUTUBE_CATEGORY[category.lower()]
  83. else:
  84. return PEERTUBE_CATEGORY[category.lower()]
  85. def getLanguage(language, platform):
  86. if platform == "youtube":
  87. return YOUTUBE_LANGUAGE[language.lower()]
  88. else:
  89. return PEERTUBE_LANGUAGE[language.lower()]
  90. def remove_empty_kwargs(**kwargs):
  91. good_kwargs = {}
  92. if kwargs is not None:
  93. for key, value in kwargs.iteritems():
  94. if value:
  95. good_kwargs[key] = value
  96. return good_kwargs
  97. def searchThumbnail(options):
  98. video_directory = dirname(options.get('--file')) + "/"
  99. # First, check for thumbnail based on videoname
  100. if options.get('--name'):
  101. if isfile(video_directory + options.get('--name') + ".jpg"):
  102. options['--thumbnail'] = video_directory + options.get('--name') + ".jpg"
  103. elif isfile(video_directory + options.get('--name') + ".jpeg"):
  104. options['--thumbnail'] = video_directory + options.get('--name') + ".jpeg"
  105. # Then, if we still not have thumbnail, check for thumbnail based on videofile name
  106. if not options.get('--thumbnail'):
  107. video_file = splitext(basename(options.get('--file')))[0]
  108. if isfile(video_directory + video_file + ".jpg"):
  109. options['--thumbnail'] = video_directory + video_file + ".jpg"
  110. elif isfile(video_directory + video_file + ".jpeg"):
  111. options['--thumbnail'] = video_directory + video_file + ".jpeg"
  112. return options
  113. # return the nfo as a RawConfigParser object
  114. def loadNFO(options):
  115. video_directory = dirname(options.get('--file')) + "/"
  116. if options.get('--nfo'):
  117. try:
  118. logging.info("Using " + options.get('--nfo') + " as NFO, loading...")
  119. if isfile(options.get('--nfo')):
  120. nfo = RawConfigParser()
  121. nfo.read(options.get('--nfo'))
  122. return nfo
  123. else:
  124. logging.error("Given NFO file does not exist, please check your path.")
  125. exit(1)
  126. except Exception as e:
  127. logging.error("Problem with NFO file: " + str(e))
  128. exit(1)
  129. else:
  130. if options.get('--name'):
  131. nfo_file = video_directory + options.get('--name') + ".txt"
  132. if isfile(nfo_file):
  133. try:
  134. logging.info("Using " + nfo_file + " as NFO, loading...")
  135. nfo = RawConfigParser()
  136. nfo.read(nfo_file)
  137. return nfo
  138. except Exception as e:
  139. logging.error("Problem with NFO file: " + str(e))
  140. exit(1)
  141. # if --nfo and --name does not exist, use --file as default
  142. video_file = splitext(basename(options.get('--file')))[0]
  143. nfo_file = video_directory + video_file + ".txt"
  144. if isfile(nfo_file):
  145. try:
  146. logging.info("Using " + nfo_file + " as NFO, loading...")
  147. nfo = RawConfigParser()
  148. nfo.read(nfo_file)
  149. return nfo
  150. except Exception as e:
  151. logging.error("Problem with nfo file: " + str(e))
  152. exit(1)
  153. logging.info("No suitable NFO found, skipping.")
  154. return False
  155. def parseNFO(options):
  156. nfo = loadNFO(options)
  157. if nfo:
  158. # We need to check all options and replace it with the nfo value if not defined (None or False)
  159. for key, value in options.iteritems():
  160. key = key.replace("-", "")
  161. try:
  162. # get string options
  163. if value is None and nfo.get('video', key):
  164. options['--' + key] = nfo.get('video', key)
  165. # get boolean options
  166. elif value is False and nfo.getboolean('video', key):
  167. options['--' + key] = nfo.getboolean('video', key)
  168. except NoOptionError:
  169. continue
  170. except NoSectionError:
  171. logging.error("Given NFO file miss section [video], please check syntax of your NFO.")
  172. exit(1)
  173. return options
  174. def upcaseFirstLetter(s):
  175. return s[0].upper() + s[1:]
  176. def publishAt(publishAt, oauth, url, idvideo, secret):
  177. try:
  178. FNULL = open(devnull, 'w')
  179. check_call(["at", "-V"], stdout=FNULL, stderr=STDOUT)
  180. except CalledProcessError:
  181. logging.error("You need to install the atd daemon to use the publishAt option.")
  182. exit(1)
  183. try:
  184. FNULL = open(devnull, 'w')
  185. check_call(["curl", "-V"], stdout=FNULL, stderr=STDOUT)
  186. except CalledProcessError:
  187. logging.error("You need to install the curl command line to use the publishAt option.")
  188. exit(1)
  189. try:
  190. FNULL = open(devnull, 'w')
  191. check_call(["jq", "-V"], stdout=FNULL, stderr=STDOUT)
  192. except CalledProcessError:
  193. logging.error("You need to install the jq command line to use the publishAt option.")
  194. exit(1)
  195. time = publishAt.split("T")
  196. # Remove leading seconds that atd does not manage
  197. if time[1].count(":") == 2:
  198. time[1] = time[1][:-3]
  199. atTime = time[1] + " " + time[0]
  200. refresh_token=str(oauth.__dict__['_client'].__dict__['refresh_token'])
  201. atFile = "/tmp/peertube_" + idvideo + "_" + publishAt + ".at"
  202. try:
  203. openfile = open(atFile,"w")
  204. openfile.write('token=$(curl -X POST -d "client_id=' + str(secret.get('peertube', 'client_id')) +
  205. '&client_secret=' + str(secret.get('peertube', 'client_secret')) +
  206. '&grant_type=refresh_token&refresh_token=' + str(refresh_token) +
  207. '" "' + url + '/api/v1/users/token" | jq -r .access_token)')
  208. openfile.write("\n")
  209. openfile.write('curl "' + url + '/api/v1/videos/' + idvideo +
  210. '" -X PUT -H "Authorization: Bearer ${token}"' +
  211. ' -H "Content-Type: multipart/form-data" -F "privacy=1"')
  212. openfile.write("\n ") # atd needs an empty line at the end of the file to load...
  213. openfile.close()
  214. except Exception as e:
  215. if hasattr(e, 'message'):
  216. logging.error("Error: " + str(e.message))
  217. else:
  218. logging.error("Error: " + str(e))
  219. try:
  220. FNULL = open(devnull, 'w')
  221. check_call(["at", "-M", "-f", atFile, atTime], stdout=FNULL, stderr=STDOUT)
  222. except Exception as e:
  223. if hasattr(e, 'message'):
  224. logging.error("Error: " + str(e.message))
  225. else:
  226. logging.error("Error: " + str(e))
  227. def mastodonTag(tag):
  228. tags = tag.split(' ')
  229. mtag = ''
  230. for s in tags:
  231. if s == '':
  232. continue
  233. strtag = unicodedata.normalize('NFKD', unicode (s, 'utf-8')).encode('ASCII', 'ignore')
  234. strtag = ''.join(e for e in strtag if e.isalnum())
  235. strtag = upcaseFirstLetter(strtag)
  236. mtag = mtag + strtag
  237. return mtag