run.py 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990
  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. # Preparing the executable of lbrynet to be running. If we don't that
  30. # there will be a nasty error of permission being denied.
  31. import os
  32. os.system("chmod u+x flbry/lbrynet")
  33. # I'm not trying to make it pretty. I'm trying to make it snappy.
  34. # It's FastLBRY not SlowLBRY.
  35. import gi
  36. import threading
  37. gi.require_version('Gtk', '3.0')
  38. from gi.repository import Gtk
  39. from gi.repository import Gdk
  40. from gi.repository import GLib
  41. # It's gonna take a couple of things to make the button run
  42. from flbry import connect
  43. from flbry import parse
  44. from flbry import settings
  45. from flbry import url
  46. from flbry import ui
  47. from flbry import claim_search
  48. from flbry import fetch
  49. from flbry import publish
  50. from flbry import downloads
  51. from flbry import analytics
  52. from flbry import livestreams
  53. from flbry import suggest
  54. from flbry import odysee
  55. settings.make_sure_file_exists()
  56. ##########################################################################
  57. # #
  58. # THE MAIN WINDOW OF FASTLBRY GTK #
  59. # #
  60. ##########################################################################
  61. win = Gtk.Window() # Make a GTK window object
  62. win.connect("destroy", Gtk.main_quit) # If you close the window, program quits
  63. win.set_title("FastLBRY GTK") # Title
  64. win.set_default_icon_from_file("icon.png") # Icon
  65. win.set_size_request(800, 700) # Minimum size
  66. ##########################################################################
  67. # #
  68. # GLOBAL VARIABLES HACK ( adding things to 'win' ) #
  69. # #
  70. ##########################################################################
  71. # This hack is very simple. Our main Gtk.Window() is a class to which we
  72. # can add variables later on. Like the example you see below.
  73. # This way, giving some function only 'win' and nothing else, will give
  74. # that other function the entire list of global variable ( added this way ).
  75. win.settings = settings.load() # Settings
  76. win.SDK_running = connect.check() # Is the SDK running ( from 0 to 1 to draw progress bar)
  77. win.resolved = {} # Last resolved data
  78. win.commenting = {} # Data for while chatting in comments
  79. win.subs = [] # What you are subscribed to
  80. win.tabs = [] # List of Gtk.Notebook tabs
  81. win.resolve_tab = 0 # What tab to resolve the page
  82. win.notebook = Gtk.Notebook() # Tabs object
  83. win.download_buttons = {} # Keep alive handlers for Download buttons
  84. win.downloads = {} # Data for the Downloads window
  85. ##########################################################################
  86. # #
  87. # TOP PANNEL #
  88. # #
  89. ##########################################################################
  90. # Gtk.HeaderBar() is like a box but it draws itself in the headerbar insead
  91. # of in the window itself.
  92. pannel = Gtk.HeaderBar()
  93. pannel.set_show_close_button(True)
  94. win.set_titlebar(pannel) # Dumbasses wrote GTK
  95. # Untill the user connected to the SDK, search and other functions should
  96. # not work. So we split the pannel into two parts. The other one is a
  97. # normal box.
  98. restofpannel = Gtk.HBox() # H for Horrizontal
  99. pannel.pack_end(restofpannel)
  100. ############## CONNECT BUTTON ################
  101. # Connect button will also be a disconnect button
  102. # Icons for both
  103. icon_connect = ui.icon(win, "network-wired")
  104. icon_disconnect = ui.icon(win, "network-wired-disconnected")
  105. # Boxes for both
  106. connect_box = Gtk.HBox()
  107. disconnect_box = Gtk.HBox()
  108. # Connect box
  109. connect_box.pack_start(icon_connect, False, False, False)
  110. connect_box.set_tooltip_text("Connect to LBRY Network")
  111. connect_box.pack_start(Gtk.Label("Connect "), False, False, False)
  112. # Disconnect box
  113. disconnect_box.pack_start(icon_disconnect, False, False, False)
  114. disconnect_box.set_tooltip_text("Disconnect from LBRY Network")
  115. disconnect_box.pack_start(Gtk.Label("Disconnect "), False, False, False)
  116. # By whether SDK was running while the software was launched
  117. # we present the correct button to the user and set the rest-of-pannel
  118. # accordingly.
  119. if win.SDK_running:
  120. title = disconnect_box
  121. restofpannel.set_sensitive(True)
  122. else:
  123. title = connect_box
  124. restofpannel.set_sensitive(False)
  125. connect_disconncet_button = Gtk.Button()
  126. connect_disconncet_button.add(title)
  127. connect_disconncet_button.set_relief(Gtk.ReliefStyle.NONE)
  128. pannel.pack_start(connect_disconncet_button)#, False, False, False)
  129. ############### CONNECT / DISCONNECT BUTTONS FUNCTION ################
  130. # The button should do something.
  131. def connect_disconnect_function(w):
  132. w.set_sensitive(False)
  133. dynamic_box = win.tabs[win.notebook.get_current_page()]["box"]
  134. for i in dynamic_box.get_children():
  135. i.destroy()
  136. progress_connect = Gtk.ProgressBar(show_text=True)
  137. dynamic_box.pack_start(progress_connect, True, True, 30)
  138. win.show_all()
  139. def do_sdk(w, pb):
  140. wasrunning = win.SDK_running
  141. if win.SDK_running == 1:
  142. connect.stop(win)
  143. else:
  144. connect.start(win)
  145. def update_pb(pb):
  146. pb.set_fraction(win.SDK_running)
  147. pb.set_text("Connecting: "+str(int(round(win.SDK_running*100)))+"%")
  148. def update_buttons(w):
  149. for i in dynamic_box.get_children():
  150. i.destroy()
  151. w.get_child().destroy()
  152. if win.SDK_running == 1:
  153. w.add(disconnect_box)
  154. restofpannel.set_sensitive(True)
  155. else:
  156. w.add(connect_box)
  157. restofpannel.set_sensitive(False)
  158. w.show_all()
  159. w.set_sensitive(True)
  160. if win.SDK_running == 1:
  161. load_following()
  162. load_channel(win)
  163. check_lock_button()
  164. ui.notify(win, "Connected to LBRY")
  165. if wasrunning == 0:
  166. while True:
  167. win.SDK_running = connect.check()
  168. if win.SDK_running == 1:
  169. break
  170. GLib.idle_add(update_pb, pb)
  171. GLib.idle_add(update_buttons, w)
  172. load_thread = threading.Thread(target=do_sdk, args=(w, progress_connect))
  173. load_thread.setDaemon(True)
  174. load_thread.start()
  175. connect_disconncet_button.connect("clicked", connect_disconnect_function)
  176. ##########################################################################
  177. # #
  178. # LOCK BUTTON #
  179. # #
  180. ##########################################################################
  181. win.lock_button = Gtk.ToggleButton()
  182. def check_lock_button():
  183. try:
  184. wallet_status = fetch.lbrynet("wallet_status")
  185. except:
  186. wallet_status = {}
  187. if wallet_status.get("is_locked"):
  188. win.lock_button.set_active(True)
  189. if not win.settings["lock_password"]:
  190. win.lock_button.set_sensitive(False)
  191. else:
  192. win.lock_button.set_sensitive(True)
  193. check_lock_button()
  194. win.lock_button.set_tooltip_text("Lock / Unlock LBRY wallet. \n\n While the wallet is locked, no transaction could be signed. So nobody could use the running SDK to harm you.\n\nTo set the password look into settings.")
  195. win.lock_button.set_relief(Gtk.ReliefStyle.NONE)
  196. win.lock_button.add(ui.icon(win, "system-lock-screen-symbolic"))
  197. restofpannel.pack_start(win.lock_button, False, False, False)
  198. def lock_unlock(w):
  199. passw = win.settings["lock_password"]
  200. wallets = fetch.lbrynet("wallet_list")
  201. ID = wallets["items"][0]["id"]
  202. if w.get_active():
  203. # If we are locking
  204. print(fetch.lbrynet("wallet_encrypt", {"new_password":passw}))
  205. print(fetch.lbrynet("wallet_lock", {"wallet_id":ID}))
  206. else:
  207. # If decrypt
  208. print(fetch.lbrynet("wallet_unlock", {"password":passw, "wallet_id":ID}))
  209. print(fetch.lbrynet("wallet_decrypt", {"wallet_id":ID}))
  210. print("I AM FUCKING PRESSED")
  211. win.lock_button.connect("clicked", lock_unlock)
  212. ##########################################################################
  213. # #
  214. # PUBLISH BUTTON #
  215. # #
  216. ##########################################################################
  217. restofpannel.pack_start(Gtk.VSeparator(), True, True, 20)
  218. publ = Gtk.Button()
  219. publ.set_tooltip_text("Publish to LBRY")
  220. publ.set_relief(Gtk.ReliefStyle.NONE)
  221. publ.add(ui.icon(win, "list-add"))
  222. restofpannel.pack_start(publ, False, False, False)
  223. def do_publish(w):
  224. publish.window(win)
  225. publ.connect("clicked", do_publish)
  226. restofpannel.pack_start(Gtk.VSeparator(), True, True, 20)
  227. ##########################################################################
  228. # #
  229. # SEARCH / URL BAR #
  230. # #
  231. ##########################################################################
  232. # Note that I add this directly into the 'win' to make it a global variable.
  233. # This let's me to start any url from anywhere in the software.
  234. win.url = Gtk.Entry() # Gtk.SearchEntry() also looks good, but nah
  235. win.url.set_size_request(400,40)
  236. restofpannel.pack_start(win.url, True, True, False)
  237. ####### SEARCH FUNCTION #######
  238. def search(w):
  239. if win.resolve_tab == "current":
  240. win.resolve_tab = win.notebook.get_current_page()
  241. try:
  242. dynamic_box = win.tabs[win.resolve_tab]["box"]
  243. except:
  244. new_tab()
  245. dynamic_box = win.tabs[win.resolve_tab]["box"]
  246. # DRAG AND DROP FOR PUBLISH
  247. if win.url.get_text().startswith("file://") or os.path.exists(win.url.get_text()):
  248. # TODO: Some filenames include weird characters like %20 for spacebar and
  249. # stuff like that. We need to filter that out.
  250. filepath = win.url.get_text().replace("file://", "")
  251. publish.window(win, {"file_path":filepath})
  252. win.url.set_text("")
  253. return
  254. for i in dynamic_box.get_children():
  255. i.destroy()
  256. ## IF WE ARE NOT FORCING IT TO SEACH, IT WILL TRY TO RESOLVE THE LBRY URL FIRST ##
  257. if not force_search.get_active() or win.url.get_text().startswith("lbry://"):
  258. win.url.set_text(parse.bar(win.url.get_text()))
  259. ltext = win.url.get_text()
  260. ltext = ltext[ltext.rfind("/")+1:]
  261. win.tabs[win.resolve_tab]["label"].set_text(ltext[:10]+"...")
  262. win.tabs[win.resolve_tab]["label"].set_tooltip_text(win.url.get_text())
  263. resolve = ui.load(win, url.resolve, url.render_resolve, w, win, win.url.get_text())
  264. dynamic_box.pack_start(resolve, True, True, True)
  265. win.show_all()
  266. else:
  267. resolve = ui.load(win, claim_search.find, claim_search.render, win, win.url.get_text(), [], 1, {"order_by":""})
  268. dynamic_box.pack_start(resolve, True, True, True)
  269. win.show_all()
  270. ltext = win.url.get_text()
  271. win.tabs[win.resolve_tab]["label"].set_text(ltext[:10]+"...")
  272. win.tabs[win.resolve_tab]["label"].set_tooltip_text(win.url.get_text())
  273. dynamic_box.show_all()
  274. win.notebook.set_current_page(win.resolve_tab)
  275. win.resolve_tab = "current"
  276. hamburger.hide()
  277. # Button to activate seach for those who don't know that you can press Enter
  278. search_button = Gtk.Button()
  279. search_icon = ui.icon(win, "system-search")
  280. search_button.add(search_icon)
  281. search_button.set_relief(Gtk.ReliefStyle.NONE)
  282. search_button.connect("clicked", search)
  283. win.url.connect("activate", search)
  284. restofpannel.pack_start(search_button, False, False, False)
  285. ##########################################################################
  286. # #
  287. # HAMBURGER MENU #
  288. # #
  289. ##########################################################################
  290. # Popover is the new GTK menu thingy that looks like a comic book dialog
  291. # box. Which is what we want here. Because we want it to look nice I suppose.
  292. def on_hamburger(w):
  293. ################# NOTIFICATIONS #####################
  294. notifications = odysee.get_odysee_notifications(win)
  295. for i in win.notifications_box.get_children():
  296. i.destroy()
  297. if notifications:
  298. scrl = Gtk.ScrolledWindow()
  299. scrl.set_size_request(200,200)
  300. win.notifications_box.pack_start(scrl, 1,1,0)
  301. notbox = Gtk.VBox()
  302. scrl.add(notbox)
  303. for i in notifications:
  304. i = i.get("notification_parameters", {})
  305. author = i.get("dynamic", {}).get("reply_author")
  306. publication = i.get("device", {}).get("target")
  307. comment = i.get("device", {}).get("text")
  308. # Channel button
  309. def resolve_channel(win, url):
  310. out = fetch.lbrynet("resolve", {"urls":url})
  311. out = out[url]
  312. return [win, out]
  313. def render_channel(out):
  314. win, out = out
  315. return ui.go_to_channel(win, out)
  316. notbox.pack_start(Gtk.HSeparator(), 1,1,5)
  317. channel_button = ui.load(win, resolve_channel, render_channel, win, author)
  318. notbox.pack_start(channel_button, 1,1,5)
  319. comment_label = Gtk.Label(comment)
  320. comment_label.set_selectable(True)
  321. comment_label.set_line_wrap_mode( Gtk.WrapMode.WORD )
  322. comment_label.set_line_wrap(True)
  323. comment_label.set_max_width_chars(20)
  324. def resolve_publication(w, publication):
  325. win.resolve_tab = "new_tab"
  326. win.url.set_text(publication)
  327. win.url.activate()
  328. comment_button = Gtk.Button()
  329. comment_button.add(comment_label)
  330. comment_button.connect("clicked", resolve_publication, publication)
  331. comment_button.set_relief(Gtk.ReliefStyle.NONE)
  332. notbox.pack_start(comment_button, 1,1,5)
  333. win.notifications_box.show_all()
  334. hamburger = Gtk.Popover()
  335. hambutton = Gtk.MenuButton(popover=hamburger)
  336. hambutton.connect("clicked", on_hamburger)
  337. hambutton_icon = ui.icon(win, "system-users")
  338. hambutton.add(hambutton_icon)
  339. hambutton.set_relief(Gtk.ReliefStyle.NONE)
  340. pannel.pack_start(hambutton)
  341. # Let's now pack the hamburger menu with items
  342. hambox = Gtk.HBox()
  343. # For the elements inside the box we need a scrolled window, to fit a lot of
  344. # them there.
  345. hamscrl = Gtk.ScrolledWindow()
  346. hamscrl.set_size_request(200,200)
  347. hamburger.add(hambox)
  348. hambox.pack_start(hamscrl, False, False, False)
  349. # "hamchannelbox" will be empty untill the user connects and it loads his channel
  350. # list
  351. hamchannelbox = Gtk.VBox()
  352. hamscrl.add_with_viewport(hamchannelbox)
  353. #hambox.pack_start(hamchannelbox, False, False, False)
  354. ###### CHANNELS SELECTOR ########
  355. win.channel = False
  356. def load_channel(win):
  357. def change_channel(w, win, channel ):
  358. win.channel = channel
  359. win.settings["channel"] = win.channel["claim_id"]
  360. settings.save(win.settings)
  361. try:
  362. hambutton.get_child().destroy()
  363. try:
  364. newicon = ui.load(win, ui.net_image_calculation, ui.net_image_render, channel["value"]["thumbnail"]["url"], 40, 40, "", False )
  365. except:
  366. newicon = ui.icon(win, "system-users")
  367. hambutton.add(newicon)
  368. pannel.show_all()
  369. except Exception as e:
  370. print("What?", e)
  371. out = fetch.lbrynet("channel_list")
  372. win.my_channels = out
  373. # We are chooseing first only if there is no channel set in the settings
  374. try:
  375. first = out["items"][0]
  376. win.channel = first
  377. except:
  378. pass
  379. if "channel" in win.settings:
  380. try:
  381. for i in out["items"]:
  382. if i["claim_id"] == win.settings["channel"]:
  383. first = i
  384. break
  385. except:
  386. pass
  387. try:
  388. change_channel(False, win, first)
  389. except:
  390. pass
  391. try:
  392. go.set_sensitive(True)
  393. for i in out["items"]:
  394. channel_button = ui.go_to_channel(win, i, resolve=False)
  395. channel_button.connect("clicked", change_channel, win, i)
  396. hamchannelbox.pack_start(channel_button, False, False, False)
  397. hamchannelbox.show_all()
  398. second_raw.show_all()
  399. except Exception as e:
  400. print("CHANNEL ERROR:", e)
  401. second_raw = Gtk.VBox()
  402. hambox.pack_start(Gtk.VSeparator(), False, False, 5)
  403. hambox.pack_start(second_raw, False, False, False)
  404. ####### Go to channel button #####
  405. def go_to_channel(w):
  406. channel_url = win.channel["name"]
  407. try:
  408. channel_url = channel_url + "#" + win.channel["claim_id"]
  409. except:
  410. pass
  411. win.url.set_text(channel_url)
  412. win.url.activate()
  413. go = Gtk.Button()
  414. b = Gtk.HBox()
  415. b.pack_start(ui.icon(win, "folder-remote"), False, False, False)
  416. b.pack_start(Gtk.Label(" Go To Channel "), True, True, False)
  417. go.add(b)
  418. go.set_sensitive(False)
  419. go.set_relief(Gtk.ReliefStyle.NONE)
  420. go.connect("clicked", go_to_channel)
  421. second_raw.pack_start(go, False, False, False)
  422. ########## UPLOADS BUTTON #########
  423. def load_uploads(w=False):
  424. hamburger.hide()
  425. dynamic_box = win.tabs[win.notebook.get_current_page()]["box"]
  426. for i in dynamic_box.get_children():
  427. i.destroy()
  428. send = {"method":"claim_list"}
  429. resolve = ui.load(win, claim_search.find, claim_search.render, win, "", [], 1, send)
  430. dynamic_box.pack_start(resolve, True, True, True)
  431. dynamic_box.show_all()
  432. win.tabs[win.notebook.get_current_page()]["label"].set_text("Uploads")
  433. win.tabs[win.notebook.get_current_page()]["label"].set_tooltip_text("Uploads")
  434. following = Gtk.Button()
  435. b = Gtk.HBox()
  436. b.pack_start(ui.icon(win, "go-up-symbolic"), False, False, False)
  437. b.pack_start(Gtk.Label(" Uploads "), True, True, False)
  438. following.add(b)
  439. following.set_relief(Gtk.ReliefStyle.NONE)
  440. following.connect("clicked", load_uploads)
  441. second_raw.pack_start(following, False, False,False)
  442. ############## ANALYTICS ################
  443. def load_analytics(w):
  444. hamburger.hide()
  445. analytics.window(win)
  446. following = Gtk.Button()
  447. b = Gtk.HBox()
  448. b.pack_start(ui.icon(win, "text-csv"), False, False, False)
  449. b.pack_start(Gtk.Label(" Wallet "), True, True, False)
  450. following.add(b)
  451. following.set_relief(Gtk.ReliefStyle.NONE)
  452. following.connect("clicked", load_analytics)
  453. second_raw.pack_start(following, False, False,False)
  454. second_raw.pack_start(Gtk.HSeparator(), False, False,5)
  455. ######## FOLLOWING BUTTON ###########
  456. def load_following( w=False, mode="following"):
  457. hamburger.hide()
  458. dynamic_box = win.tabs[win.notebook.get_current_page()]["box"]
  459. for i in dynamic_box.get_children():
  460. i.destroy()
  461. try:
  462. if not win.subs:
  463. out = fetch.lbrynet("preference_get")
  464. win.subs = out["shared"]["value"]["subscriptions"]
  465. subs_raw = fetch.lbrynet("resolve", {"urls":win.subs})
  466. subs = []
  467. for i in subs_raw:
  468. try:
  469. subs.append(subs_raw[i]["claim_id"])
  470. except:
  471. pass
  472. except Exception as e:
  473. print("\n\nERROR:", e, "\n\n")
  474. subs = []
  475. suggest_tags = suggest.suggest()
  476. if mode == "suggest":
  477. send = {"any_tags":suggest_tags,
  478. "order_by":"trending_mixed"}
  479. resolve = ui.load(win, claim_search.find, claim_search.render, win, "", [], 1, send)
  480. else:
  481. resolve = ui.load(win, claim_search.find, claim_search.render, win, "", subs)
  482. # Currently live!
  483. all_lives = livestreams.get_all()
  484. liveids = []
  485. if mode != "suggest":
  486. upcoming = livestreams.get_upcoming(channels=subs)
  487. for s in upcoming.get("items", []):
  488. if "claim_id" in s:
  489. liveids.append(s["claim_id"])
  490. for livestream in all_lives.get("data", []):
  491. live_id = livestream.get("ActiveClaim", {}).get("ClaimID", "")
  492. if livestream.get("ChannelClaimID", "") in subs:
  493. liveids.append(live_id)
  494. else:
  495. upcoming = livestreams.get_upcoming(tags=suggest_tags)
  496. for s in upcoming.get("items", []):
  497. if "claim_id" in s:
  498. liveids.append(s["claim_id"])
  499. for livestream in all_lives.get("data", []):
  500. live_id = livestream.get("ActiveClaim", {}).get("ClaimID", "")
  501. liveids.append(live_id)
  502. if liveids:
  503. send = {"claim_ids":liveids}
  504. if mode == "suggest":
  505. send["any_tags"] = suggest.suggest()
  506. resolve_lives = ui.load(win, claim_search.find, claim_search.render, win, "", [], 1, send)
  507. lives_frame = Gtk.Expander(label=" Livestreams: ")
  508. if mode != "suggest":
  509. lives_frame.set_expanded(True)
  510. resolve_lives.set_size_request(350,350)
  511. lives_frame.add(resolve_lives)
  512. dynamic_box.pack_start(lives_frame, 0,0,0)
  513. dynamic_box.pack_start(Gtk.HSeparator(), 0,0,5)
  514. dynamic_box.pack_start(resolve, True, True, True)
  515. dynamic_box.show_all()
  516. if mode == "suggest":
  517. win.tabs[win.notebook.get_current_page()]["label"].set_text("Suggested")
  518. win.tabs[win.notebook.get_current_page()]["label"].set_tooltip_text("Suggested")
  519. else:
  520. win.tabs[win.notebook.get_current_page()]["label"].set_text("Following")
  521. win.tabs[win.notebook.get_current_page()]["label"].set_tooltip_text("Following")
  522. following = Gtk.Button()
  523. b = Gtk.HBox()
  524. b.pack_start(ui.icon(win, "semi-starred"), False, False, False)
  525. b.pack_start(Gtk.Label(" Suggested "), True, True, False)
  526. following.add(b)
  527. following.set_relief(Gtk.ReliefStyle.NONE)
  528. following.connect("clicked", load_following, "suggest")
  529. second_raw.pack_start(following, False, False,False)
  530. following = Gtk.Button()
  531. b = Gtk.HBox()
  532. b.pack_start(ui.icon(win, "emblem-favorite"), False, False, False)
  533. b.pack_start(Gtk.Label(" Following "), True, True, False)
  534. following.add(b)
  535. following.set_relief(Gtk.ReliefStyle.NONE)
  536. following.connect("clicked", load_following)
  537. second_raw.pack_start(following, False, False,False)
  538. ######## TRENDING BUTTONS ###########
  539. def load_trending(w=False, articles=False):
  540. hamburger.hide()
  541. dynamic_box = win.tabs[win.notebook.get_current_page()]["box"]
  542. for i in dynamic_box.get_children():
  543. i.destroy()
  544. send = {"order_by":"trending_mixed"}
  545. if articles:
  546. send["media_types"] = ["text/markdown"]
  547. resolve = ui.load(win, claim_search.find, claim_search.render, win, "", [], 1, send)
  548. dynamic_box.pack_start(resolve, True, True, True)
  549. dynamic_box.show_all()
  550. if articles:
  551. lname = "Articles"
  552. else:
  553. lname = "Trending"
  554. win.tabs[win.notebook.get_current_page()]["label"].set_text(lname)
  555. win.tabs[win.notebook.get_current_page()]["label"].set_tooltip_text(lname)
  556. following = Gtk.Button()
  557. b = Gtk.HBox()
  558. # The other one was showing web-browsers at some themes
  559. b.pack_start(ui.icon(win, "emblem-shared"), False, False, False)
  560. b.pack_start(Gtk.Label(" Trending "), True, True, False)
  561. following.add(b)
  562. following.set_relief(Gtk.ReliefStyle.NONE)
  563. following.connect("clicked", load_trending)
  564. second_raw.pack_start(following, False, False,False)
  565. following = Gtk.Button()
  566. b = Gtk.HBox()
  567. b.pack_start(ui.icon(win, "text-x-generic"), False, False, False)
  568. b.pack_start(Gtk.Label(" Articles "), True, True, False)
  569. following.add(b)
  570. following.set_relief(Gtk.ReliefStyle.NONE)
  571. following.connect("clicked", load_trending, True)
  572. second_raw.pack_start(following, False, False,False)
  573. #### LIVESTREAMS ####
  574. def load_livestreams(w):
  575. hamburger.hide()
  576. # First let's fetch the data about all livestreams
  577. all_lives = livestreams.get_all()
  578. liveids = []
  579. # Upcoming
  580. upcoming = livestreams.get_upcoming()
  581. for s in upcoming.get("items", []):
  582. if "claim_id" in s:
  583. liveids.append(s["claim_id"])
  584. for livestream in all_lives.get("data", []):
  585. live_id = livestream.get("ActiveClaim", {}).get("ClaimID", "")
  586. liveids.append(live_id)
  587. # Then we gonna search all the liveids
  588. dynamic_box = win.tabs[win.notebook.get_current_page()]["box"]
  589. for i in dynamic_box.get_children():
  590. i.destroy()
  591. send = {"claim_ids":liveids}
  592. resolve = ui.load(win, claim_search.find, claim_search.render, win, "", [], 1, send)
  593. dynamic_box.pack_start(resolve, True, True, True)
  594. dynamic_box.show_all()
  595. lname = "Live Streams"
  596. win.tabs[win.notebook.get_current_page()]["label"].set_text(lname)
  597. win.tabs[win.notebook.get_current_page()]["label"].set_tooltip_text(lname)
  598. following = Gtk.Button()
  599. b = Gtk.HBox()
  600. b.pack_start(ui.icon(win, "emblem-new"), False, False, False)
  601. b.pack_start(Gtk.Label(" Live Streams "), True, True, False)
  602. following.add(b)
  603. following.set_relief(Gtk.ReliefStyle.NONE)
  604. following.connect("clicked", load_livestreams)
  605. second_raw.pack_start(following, False, False,False)
  606. second_raw.pack_start(Gtk.HSeparator(), False, False,5)
  607. ######## DOWNLOADS #########
  608. def load_downloads(w):
  609. hamburger.hide()
  610. downloads.window(win)
  611. following = Gtk.Button()
  612. b = Gtk.HBox()
  613. b.pack_start(ui.icon(win, "go-down-symbolic"), False, False, False)
  614. b.pack_start(Gtk.Label(" Downloads "), True, True, False)
  615. following.add(b)
  616. following.set_relief(Gtk.ReliefStyle.NONE)
  617. following.connect("clicked", load_downloads)
  618. second_raw.pack_start(following, False, False,False)
  619. second_raw.pack_start(Gtk.HSeparator(), False, False,5)
  620. ######## SETTINGS BUTTON ############
  621. second_raw.pack_start(Gtk.HSeparator(), False, False,5)
  622. settings_button = Gtk.Button()
  623. b = Gtk.HBox()
  624. b.pack_start(ui.icon(win, "preferences-system"), False, False, False)
  625. b.pack_start(Gtk.Label(" Settings "), True, True, False)
  626. settings_button.add(b)
  627. settings_button.set_relief(Gtk.ReliefStyle.NONE)
  628. settings_button.connect("clicked", settings.dialogue, win)
  629. second_raw.pack_start(settings_button, False, False, False)
  630. second_raw.pack_start(Gtk.HSeparator(), False, False,5)
  631. ########## FORCE SEARCH BUTTON #######
  632. force_search_box = Gtk.HBox()
  633. force_search = Gtk.Switch()
  634. force_search_box.pack_end(force_search, False, False, 0)
  635. force_search_box.pack_start(ui.icon(win, "system-search"), False, False, 0)
  636. force_search_box.pack_start(Gtk.Label(" Force Search "), True, True, 0)
  637. force_search.set_tooltip_text("If not activated it will try to resolve the claim first.")
  638. second_raw.pack_start(force_search_box, False, False, False)
  639. ###### THE BOX FOR THE NOTIFICATIONS #####
  640. win.notifications_box = Gtk.VBox()
  641. hambox.pack_start(Gtk.VSeparator(), False, False, 5)
  642. hambox.pack_start(win.notifications_box, False, False, False)
  643. hambox.show_all()
  644. ##########################################################################
  645. # #
  646. # DYNAMIC BOX ( REST ) #
  647. # #
  648. ##########################################################################
  649. # Dynamic box is a box contents of which will change depending on the "page"
  650. # that the user is loading.
  651. box = Gtk.VBox() # I'm making a top level box just in case
  652. win.add(box)
  653. win.notebook.set_scrollable(True)
  654. def new_tab():
  655. dynamic_box = Gtk.VBox() # And here our dynamic box
  656. label = Gtk.Label("Tab "+str(len(win.tabs)+1))
  657. kill = Gtk.Button()
  658. kill.add(ui.icon(win, "delete"))
  659. kill.set_relief(Gtk.ReliefStyle.NONE)
  660. box = Gtk.HBox()
  661. box.pack_start(label, False, False, 5)
  662. box.pack_start(kill, False, False, 0)
  663. box.show_all()
  664. tab = {"box":dynamic_box, "label":label, "index":len(win.tabs)}
  665. def on_kill(w, tab):
  666. if len(win.tabs) == 1:
  667. load_following(mode=win.settings["default_tab"])
  668. return
  669. i = tab["index"]
  670. win.notebook.remove_page(i)
  671. del win.tabs[i]
  672. for r, t in enumerate(win.tabs):
  673. t["index"] = r
  674. kill.connect("clicked", on_kill, tab)
  675. win.tabs.append(tab)
  676. win.resolve_tab = len(win.tabs) - 1
  677. win.notebook.append_page(win.tabs[-1]["box"], box)
  678. new_tab()
  679. box.pack_start(win.notebook, True, True, False)
  680. # Just something to run if we are connected
  681. if win.SDK_running == 1:
  682. load_following(mode=win.settings["default_tab"])
  683. load_channel(win)
  684. else:
  685. win.tabs[0]["box"].pack_start(Gtk.Label("LBRY is not connected. Please connect."), True, True, 20)
  686. ##########################################################################
  687. # #
  688. # DRAG AND DROP #
  689. # #
  690. ##########################################################################
  691. def on_drop(widget, drag_context, x, y, data, info, time):
  692. win.url.set_text(data.get_text())
  693. win.url.activate()
  694. enforce_target = Gtk.TargetEntry.new('text/plain', Gtk.TargetFlags(4), 129)
  695. box.drag_dest_set(Gtk.DestDefaults.ALL, [enforce_target], Gdk.DragAction.COPY)
  696. box.connect("drag-data-received", on_drop)
  697. if win.settings["autoconnect"] and win.SDK_running != 1:
  698. connect_disconnect_function(connect_disconncet_button)
  699. # Application CSS
  700. style_provider=Gtk.CssProvider()
  701. css=b"""
  702. .thumbnail_overlay {
  703. background-color: rgba(.5, .5, .5, .7);
  704. border-radius: 5px;
  705. padding: 5px;
  706. }
  707. """
  708. style_provider.load_from_data(css)
  709. Gtk.StyleContext.add_provider_for_screen(Gdk.Screen.get_default(), style_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
  710. # Starting Everything
  711. win.show_all()
  712. Gtk.main()
  713. #### Clearing the cache of images ###
  714. ui.clean_image_cache()
  715. if win.settings["autoconnect"]:
  716. connect.stop(win)