123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555 |
- #####################################################################
- # #
- # THIS IS A SOURCE CODE FILE FROM A PROGRAM TO INTERACT WITH THE #
- # LBRY PROTOCOL ( lbry.com ). IT WILL USE THE LBRY SDK ( lbrynet ) #
- # FROM THEIR REPOSITORY ( https://github.com/lbryio/lbry-sdk ) #
- # WHICH I GONNA PRESENT TO YOU AS A BINARY. SINCE I DID NOT DEVELOP #
- # IT AND I'M LAZY TO INTEGRATE IN A MORE SMART WAY. THE SOURCE CODE #
- # OF THE SDK IS AVAILABLE IN THE REPOSITORY MENTIONED ABOVE. #
- # #
- # ALL THE CODE IN THIS REPOSITORY INCLUDING THIS FILE IS #
- # (C) J.Y.Amihud and Other Contributors 2021. EXCEPT THE LBRY SDK. #
- # YOU CAN USE THIS FILE AND ANY OTHER FILE IN THIS REPOSITORY UNDER #
- # THE TERMS OF GNU GENERAL PUBLIC LICENSE VERSION 3 OR ANY LATER #
- # VERSION. TO FIND THE FULL TEXT OF THE LICENSE GO TO THE GNU.ORG #
- # WEBSITE AT ( https://www.gnu.org/licenses/gpl-3.0.html ). #
- # #
- # THE LBRY SDK IS UNFORTUNATELY UNDER THE MIT LICENSE. IF YOU ARE #
- # NOT INTENDING TO USE MY CODE AND JUST THE SDK. YOU CAN FIND IT ON #
- # THEIR OFFICIAL REPOSITORY ABOVE. THEIR LICENSE CHOICE DOES NOT #
- # SPREAD ONTO THIS PROJECT. DON'T GET A FALSE ASSUMPTION THAT SINCE #
- # THEY USE A PUSH-OVER LICENSE, I GONNA DO THE SAME. I'M NOT. #
- # #
- # THE LICENSE CHOSEN FOR THIS PROJECT WILL PROTECT THE 4 ESSENTIAL #
- # FREEDOMS OF THE USER FURTHER, BY NOT ALLOWING ANY WHO TO CHANGE #
- # THE LICENSE AT WILL. SO NO PROPRIETARY SOFTWARE DEVELOPER COULD #
- # TAKE THIS CODE AND MAKE THEIR USER-SUBJUGATING SOFTWARE FROM IT. #
- # #
- #####################################################################
- # Preparing the executable of lbrynet to be running. If we don't that
- # there will be a nasty error of permission being denied.
- import os
- os.system("chmod u+x flbry/lbrynet")
- # I'm not trying to make it pretty. I'm trying to make it snappy.
- # It's FastLBRY not SlowLBRY.
- import gi
- import threading
- gi.require_version('Gtk', '3.0')
- from gi.repository import Gtk
- from gi.repository import Gdk
- from gi.repository import GLib
- # It's gonna take a couple of things to make the button run
- from flbry import connect
- from flbry import parse
- from flbry import settings
- from flbry import url
- from flbry import ui
- from flbry import claim_search
- from flbry import fetch
- from flbry import publish
- settings.make_sure_file_exists()
- ##########################################################################
- # #
- # THE MAIN WINDOW OF FASTLBRY GTK #
- # #
- ##########################################################################
- win = Gtk.Window() # Make a GTK window object
- win.connect("destroy", Gtk.main_quit) # If you close the window, program quits
- win.set_title("FastLBRY GTK") # Title
- win.set_default_icon_from_file("icon.png") # Icon
- win.set_size_request(800, 900) # Minimum size
- ##########################################################################
- # #
- # GLOBAL VARIABLES HACK ( adding things to 'win' ) #
- # #
- ##########################################################################
- # This hack is very simple. Our main Gtk.Window() is a class to which we
- # can add variables later on. Like the example you see below.
- # This way, giving some function only 'win' and nothing else, will give
- # that other function the entire list of global variable ( added this way ).
- win.settings = settings.load() # Settings
- win.SDK_running = connect.check() # Is the SDK running ( from 0 to 1 to draw progress bar)
- win.resolved = {} # Last resolved urls
- win.commenting = {} # Data for while chatting in comments
- ##########################################################################
- # #
- # TOP PANNEL #
- # #
- ##########################################################################
- # Gtk.HeaderBar() is like a box but it draws itself in the headerbar insead
- # of in the window itself.
- pannel = Gtk.HeaderBar()
- pannel.set_show_close_button(True)
- win.set_titlebar(pannel) # Dumbasses wrote GTK
- # Untill the user connected to the SDK, search and other functions should
- # not work. So we split the pannel into two parts. The other one is a
- # normal box.
- restofpannel = Gtk.HBox() # H for Horrizontal
- pannel.pack_end(restofpannel)
- ############## CONNECT BUTTON ################
- # Connect button will also be a disconnect button
- # Icons for both
- icon_connect = ui.icon(win, "network-wired")
- icon_disconnect = ui.icon(win, "network-wired-disconnected")
- # Boxes for both
- connect_box = Gtk.HBox()
- disconnect_box = Gtk.HBox()
- # Connect box
- connect_box.pack_start(icon_connect, False, False, False)
- connect_box.set_tooltip_text("Connect to LBRY Network")
- connect_box.pack_start(Gtk.Label("Connect "), False, False, False)
- # Disconnect box
- disconnect_box.pack_start(icon_disconnect, False, False, False)
- disconnect_box.set_tooltip_text("Disconnect from LBRY Network")
- disconnect_box.pack_start(Gtk.Label("Disconnect "), False, False, False)
- # By whether SDK was running while the software was launched
- # we present the correct button to the user and set the rest-of-pannel
- # accordingly.
- if win.SDK_running:
- title = disconnect_box
- restofpannel.set_sensitive(True)
- else:
- title = connect_box
- restofpannel.set_sensitive(False)
- connect_disconncet_button = Gtk.Button()
- connect_disconncet_button.add(title)
- connect_disconncet_button.set_relief(Gtk.ReliefStyle.NONE)
- pannel.pack_start(connect_disconncet_button)#, False, False, False)
- ############### CONNECT / DISCONNECT BUTTONS FUNCTION ################
- # The button should do something.
- def connect_disconnect_function(w):
-
- w.set_sensitive(False)
- for i in dynamic_box.get_children():
- i.destroy()
- progress_connect = Gtk.ProgressBar(show_text=True)
- dynamic_box.pack_start(progress_connect, True, True, 30)
- win.show_all()
- def do_sdk(w, pb):
- wasrunning = win.SDK_running
- if win.SDK_running == 1:
- connect.stop()
- else:
- connect.start()
- def update_pb(pb):
- pb.set_fraction(win.SDK_running)
- pb.set_text("Connecting: "+str(int(round(win.SDK_running*100)))+"%")
-
- def update_buttons(w):
- for i in dynamic_box.get_children():
- i.destroy()
- w.get_child().destroy()
- if win.SDK_running == 1:
- w.add(disconnect_box)
- restofpannel.set_sensitive(True)
- else:
- w.add(connect_box)
- restofpannel.set_sensitive(False)
- w.show_all()
- w.set_sensitive(True)
- if win.SDK_running == 1:
- load_following()
- load_channel(win)
- ui.notify(win, "Connected to LBRY")
- if wasrunning == 0:
- while True:
- win.SDK_running = connect.check()
- if win.SDK_running == 1:
- break
- GLib.idle_add(update_pb, pb)
-
- GLib.idle_add(update_buttons, w)
-
- load_thread = threading.Thread(target=do_sdk, args=(w, progress_connect))
- load_thread.setDaemon(True)
- load_thread.start()
-
- connect_disconncet_button.connect("clicked", connect_disconnect_function)
- ##########################################################################
- # #
- # PUBLISH BUTTON #
- # #
- ##########################################################################
- restofpannel.pack_start(Gtk.VSeparator(), True, True, 20)
- publ = Gtk.Button()
- publ.set_tooltip_text("Publish to LBRY")
- publ.set_relief(Gtk.ReliefStyle.NONE)
- publ.add(ui.icon(win, "list-add"))
- restofpannel.pack_start(publ, False, False, False)
- def do_publish(w):
- publish.window(win)
- publ.connect("clicked", do_publish)
- restofpannel.pack_start(Gtk.VSeparator(), True, True, 20)
- ##########################################################################
- # #
- # SEARCH / URL BAR #
- # #
- ##########################################################################
- # Note that I add this directly into the 'win' to make it a global variable.
- # This let's me to start any url from anywhere in the software.
- win.url = Gtk.Entry() # Gtk.SearchEntry() also looks good, but nah
- win.url.set_size_request(400,40)
- restofpannel.pack_start(win.url, True, True, False)
- ####### SEARCH FUNCTION #######
- def search(w):
- # DRAG AND DROP FOR PUBLISH
- if win.url.get_text().startswith("file://") or os.path.exists(win.url.get_text()):
- # TODO: Some filenames include weird characters like %20 for spacebar and
- # stuff like that. We need to filter that out.
-
- filepath = win.url.get_text().replace("file://", "")
- publish.window(win, {"file_path":filepath})
- win.url.set_text("")
- return
- for i in dynamic_box.get_children():
- i.destroy()
- ## IF WE ARE NOT FORCING IT TO SEACH, IT WILL TRY TO RESOLVE THE LBRY URL FIRST ##
-
- if not force_search.get_active() or win.url.get_text().startswith("lbry://"):
- win.url.set_text(parse.bar(win.url.get_text()))
- resolve = ui.load(win, url.resolve, url.render_resolve, w, win, win.url.get_text())
- dynamic_box.pack_start(resolve, True, True, True)
- win.show_all()
- else:
- resolve = ui.load(win, claim_search.find, claim_search.render, win, win.url.get_text(), [], 1, {"order_by":""})
- dynamic_box.pack_start(resolve, True, True, True)
- win.show_all()
- # Button to activate seach for those who don't know that you can press Enter
- search_button = Gtk.Button()
- search_icon = ui.icon(win, "system-search")
- search_button.add(search_icon)
- search_button.set_relief(Gtk.ReliefStyle.NONE)
- search_button.connect("clicked", search)
- win.url.connect("activate", search)
- restofpannel.pack_start(search_button, False, False, False)
-
- ##########################################################################
- # #
- # HAMBURGER MENU #
- # #
- ##########################################################################
- # Popover is the new GTK menu thingy that looks like a comic book dialog
- # box. Which is what we want here. Because we want it to look nice I suppose.
- hamburger = Gtk.Popover()
- hambutton = Gtk.MenuButton(popover=hamburger)
- hambutton_icon = ui.icon(win, "system-users")
- hambutton.add(hambutton_icon)
- hambutton.set_relief(Gtk.ReliefStyle.NONE)
- pannel.pack_start(hambutton)
- # Let's now pack the hamburger menu with items
- hambox = Gtk.HBox()
- # For the elements inside the box we need a scrolled window, to fit a lot of
- # them there.
- hamscrl = Gtk.ScrolledWindow()
- hamscrl.set_size_request(200,200)
- hamburger.add(hambox)
- hambox.pack_start(hamscrl, False, False, False)
- # "hamchannelbox" will be empty untill the user connects and it loads his channel
- # list
- hamchannelbox = Gtk.VBox()
- hamscrl.add_with_viewport(hamchannelbox)
- #hambox.pack_start(hamchannelbox, False, False, False)
- ###### CHANNELS SELECTOR ########
- win.channel = False
- def load_channel(win):
- def change_channel(w, win, channel ):
- win.channel = channel
- win.settings["channel"] = win.channel["claim_id"]
- settings.save(win.settings)
- try:
- hambutton.get_child().destroy()
- try:
- newicon = ui.load(win, ui.net_image_calculation, ui.net_image_render, channel["value"]["thumbnail"]["url"], 40, "", False )
- except:
- newicon = ui.icon(win, "system-users")
- hambutton.add(newicon)
- pannel.show_all()
- except Exception as e:
- print("What?", e)
-
- out = fetch.lbrynet("channel_list")
- win.my_channels = out
- # We are chooseing first only if there is no channel set in the settings
-
-
- try:
- first = out["items"][0]
- win.channel = first
- except:
- pass
- if "channel" in win.settings:
- try:
- for i in out["items"]:
- if i["claim_id"] == win.settings["channel"]:
- first = i
- break
- except:
- pass
-
- try:
- change_channel(False, win, first)
- except:
- pass
- try:
-
-
- go.set_sensitive(True)
-
- for i in out["items"]:
- channel_button = ui.go_to_channel(win, i, resolve=False)
- channel_button.connect("clicked", change_channel, win, i)
- hamchannelbox.pack_start(channel_button, False, False, False)
- hamchannelbox.show_all()
- second_raw.show_all()
- except Exception as e:
- print("CHANNEL ERROR:", e)
- second_raw = Gtk.VBox()
- hambox.pack_start(Gtk.VSeparator(), False, False, 5)
- hambox.pack_start(second_raw, False, False, False)
-
- ####### Go to channel button #####
- def go_to_channel(w):
- channel_url = win.channel["name"]
- try:
- channel_url = channel_url + "#" + win.channel["claim_id"]
- except:
- pass
-
- win.url.set_text(channel_url)
- win.url.activate()
-
- go = Gtk.Button()
- b = Gtk.HBox()
- b.pack_start(ui.icon(win, "folder-remote"), False, False, False)
- b.pack_start(Gtk.Label(" Go To Channel "), True, True, False)
- go.add(b)
- go.set_sensitive(False)
- go.set_relief(Gtk.ReliefStyle.NONE)
- go.connect("clicked", go_to_channel)
- second_raw.pack_start(go, False, False, False)
-
- second_raw.pack_start(Gtk.HSeparator(), False, False,5)
- ######## FOLLOWING BUTTON ###########
- def load_following( w=False):
- for i in dynamic_box.get_children():
- i.destroy()
-
- try:
- out = fetch.lbrynet("preference_get")
- subs_raw = out["shared"]["value"]["subscriptions"]
- subs_raw = fetch.lbrynet("resolve", {"urls":subs_raw})
- subs = []
- for i in subs_raw:
- try:
- subs.append(subs_raw[i]["claim_id"])
- except:
- pass
- except Exception as e:
- print("\n\nERROR:", e, "\n\n")
- subs = []
-
-
- resolve = ui.load(win, claim_search.find, claim_search.render, win, "", subs)
- dynamic_box.pack_start(resolve, True, True, True)
- dynamic_box.show_all()
- following = Gtk.Button()
- b = Gtk.HBox()
- b.pack_start(ui.icon(win, "emblem-favorite"), False, False, False)
- b.pack_start(Gtk.Label(" Following "), True, True, False)
- following.add(b)
- following.set_relief(Gtk.ReliefStyle.NONE)
- following.connect("clicked", load_following)
- second_raw.pack_start(following, False, False,False)
- ######## TRENDING BUTTONS ###########
- def load_trending(w=False, articles=False):
- for i in dynamic_box.get_children():
- i.destroy()
-
- send = {"order_by":"trending_mixed"}
- if articles:
- send["media_types"] = ["text/markdown"]
- resolve = ui.load(win, claim_search.find, claim_search.render, win, "", [], 1, send)
- dynamic_box.pack_start(resolve, True, True, True)
- dynamic_box.show_all()
- following = Gtk.Button()
- b = Gtk.HBox()
- b.pack_start(ui.icon(win, "applications-internet"), False, False, False)
- b.pack_start(Gtk.Label(" Trending "), True, True, False)
- following.add(b)
- following.set_relief(Gtk.ReliefStyle.NONE)
- following.connect("clicked", load_trending)
- second_raw.pack_start(following, False, False,False)
- following = Gtk.Button()
- b = Gtk.HBox()
- b.pack_start(ui.icon(win, "text-x-generic"), False, False, False)
- b.pack_start(Gtk.Label(" Articles "), True, True, False)
- following.add(b)
- following.set_relief(Gtk.ReliefStyle.NONE)
- following.connect("clicked", load_trending, True)
- second_raw.pack_start(following, False, False,False)
- ######## SETTINGS BUTTON ############
- second_raw.pack_start(Gtk.HSeparator(), False, False,5)
- settings_button = Gtk.Button()
- b = Gtk.HBox()
- b.pack_start(ui.icon(win, "preferences-system"), False, False, False)
- b.pack_start(Gtk.Label(" Settings "), True, True, False)
- settings_button.add(b)
- settings_button.set_relief(Gtk.ReliefStyle.NONE)
- settings_button.connect("clicked", settings.dialogue, win)
- second_raw.pack_start(settings_button, False, False, False)
- second_raw.pack_start(Gtk.HSeparator(), False, False,5)
- ########## FORCE SEARCH BUTTON #######
- force_search_box = Gtk.HBox()
- force_search = Gtk.Switch()
- force_search_box.pack_end(force_search, False, False, 0)
- force_search_box.pack_start(ui.icon(win, "system-search"), False, False, 0)
- force_search_box.pack_start(Gtk.Label(" Force Search "), True, True, 0)
- force_search.set_tooltip_text("If not activated it will try to resolve the claim first.")
- second_raw.pack_start(force_search_box, False, False, False)
- hambox.show_all()
- ##########################################################################
- # #
- # DYNAMIC BOX ( REST ) #
- # #
- ##########################################################################
- # Dynamic box is a box contents of which will change depending on the "page"
- # that the user is loading.
- box = Gtk.VBox() # I'm making a top level box just in case
- win.add(box)
- dynamic_box = Gtk.VBox() # And here our dynamic box
- scrl = Gtk.ScrolledWindow()
- box.pack_start(dynamic_box, True, True, False)
- # Just something to run if we are connected
- if win.SDK_running == 1:
- load_following()
- load_channel(win)
- else:
- dynamic_box.pack_start(Gtk.Label("LBRY is not connected. Please connect."), True, True, 20)
- ##########################################################################
- # #
- # DRAG AND DROP #
- # #
- ##########################################################################
- def on_drop(widget, drag_context, x, y, data, info, time):
- win.url.set_text(data.get_text())
- win.url.activate()
-
- enforce_target = Gtk.TargetEntry.new('text/plain', Gtk.TargetFlags(4), 129)
- box.drag_dest_set(Gtk.DestDefaults.ALL, [enforce_target], Gdk.DragAction.COPY)
- box.connect("drag-data-received", on_drop)
-
- # Starting Everything
-
- win.show_all()
- Gtk.main()
- #### Clearing the cache of images ###
- ui.clean_image_cache()
|