123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712 |
- #####################################################################
- # #
- # 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. #
- # #
- #####################################################################
- import os
- import time
- import json
- import random
- import threading
- import mimetypes
- import urllib.request
- from gi.repository import Gtk
- from gi.repository import Gdk
- from gi.repository import GLib
- from gi.repository import Pango
- from gi.repository import GdkPixbuf
- from flbry import markdown
- from flbry import ui
- from flbry import fetch
- from flbry import settings
- ##################################################################
- # This file touches publication to LBRY network. #
- ##################################################################
- def lbryname(name="", force=True):
- # This creates a random string of characters.
- good = "qwertyuiopasdfghjklzxcvbnm-_QWERTYUIOPASDFGHJKLZXCVBNM1234567890"
- if not name and force:
- for i in range(70):
- name = name + random.choice(good)
- return name
- # This removes all non-good characters from the name
-
- new_name = unicodedata.normalize('NFD', name).strip().casefold()
- # Patterns to keep out.
- # Hint from <lbry-sdk>/lbry/schema/url.py
- # - replaced "[^=" with "[="
- # - replaced 'FF]+"' with 'FF" \'
- # - added 'r"\s]+"' line
- bad_pattern = re.compile(
- r"[=&#:$@%?;\"/\\<>%{}|^~`\[\]" \
- r"\u0000-\u0020\uD800-\uDFFF\uFFFE-\uFFFF" \
- r"\s]+"
- )
- new_name = bad_pattern.sub("-", new_name)
- return new_name
- DEFAULT_DATA = {"name":"",
- "bid":0.001,
- "file_path":"",
- "title":"",
- "license":"",
- "license_url":"",
- "thumbnail_url":"",
- "channel_id":"",
- "channel_name":"",
- "description":"",
- "fee_amount":0,
- "tags":[]
- }
- LICENSES = [
- # NAME , URL , COMMENT
- ["GNU General Public License Version 3 (or later)",
- "https://www.gnu.org/licenses/gpl-3.0.html",
- "Strong Copyleft. Recommended for Software."],
- ["GNU General Public License Version 3 (only)",
- "https://www.gnu.org/licenses/gpl-3.0.html",
- "Strong Copyleft."],
- ["GNU Free Documentation License",
- "https://www.gnu.org/licenses/fdl-1.3.html",
- "Strong Copyleft. Recommended for books."],
- ["Creative Commons Attribution-ShareAlike 4.0 International",
- "https://creativecommons.org/licenses/by-sa/4.0/",
- "Copylefted, Recommended for Art."],
- ["Creative Commons Attribution 4.0 International",
- "https://creativecommons.org/licenses/by/4.0/",
- "Non Copylefted, Free License."],
- ["Creative Commons Zero 1.0 International",
- "https://creativecommons.org/publicdomain/zero/1.0/",
- "Public Domain"],
- ["Creative Commons Attribution-NoDerivatives 4.0 International",
- "https://creativecommons.org/licenses/by-nd/4.0/",
- "Does not allow changes. Recommended for opinion pieces."]
- ]
- def window(win, data=DEFAULT_DATA):
- # First, the data recieved could be not full.
- for i in DEFAULT_DATA:
- if i not in data:
- data[i] = DEFAULT_DATA[i]
- # Upload window
- dialogWindow = Gtk.Dialog("Publish",
- buttons=(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
- Gtk.STOCK_OK, Gtk.ResponseType.OK),
- )
- box = dialogWindow.get_content_area()
- #######################################################################
- # #
- # PRESETS #
- # #
- #######################################################################
- #making sure the folder exists
- try:
- os.mkdir(settings.get_settings_folder()+"presets")
- except:
- pass
-
- presets_list = []
- for i in os.listdir(settings.get_settings_folder()+"presets"):
- if i.endswith(".json"):
- presets_list.append(i.replace(".json", ""))
- def on_set_preset(w):
- # This might be a little long one, but to hell with it.
- ######################################################
- pn = presets_list[presets.get_active()]
-
- # Opening the preset's json.
-
- with open(settings.get_settings_folder()+"presets/"+pn+'.json') as f:
- pdata = json.load(f)
- # The data should be very similar. It was saved ( hopefully ) from
- # the same data as what we use to publish.
- exclude = ["name", "file_path", "tags"]
-
- for d in pdata:
- if d in exclude:
- continue
- data[d] = pdata[d]
-
-
- # Now let's update the UI itself
-
- # THIS SHOULD NOT RUN BEFORE THE UI IS FULLY LOADED !!!
- # BID
- bid_entry.set_value(data["bid"])
-
- # TITLE
- title.set_text(data["title"])
-
- # LICENSE
- for n, ch in enumerate(LICENSES):
- if data["license"] == ch[0]:
- licenses.set_active(n)
-
- # THUMBNAIL_URL
- thumbentry.set_text(data["thumbnail_url"])
- refresh_thumb()
-
- # CHANNEL_NAME
- try:
- for n, ch in enumerate(win.my_channels["items"]):
- if data["channel_name"] == ch["name"] or \
- data["channel_id"] == ch["claim_id"]:
- select = n+1
- channels_select.set_active(select)
- except:
- pass
-
- # DESCRIPTION
- detext.get_buffer().set_text(data["description"])
-
- # FEE_AMOUNT
- price_entry.set_value(data["fee_amount"])
-
- # TAGS
- # Tags ( because of the tags editor ) is a special case
- # and we need to do this, so not to create a separate id()
- # for the tags list. ( Tags editor is a separate function
- # that accesses the same list and edit's it.
- # First we need to remove all of the data.
- tags = data["tags"].copy()
- for t in tags:
- data["tags"].remove(t)
- # And now we want to add all of the tags one by one.
- for t in pdata["tags"]:
- data["tags"].append(t)
- # And finally we want to give commands obtained from the tags
- # object to update it visually.
- for i in tagsbox.get_children():
- i.destroy()
- for tag in data["tags"]:
- add_tag(tag)
-
-
- presbox = Gtk.HBox()
- box.pack_start(presbox, False, False, 0)
- presbox.pack_start(Gtk.Label(" Presets: "), False, False, 0)
-
- presets = Gtk.ComboBoxText()
- presets.connect("changed", on_set_preset)
- for n, ch in enumerate(presets_list):
- presets.append_text(ch)
- presbox.pack_start(presets, True, True, 0)
- #####################################################################
-
- presetsave = Gtk.Popover()
- addb = Gtk.MenuButton(popover=presetsave)
- addb.set_relief(Gtk.ReliefStyle.NONE)
- addb.add(ui.icon(win, "list-add"))
- presbox.pack_start(addb, False, False, 0)
- psavebox = Gtk.VBox()
- presetsave.add(psavebox)
- pname = Gtk.Entry()
- pname.set_text("name")
- psavebox.pack_start(pname, False, False, False)
- def save_preset(w):
- update_data() # Making sure all data is in 'data'
-
- pn = pname.get_text()
-
- # Write the json file
- with open(settings.get_settings_folder()+"presets/"+pn+'.json', 'w') as f:
- json.dump(data, f, indent=4, sort_keys=True)
- # Add the preset into presets
- if pn not in presets_list:
- presets.append_text(pn)
- presets_list.append(pn)
- # Select the current preset
- for n, ch in enumerate(presets_list):
- if ch == pn:
- presets.set_active(n)
-
- presets.grab_focus() # Closing the menu
- pname.set_text("name")
-
- psave = Gtk.Button("Save")
- psave.connect("clicked", save_preset)
- pname.connect("activate", save_preset)
- psave.set_relief(Gtk.ReliefStyle.NONE)
- psavebox.pack_start(psave, False, False, False)
- psavebox.show_all()
-
-
- #######################################################################
- # #
- # LBRY URL PART #
- # #
- #######################################################################
- box.pack_start(Gtk.Label(" Channel | LBRY url "), False, True, 5)
-
- urlbox = Gtk.HBox()
- box.pack_start(urlbox, False, False, 0)
- urlbox.pack_start(Gtk.Label(" lbry:// "), False, False, 0)
- # This will work separately from the main channel selector, though it
- # will be influenced by the main channel selector a bit.
- def on_channel_changed(w):
- ch = channels_select.get_active() - 1
- if ch == -1:
- data["channel_name"] = ""
- data["channel_id"] = ""
- else:
- data["channel_name"] = win.my_channels["items"][ch]["name"]
- data["channel_id"] = win.my_channels["items"][ch]["claim_id"]
-
- channels_select = Gtk.ComboBoxText()
- channels_select.connect("changed", on_channel_changed)
- # Let's get a list of channels to show
- channels = ["[anonymous]"]
- select = 0
- try: # could be no channel. So publication only anonymous
- for n, ch in enumerate(win.my_channels["items"]):
- channels.append(ch["name"]+":"+ch["claim_id"][0])
- if data["channel_name"] == ch["name"] or \
- data["channel_id"] == ch["claim_id"] or \
- win.channel["claim_id"] == ch["claim_id"]:
- select = n+1
-
- except Exception as e:
- print("DAMN!", e)
-
- for ch in channels:
- channels_select.append_text(ch)
- channels_select.set_active(select)
- urlbox.pack_start(channels_select, False, False, 0)
- urlbox.pack_start(Gtk.Label(" / "), False, False, 0)
- def on_url(w):
- url_field.set_text(lbryname(url_field.get_text(), force=False))
-
- url_field = Gtk.Entry()
- url_field.connect("changed", on_url)
- url_field.set_text(data["name"])
- urlbox.pack_start(url_field, True, True, 0)
-
- #######################################################################
- # #
- # THUMBNAIL #
- # #
- #######################################################################
- box.pack_start(Gtk.HSeparator(), False, False, 5)
-
- thumb_file_box = Gtk.HBox()
- box.pack_start(thumb_file_box, False, False, 0)
- ######################################################################
- thumbvbox = Gtk.VBox()
- thumb_file_box.pack_start(thumbvbox, False, False, 0)
- def refresh_thumb():
- if thumbentry.get_text():
- t = ui.load(win, ui.net_image_calculation, ui.net_image_render, thumbentry.get_text(), 300, 300, "FORCELOAD", True)
- for ch in thumbbox.get_children():
- ch.destroy()
- thumbbox.pack_start(t, True, True, 0)
- thumbbox.show_all()
-
- def on_thumb(w):
- ans = ui.select_file(data["thumbnail_url"], filter=["*jpg", "*png", "*webp", "*gif"])
- if ans:
- thumbentry.set_text(ans)
- refresh_thumb()
-
- thumb = Gtk.Button()
- thumb.connect("clicked", on_thumb)
- thumb.set_size_request(300,300)
- thumb.set_relief(Gtk.ReliefStyle.NONE)
- thumbbox = Gtk.HBox()
- thumbbox.pack_start(ui.icon(win, "image-x-generic"), False, True, 0)
- thumbbox.pack_start(Gtk.Label(" Thumbnail "), False, True, 0)
- thumb.add(thumbbox)
-
- thumbvbox.pack_start(thumb, False, False, 0)
- def on_toggle(w):
- thumbentry.set_visible(w.get_active())
- thumb.set_visible(not w.get_active())
- refresh_thumb()
-
- manually = Gtk.ToggleButton("Set thumbnail manually")
- manually.connect("clicked", on_toggle)
- manually.set_relief(Gtk.ReliefStyle.NONE)
- thumbvbox.pack_end(manually, False, False, 0)
- thumbentry = Gtk.Entry()
- thumbentry.set_text(data["thumbnail_url"])
- refresh_thumb()
- thumbvbox.pack_end(thumbentry, False, False, 0)
-
- thumb_file_box.pack_start(Gtk.VSeparator(), False, False, 5)
-
- #######################################################################
- # #
- # TITLE #
- # #
- #######################################################################
- filebox = Gtk.VBox()
- thumb_file_box.pack_start(filebox, True, True, 0)
- #######################################################################
-
- titlebox = Gtk.HBox()
- filebox.pack_start(titlebox, False, False, 5)
- titlebox.pack_start(Gtk.Label(" Title: "), False, False, False)
- def on_title(w):
- data["title"] = title.get_text()
-
- title = Gtk.Entry()
- title.set_text(data["title"])
- title.connect("changed", on_title)
- titlebox.pack_start(title, True, True, False)
-
- #######################################################################
- # #
- # FILE #
- # #
- #######################################################################
- def choose_file(filename=""):
-
- for ch in filebbox.get_children():
- ch.destroy()
- if filename:
- icon_is = mimetypes.guess_type(filename)[0].replace("/", "-")
- name_of_file = filename[filename.rfind("/")+1:]
-
- def clear_name(name):
- name = name[:name.rfind(".")]
- name = name.replace("_", " ")
- try:
- name = name[0].upper()+name[1:]
- except:
- pass
- return name
-
- ofilename = data["file_path"]
- if not title.get_text() or clear_name(ofilename[ofilename.rfind("/")+1:]) == title.get_text():
- title.set_text(clear_name(name_of_file))
- data["title"] = clear_name(name_of_file)
- if not url_field.get_text() or lbryname(clear_name(ofilename[ofilename.rfind("/")+1:])) == url_field.get_text():
- url_field.set_text(lbryname(title.get_text(), force=True))
- else:
- icon_is = "document-open"
- name_of_file = "Choose File"
- filebbox.pack_start(ui.icon(win, icon_is), False, True, 0)
- filebbox.pack_start(Gtk.Label(" "+name_of_file+" "), False, True, 0)
- filebbox.show_all()
- def on_filebutton(w):
- name = ui.select_file(data["file_path"])
- choose_file(name)
- data["file_path"] = name
-
- filebutton = Gtk.Button()
- filebutton.connect("clicked", on_filebutton)
- filebbox = Gtk.HBox()
- filebutton.add(filebbox)
- choose_file(data["file_path"])
- filebutton.set_relief(Gtk.ReliefStyle.NONE)
- filebox.pack_start(filebutton, False, False, 0)
-
- box.pack_start(Gtk.HSeparator(), False, False, 5)
-
- #######################################################################
- # #
- # BID #
- # #
- #######################################################################
- bid_adjust = Gtk.Adjustment(data["bid"],
- lower=0.0001,
- upper=1000000000,
- step_increment=0.1)
- bid_entry = Gtk.SpinButton(adjustment=bid_adjust,
- digits=4)
- bid_box = Gtk.HBox()
- filebox.pack_start(bid_box, False, False, 5)
- bid_box.pack_start(Gtk.Label(" Bid: "), False, False, 0)
- bid_box.pack_end(bid_entry, False, False, 0)
- #######################################################################
- # #
- # PRICE #
- # #
- #######################################################################
- price_adjust = Gtk.Adjustment(data["fee_amount"],
- lower=0,
- upper=1000000000,
- step_increment=0.1)
- price_entry = Gtk.SpinButton(adjustment=price_adjust,
- digits=4)
- price_box = Gtk.HBox()
- filebox.pack_start(price_box, False, False, 5)
- price_box.pack_start(Gtk.Label(" Price: "), False, False, 0)
- price_box.pack_end(price_entry, False, False, 0)
- #######################################################################
- # #
- # DESCRIPTION #
- # #
- #######################################################################
- filebox.pack_start(Gtk.Label(" Description: "), False, True, 5)
-
- descrl = Gtk.ScrolledWindow()
- detext = Gtk.TextView()
- detext.set_wrap_mode(Gtk.WrapMode.WORD)
- detext.get_buffer().set_text(data["description"])
- descrl.add(detext)
- filebox.pack_start(descrl, True, True, 0)
- #######################################################################
- # #
- # TAGS #
- # #
- #######################################################################
- box.pack_start(Gtk.Label(" Tags "), False, True, 5)
- tags_editor, tagsbox, add_tag = ui.tags_editor(win, data["tags"], return_edit_functions=True)
-
- box.pack_start(tags_editor, False, False, 0)
-
- #######################################################################
- # #
- # LICENSE #
- # #
- #######################################################################
- box.pack_start(Gtk.Label(" License "), False, True, 5)
- def on_license(w):
- l = licenses.get_active()
- data["license"] = LICENSES[l][0]
- data["license_url"] = LICENSES[l][1]
-
- licenses = Gtk.ComboBoxText()
- licenses.connect("changed", on_license)
-
- for n, ch in enumerate(LICENSES):
- licenses.append_text(ch[0])
- if data["license"] == ch[0]:
- licenses.set_active(n)
- box.pack_start(licenses, False, True, 0)
-
- #######################################################################
- # #
- # RUNING THE SETTINGS DIALOG #
- # #
- #######################################################################
-
-
- box.show_all()
- def update_data():
- # Not all widgets update data automatically. We need this function
- # whenever we want the up-to-date data.
- data["name"] = url_field.get_text()
- data["thumbnail_url"] = thumbentry.get_text()
- data["bid"] = bid_entry.get_value()
- data["fee_amount"] = price_entry.get_value()
- tb = detext.get_buffer()
- data["description"] = tb.get_text(tb.get_start_iter(), tb.get_end_iter(), True)
-
- # Hide all hidden
- thumbentry.set_visible(False)
-
- response = dialogWindow.run()
- if response == Gtk.ResponseType.OK:
-
- update_data()
-
- print("\n\n############## PUBLISH INPUT ###########\n\n{")
- for i in data:
- print(' "'+i+'" :', data[i])
- print("}\n")
- out = upload(data)
- print("\n\n############## PUBLISH OUTPUT ###########\n\n")
- print(out)
- try:
- ui.notify(win, "Published successfully to: ", out['outputs'][0]['permanent_url']+"\n\nConfirming...")
- except:
- try:
- ui.notify(win, "Error while publishing!!!", out["message"])
- except:
- ui.notify(win, "Error while publishing!!!", str(out))
- # It has to be DESTORYED
- dialogWindow.destroy()
- def upload(data):
- ###############################################################
- # PUBLISH ! PUBLISH ! PUBLISH ! PUBLISH ! PUBLISH ! PUBLISH ! #
- ###############################################################
- # The required datapoints
- fetch_data = {"name":data["name"],
- "bid":str(float(data["bid"])),
- "file_path":data["file_path"]}
-
- # Uploading the thumbnail maybe
- data["thumbnail_url"] = speech_upload(data["thumbnail_url"])
- for i in ["title", "license", "license_url", "thumbnail_url", "description", "channel_name", "fee_amount", "tags"]:
- if i in data:
- if data[i]:
- if type(data[i]) not in [float, int]:
- fetch_data[i] = data[i]
- else:
- fetch_data[i] = str(float(data[i]))
- if data["fee_amount"]:
- fetch_data["fee_currency"] = "LBC"
- out = fetch.lbrynet("publish", fetch_data)
- return out
- def speech_upload(file, name="", fee=0, speech=True):
- file = os.path.expanduser(file)
- if os.path.isfile(file):
- print("Uploading '"+file+"' to LBRY")
- if not name:
- rndname = ""
- else:
- rndname = name + "_"
- length = 70 - len(rndname)
- good = "qwertyuiopasdfghjklzxcvbnm-_QWERTYUIOPASDFGHJKLZXCVBNM1234567890"
- for i in range(length):
- rndname = rndname + random.choice(good)
- try:
- out = upload({"name":rndname,
- "bid":0.001,
- "file_path":file,
- "title":"",
- "license":"",
- "license_url":"",
- "thumbnail_url":"",
- "channel_id":"",
- "channel_name":"",
- "description":"",
- "fee_amount":fee,
- "tags":[]
- })
- if speech:
- return out['outputs'][0]['permanent_url'].replace("lbry://","https://spee.ch/")
- else:
- return out['outputs'][0]['permanent_url']
- except:
- return ""
-
- else:
- return file
- # Gets Librarian URL from LBRY URL
- def librarian_url(lbry_url, librarian_location):
- # Get rid of ending slash if it has one
- librarian_location = librarian_location.rstrip("/")
- return lbry_url.replace("lbry:/", librarian_location, 1).replace("#", ":", 2)
|