Add files from 6a34355fda commit of FastLBRY-GTK notabug.org repo by jyamihud without the flbry/lbrynet binary
1062 lines
38 KiB
Python
1062 lines
38 KiB
Python
#####################################################################
|
|
# #
|
|
# 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 urllib.request
|
|
import threading
|
|
import json
|
|
|
|
from subprocess import *
|
|
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 PIL import Image, ImageSequence
|
|
|
|
from flbry import markdown
|
|
from flbry import data_view
|
|
from flbry import ui
|
|
from flbry import fetch
|
|
from flbry import claim_search
|
|
from flbry import comments
|
|
from flbry import follow
|
|
from flbry import publish
|
|
from flbry import analytics
|
|
from flbry import livestreams
|
|
from flbry import suggest
|
|
from flbry import odysee
|
|
from flbry import oscalls
|
|
|
|
def resolve(w, win, url):
|
|
|
|
# This function will draw a widget of resolved url
|
|
|
|
|
|
#####################################################
|
|
# RESOLVING PART ( NO GTK ) #
|
|
#####################################################
|
|
|
|
|
|
out = fetch.lbrynet("resolve",
|
|
{"urls":[url]}
|
|
)
|
|
out = out[url]
|
|
|
|
|
|
# Saving the resolved thing into the win for later use
|
|
win.resolved = out
|
|
|
|
# out = check_output(["flbry/lbrynet",
|
|
# "resolve", url])
|
|
# raw_data = out.decode("utf-8")
|
|
|
|
# # Now we want to parse the json
|
|
|
|
# try:
|
|
# out = json.loads(out)
|
|
# out = out[url]
|
|
# except:
|
|
# print("Resolve Failed")
|
|
# return False
|
|
try:
|
|
is_channel = "value_type" in out and out["value_type"] == "channel"
|
|
percentage = downloaded(out["claim_id"])
|
|
except:
|
|
# If this fails we activate search
|
|
return ["search", False, False, w, win, url, False]
|
|
|
|
# make sure the url path starts with lbry://
|
|
if not url.startswith("lbry://"):
|
|
url = "lbry://"+url
|
|
win.url.set_text(url)
|
|
|
|
if is_channel:
|
|
stream_data = livestreams.get_data(out["claim_id"])
|
|
else:
|
|
try:
|
|
stream_data = livestreams.get_data(out["signing_channel"]["claim_id"])
|
|
except:
|
|
stream_data = {}
|
|
|
|
# Aditional data
|
|
out["address_is_mine"] = fetch.lbrynet("address_is_mine", {"address":out["address"]})
|
|
out["supports_preview"] = fetch.lbrynet("support_abandon", {"claim_id":out["claim_id"],
|
|
"preview":True})
|
|
|
|
|
|
return [out, is_channel, percentage, w, win, url, stream_data]
|
|
|
|
def render_resolve(data):
|
|
|
|
out, is_channel, percentage, w, win, url, stream_data = data
|
|
|
|
|
|
# It could be failed
|
|
if out == "search":
|
|
return ui.load(win, claim_search.find, claim_search.render, win, win.url.get_text(), [], 1, {"order_by":""})
|
|
|
|
|
|
|
|
#####################################################
|
|
# DRAWING PART ( GTK ) #
|
|
#####################################################
|
|
try:
|
|
price = out["value"]["fee"]["amount"]
|
|
except:
|
|
price = 0
|
|
try:
|
|
currency = out["value"]["fee"]["currency"]
|
|
except:
|
|
currency = "LBC"
|
|
|
|
|
|
|
|
box = Gtk.HBox()
|
|
outbox = Gtk.VBox()
|
|
|
|
#### THUMBNAIL ###
|
|
|
|
thumb = ""
|
|
try:
|
|
thumb = out["value"]["thumbnail"]["url"]
|
|
thumb_url = ui.image_save_name(thumb)
|
|
def thumb_open(w):
|
|
oscalls.Open(thumb_url)
|
|
|
|
thumb_button = Gtk.Button()
|
|
thumb_button.set_tooltip_text(thumb)
|
|
if is_channel:
|
|
thumb_image = ui.load(win, ui.net_image_calculation, ui.net_image_render, thumb, 150, 150, "", True)
|
|
else:
|
|
thumb_image = ui.load(win, ui.net_image_calculation, ui.net_image_render, thumb, 400, 400, "", True)
|
|
thumb_button.add(thumb_image)
|
|
thumb_button.set_relief(Gtk.ReliefStyle.NONE)
|
|
thumb_button.connect("clicked", thumb_open)
|
|
|
|
box.pack_start(thumb_button, False, False, False)
|
|
except:
|
|
pass
|
|
|
|
outbox.pack_start(box, False, False, False)
|
|
|
|
from_right_to_thumbnail = Gtk.VBox()
|
|
box.pack_end(from_right_to_thumbnail, True, True, False)
|
|
|
|
# If channel load banner
|
|
if is_channel:
|
|
if "cover" in out["value"]:
|
|
|
|
# This is a hack to set the scroller at the center
|
|
# of the cover image
|
|
|
|
def render_cover(calc):
|
|
|
|
# So this fucntion pretends to be the net_image_render
|
|
|
|
r = ui.net_image_render(calc) # It does call it though
|
|
|
|
# But we need the size of the image and we need to do
|
|
# something after the image is loaded.
|
|
|
|
def wait():
|
|
|
|
# This will execute after a delay
|
|
|
|
v = channel_scroll.get_vadjustment()
|
|
h = channel_scroll.get_hadjustment()
|
|
|
|
# TODO: Check that the math is correct. It seems
|
|
# to move the scrolls a bit too far.
|
|
v.set_value( r.get_pixbuf().get_height() / 2 - 75)
|
|
h.set_value( r.get_pixbuf().get_width() / 2 - 100)
|
|
|
|
def start_waiting():
|
|
|
|
# This is the delay thread
|
|
|
|
time.sleep(0.1)
|
|
GLib.idle_add(wait) # Making sure to use GLib so not
|
|
# to get Segmentation Fault
|
|
|
|
load_thread = threading.Thread(target=start_waiting)
|
|
load_thread.start()
|
|
|
|
# And we need to give the renderer the image itself, so it
|
|
# could be added into the UI.
|
|
return r # This happens before we scroll to the center
|
|
|
|
# So think about it:
|
|
# 1. ui.load loads the ui.net_image_calclation which downloads the cover
|
|
# 2. This function runs. Activating ui.net_image_render getting the Gtk.Image
|
|
# 3. We setup the thread that waits for 0.1 seconds
|
|
# 4. Meanwhile ui.load recieves the ui.image and window updates with it
|
|
# 5. 0.1 seconds later: we calculate the width and height of the image
|
|
# and set the scroll adjustments accordilgly.
|
|
|
|
# Simple isn't it?
|
|
|
|
channel_banner = ui.load(win, ui.net_image_calculation, render_cover, out["value"]["cover"]["url"], False, False, "", True)
|
|
channel_scroll = Gtk.ScrolledWindow()
|
|
channel_scroll.set_size_request(400,150)
|
|
channel_scroll.add_with_viewport(channel_banner)
|
|
from_right_to_thumbnail.pack_start(channel_scroll, False, False, False)
|
|
else:
|
|
try:
|
|
channel_scroll.destroy()
|
|
except:
|
|
pass
|
|
|
|
|
|
#### NAME / CHANNEL ####
|
|
|
|
the_packing_box = from_right_to_thumbnail
|
|
if is_channel:
|
|
the_packing_box = outbox
|
|
|
|
name_channel_box = Gtk.VBox()
|
|
the_packing_box.pack_start(name_channel_box, False, False, False)
|
|
|
|
# Channel
|
|
|
|
if "signing_channel" in out:
|
|
chbox = Gtk.HBox()
|
|
name_channel_box.pack_start(chbox, False, False, 0)
|
|
chbox.pack_start(ui.go_to_channel(win, out["signing_channel"]),True,True,False)
|
|
|
|
fbutton = follow.button(win, out["signing_channel"]["permanent_url"])
|
|
chbox.pack_start(fbutton, False, False, 0 )
|
|
|
|
elif is_channel:
|
|
fbutton = follow.button(win, out["permanent_url"])
|
|
name_channel_box.pack_start(fbutton, False, False, False)
|
|
|
|
# name it self
|
|
title = out["name"]
|
|
try:
|
|
title = out["value"]["title"]
|
|
except:
|
|
pass
|
|
title_label = Gtk.Label()
|
|
title_label.set_line_wrap_mode( Gtk.WrapMode.WORD )
|
|
title_label.set_line_wrap(True)
|
|
title_label.set_markup('<span size="x-large"> '+title+'</span> ')
|
|
title_label.set_selectable(True)
|
|
title_label.set_css_name("")
|
|
name_channel_box.pack_start(title_label, False, False, False)
|
|
|
|
################# TOOL BAR ##################
|
|
|
|
print("streamdata ++++++++++++++++++++++++++++++++++++ ", stream_data)
|
|
|
|
toolbox = Gtk.HBox()
|
|
the_packing_box.pack_start(toolbox, False, False,False)
|
|
|
|
# The channel might be live from a different publication. I want to give a button to resolve it.
|
|
active_id = stream_data.get("data",{}).get("ActiveClaim", {}).get("ClaimID", "")
|
|
if stream_data.get("data",{}).get("Live", False) and active_id != out["claim_id"]:
|
|
|
|
live_elsewhere_box = Gtk.HBox()
|
|
the_packing_box.pack_start(Gtk.HSeparator(), False, False, 5)
|
|
the_packing_box.pack_start(live_elsewhere_box, False, False,False)
|
|
|
|
live_elsewhere_box.pack_start(Gtk.VSeparator(), 0,0,10)
|
|
live_elsewhere_box.pack_start(ui.icon(win, "dialog-warning"), 0,0,0)
|
|
|
|
live_elsewhere_box.pack_start(Gtk.Label(" Currently Streaming! "), 0,0,0)
|
|
|
|
def go_to_livestream(widget):
|
|
resolve_url = fetch.lbrynet("claim_search", {"claim_id":active_id}).get("items", [{}])[0].get("canonical_url","")
|
|
win.resolve_tab = "new_tab"
|
|
win.url.set_text(resolve_url)
|
|
win.url.activate()
|
|
|
|
go_button = Gtk.Button()
|
|
go_button.set_relief(Gtk.ReliefStyle.NONE)
|
|
go_box = Gtk.HBox()
|
|
go_button.add(go_box)
|
|
go_box.pack_start(ui.icon(win, "go-jump"), 0,0,0)
|
|
go_box.pack_start(Gtk.Label(" Go To Livestream "),0,0,0)
|
|
go_button.connect("clicked", go_to_livestream)
|
|
live_elsewhere_box.pack_start(go_button, 0,0,0)
|
|
|
|
|
|
if not is_channel:
|
|
|
|
# LIVESTREAM LAUNCH
|
|
if stream_data.get("data",{}).get("Live", False) and active_id == out["claim_id"]:
|
|
def live_launch_action(w):
|
|
suggest.record_tags_to_suggestions(out.get("value", {}).get("tags", []))
|
|
Popen([win.settings["live_stream_player"], win.settings["librarian_instance"]+"/live/content/"+out["signing_channel"]["claim_id"]+"/master.m3u8"])
|
|
|
|
live_launch_button = Gtk.Button()
|
|
live_launch_button.connect("clicked", live_launch_action)
|
|
live_launch_button.set_relief(Gtk.ReliefStyle.NONE)
|
|
live_launch_box = Gtk.HBox()
|
|
live_launch_button.add(live_launch_box)
|
|
live_launch_icon = ui.icon(win, "media-playback-start")
|
|
live_launch_box.pack_start(live_launch_icon, False, False, False)
|
|
live_launch_box.pack_start(Gtk.Label(" Watch Livestream "), False, False, False)
|
|
toolbox.pack_start(live_launch_button, False,False,False)
|
|
else:
|
|
|
|
|
|
def download_action(w):
|
|
suggest.record_tags_to_suggestions(out.get("value", {}).get("tags", []))
|
|
start_downloading(url)
|
|
|
|
download_button = Gtk.Button()
|
|
download_button.connect("clicked", download_action)
|
|
download_button.set_relief(Gtk.ReliefStyle.NONE)
|
|
download_box = Gtk.HBox()
|
|
download_button.add(download_box)
|
|
download_icon = ui.icon(win, "go-down")
|
|
download_box.pack_start(download_icon, False, False, False)
|
|
#filesize
|
|
labeltext = " Download"
|
|
if price:
|
|
labeltext = " Buy for "+str(price)+" "+currency
|
|
try:
|
|
filesize = out["value"]["source"]["size"]
|
|
labeltext = labeltext + " ("+csize(filesize)+")"
|
|
except:
|
|
filesize = 0
|
|
|
|
download_box.pack_start(Gtk.Label(labeltext), False, False, False)
|
|
toolbox.pack_start(download_button, False,False,False)
|
|
|
|
download_bar = Gtk.ProgressBar()
|
|
the_packing_box.pack_start(download_bar, False, False,False)
|
|
|
|
def delete_action(w):
|
|
filename = get_downloaded_file(out["claim_id"])
|
|
delete_file(out["claim_id"])
|
|
|
|
try:
|
|
os.remove(filename)
|
|
except:
|
|
pass
|
|
|
|
delete_button = Gtk.Button()
|
|
delete_button.connect("clicked", delete_action)
|
|
delete_button.set_relief(Gtk.ReliefStyle.NONE)
|
|
delete_box = Gtk.HBox()
|
|
delete_button.add(delete_box)
|
|
delete_icon = ui.icon(win,"edit-delete")
|
|
delete_box.pack_start(delete_icon, False, False, False)
|
|
delete_box.pack_start(Gtk.Label(" Delete "), False, False, False)
|
|
toolbox.pack_start(delete_button, False,False,False)
|
|
|
|
def launch_action(w):
|
|
oscalls.Open(get_downloaded_file(out["claim_id"]))
|
|
|
|
launch_button = Gtk.Button()
|
|
launch_button.connect("clicked", launch_action)
|
|
launch_button.set_relief(Gtk.ReliefStyle.NONE)
|
|
launch_box = Gtk.HBox()
|
|
launch_button.add(launch_box)
|
|
launch_icon = ui.icon(win, "media-playback-start")
|
|
launch_box.pack_start(launch_icon, False, False, False)
|
|
launch_box.pack_start(Gtk.Label(" Launch "), False, False, False)
|
|
toolbox.pack_start(launch_button, False,False,False)
|
|
|
|
|
|
|
|
|
|
win.download_buttons[out["claim_id"]] = True
|
|
|
|
t = threading.Thread(target=downloading_check_thread, args=(win, out["claim_id"],download_button, delete_button, launch_button, download_bar))
|
|
t.setDaemon(True)
|
|
t.start()
|
|
def kill_daemon(w):
|
|
win.download_buttons[out["claim_id"]] = False
|
|
download_button.connect("destroy", kill_daemon)
|
|
|
|
#################### REPOST BUTTON #######################
|
|
|
|
try:
|
|
|
|
toolbox.pack_start(Gtk.HSeparator(), False,False,5)
|
|
|
|
def repost_action(w):
|
|
# If name is empty, show message and let user input a new
|
|
# value without hiding the popup
|
|
if repost_name_entry.get_text() == "":
|
|
ui.simple_message_box("Name is empty",
|
|
"Name cannot be empty. Please enter something and try again.")
|
|
return
|
|
print("name", repost_name_entry.get_text())
|
|
print("bid", repost_bid_entry.get_value())
|
|
print("channel", win.channel["name"])
|
|
print("channel_id", win.channel["claim_id"])
|
|
|
|
repost_out = fetch.lbrynet("stream_repost",
|
|
{"name":repost_name_entry.get_text(),
|
|
"bid":str(float(repost_bid_entry.get_value())),
|
|
"claim_id":out["claim_id"],
|
|
"channel_id":win.channel["claim_id"]})
|
|
print(repost_out)
|
|
if "error" in repost_out:
|
|
ui.notify(win, "Error while reposting", str(repost_out["error"]))
|
|
else:
|
|
ui.notify(win, "Reposted succesfully.", "lbry://"+win.channel["name"]+"/"+repost_name_entry.get_text())
|
|
|
|
repost_menu.hide()
|
|
|
|
repost_menu = Gtk.Popover()
|
|
# Make the re-post popup show at the bottom of the button
|
|
repost_menu.set_position(Gtk.PositionType.BOTTOM)
|
|
repost_menu_box = Gtk.VBox()
|
|
repost_menu.add(repost_menu_box)
|
|
|
|
# Repost requires 3 entries of data:
|
|
|
|
# Name:
|
|
def on_url(w):
|
|
w.set_text(publish.lbryname(w.get_text(), force=False))
|
|
repost_name_box = Gtk.HBox()
|
|
repost_menu_box.pack_start(repost_name_box, 0,0,0)
|
|
channel_name = win.channel.get("name", "")
|
|
repost_name_box_label = Gtk.Label(" lbry://")
|
|
repost_name_box.pack_start(repost_name_box_label, 0,0,0)
|
|
repost_name_entry = Gtk.Entry()
|
|
repost_name_entry.connect("changed", on_url)
|
|
repost_name_box.pack_start(repost_name_entry, 1,1,0)
|
|
|
|
# Bid:
|
|
repost_bid_box = Gtk.HBox()
|
|
repost_menu_box.pack_start(repost_bid_box, 0,0,0)
|
|
|
|
bid_adjust = Gtk.Adjustment(0.01,
|
|
lower=0.0001,
|
|
upper=1000000000,
|
|
step_increment=0.1)
|
|
repost_bid_entry = Gtk.SpinButton(adjustment=bid_adjust,
|
|
digits=4)
|
|
|
|
repost_bid_box.pack_start(Gtk.Label(" Bid: "), False, False, 0)
|
|
repost_bid_box.pack_end(repost_bid_entry, False, False, 0)
|
|
|
|
# Repost button
|
|
do_repost_button = Gtk.Button()
|
|
do_repost_button.connect("clicked", repost_action)
|
|
do_repost_button.set_relief(Gtk.ReliefStyle.NONE)
|
|
do_repost_box = Gtk.HBox()
|
|
do_repost_box.pack_start(ui.icon(win, "media-playlist-repeat"), 0,0,0)
|
|
do_repost_box.pack_start(Gtk.Label(" Do Re-Post "), False, False, False)
|
|
do_repost_button.add(do_repost_box)
|
|
repost_menu_box.pack_start(do_repost_button, 0,0,0)
|
|
|
|
|
|
repost_menu_box.show_all()
|
|
|
|
repost_button = Gtk.Button()
|
|
repost_menu.set_relative_to(repost_button)
|
|
|
|
repost_button.set_relief(Gtk.ReliefStyle.NONE)
|
|
repost_box = Gtk.HBox()
|
|
repost_button.add(repost_box)
|
|
repost_icon = ui.icon(win, "media-playlist-repeat")
|
|
repost_box.pack_start(repost_icon, False, False, False)
|
|
try:
|
|
reposted_times = out["meta"]["reposted"]
|
|
if not reposted_times:
|
|
1/0 # kill switch to go straig to except LOL. I'm hacking. What
|
|
# do you want from me.
|
|
repost_label = " Re-Post ( "+str(reposted_times)+" ) "
|
|
except:
|
|
repost_label = " Re-Post "
|
|
repost_box.pack_start(Gtk.Label(repost_label), False, False, False)
|
|
def repost_button_action(button, event):
|
|
# If:
|
|
# - mouse clicked or
|
|
# - key pressed
|
|
# - either space (32)
|
|
# - or enter (65293)
|
|
if isinstance(event, Gdk.EventButton) or ( isinstance(event, Gdk.EventKey) and (event.keyval == 32 or event.keyval == 65293) ):
|
|
channel_name = win.channel.get("name", "")
|
|
# Show current channel name on label (not editable by user)
|
|
repost_name_box_label.set_text(" lbry://"+channel_name+"/")
|
|
# Show name of Re-Post (editable by user)
|
|
repost_name_entry.set_text(out["normalized_name"])
|
|
# Show Re-Post popup
|
|
repost_menu.popup()
|
|
# Capture mouse events on Re-Post button
|
|
repost_button.connect("button-release-event", repost_button_action)
|
|
# Capture key presses on Re-Post button (enter, space)
|
|
repost_button.connect("key-release-event", repost_button_action)
|
|
toolbox.pack_start(repost_button, False,False,False)
|
|
|
|
except Exception as e:
|
|
print("Re-Post error: ", e)
|
|
|
|
############# SUPPORT BUTTON ############
|
|
|
|
support_menu = Gtk.Popover()
|
|
support_menu_box = Gtk.VBox()
|
|
support_menu.add(support_menu_box)
|
|
|
|
|
|
# Bid:
|
|
support_bid_box = Gtk.HBox()
|
|
support_menu_box.pack_start(support_bid_box, 0,0,0)
|
|
|
|
support_bid_adjust = Gtk.Adjustment(0.01,
|
|
lower=0.0001,
|
|
upper=1000000000,
|
|
step_increment=0.1)
|
|
support_bid_entry = Gtk.SpinButton(adjustment=support_bid_adjust,
|
|
digits=4)
|
|
|
|
support_bid_box.pack_start(Gtk.Label(" Amount: "), False, False, 0)
|
|
support_bid_box.pack_end(support_bid_entry, False, False, 0)
|
|
|
|
# Do button
|
|
def support_action(w):
|
|
print("support amount", support_bid_entry.get_value())
|
|
print("channel", win.channel["name"])
|
|
print("channel_id", win.channel["claim_id"])
|
|
|
|
support_out = fetch.lbrynet("support_create",
|
|
{"amount":str(float(support_bid_entry.get_value())),
|
|
"claim_id":out["claim_id"],
|
|
"channel_id":win.channel["claim_id"],
|
|
"tip":True})
|
|
print(support_out)
|
|
if "error" in support_out:
|
|
ui.notify(win, "Error while reposting", str(support_out["error"]))
|
|
else:
|
|
ui.notify(win, "Supported succesfully.")
|
|
|
|
support_menu.hide()
|
|
|
|
|
|
do_support_button = Gtk.Button()
|
|
do_support_button.connect("clicked", support_action)
|
|
do_support_button.set_relief(Gtk.ReliefStyle.NONE)
|
|
do_support_box = Gtk.HBox()
|
|
do_support_box.pack_start(ui.icon(win, "emblem-favorite"), 0,0,0)
|
|
do_support_box.pack_start(Gtk.Label(" Do Support "), False, False, False)
|
|
do_support_button.add(do_support_box)
|
|
support_menu_box.pack_start(do_support_button, 0,0,0)
|
|
|
|
support_menu_box.show_all()
|
|
|
|
support_button = Gtk.MenuButton(popover=support_menu)
|
|
|
|
support_button.set_relief(Gtk.ReliefStyle.NONE)
|
|
support_box = Gtk.HBox()
|
|
support_button.add(support_box)
|
|
support_icon = ui.icon(win, "emblem-favorite")
|
|
support_box.pack_start(support_icon, False, False, False)
|
|
try:
|
|
rounded = round(float(out["meta"]["effective_amount"]), 2)
|
|
if not rounded:
|
|
rounded = out["meta"]["effective_amount"]
|
|
support_label = " Support ( "+str(rounded)+" ) "
|
|
except Exception as e:
|
|
print(e)
|
|
support_label = " Support "
|
|
support_box.pack_start(Gtk.Label(support_label), False, False, False)
|
|
toolbox.pack_start(support_button, False,False,False)
|
|
|
|
|
|
############# ADITIONAL MENU #############
|
|
|
|
|
|
aditional_menu = Gtk.Popover()
|
|
aditional_menu_box = Gtk.VBox()
|
|
aditional_menu.add(aditional_menu_box)
|
|
|
|
aditional_menu_button = Gtk.MenuButton(popover=aditional_menu)
|
|
aditional_menu_button.add(ui.icon(win, "view-more"))
|
|
aditional_menu_button.set_relief(Gtk.ReliefStyle.NONE)
|
|
toolbox.pack_end(aditional_menu_button, False,False,False)
|
|
|
|
# If the publication is mine.
|
|
|
|
|
|
## Unlock Tips ##
|
|
unlock_menu = Gtk.Popover()
|
|
unlock_menu_box = Gtk.VBox()
|
|
unlock_menu.add(unlock_menu_box)
|
|
|
|
unlock_button = Gtk.MenuButton(popover=unlock_menu)
|
|
unlock_button.set_relief(Gtk.ReliefStyle.NONE)
|
|
unlock_bbox = Gtk.HBox()
|
|
unlock_button.add(unlock_bbox)
|
|
unlock_bbox.pack_start(ui.icon(win, "insert-object-symbolic"), 0,0,0)
|
|
unlock_bbox.pack_start(Gtk.Label("Unlock Tips"), 0,0,5)
|
|
aditional_menu_box.pack_start(unlock_button, 0,0,5)
|
|
|
|
unlockable = float(out["supports_preview"].get("total_output", 0))
|
|
unlock_menu_box.pack_start(Gtk.Label(" You can unlock up to: "+str(unlockable)+" LBC "), 0,0,5)
|
|
|
|
unlock_counter_box = Gtk.HBox()
|
|
unlock_menu_box.pack_start(unlock_counter_box, 0,0,5)
|
|
|
|
unlock_amount_adjust = Gtk.Adjustment(0.01,
|
|
lower=0,
|
|
upper=unlockable,
|
|
step_increment=0.1)
|
|
unlock_amount_entry = Gtk.SpinButton(adjustment=unlock_amount_adjust,
|
|
digits=4)
|
|
unlock_counter_box.pack_start(Gtk.Label(" Unlock:"), 0,0,5)
|
|
unlock_counter_box.pack_end(unlock_amount_entry, 0,0,5)
|
|
|
|
def do_unlock_action(w):
|
|
result = fetch.lbrynet("support_abandon", {"claim_id":out["claim_id"],
|
|
"keep": str(unlockable - unlock_amount_entry.get_value())})
|
|
print("\n\nUNLOCKING RESULT:\n\n", result, "\n\n")
|
|
unlock_menu.hide()
|
|
aditional_menu.hide()
|
|
|
|
do_unlock_button = Gtk.Button()
|
|
do_unlock_button.set_relief(Gtk.ReliefStyle.NONE)
|
|
do_unlock_button.connect("clicked", do_unlock_action)
|
|
do_unlock_box = Gtk.HBox()
|
|
do_unlock_button.add(do_unlock_box)
|
|
do_unlock_box.pack_start(ui.icon(win, "insert-object-symbolic"), 0,0,0)
|
|
do_unlock_box.pack_start(Gtk.Label(" Do Unlock "), 0,0,0)
|
|
unlock_menu_box.pack_start(do_unlock_button, 0,0,5)
|
|
|
|
|
|
unlock_menu_box.show_all()
|
|
|
|
|
|
|
|
## Share ##
|
|
share_menu = Gtk.Popover()
|
|
share_menu_box = Gtk.VBox()
|
|
share_menu_box.set_size_request(600, -1)
|
|
share_menu.add(share_menu_box)
|
|
|
|
share_button = Gtk.MenuButton(popover=share_menu)
|
|
share_button.set_relief(Gtk.ReliefStyle.NONE)
|
|
share_bbox = Gtk.HBox()
|
|
share_button.add(share_bbox)
|
|
share_bbox.pack_start(ui.icon(win, "emblem-shared-symbolic"), 0,0,0)
|
|
share_bbox.pack_start(Gtk.Label("Share"), 0,0,5)
|
|
aditional_menu_box.pack_start(share_button, 0,0,5)
|
|
|
|
# LBRY share #
|
|
|
|
def do_share_lbry_copy(w):
|
|
share_lbry_entry.grab_focus()
|
|
clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
|
|
clipboard.set_text(share_lbry_entry.get_text(), -1)
|
|
clipboard.store()
|
|
|
|
share_lbry_box = Gtk.HBox()
|
|
share_menu_box.pack_start(share_lbry_box, 0,0,5)
|
|
share_lbry_entry = Gtk.Entry(text=url)
|
|
share_lbry_entry.set_editable(False)
|
|
share_lbry_box.pack_start(Gtk.Label(" LBRY:"), 0,0,5)
|
|
share_lbry_box.pack_start(share_lbry_entry, 1,1,5)
|
|
share_lbry_copy_button = Gtk.Button()
|
|
share_lbry_copy_button.connect("clicked", do_share_lbry_copy)
|
|
share_lbry_copy_button.add(ui.icon(win, "edit-copy"))
|
|
share_lbry_copy_button.set_relief(Gtk.ReliefStyle.NONE)
|
|
share_lbry_box.pack_end(share_lbry_copy_button, 0,0,5)
|
|
|
|
# Librarian share #
|
|
|
|
def do_share_librarian_copy(w):
|
|
share_librarian_entry.grab_focus()
|
|
clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
|
|
clipboard.set_text(share_librarian_entry.get_text(), -1)
|
|
clipboard.store()
|
|
|
|
share_librarian_box = Gtk.HBox()
|
|
share_menu_box.pack_start(share_librarian_box, 0,0,5)
|
|
share_librarian_entry = Gtk.Entry(text=publish.librarian_url(url, win.settings["librarian_instance"]))
|
|
share_librarian_entry.set_editable(False)
|
|
share_librarian_box.pack_start(Gtk.Label(" Librarian:"), 0,0,5)
|
|
share_librarian_box.pack_start(share_librarian_entry, 1,1,5)
|
|
share_librarian_copy_button = Gtk.Button()
|
|
share_librarian_copy_button.connect("clicked", do_share_librarian_copy)
|
|
share_librarian_copy_button.add(ui.icon(win, "edit-copy"))
|
|
share_librarian_copy_button.set_relief(Gtk.ReliefStyle.NONE)
|
|
share_librarian_box.pack_end(share_librarian_copy_button, 0,0,5)
|
|
|
|
|
|
|
|
share_menu_box.show_all()
|
|
|
|
|
|
|
|
|
|
|
|
aditional_menu_box.show_all()
|
|
|
|
|
|
########### BOTTOM NOTEBOOK ##############
|
|
|
|
notebook = Gtk.Notebook()
|
|
notebook.set_scrollable(True)
|
|
outbox.pack_start(notebook, True, True,False)
|
|
|
|
|
|
|
|
|
|
|
|
# If article read article
|
|
try:
|
|
if out["value"]["source"]["media_type"] == "text/markdown" and price == 0:
|
|
|
|
suggest.record_tags_to_suggestions(out.get("value", {}).get("tags", []))
|
|
|
|
# We download it first
|
|
|
|
playout = fetch.lbrynet("get", {"uri":url, "save_file":True})
|
|
|
|
# playout = check_output(["flbry/lbrynet",
|
|
# "get", url])
|
|
# # Parsing the Json
|
|
# playout = json.loads(playout)
|
|
|
|
md_text = open(playout['download_path'])
|
|
|
|
md_text = md_text.read()
|
|
|
|
|
|
|
|
|
|
# Markdown covenreted
|
|
md_scrl = Gtk.ScrolledWindow()
|
|
md_view = Gtk.TextView()
|
|
md_view.set_wrap_mode(Gtk.WrapMode.WORD )
|
|
md_buffer = md_view.get_buffer()
|
|
md_view.set_editable(False)
|
|
md_scrl.add(md_view)
|
|
md_buffer.set_text(md_text)
|
|
markdown.convert(win, md_view)
|
|
detailsbox = Gtk.HBox()
|
|
detailsbox.pack_start(ui.icon(win, "text-x-generic"), False, False, False)
|
|
detailsbox.pack_start(Gtk.Label(" Read Article "), True, True, True)
|
|
detailsbox.show_all()
|
|
notebook.append_page(md_scrl, detailsbox)
|
|
|
|
# Markdown source
|
|
md_scrl = Gtk.ScrolledWindow()
|
|
md_view = Gtk.TextView()
|
|
#md_view.override_background_color(Gtk.StateType.NORMAL, Gdk.RGBA(0.2,0.2,0.2, 1))
|
|
#md_view.override_color(Gtk.StateType.NORMAL, Gdk.RGBA(0.9,0.9,0.9, 1))
|
|
md_view.override_font(Pango.FontDescription("Monospace"))
|
|
md_view.set_wrap_mode(Gtk.WrapMode.WORD )
|
|
md_buffer = md_view.get_buffer()
|
|
md_view.set_editable(False)
|
|
md_scrl.add(md_view)
|
|
md_buffer.set_text(md_text)
|
|
detailsbox = Gtk.HBox()
|
|
detailsbox.pack_start(ui.icon(win, "text-x-preview"), False, False, False)
|
|
detailsbox.pack_start(Gtk.Label(" Source of Article "), True, True, True)
|
|
detailsbox.show_all()
|
|
notebook.append_page(md_scrl, detailsbox)
|
|
|
|
|
|
except Exception as e:
|
|
print("FUCKING ERROR")
|
|
print(e)
|
|
print()
|
|
|
|
# Channel Uploads / Publications
|
|
|
|
if is_channel:
|
|
|
|
uploads_box = ui.load(win, claim_search.find, claim_search.render, win, "", [out["claim_id"]], )
|
|
detailsbox = Gtk.HBox()
|
|
detailsbox.pack_start(ui.icon(win, "folder-remote"), False, False, False)
|
|
detailsbox.pack_start(Gtk.Label(" Publications "), True, True, True)
|
|
detailsbox.show_all()
|
|
notebook.append_page(uploads_box, detailsbox)
|
|
|
|
|
|
##### DESCRIPTION ####
|
|
|
|
try:
|
|
description_scrl = Gtk.ScrolledWindow()
|
|
description_scrl.set_size_request(500,200)
|
|
description_field = Gtk.TextView()
|
|
description_field.set_wrap_mode(Gtk.WrapMode.WORD )
|
|
description_field.set_editable(False)
|
|
description_buffer = description_field.get_buffer()
|
|
description_buffer.set_text(out["value"]["description"])
|
|
markdown.convert(win, description_field)
|
|
|
|
description_box = Gtk.VBox()
|
|
detailsbox = Gtk.HBox()
|
|
detailsbox.pack_start(ui.icon(win, "text-x-generic"), False, False, False)
|
|
detailsbox.pack_start(Gtk.Label(" Description "), True, True, True)
|
|
detailsbox.show_all()
|
|
notebook.append_page(description_scrl, detailsbox)
|
|
|
|
description_box.pack_start(description_scrl, False, False, False)
|
|
description_scrl.add(description_field)
|
|
except:
|
|
pass
|
|
|
|
######## COMMENTS ########
|
|
|
|
|
|
com_box = Gtk.VPaned()
|
|
com_box.set_position(250)
|
|
com_scrl = Gtk.ScrolledWindow()
|
|
com_scrl.set_vexpand(0)
|
|
#com_scrl.set_hexpand(0)
|
|
comments_widget = ui.load(win, comments.list_comments, comments.render_comments, win, out )
|
|
com_scrl.add(comments_widget)
|
|
detailsbox = Gtk.HBox()
|
|
detailsbox.pack_start(ui.icon(win, "document-send"), False, False, False)
|
|
detailsbox.pack_start(Gtk.Label(" Comments "), True, True, True)
|
|
detailsbox.show_all()
|
|
com_box.add1(comments.comment_input(win, out["claim_id"]))
|
|
com_box.add2(com_scrl)
|
|
notebook.append_page(com_box, detailsbox)
|
|
|
|
|
|
##### Details #######
|
|
|
|
# Almost like Raw Data but shows only the important stuff
|
|
|
|
|
|
details = {"LBRY URL: ":url,
|
|
"Price: ":str(price)+" "+str(currency)}
|
|
try:
|
|
details["Claim ID: "] = out["claim_id"]
|
|
except:
|
|
pass
|
|
|
|
try:
|
|
details["Upload Bid: "] = out["amount"]+" LBC"
|
|
except:
|
|
pass
|
|
|
|
try:
|
|
details["Support: "] = out["meta"]["support_amount"]+" LBC"
|
|
except:
|
|
pass
|
|
|
|
try:
|
|
if is_channel:
|
|
details["Odysee Subscribers: "] = odysee.get_odysee_subs(win, out["claim_id"])[0]
|
|
else:
|
|
details["Odysee Views: "] = odysee.get_odysee_views(win, out["claim_id"])[0]
|
|
except:
|
|
pass
|
|
|
|
try:
|
|
details["Filename: "] = out["value"]["source"]["name"]
|
|
except:
|
|
pass
|
|
try:
|
|
details["File Size:"] = csize(filesize)
|
|
except:
|
|
pass
|
|
try:
|
|
details["License: "] = out["value"]["license"]
|
|
except:
|
|
pass
|
|
|
|
try:
|
|
details["Released at: "] = time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime(int(out["value"]["release_time"])))
|
|
except:
|
|
pass
|
|
|
|
try:
|
|
#print(out["value"]["tags"])
|
|
details["Tags: "] = out["value"]["tags"]
|
|
except:
|
|
pass
|
|
|
|
det_scrl = Gtk.ScrolledWindow()
|
|
det_view = data_view.data_widget(details)
|
|
det_scrl.add(det_view)
|
|
detailsbox = Gtk.HBox()
|
|
detailsbox.pack_start(ui.icon(win, "dialog-information"), False, False, False)
|
|
detailsbox.pack_start(Gtk.Label(" Details "), True, True, True)
|
|
detailsbox.show_all()
|
|
notebook.append_page(det_scrl, detailsbox)
|
|
|
|
######### ANALYTICS GRAPH #######
|
|
|
|
plot_chart = fetch.lbrynet("txo_plot", { "days_back":1000, # Fetch 100 days of txo
|
|
"exclude_internal_transfers":True, # Without crap
|
|
"is_not_my_input":True, # Not from me ( as in support only )
|
|
"claim_id":out["claim_id"]
|
|
})
|
|
if plot_chart:
|
|
|
|
chart_box = Gtk.VBox()
|
|
detailsbox = Gtk.HBox()
|
|
detailsbox.pack_start(ui.icon(win, "text-csv"), False, False, False)
|
|
detailsbox.pack_start(Gtk.Label(" Analytics "), True, True, True)
|
|
detailsbox.show_all()
|
|
notebook.append_page(chart_box, detailsbox)
|
|
|
|
graph_data = {"items":[],
|
|
"zoom":[0,0],
|
|
"allow_negative":False
|
|
}
|
|
|
|
for i in plot_chart:
|
|
|
|
a = {}
|
|
a["amount"] = i["total"]
|
|
a["timestamp"] = int(time.mktime(time.strptime(i["day"],"%Y-%m-%d")))
|
|
|
|
graph_data["items"].append(a)
|
|
|
|
|
|
the_graph = analytics.graph(win, graph_data, "Analytics")
|
|
chart_box.pack_start(the_graph,1,1,1)
|
|
|
|
try:
|
|
|
|
t = title
|
|
if is_channel:
|
|
t = ""
|
|
|
|
uploads_box = ui.load(win, claim_search.find, claim_search.render, win, t, [], 1, {"any_tags":out["value"]["tags"] , "claim_type":out["value_type"]} )
|
|
detailsbox = Gtk.HBox()
|
|
detailsbox.pack_start(ui.icon(win, "folder-remote"), False, False, False)
|
|
detailsbox.pack_start(Gtk.Label(" Similar "), True, True, True)
|
|
detailsbox.show_all()
|
|
notebook.append_page(uploads_box, detailsbox)
|
|
except:
|
|
pass
|
|
|
|
##### Raw Data #######
|
|
|
|
raw_scrl = Gtk.ScrolledWindow()
|
|
raw_view = data_view.data_widget(out)
|
|
raw_scrl.add(raw_view)
|
|
detailsbox = Gtk.HBox()
|
|
detailsbox.pack_start(ui.icon(win, "dialog-warning"), False, False, False)
|
|
detailsbox.pack_start(Gtk.Label(" Extra Details "), True, True, True)
|
|
detailsbox.show_all()
|
|
notebook.append_page(raw_scrl, detailsbox)
|
|
|
|
|
|
|
|
|
|
#outbox.show_all()
|
|
return outbox
|
|
|
|
|
|
|
|
def downloaded(claim_id):
|
|
|
|
# Returns a fraction ( from 0 to 1 ) of the download
|
|
# percentage. If it's a 0, we can use it to display
|
|
# the download button.
|
|
|
|
#out = check_output(["flbry/lbrynet",
|
|
# "file", "list", "--claim_id="+claim_id])
|
|
out = fetch.lbrynet("file_list", {"claim_id":claim_id})
|
|
#print(out, '\n\n')
|
|
|
|
try:
|
|
#out = json.loads(out)
|
|
out = out["items"][0]
|
|
|
|
if out["status"] == "finished":
|
|
return 1
|
|
else:
|
|
return out["written_bytes"] / out["total_bytes"]
|
|
|
|
except:
|
|
return 0
|
|
|
|
def get_downloaded_file(claim_id):
|
|
|
|
#out = check_output(["flbry/lbrynet",
|
|
# "file", "list", "--claim_id="+claim_id])
|
|
out = fetch.lbrynet("file_list", {"claim_id":claim_id})
|
|
|
|
try:
|
|
#out = json.loads(out)
|
|
out = out["items"][0]
|
|
|
|
return out["download_path"]
|
|
except:
|
|
return ""
|
|
|
|
def delete_file(claim_id):
|
|
#check_output(["flbry/lbrynet",
|
|
# "file", "delete", "--claim_id="+claim_id])
|
|
fetch.lbrynet("file_delete", {"claim_id":claim_id})
|
|
def start_downloading(url):
|
|
out = fetch.lbrynet("get", {"uri":url, "save_file":True})
|
|
|
|
def downloading_check_thread(win, claim_id,
|
|
download_button,
|
|
delete_button,
|
|
launch_button,
|
|
progress_bar):
|
|
|
|
# This is a thread that will toggle buttons on/off
|
|
# based on a curretly downloading file.
|
|
|
|
def update(fraction):
|
|
if not fraction: # if it's 0
|
|
download_button.set_visible(True)
|
|
delete_button.set_visible(False)
|
|
launch_button.set_visible(False)
|
|
progress_bar.set_visible(False)
|
|
else:
|
|
download_button.set_visible(False)
|
|
delete_button.set_visible(True)
|
|
launch_button.set_visible(True)
|
|
progress_bar.set_visible(True)
|
|
progress_bar.set_fraction(fraction)
|
|
if fraction == 1:
|
|
progress_bar.set_visible(False)
|
|
|
|
while True:
|
|
|
|
fraction = downloaded(claim_id)
|
|
GLib.idle_add(update, fraction)
|
|
time.sleep(2) # The new algorithm is too fast LOL
|
|
|
|
if not win.download_buttons[claim_id]:
|
|
return
|
|
def csize(x):
|
|
|
|
x = float(x)
|
|
|
|
l = ["B","KB", "MB", "GB", "TB"]
|
|
|
|
for i in range(5):
|
|
if x > 1024:
|
|
x = x / 1024
|
|
else:
|
|
return str(round(x, 2))+" "+l[i]
|
|
return str(round(x, 2))+" "+l[i]
|