publish.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712
  1. #####################################################################
  2. # #
  3. # THIS IS A SOURCE CODE FILE FROM A PROGRAM TO INTERACT WITH THE #
  4. # LBRY PROTOCOL ( lbry.com ). IT WILL USE THE LBRY SDK ( lbrynet ) #
  5. # FROM THEIR REPOSITORY ( https://github.com/lbryio/lbry-sdk ) #
  6. # WHICH I GONNA PRESENT TO YOU AS A BINARY. SINCE I DID NOT DEVELOP #
  7. # IT AND I'M LAZY TO INTEGRATE IN A MORE SMART WAY. THE SOURCE CODE #
  8. # OF THE SDK IS AVAILABLE IN THE REPOSITORY MENTIONED ABOVE. #
  9. # #
  10. # ALL THE CODE IN THIS REPOSITORY INCLUDING THIS FILE IS #
  11. # (C) J.Y.Amihud and Other Contributors 2021. EXCEPT THE LBRY SDK. #
  12. # YOU CAN USE THIS FILE AND ANY OTHER FILE IN THIS REPOSITORY UNDER #
  13. # THE TERMS OF GNU GENERAL PUBLIC LICENSE VERSION 3 OR ANY LATER #
  14. # VERSION. TO FIND THE FULL TEXT OF THE LICENSE GO TO THE GNU.ORG #
  15. # WEBSITE AT ( https://www.gnu.org/licenses/gpl-3.0.html ). #
  16. # #
  17. # THE LBRY SDK IS UNFORTUNATELY UNDER THE MIT LICENSE. IF YOU ARE #
  18. # NOT INTENDING TO USE MY CODE AND JUST THE SDK. YOU CAN FIND IT ON #
  19. # THEIR OFFICIAL REPOSITORY ABOVE. THEIR LICENSE CHOICE DOES NOT #
  20. # SPREAD ONTO THIS PROJECT. DON'T GET A FALSE ASSUMPTION THAT SINCE #
  21. # THEY USE A PUSH-OVER LICENSE, I GONNA DO THE SAME. I'M NOT. #
  22. # #
  23. # THE LICENSE CHOSEN FOR THIS PROJECT WILL PROTECT THE 4 ESSENTIAL #
  24. # FREEDOMS OF THE USER FURTHER, BY NOT ALLOWING ANY WHO TO CHANGE #
  25. # THE LICENSE AT WILL. SO NO PROPRIETARY SOFTWARE DEVELOPER COULD #
  26. # TAKE THIS CODE AND MAKE THEIR USER-SUBJUGATING SOFTWARE FROM IT. #
  27. # #
  28. #####################################################################
  29. import os
  30. import time
  31. import json
  32. import random
  33. import threading
  34. import mimetypes
  35. import urllib.request
  36. from gi.repository import Gtk
  37. from gi.repository import Gdk
  38. from gi.repository import GLib
  39. from gi.repository import Pango
  40. from gi.repository import GdkPixbuf
  41. from flbry import markdown
  42. from flbry import ui
  43. from flbry import fetch
  44. from flbry import settings
  45. ##################################################################
  46. # This file touches publication to LBRY network. #
  47. ##################################################################
  48. def lbryname(name="", force=True):
  49. # This creates a random string of characters.
  50. good = "qwertyuiopasdfghjklzxcvbnm-_QWERTYUIOPASDFGHJKLZXCVBNM1234567890"
  51. if not name and force:
  52. for i in range(70):
  53. name = name + random.choice(good)
  54. return name
  55. # This removes all non-good characters from the name
  56. new_name = unicodedata.normalize('NFD', name).strip().casefold()
  57. # Patterns to keep out.
  58. # Hint from <lbry-sdk>/lbry/schema/url.py
  59. # - replaced "[^=" with "[="
  60. # - replaced 'FF]+"' with 'FF" \'
  61. # - added 'r"\s]+"' line
  62. bad_pattern = re.compile(
  63. r"[=&#:$@%?;\"/\\<>%{}|^~`\[\]" \
  64. r"\u0000-\u0020\uD800-\uDFFF\uFFFE-\uFFFF" \
  65. r"\s]+"
  66. )
  67. new_name = bad_pattern.sub("-", new_name)
  68. return new_name
  69. DEFAULT_DATA = {"name":"",
  70. "bid":0.001,
  71. "file_path":"",
  72. "title":"",
  73. "license":"",
  74. "license_url":"",
  75. "thumbnail_url":"",
  76. "channel_id":"",
  77. "channel_name":"",
  78. "description":"",
  79. "fee_amount":0,
  80. "tags":[]
  81. }
  82. LICENSES = [
  83. # NAME , URL , COMMENT
  84. ["GNU General Public License Version 3 (or later)",
  85. "https://www.gnu.org/licenses/gpl-3.0.html",
  86. "Strong Copyleft. Recommended for Software."],
  87. ["GNU General Public License Version 3 (only)",
  88. "https://www.gnu.org/licenses/gpl-3.0.html",
  89. "Strong Copyleft."],
  90. ["GNU Free Documentation License",
  91. "https://www.gnu.org/licenses/fdl-1.3.html",
  92. "Strong Copyleft. Recommended for books."],
  93. ["Creative Commons Attribution-ShareAlike 4.0 International",
  94. "https://creativecommons.org/licenses/by-sa/4.0/",
  95. "Copylefted, Recommended for Art."],
  96. ["Creative Commons Attribution 4.0 International",
  97. "https://creativecommons.org/licenses/by/4.0/",
  98. "Non Copylefted, Free License."],
  99. ["Creative Commons Zero 1.0 International",
  100. "https://creativecommons.org/publicdomain/zero/1.0/",
  101. "Public Domain"],
  102. ["Creative Commons Attribution-NoDerivatives 4.0 International",
  103. "https://creativecommons.org/licenses/by-nd/4.0/",
  104. "Does not allow changes. Recommended for opinion pieces."]
  105. ]
  106. def window(win, data=DEFAULT_DATA):
  107. # First, the data recieved could be not full.
  108. for i in DEFAULT_DATA:
  109. if i not in data:
  110. data[i] = DEFAULT_DATA[i]
  111. # Upload window
  112. dialogWindow = Gtk.Dialog("Publish",
  113. buttons=(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
  114. Gtk.STOCK_OK, Gtk.ResponseType.OK),
  115. )
  116. box = dialogWindow.get_content_area()
  117. #######################################################################
  118. # #
  119. # PRESETS #
  120. # #
  121. #######################################################################
  122. #making sure the folder exists
  123. try:
  124. os.mkdir(settings.get_settings_folder()+"presets")
  125. except:
  126. pass
  127. presets_list = []
  128. for i in os.listdir(settings.get_settings_folder()+"presets"):
  129. if i.endswith(".json"):
  130. presets_list.append(i.replace(".json", ""))
  131. def on_set_preset(w):
  132. # This might be a little long one, but to hell with it.
  133. ######################################################
  134. pn = presets_list[presets.get_active()]
  135. # Opening the preset's json.
  136. with open(settings.get_settings_folder()+"presets/"+pn+'.json') as f:
  137. pdata = json.load(f)
  138. # The data should be very similar. It was saved ( hopefully ) from
  139. # the same data as what we use to publish.
  140. exclude = ["name", "file_path", "tags"]
  141. for d in pdata:
  142. if d in exclude:
  143. continue
  144. data[d] = pdata[d]
  145. # Now let's update the UI itself
  146. # THIS SHOULD NOT RUN BEFORE THE UI IS FULLY LOADED !!!
  147. # BID
  148. bid_entry.set_value(data["bid"])
  149. # TITLE
  150. title.set_text(data["title"])
  151. # LICENSE
  152. for n, ch in enumerate(LICENSES):
  153. if data["license"] == ch[0]:
  154. licenses.set_active(n)
  155. # THUMBNAIL_URL
  156. thumbentry.set_text(data["thumbnail_url"])
  157. refresh_thumb()
  158. # CHANNEL_NAME
  159. try:
  160. for n, ch in enumerate(win.my_channels["items"]):
  161. if data["channel_name"] == ch["name"] or \
  162. data["channel_id"] == ch["claim_id"]:
  163. select = n+1
  164. channels_select.set_active(select)
  165. except:
  166. pass
  167. # DESCRIPTION
  168. detext.get_buffer().set_text(data["description"])
  169. # FEE_AMOUNT
  170. price_entry.set_value(data["fee_amount"])
  171. # TAGS
  172. # Tags ( because of the tags editor ) is a special case
  173. # and we need to do this, so not to create a separate id()
  174. # for the tags list. ( Tags editor is a separate function
  175. # that accesses the same list and edit's it.
  176. # First we need to remove all of the data.
  177. tags = data["tags"].copy()
  178. for t in tags:
  179. data["tags"].remove(t)
  180. # And now we want to add all of the tags one by one.
  181. for t in pdata["tags"]:
  182. data["tags"].append(t)
  183. # And finally we want to give commands obtained from the tags
  184. # object to update it visually.
  185. for i in tagsbox.get_children():
  186. i.destroy()
  187. for tag in data["tags"]:
  188. add_tag(tag)
  189. presbox = Gtk.HBox()
  190. box.pack_start(presbox, False, False, 0)
  191. presbox.pack_start(Gtk.Label(" Presets: "), False, False, 0)
  192. presets = Gtk.ComboBoxText()
  193. presets.connect("changed", on_set_preset)
  194. for n, ch in enumerate(presets_list):
  195. presets.append_text(ch)
  196. presbox.pack_start(presets, True, True, 0)
  197. #####################################################################
  198. presetsave = Gtk.Popover()
  199. addb = Gtk.MenuButton(popover=presetsave)
  200. addb.set_relief(Gtk.ReliefStyle.NONE)
  201. addb.add(ui.icon(win, "list-add"))
  202. presbox.pack_start(addb, False, False, 0)
  203. psavebox = Gtk.VBox()
  204. presetsave.add(psavebox)
  205. pname = Gtk.Entry()
  206. pname.set_text("name")
  207. psavebox.pack_start(pname, False, False, False)
  208. def save_preset(w):
  209. update_data() # Making sure all data is in 'data'
  210. pn = pname.get_text()
  211. # Write the json file
  212. with open(settings.get_settings_folder()+"presets/"+pn+'.json', 'w') as f:
  213. json.dump(data, f, indent=4, sort_keys=True)
  214. # Add the preset into presets
  215. if pn not in presets_list:
  216. presets.append_text(pn)
  217. presets_list.append(pn)
  218. # Select the current preset
  219. for n, ch in enumerate(presets_list):
  220. if ch == pn:
  221. presets.set_active(n)
  222. presets.grab_focus() # Closing the menu
  223. pname.set_text("name")
  224. psave = Gtk.Button("Save")
  225. psave.connect("clicked", save_preset)
  226. pname.connect("activate", save_preset)
  227. psave.set_relief(Gtk.ReliefStyle.NONE)
  228. psavebox.pack_start(psave, False, False, False)
  229. psavebox.show_all()
  230. #######################################################################
  231. # #
  232. # LBRY URL PART #
  233. # #
  234. #######################################################################
  235. box.pack_start(Gtk.Label(" Channel | LBRY url "), False, True, 5)
  236. urlbox = Gtk.HBox()
  237. box.pack_start(urlbox, False, False, 0)
  238. urlbox.pack_start(Gtk.Label(" lbry:// "), False, False, 0)
  239. # This will work separately from the main channel selector, though it
  240. # will be influenced by the main channel selector a bit.
  241. def on_channel_changed(w):
  242. ch = channels_select.get_active() - 1
  243. if ch == -1:
  244. data["channel_name"] = ""
  245. data["channel_id"] = ""
  246. else:
  247. data["channel_name"] = win.my_channels["items"][ch]["name"]
  248. data["channel_id"] = win.my_channels["items"][ch]["claim_id"]
  249. channels_select = Gtk.ComboBoxText()
  250. channels_select.connect("changed", on_channel_changed)
  251. # Let's get a list of channels to show
  252. channels = ["[anonymous]"]
  253. select = 0
  254. try: # could be no channel. So publication only anonymous
  255. for n, ch in enumerate(win.my_channels["items"]):
  256. channels.append(ch["name"]+":"+ch["claim_id"][0])
  257. if data["channel_name"] == ch["name"] or \
  258. data["channel_id"] == ch["claim_id"] or \
  259. win.channel["claim_id"] == ch["claim_id"]:
  260. select = n+1
  261. except Exception as e:
  262. print("DAMN!", e)
  263. for ch in channels:
  264. channels_select.append_text(ch)
  265. channels_select.set_active(select)
  266. urlbox.pack_start(channels_select, False, False, 0)
  267. urlbox.pack_start(Gtk.Label(" / "), False, False, 0)
  268. def on_url(w):
  269. url_field.set_text(lbryname(url_field.get_text(), force=False))
  270. url_field = Gtk.Entry()
  271. url_field.connect("changed", on_url)
  272. url_field.set_text(data["name"])
  273. urlbox.pack_start(url_field, True, True, 0)
  274. #######################################################################
  275. # #
  276. # THUMBNAIL #
  277. # #
  278. #######################################################################
  279. box.pack_start(Gtk.HSeparator(), False, False, 5)
  280. thumb_file_box = Gtk.HBox()
  281. box.pack_start(thumb_file_box, False, False, 0)
  282. ######################################################################
  283. thumbvbox = Gtk.VBox()
  284. thumb_file_box.pack_start(thumbvbox, False, False, 0)
  285. def refresh_thumb():
  286. if thumbentry.get_text():
  287. t = ui.load(win, ui.net_image_calculation, ui.net_image_render, thumbentry.get_text(), 300, 300, "FORCELOAD", True)
  288. for ch in thumbbox.get_children():
  289. ch.destroy()
  290. thumbbox.pack_start(t, True, True, 0)
  291. thumbbox.show_all()
  292. def on_thumb(w):
  293. ans = ui.select_file(data["thumbnail_url"], filter=["*jpg", "*png", "*webp", "*gif"])
  294. if ans:
  295. thumbentry.set_text(ans)
  296. refresh_thumb()
  297. thumb = Gtk.Button()
  298. thumb.connect("clicked", on_thumb)
  299. thumb.set_size_request(300,300)
  300. thumb.set_relief(Gtk.ReliefStyle.NONE)
  301. thumbbox = Gtk.HBox()
  302. thumbbox.pack_start(ui.icon(win, "image-x-generic"), False, True, 0)
  303. thumbbox.pack_start(Gtk.Label(" Thumbnail "), False, True, 0)
  304. thumb.add(thumbbox)
  305. thumbvbox.pack_start(thumb, False, False, 0)
  306. def on_toggle(w):
  307. thumbentry.set_visible(w.get_active())
  308. thumb.set_visible(not w.get_active())
  309. refresh_thumb()
  310. manually = Gtk.ToggleButton("Set thumbnail manually")
  311. manually.connect("clicked", on_toggle)
  312. manually.set_relief(Gtk.ReliefStyle.NONE)
  313. thumbvbox.pack_end(manually, False, False, 0)
  314. thumbentry = Gtk.Entry()
  315. thumbentry.set_text(data["thumbnail_url"])
  316. refresh_thumb()
  317. thumbvbox.pack_end(thumbentry, False, False, 0)
  318. thumb_file_box.pack_start(Gtk.VSeparator(), False, False, 5)
  319. #######################################################################
  320. # #
  321. # TITLE #
  322. # #
  323. #######################################################################
  324. filebox = Gtk.VBox()
  325. thumb_file_box.pack_start(filebox, True, True, 0)
  326. #######################################################################
  327. titlebox = Gtk.HBox()
  328. filebox.pack_start(titlebox, False, False, 5)
  329. titlebox.pack_start(Gtk.Label(" Title: "), False, False, False)
  330. def on_title(w):
  331. data["title"] = title.get_text()
  332. title = Gtk.Entry()
  333. title.set_text(data["title"])
  334. title.connect("changed", on_title)
  335. titlebox.pack_start(title, True, True, False)
  336. #######################################################################
  337. # #
  338. # FILE #
  339. # #
  340. #######################################################################
  341. def choose_file(filename=""):
  342. for ch in filebbox.get_children():
  343. ch.destroy()
  344. if filename:
  345. icon_is = mimetypes.guess_type(filename)[0].replace("/", "-")
  346. name_of_file = filename[filename.rfind("/")+1:]
  347. def clear_name(name):
  348. name = name[:name.rfind(".")]
  349. name = name.replace("_", " ")
  350. try:
  351. name = name[0].upper()+name[1:]
  352. except:
  353. pass
  354. return name
  355. ofilename = data["file_path"]
  356. if not title.get_text() or clear_name(ofilename[ofilename.rfind("/")+1:]) == title.get_text():
  357. title.set_text(clear_name(name_of_file))
  358. data["title"] = clear_name(name_of_file)
  359. if not url_field.get_text() or lbryname(clear_name(ofilename[ofilename.rfind("/")+1:])) == url_field.get_text():
  360. url_field.set_text(lbryname(title.get_text(), force=True))
  361. else:
  362. icon_is = "document-open"
  363. name_of_file = "Choose File"
  364. filebbox.pack_start(ui.icon(win, icon_is), False, True, 0)
  365. filebbox.pack_start(Gtk.Label(" "+name_of_file+" "), False, True, 0)
  366. filebbox.show_all()
  367. def on_filebutton(w):
  368. name = ui.select_file(data["file_path"])
  369. choose_file(name)
  370. data["file_path"] = name
  371. filebutton = Gtk.Button()
  372. filebutton.connect("clicked", on_filebutton)
  373. filebbox = Gtk.HBox()
  374. filebutton.add(filebbox)
  375. choose_file(data["file_path"])
  376. filebutton.set_relief(Gtk.ReliefStyle.NONE)
  377. filebox.pack_start(filebutton, False, False, 0)
  378. box.pack_start(Gtk.HSeparator(), False, False, 5)
  379. #######################################################################
  380. # #
  381. # BID #
  382. # #
  383. #######################################################################
  384. bid_adjust = Gtk.Adjustment(data["bid"],
  385. lower=0.0001,
  386. upper=1000000000,
  387. step_increment=0.1)
  388. bid_entry = Gtk.SpinButton(adjustment=bid_adjust,
  389. digits=4)
  390. bid_box = Gtk.HBox()
  391. filebox.pack_start(bid_box, False, False, 5)
  392. bid_box.pack_start(Gtk.Label(" Bid: "), False, False, 0)
  393. bid_box.pack_end(bid_entry, False, False, 0)
  394. #######################################################################
  395. # #
  396. # PRICE #
  397. # #
  398. #######################################################################
  399. price_adjust = Gtk.Adjustment(data["fee_amount"],
  400. lower=0,
  401. upper=1000000000,
  402. step_increment=0.1)
  403. price_entry = Gtk.SpinButton(adjustment=price_adjust,
  404. digits=4)
  405. price_box = Gtk.HBox()
  406. filebox.pack_start(price_box, False, False, 5)
  407. price_box.pack_start(Gtk.Label(" Price: "), False, False, 0)
  408. price_box.pack_end(price_entry, False, False, 0)
  409. #######################################################################
  410. # #
  411. # DESCRIPTION #
  412. # #
  413. #######################################################################
  414. filebox.pack_start(Gtk.Label(" Description: "), False, True, 5)
  415. descrl = Gtk.ScrolledWindow()
  416. detext = Gtk.TextView()
  417. detext.set_wrap_mode(Gtk.WrapMode.WORD)
  418. detext.get_buffer().set_text(data["description"])
  419. descrl.add(detext)
  420. filebox.pack_start(descrl, True, True, 0)
  421. #######################################################################
  422. # #
  423. # TAGS #
  424. # #
  425. #######################################################################
  426. box.pack_start(Gtk.Label(" Tags "), False, True, 5)
  427. tags_editor, tagsbox, add_tag = ui.tags_editor(win, data["tags"], return_edit_functions=True)
  428. box.pack_start(tags_editor, False, False, 0)
  429. #######################################################################
  430. # #
  431. # LICENSE #
  432. # #
  433. #######################################################################
  434. box.pack_start(Gtk.Label(" License "), False, True, 5)
  435. def on_license(w):
  436. l = licenses.get_active()
  437. data["license"] = LICENSES[l][0]
  438. data["license_url"] = LICENSES[l][1]
  439. licenses = Gtk.ComboBoxText()
  440. licenses.connect("changed", on_license)
  441. for n, ch in enumerate(LICENSES):
  442. licenses.append_text(ch[0])
  443. if data["license"] == ch[0]:
  444. licenses.set_active(n)
  445. box.pack_start(licenses, False, True, 0)
  446. #######################################################################
  447. # #
  448. # RUNING THE SETTINGS DIALOG #
  449. # #
  450. #######################################################################
  451. box.show_all()
  452. def update_data():
  453. # Not all widgets update data automatically. We need this function
  454. # whenever we want the up-to-date data.
  455. data["name"] = url_field.get_text()
  456. data["thumbnail_url"] = thumbentry.get_text()
  457. data["bid"] = bid_entry.get_value()
  458. data["fee_amount"] = price_entry.get_value()
  459. tb = detext.get_buffer()
  460. data["description"] = tb.get_text(tb.get_start_iter(), tb.get_end_iter(), True)
  461. # Hide all hidden
  462. thumbentry.set_visible(False)
  463. response = dialogWindow.run()
  464. if response == Gtk.ResponseType.OK:
  465. update_data()
  466. print("\n\n############## PUBLISH INPUT ###########\n\n{")
  467. for i in data:
  468. print(' "'+i+'" :', data[i])
  469. print("}\n")
  470. out = upload(data)
  471. print("\n\n############## PUBLISH OUTPUT ###########\n\n")
  472. print(out)
  473. try:
  474. ui.notify(win, "Published successfully to: ", out['outputs'][0]['permanent_url']+"\n\nConfirming...")
  475. except:
  476. try:
  477. ui.notify(win, "Error while publishing!!!", out["message"])
  478. except:
  479. ui.notify(win, "Error while publishing!!!", str(out))
  480. # It has to be DESTORYED
  481. dialogWindow.destroy()
  482. def upload(data):
  483. ###############################################################
  484. # PUBLISH ! PUBLISH ! PUBLISH ! PUBLISH ! PUBLISH ! PUBLISH ! #
  485. ###############################################################
  486. # The required datapoints
  487. fetch_data = {"name":data["name"],
  488. "bid":str(float(data["bid"])),
  489. "file_path":data["file_path"]}
  490. # Uploading the thumbnail maybe
  491. data["thumbnail_url"] = speech_upload(data["thumbnail_url"])
  492. for i in ["title", "license", "license_url", "thumbnail_url", "description", "channel_name", "fee_amount", "tags"]:
  493. if i in data:
  494. if data[i]:
  495. if type(data[i]) not in [float, int]:
  496. fetch_data[i] = data[i]
  497. else:
  498. fetch_data[i] = str(float(data[i]))
  499. if data["fee_amount"]:
  500. fetch_data["fee_currency"] = "LBC"
  501. out = fetch.lbrynet("publish", fetch_data)
  502. return out
  503. def speech_upload(file, name="", fee=0, speech=True):
  504. file = os.path.expanduser(file)
  505. if os.path.isfile(file):
  506. print("Uploading '"+file+"' to LBRY")
  507. if not name:
  508. rndname = ""
  509. else:
  510. rndname = name + "_"
  511. length = 70 - len(rndname)
  512. good = "qwertyuiopasdfghjklzxcvbnm-_QWERTYUIOPASDFGHJKLZXCVBNM1234567890"
  513. for i in range(length):
  514. rndname = rndname + random.choice(good)
  515. try:
  516. out = upload({"name":rndname,
  517. "bid":0.001,
  518. "file_path":file,
  519. "title":"",
  520. "license":"",
  521. "license_url":"",
  522. "thumbnail_url":"",
  523. "channel_id":"",
  524. "channel_name":"",
  525. "description":"",
  526. "fee_amount":fee,
  527. "tags":[]
  528. })
  529. if speech:
  530. return out['outputs'][0]['permanent_url'].replace("lbry://","https://spee.ch/")
  531. else:
  532. return out['outputs'][0]['permanent_url']
  533. except:
  534. return ""
  535. else:
  536. return file
  537. # Gets Librarian URL from LBRY URL
  538. def librarian_url(lbry_url, librarian_location):
  539. # Get rid of ending slash if it has one
  540. librarian_location = librarian_location.rstrip("/")
  541. return lbry_url.replace("lbry:/", librarian_location, 1).replace("#", ":", 2)