url.py 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544
  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 urllib.request
  32. import threading
  33. import json
  34. from subprocess import *
  35. from gi.repository import Gtk
  36. from gi.repository import Gdk
  37. from gi.repository import GLib
  38. from gi.repository import Pango
  39. from gi.repository import GdkPixbuf
  40. from PIL import Image, ImageSequence
  41. from flbry import markdown
  42. from flbry import data_view
  43. from flbry import ui
  44. from flbry import fetch
  45. from flbry import claim_search
  46. from flbry import comments
  47. def resolve(w, win, url):
  48. # This function will draw a widget of rsolved url
  49. #####################################################
  50. # RESOLVING PART ( NO GTK ) #
  51. #####################################################
  52. out = fetch.lbrynet("resolve",
  53. {"urls":[url]}
  54. )
  55. out = out[url]
  56. # Saving the resolved thing into the win for later use
  57. win.resolved = out
  58. # out = check_output(["flbry/lbrynet",
  59. # "resolve", url])
  60. # raw_data = out.decode("utf-8")
  61. # # Now we want to parse the json
  62. # try:
  63. # out = json.loads(out)
  64. # out = out[url]
  65. # except:
  66. # print("Resolve Failed")
  67. # return False
  68. try:
  69. is_channel = "value_type" in out and out["value_type"] == "channel"
  70. percentage = downloaded(out["claim_id"])
  71. except:
  72. # If this fails we activate search
  73. return ["search", False, False, w, win, url]
  74. # make sure the url path starts with lbry://
  75. if not url.startswith("lbry://"):
  76. url = "lbry://"+url
  77. win.url.set_text(url)
  78. return [out, is_channel, percentage, w, win, url]
  79. def render_resolve(data):
  80. out, is_channel, percentage, w, win, url = data
  81. # It could be failed
  82. if out == "search":
  83. return ui.load(win, claim_search.find, claim_search.render, win, win.url.get_text())
  84. #####################################################
  85. # DRAWING PART ( GTK ) #
  86. #####################################################
  87. try:
  88. price = out["value"]["fee"]["amount"]
  89. except:
  90. price = 0
  91. try:
  92. currency = out["value"]["fee"]["currency"]
  93. except:
  94. currency = "LBC"
  95. box = Gtk.HBox()
  96. outbox = Gtk.VBox()
  97. #### THUMBNAIL ###
  98. thumb = ""
  99. try:
  100. thumb = out["value"]["thumbnail"]["url"]
  101. thumb_url = ui.image_save_name(thumb)
  102. def thumb_open(w):
  103. os.system("xdg-open "+thumb_url)
  104. thumb_button = Gtk.Button()
  105. thumb_button.set_tooltip_text(thumb)
  106. if is_channel:
  107. thumb_image = ui.load(win, ui.net_image_calculation, ui.net_image_render, thumb, 150, "", True)
  108. else:
  109. thumb_image = ui.load(win, ui.net_image_calculation, ui.net_image_render, thumb, 400, "", True)
  110. thumb_button.add(thumb_image)
  111. thumb_button.set_relief(Gtk.ReliefStyle.NONE)
  112. thumb_button.connect("clicked", thumb_open)
  113. box.pack_start(thumb_button, False, False, False)
  114. except:
  115. pass
  116. outbox.pack_start(box, False, False, False)
  117. from_right_to_thumbnail = Gtk.VBox()
  118. box.pack_end(from_right_to_thumbnail, True, True, False)
  119. # If channel load banner
  120. if is_channel:
  121. if "cover" in out["value"]:
  122. channel_banner = ui.load(win, ui.net_image_calculation, ui.net_image_render, out["value"]["cover"]["url"], False, "", True)
  123. channel_scroll = Gtk.ScrolledWindow()
  124. channel_scroll.set_size_request(400,150)
  125. channel_scroll.add_with_viewport(channel_banner)
  126. from_right_to_thumbnail.pack_start(channel_scroll, False, False, False)
  127. else:
  128. try:
  129. channel_scroll.destroy()
  130. except:
  131. pass
  132. #### NAME / CHANNEL ####
  133. the_packing_box = from_right_to_thumbnail
  134. if is_channel:
  135. the_packing_box = outbox
  136. name_channel_box = Gtk.VBox()
  137. the_packing_box.pack_start(name_channel_box, False, False, False)
  138. # Channel
  139. if "signing_channel" in out:
  140. name_channel_box.pack_start(ui.go_to_channel(win, out["signing_channel"]),False,False,False)
  141. # name it self
  142. title = out["name"]
  143. try:
  144. title = out["value"]["title"]
  145. except:
  146. pass
  147. title_label = Gtk.Label()
  148. title_label.set_line_wrap_mode( Gtk.WrapMode.WORD )
  149. title_label.set_line_wrap(True)
  150. title_label.set_markup('<span size="x-large"> '+title+'</span> ')
  151. title_label.set_selectable(True)
  152. title_label.set_css_name("")
  153. name_channel_box.pack_start(title_label, False, False, False)
  154. ################# TOOL BAR ##################
  155. toolbox = Gtk.HBox()
  156. the_packing_box.pack_start(toolbox, False, False,False)
  157. if not is_channel:
  158. def download_action(w):
  159. start_downloading(url)
  160. download_button = Gtk.Button()
  161. download_button.connect("clicked", download_action)
  162. download_button.set_relief(Gtk.ReliefStyle.NONE)
  163. download_box = Gtk.HBox()
  164. download_button.add(download_box)
  165. download_icon = ui.icon(win, "download")
  166. download_box.pack_start(download_icon, False, False, False)
  167. #filesize
  168. labeltext = "Download"
  169. if price:
  170. labeltext = "Buy for "+str(price)+" "+currency
  171. try:
  172. filesize = out["value"]["source"]["size"]
  173. labeltext = labeltext + " ("+csize(filesize)+")"
  174. except:
  175. filesize = 0
  176. download_box.pack_start(Gtk.Label(labeltext), False, False, False)
  177. toolbox.pack_start(download_button, False,False,False)
  178. download_bar = Gtk.ProgressBar()
  179. the_packing_box.pack_start(download_bar, False, False,False)
  180. def delete_action(w):
  181. filename = get_downloaded_file(out["claim_id"])
  182. delete_file(out["claim_id"])
  183. try:
  184. os.remove(filename)
  185. except:
  186. pass
  187. delete_button = Gtk.Button()
  188. delete_button.connect("clicked", delete_action)
  189. delete_button.set_relief(Gtk.ReliefStyle.NONE)
  190. delete_box = Gtk.HBox()
  191. delete_button.add(delete_box)
  192. delete_icon = ui.icon(win,"delete")
  193. delete_box.pack_start(delete_icon, False, False, False)
  194. delete_box.pack_start(Gtk.Label("Delete"), False, False, False)
  195. toolbox.pack_start(delete_button, False,False,False)
  196. def launch_action(w):
  197. Popen(["xdg-open", get_downloaded_file(out["claim_id"])])
  198. launch_button = Gtk.Button()
  199. launch_button.connect("clicked", launch_action)
  200. launch_button.set_relief(Gtk.ReliefStyle.NONE)
  201. launch_box = Gtk.HBox()
  202. launch_button.add(launch_box)
  203. launch_icon = ui.icon(win, "launch")
  204. launch_box.pack_start(launch_icon, False, False, False)
  205. launch_box.pack_start(Gtk.Label("Launch"), False, False, False)
  206. toolbox.pack_start(launch_button, False,False,False)
  207. win.check_download_daemon_keep_alive = True
  208. t = threading.Thread(target=downloading_check_thread, args=(win, out["claim_id"],download_button, delete_button, launch_button, download_bar))
  209. t.setDaemon(True)
  210. t.start()
  211. def kill_daemon(w):
  212. win.check_download_daemon_keep_alive = False
  213. download_button.connect("destroy", kill_daemon)
  214. ########### BOTTOM NOTEBOOK ##############
  215. notebook = Gtk.Notebook()
  216. outbox.pack_start(notebook, True, True,False)
  217. # If article read article
  218. try:
  219. if out["value"]["source"]["media_type"] == "text/markdown" and price == 0:
  220. # We download it first
  221. playout = fetch.lbrynet("get", {"uri":url, "save_file":True})
  222. # playout = check_output(["flbry/lbrynet",
  223. # "get", url])
  224. # # Parsing the Json
  225. # playout = json.loads(playout)
  226. md_text = open(playout['download_path'])
  227. md_text = md_text.read()
  228. # Markdown covenreted
  229. md_scrl = Gtk.ScrolledWindow()
  230. md_view = Gtk.TextView()
  231. md_view.set_wrap_mode(Gtk.WrapMode.WORD )
  232. md_buffer = md_view.get_buffer()
  233. md_view.set_editable(False)
  234. md_scrl.add(md_view)
  235. md_buffer.set_text(md_text)
  236. markdown.convert(win, md_view)
  237. detailsbox = Gtk.HBox()
  238. detailsbox.pack_start(ui.icon(win, "text-plain"), False, False, False)
  239. detailsbox.pack_start(Gtk.Label(" Read Article "), True, True, True)
  240. detailsbox.show_all()
  241. notebook.append_page(md_scrl, detailsbox)
  242. # Markdown source
  243. md_scrl = Gtk.ScrolledWindow()
  244. md_view = Gtk.TextView()
  245. #md_view.override_background_color(Gtk.StateType.NORMAL, Gdk.RGBA(0.2,0.2,0.2, 1))
  246. #md_view.override_color(Gtk.StateType.NORMAL, Gdk.RGBA(0.9,0.9,0.9, 1))
  247. md_view.override_font(Pango.FontDescription("Monospace"))
  248. md_view.set_wrap_mode(Gtk.WrapMode.WORD )
  249. md_buffer = md_view.get_buffer()
  250. md_view.set_editable(False)
  251. md_scrl.add(md_view)
  252. md_buffer.set_text(md_text)
  253. detailsbox = Gtk.HBox()
  254. detailsbox.pack_start(ui.icon(win, "text-markdown"), False, False, False)
  255. detailsbox.pack_start(Gtk.Label(" Source of Article "), True, True, True)
  256. detailsbox.show_all()
  257. notebook.append_page(md_scrl, detailsbox)
  258. except Exception as e:
  259. print("FUCKING ERROR")
  260. print(e)
  261. print()
  262. # Channel Uploads / Publications
  263. if is_channel:
  264. uploads_box = ui.load(win, claim_search.find, claim_search.render, win, "", [out["claim_id"]], )
  265. detailsbox = Gtk.HBox()
  266. detailsbox.pack_start(ui.icon(win, "folder-remote"), False, False, False)
  267. detailsbox.pack_start(Gtk.Label(" Publications "), True, True, True)
  268. detailsbox.show_all()
  269. notebook.append_page(uploads_box, detailsbox)
  270. ##### DESCRIPTION ####
  271. try:
  272. description_scrl = Gtk.ScrolledWindow()
  273. description_scrl.set_size_request(500,200)
  274. description_field = Gtk.TextView()
  275. description_field.set_wrap_mode(Gtk.WrapMode.WORD )
  276. description_field.set_editable(False)
  277. description_buffer = description_field.get_buffer()
  278. description_buffer.set_text(out["value"]["description"])
  279. markdown.convert(win, description_field)
  280. description_box = Gtk.VBox()
  281. detailsbox = Gtk.HBox()
  282. detailsbox.pack_start(ui.icon(win, "text-plain"), False, False, False)
  283. detailsbox.pack_start(Gtk.Label(" Description "), True, True, True)
  284. detailsbox.show_all()
  285. notebook.append_page(description_scrl, detailsbox)
  286. description_box.pack_start(description_scrl, False, False, False)
  287. description_scrl.add(description_field)
  288. except:
  289. pass
  290. ######## COMMENTS ########
  291. com_box = Gtk.VBox()
  292. com_scrl = Gtk.ScrolledWindow()
  293. comments_widget = ui.load(win, comments.list_comments, comments.render_comments, win, out["claim_id"], )
  294. com_scrl.add(comments_widget)
  295. detailsbox = Gtk.HBox()
  296. detailsbox.pack_start(ui.icon(win, "message-new"), False, False, False)
  297. detailsbox.pack_start(Gtk.Label(" Comments "), True, True, True)
  298. detailsbox.show_all()
  299. com_box.pack_start(comments.comment_input(win), False, False, 0)
  300. com_box.pack_start(com_scrl, True, True, 0)
  301. notebook.append_page(com_box, detailsbox)
  302. ##### Details #######
  303. # Almost like Raw Data but shows only the important stuff
  304. details = {"LBRY URL: ":url,
  305. "Price: ":str(price)+" "+str(currency)}
  306. try:
  307. details["Claim ID: "] = out["claim_id"]
  308. except:
  309. pass
  310. try:
  311. details["Upload Bid: "] = out["amount"]+" LBC"
  312. except:
  313. pass
  314. try:
  315. details["Support: "] = out["meta"]["support_amount"]+" LBC"
  316. except:
  317. pass
  318. try:
  319. details["Filename: "] = out["value"]["source"]["name"]
  320. except:
  321. pass
  322. try:
  323. details["File Size:"] = csize(filesize)
  324. except:
  325. pass
  326. try:
  327. details["License: "] = out["value"]["license"]
  328. except:
  329. pass
  330. try:
  331. details["Released at: "] = time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime(int(out["value"]["release_time"])))
  332. except:
  333. pass
  334. det_scrl = Gtk.ScrolledWindow()
  335. det_view = data_view.data_widget(details)
  336. det_scrl.add(det_view)
  337. detailsbox = Gtk.HBox()
  338. detailsbox.pack_start(ui.icon(win, "emblem-information"), False, False, False)
  339. detailsbox.pack_start(Gtk.Label(" Details "), True, True, True)
  340. detailsbox.show_all()
  341. notebook.append_page(det_scrl, detailsbox)
  342. ##### Raw Data #######
  343. raw_scrl = Gtk.ScrolledWindow()
  344. raw_view = data_view.data_widget(out)
  345. raw_scrl.add(raw_view)
  346. detailsbox = Gtk.HBox()
  347. detailsbox.pack_start(ui.icon(win, "emblem-warning"), False, False, False)
  348. detailsbox.pack_start(Gtk.Label(" Extra Details "), True, True, True)
  349. detailsbox.show_all()
  350. notebook.append_page(raw_scrl, detailsbox)
  351. #outbox.show_all()
  352. return outbox
  353. def downloaded(claim_id):
  354. # Returns a fraction ( from 0 to 1 ) of the download
  355. # percentage. If it's a 0, we can use it to display
  356. # the download button.
  357. #out = check_output(["flbry/lbrynet",
  358. # "file", "list", "--claim_id="+claim_id])
  359. out = fetch.lbrynet("file_list", {"claim_id":claim_id})
  360. #print(out, '\n\n')
  361. try:
  362. #out = json.loads(out)
  363. out = out["items"][0]
  364. if out["status"] == "finished":
  365. return 1
  366. else:
  367. return out["written_bytes"] / out["total_bytes"]
  368. except:
  369. return 0
  370. def get_downloaded_file(claim_id):
  371. #out = check_output(["flbry/lbrynet",
  372. # "file", "list", "--claim_id="+claim_id])
  373. out = fetch.lbrynet("file_list", {"claim_id":claim_id})
  374. try:
  375. #out = json.loads(out)
  376. out = out["items"][0]
  377. return out["download_path"]
  378. except:
  379. return ""
  380. def delete_file(claim_id):
  381. #check_output(["flbry/lbrynet",
  382. # "file", "delete", "--claim_id="+claim_id])
  383. fetch.lbrynet("file_delete", {"claim_id":claim_id})
  384. def start_downloading(url):
  385. out = fetch.lbrynet("get", {"uri":url, "save_file":True})
  386. def downloading_check_thread(win, claim_id,
  387. download_button,
  388. delete_button,
  389. launch_button,
  390. progress_bar):
  391. # This is a thread that will toggle buttons on/off
  392. # based on a curretly downloading file.
  393. def update(fraction):
  394. if not fraction: # if it's 0
  395. download_button.set_visible(True)
  396. delete_button.set_visible(False)
  397. launch_button.set_visible(False)
  398. progress_bar.set_visible(False)
  399. else:
  400. download_button.set_visible(False)
  401. delete_button.set_visible(True)
  402. launch_button.set_visible(True)
  403. progress_bar.set_visible(True)
  404. progress_bar.set_fraction(fraction)
  405. if fraction == 1:
  406. progress_bar.set_visible(False)
  407. while True:
  408. fraction = downloaded(claim_id)
  409. GLib.idle_add(update, fraction)
  410. time.sleep(2) # The new algorithm is too fast LOL
  411. if not win.check_download_daemon_keep_alive:
  412. return
  413. def csize(x):
  414. x = float(x)
  415. l = ["B","KB", "MB", "GB", "TB"]
  416. for i in range(5):
  417. if x > 1024:
  418. x = x / 1024
  419. else:
  420. return str(round(x, 2))+" "+l[i]
  421. return str(round(x, 2))+" "+l[i]