url.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392
  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 urllib.request
  31. import threading
  32. import json
  33. from subprocess import *
  34. from gi.repository import Gtk
  35. from gi.repository import Gdk
  36. from gi.repository import Pango
  37. from gi.repository import GdkPixbuf
  38. from PIL import Image, ImageSequence
  39. from flbry import markdown
  40. from flbry import data_view
  41. from flbry import ui
  42. def resolve(w, win, outbox, url):
  43. # This function will draw a widget of rsolved url
  44. out = check_output(["flbry/lbrynet",
  45. "resolve", url])
  46. raw_data = out.decode("utf-8")
  47. # Now we want to parse the json
  48. try:
  49. out = json.loads(out)
  50. out = out[url]
  51. except:
  52. print("Resolve Failed")
  53. return False
  54. is_channel = "value_type" in out and out["value_type"] == "channel"
  55. percentage = downloaded(out["claim_id"])
  56. # make sure the url path starts with lbry://
  57. if not url.startswith("lbry://"):
  58. url = "lbry://"+url
  59. win.url.set_text(url)
  60. box = Gtk.HBox()
  61. #### THUMBNAIL ###
  62. thumb = ""
  63. try:
  64. thumb = out["value"]["thumbnail"]["url"]
  65. except Exception as e:
  66. print("Error loading thumbnail: ", e)
  67. def thumb_open(w):
  68. os.system("xdg-open /tmp/flbry_thumbnail")
  69. thumb_button = Gtk.Button()
  70. thumb_button.set_tooltip_text(thumb)
  71. if is_channel:
  72. thumb_image = ui.load(win, ui.net_image, thumb, 150, "/tmp/flbry_thumbnail", True)
  73. else:
  74. thumb_image = ui.load(win, ui.net_image, thumb, 400, "/tmp/flbry_thumbnail", True)
  75. thumb_button.add(thumb_image)
  76. thumb_button.set_relief(Gtk.ReliefStyle.NONE)
  77. thumb_button.connect("clicked", thumb_open)
  78. box.pack_start(thumb_button, False, False, False)
  79. outbox.pack_start(box, False, False, False)
  80. from_right_to_thumbnail = Gtk.VBox()
  81. box.pack_end(from_right_to_thumbnail, True, True, False)
  82. # If channel load banner
  83. if is_channel:
  84. if "cover" in out["value"]:
  85. channel_banner = ui.load(win, ui.net_image, out["value"]["cover"]["url"], False, "", True)
  86. channel_scroll = Gtk.ScrolledWindow()
  87. channel_scroll.set_size_request(400,150)
  88. channel_scroll.add_with_viewport(channel_banner)
  89. from_right_to_thumbnail.pack_start(channel_scroll, False, False, False)
  90. else:
  91. try:
  92. channel_scroll.destroy()
  93. except:
  94. pass
  95. #### NAME / CHANNEL ####
  96. the_packing_box = from_right_to_thumbnail
  97. if is_channel:
  98. the_packing_box = outbox
  99. name_channel_box = Gtk.VBox()
  100. the_packing_box.pack_start(name_channel_box, False, False, False)
  101. # Channel
  102. if "signing_channel" in out:
  103. # Get channel name
  104. try:
  105. channel_name = out["signing_channel"]["value"]["title"]
  106. except:
  107. channel_name = out["signing_channel"]["name"]
  108. def channel_resolve(w):
  109. win.url.set_text(out["signing_channel"]["canonical_url"])
  110. win.url.activate()
  111. channel_button = Gtk.Button()
  112. channel_button.set_tooltip_text(out["signing_channel"]["canonical_url"])
  113. channel_button.connect("clicked", channel_resolve)
  114. channel_button.set_relief(Gtk.ReliefStyle.NONE)
  115. channel_button_box = Gtk.HBox()
  116. channel_button.add(channel_button_box)
  117. # If channel thumbnail exists.
  118. try:
  119. channel_thumb = ui.load(win, ui.net_image, out["signing_channel"]["value"]["thumbnail"]["url"], 40 , "", False)
  120. channel_button_box.pack_start(channel_thumb, False,False,False)
  121. except:
  122. pass
  123. channel_button_box.pack_start(Gtk.Label(channel_name), False, False, False)
  124. name_channel_box.pack_start(channel_button,False,False,False)
  125. # name it self
  126. title = "[no title]"
  127. try:
  128. title = out["value"]["title"]
  129. except:
  130. pass
  131. title_label = Gtk.Label()
  132. title_label.set_line_wrap_mode( Gtk.WrapMode.WORD )
  133. title_label.set_line_wrap(True)
  134. title_label.set_markup('<span size="x-large"> '+title+'</span> ')
  135. title_label.set_selectable(True)
  136. title_label.set_css_name("")
  137. name_channel_box.pack_start(title_label, False, False, False)
  138. ################# TOOL BAR ##################
  139. toolbox = Gtk.HBox()
  140. the_packing_box.pack_start(toolbox, False, False,False)
  141. if not is_channel:
  142. def download_action(w):
  143. start_downloading(out["permanent_url"])
  144. download_button = Gtk.Button()
  145. download_button.connect("clicked", download_action)
  146. download_button.set_relief(Gtk.ReliefStyle.NONE)
  147. download_box = Gtk.HBox()
  148. download_button.add(download_box)
  149. download_icon = ui.icon(win, "download")
  150. download_box.pack_start(download_icon, False, False, False)
  151. download_box.pack_start(Gtk.Label("Download"), False, False, False)
  152. toolbox.pack_start(download_button, False,False,False)
  153. download_bar = Gtk.ProgressBar()
  154. the_packing_box.pack_start(download_bar, False, False,False)
  155. def delete_action(w):
  156. filename = get_downloaded_file(out["claim_id"])
  157. delete_file(out["claim_id"])
  158. try:
  159. os.remove(filename)
  160. except:
  161. pass
  162. delete_button = Gtk.Button()
  163. delete_button.connect("clicked", delete_action)
  164. delete_button.set_relief(Gtk.ReliefStyle.NONE)
  165. delete_box = Gtk.HBox()
  166. delete_button.add(delete_box)
  167. delete_icon = ui.icon(win,"delete")
  168. delete_box.pack_start(delete_icon, False, False, False)
  169. delete_box.pack_start(Gtk.Label("Delete"), False, False, False)
  170. toolbox.pack_start(delete_button, False,False,False)
  171. def launch_action(w):
  172. Popen(["xdg-open", get_downloaded_file(out["claim_id"])])
  173. launch_button = Gtk.Button()
  174. launch_button.connect("clicked", launch_action)
  175. launch_button.set_relief(Gtk.ReliefStyle.NONE)
  176. launch_box = Gtk.HBox()
  177. launch_button.add(launch_box)
  178. launch_icon = ui.icon(win, "launch")
  179. launch_box.pack_start(launch_icon, False, False, False)
  180. launch_box.pack_start(Gtk.Label("Launch"), False, False, False)
  181. toolbox.pack_start(launch_button, False,False,False)
  182. win.check_download_daemon_keep_alive = True
  183. t = threading.Thread(target=downloading_check_thread, args=(win, out["claim_id"],download_button, delete_button, launch_button, download_bar))
  184. t.setDaemon(True)
  185. t.start()
  186. def kill_daemon(w):
  187. win.check_download_daemon_keep_alive = False
  188. download_button.connect("destroy", kill_daemon)
  189. ########### BOTTOM NOTEBOOK ##############
  190. notebook = Gtk.Notebook()
  191. outbox.pack_start(notebook, True, True,False)
  192. # If article read article
  193. try:
  194. if out["value"]["source"]["media_type"] == "text/markdown":
  195. playout = check_output(["flbry/lbrynet",
  196. "get", url])
  197. # Parsing the Json
  198. playout = json.loads(playout)
  199. md_text = open(playout['download_path'])
  200. md_text = md_text.read()
  201. # Markdown covenreted
  202. md_scrl = Gtk.ScrolledWindow()
  203. md_view = Gtk.TextView()
  204. md_view.set_wrap_mode(Gtk.WrapMode.WORD )
  205. md_buffer = md_view.get_buffer()
  206. md_view.set_editable(False)
  207. md_scrl.add(md_view)
  208. md_buffer.set_text(md_text)
  209. markdown.convert(win, md_view)
  210. notebook.append_page(md_scrl, Gtk.Label("Read Article"))
  211. # Markdown source
  212. md_scrl = Gtk.ScrolledWindow()
  213. md_view = Gtk.TextView()
  214. #md_view.override_background_color(Gtk.StateType.NORMAL, Gdk.RGBA(0.2,0.2,0.2, 1))
  215. #md_view.override_color(Gtk.StateType.NORMAL, Gdk.RGBA(0.9,0.9,0.9, 1))
  216. md_view.override_font(Pango.FontDescription("Monospace"))
  217. md_view.set_wrap_mode(Gtk.WrapMode.WORD )
  218. md_buffer = md_view.get_buffer()
  219. md_view.set_editable(False)
  220. md_scrl.add(md_view)
  221. md_buffer.set_text(md_text)
  222. notebook.append_page(md_scrl, Gtk.Label("Source Code"))
  223. except:
  224. pass
  225. # Channel Uploads / Publications
  226. if is_channel:
  227. uploads_box = Gtk.VBox()
  228. notebook.append_page(uploads_box, Gtk.Label("Publications"))
  229. ##### DESCRIPTION ####
  230. try:
  231. description_scrl = Gtk.ScrolledWindow()
  232. description_scrl.set_size_request(500,200)
  233. description_field = Gtk.TextView()
  234. description_field.set_wrap_mode(Gtk.WrapMode.WORD )
  235. description_field.set_editable(False)
  236. description_buffer = description_field.get_buffer()
  237. description_buffer.set_text(out["value"]["description"])
  238. markdown.convert(win, description_field)
  239. description_box = Gtk.VBox()
  240. notebook.append_page(description_scrl, Gtk.Label("Description"))
  241. description_box.pack_start(description_scrl, False, False, False)
  242. description_scrl.add(description_field)
  243. except:
  244. pass
  245. ##### Raw Data #######
  246. raw_scrl = Gtk.ScrolledWindow()
  247. raw_view = data_view.data_widget(out)
  248. raw_scrl.add(raw_view)
  249. notebook.append_page(raw_scrl, Gtk.Label("Raw Data"))
  250. outbox.show_all()
  251. return True
  252. def downloaded(claim_id):
  253. # Returns a fraction ( from 0 to 1 ) of the download
  254. # percentage. If it's a 0, we can use it to display
  255. # the download button.
  256. out = check_output(["flbry/lbrynet",
  257. "file", "list", "--claim_id="+claim_id])
  258. try:
  259. out = json.loads(out)
  260. out = out["items"][0]
  261. if out["status"] == "finished":
  262. return 1
  263. else:
  264. return out["written_bytes"] / out["total_bytes"]
  265. except:
  266. return 0
  267. def get_downloaded_file(claim_id):
  268. out = check_output(["flbry/lbrynet",
  269. "file", "list", "--claim_id="+claim_id])
  270. try:
  271. out = json.loads(out)
  272. out = out["items"][0]
  273. return out["download_path"]
  274. except:
  275. return ""
  276. def delete_file(claim_id):
  277. check_output(["flbry/lbrynet",
  278. "file", "delete", "--claim_id="+claim_id])
  279. def start_downloading(url):
  280. check_output(["flbry/lbrynet",
  281. "get", url])
  282. def downloading_check_thread(win, claim_id,
  283. download_button,
  284. delete_button,
  285. launch_button,
  286. progress_bar):
  287. # This is a thread that will toggle buttons on/off
  288. # based on a curretly downloading file.
  289. while True:
  290. fraction = downloaded(claim_id)
  291. if not fraction: # if it's 0
  292. download_button.set_visible(True)
  293. delete_button.set_visible(False)
  294. launch_button.set_visible(False)
  295. progress_bar.set_visible(False)
  296. else:
  297. download_button.set_visible(False)
  298. delete_button.set_visible(True)
  299. launch_button.set_visible(True)
  300. progress_bar.set_visible(True)
  301. progress_bar.set_fraction(fraction)
  302. if fraction == 1:
  303. progress_bar.set_visible(False)
  304. if not win.check_download_daemon_keep_alive:
  305. return