channel.py 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576
  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. # This file will perform a simple search on the LBRY network.
  30. from subprocess import *
  31. import os
  32. import json
  33. from flbry import url
  34. from flbry import following
  35. from flbry import wallet
  36. from flbry import markdown
  37. from flbry import publish
  38. from flbry import settings
  39. from flbry import search
  40. from flbry.variables import *
  41. def simple(args=""):
  42. # The user might write the search argument right in the same
  43. # line as the work search.
  44. #
  45. # : channel blenderdumbass
  46. #
  47. # Or they can type nothing. And be confused of what happened.
  48. # So I want to provide a catcher here. If they type nothing it
  49. # will ask them to provide a search query.
  50. if not args:
  51. args = input(typing_dots("Channel url"))
  52. if not args.startswith("@") and not args.startswith("lbry://@"):
  53. args = "@"+args
  54. # So we want to request a query to the SDK to search what ever
  55. # the user wants. The problem is it can be a very large output.
  56. # For example the "blender dumbass" query returns 1000 claims
  57. # on the LBRY network. And people will wait for a very long time
  58. # on something that might have a million claims.
  59. # So instead we are going to request only the first 20 and let
  60. # the user load more.
  61. w, h = tsize()
  62. page_size = h - 5
  63. page = 1
  64. while True:
  65. # Printing the search query and page number
  66. center("CHANNEL: "+args+" PAGE:"+str(page))
  67. out = check_output(["flbry/lbrynet",
  68. "claim", "search", '--channel='+args,
  69. '--page='+str(page),
  70. '--page_size='+str(page_size),
  71. "--no_totals",
  72. '--order_by=release_time'])
  73. # Now we want to parse the json
  74. try:
  75. out = json.loads(out)
  76. except:
  77. print(" Connect to LBRY first.")
  78. return
  79. try:
  80. data_print = {"categories":["Type", "Title"],
  81. "size":[1,5],
  82. "data":[]}
  83. # List what we found
  84. for n, i in enumerate(out["items"]):
  85. title = "---!Failed Loading Title---"
  86. ftype = "claim"
  87. try:
  88. try:
  89. title = i["value"]["title"]
  90. except:
  91. title = i['name']
  92. try:
  93. ftype = what[i["value"]["stream_type"]]
  94. except:
  95. ftype = what[i["value_type"]]
  96. except:
  97. pass
  98. data_print["data"].append([ftype, title])
  99. table(data_print)
  100. # Tell the user that he might want to load more
  101. center("---type 'more' to load more---")
  102. page = page +1
  103. # Error messages
  104. except Exception as e:
  105. if "code" in out:
  106. print(" Error code: ", out["code"] )
  107. if out["code"] == -32500:
  108. print(" SDK is still starting. Patience!")
  109. else:
  110. print(" Error :", e)
  111. return
  112. channel_commands = [
  113. "rss",
  114. "follow",
  115. "unfollow",
  116. "more",
  117. "boost",
  118. "tip",
  119. "search"
  120. ]
  121. complete(channel_commands)
  122. try:
  123. signing_channel = out["items"][0]["signing_channel"]
  124. except:
  125. center("Channel '"+args+"' is not found.", "bdrd")
  126. return
  127. # Making sure that we stop every time a new page is reached
  128. while True:
  129. c = input(typing_dots())
  130. if c == "rss":
  131. rss = signing_channel["short_url"]
  132. rss = rss.replace("#", ":")
  133. rss = rss.split("lbry://", 1)[1]
  134. print(" https://odysee.com/$/rss/"+rss)
  135. elif c == "follow":
  136. channel = signing_channel["permanent_url"]
  137. try:
  138. name = signing_channel["value"]["title"]
  139. except:
  140. name = signing_channel["signing_channel"]["normalized_name"]
  141. following.follow_channel(channel, name)
  142. elif c == "unfollow":
  143. channel = signing_channel["permanent_url"]
  144. try:
  145. name = signing_channel["value"]["title"]
  146. except:
  147. name = signing_channel["normalized_name"]
  148. following.unfollow_channel(channel, name)
  149. elif c in ["support", "boost"]:
  150. wallet.support(signing_channel["claim_id"])
  151. elif c == "tip":
  152. wallet.support(signing_channel["claim_id"], True)
  153. elif c.startswith("boost"):
  154. if " " in c:
  155. wallet.support(signing_channel["claim_id"], amount=c[c.find(" ")+1:])
  156. else:
  157. wallet.support(signing_channel["claim_id"])
  158. elif c.startswith("tip"):
  159. if " " in c:
  160. wallet.support(signing_channel["claim_id"], amount=c[c.find(" ")+1:], tip=True)
  161. else:
  162. wallet.support(signing_channel["claim_id"], tip=True)
  163. elif c.startswith("search"):
  164. channel = signing_channel["permanent_url"]
  165. if " " in c:
  166. search.simple(c[c.find(" ")+1:], channel)
  167. else:
  168. search.simple(channel=channel)
  169. else:
  170. break
  171. complete(channel_commands)
  172. if c != "more":
  173. break
  174. try:
  175. c = int(c)
  176. except:
  177. return
  178. while True:
  179. url.get(out["items"][c]["canonical_url"])
  180. c = input(typing_dots())
  181. if not c:
  182. break
  183. try:
  184. c = int(c)
  185. except:
  186. return
  187. def select(message="", claim_id=False, anonymous=False):
  188. # This fucntion will give users to select one of their channels.
  189. center(message)
  190. out = check_output(["flbry/lbrynet",
  191. "channel", "list"])
  192. # Now we want to parse the json
  193. try:
  194. out = json.loads(out)
  195. except:
  196. print(" Connect to LBRY first.")
  197. return
  198. d = {"categories":["lbry url", "title"],
  199. "size":[1,2],
  200. "data":[]}
  201. for n, i in enumerate(out["items"]):
  202. name = "[no name]"
  203. title = "[no title]"
  204. try:
  205. name = i["name"]
  206. title = i["value"]["title"]
  207. except:
  208. pass
  209. d["data"].append([name, title])
  210. if anonymous:
  211. d["data"].append(["[anonymous]", "[no title]"])
  212. table(d)
  213. center("select a channel by typing it's number")
  214. select = input(typing_dots())
  215. try:
  216. select = int(select)
  217. if select > len(out["items"])-1 and anonymous:
  218. if claim_id:
  219. return None, None
  220. return None
  221. if claim_id:
  222. return out["items"][select]["name"], out["items"][select]["claim_id"]
  223. return out["items"][select]["name"]
  224. except:
  225. raise()
  226. if claim_id:
  227. return out["items"][0]["name"], out["items"][0]["claim_id"]
  228. return out["items"][0]["name"]
  229. def show_data(data):
  230. # This will show the data for create()
  231. # If a value is not set, we set it to a default value
  232. if not "title" in data:
  233. title = "[no title]"
  234. else:
  235. title = data["title"]
  236. if not "description" in data:
  237. description = "[no description]"
  238. else:
  239. description = data["description"]
  240. d = {"categories": ["Name", "Bid", "Title", "Description"],
  241. "size": [2,1,3,6],
  242. "data": [[data["name"], data["bid"], title, description]]}
  243. table(d, False)
  244. if not "email" in data:
  245. email = "[no email]"
  246. else:
  247. email = data["email"]
  248. if not "website_url" in data:
  249. web_url = "[no website]"
  250. else:
  251. web_url = data["website_url"]
  252. d = {"categories": ["Email", "Website URL"],
  253. "size": [1,1],
  254. "data": [[email, web_url]]}
  255. table(d, False)
  256. if not "thumbnail_url" in data:
  257. thumb_url = "[no thumbnail]"
  258. else:
  259. thumb_url = data["thumbnail_url"]
  260. if not "cover_url" in data:
  261. cover_url = "[no cover image]"
  262. else:
  263. cover_url = data["cover_url"]
  264. d = {"categories": ["Thumbnail URL", "Cover Image URL"],
  265. "size": [1,1],
  266. "data": [[thumb_url, cover_url]]}
  267. table(d, False)
  268. if not "tags" in data:
  269. tags = "[no tags]"
  270. else:
  271. tags = data["tags"]
  272. d = {"categories":["Tags"],
  273. "size": [1],
  274. "data": [[tags]]}
  275. table(d, False)
  276. if not "languages" in data:
  277. langs = "[no languages]"
  278. else:
  279. langs = data["languages"]
  280. d = {"categories": ["Languages"],
  281. "size": [1],
  282. "data": [[langs]]}
  283. table(d, False)
  284. center("--- for commands type 'help' ---")
  285. def create(name=""):
  286. """Creates a new channel on the LBRY network"""
  287. # Get the name for the channel, since it's required.
  288. # If the user just presses enter without typing anything, it will prompt again.
  289. while not name:
  290. name = input(" Name: ")
  291. if not name.startswith("@"):
  292. name = "@" + name
  293. # This is the data dictionary we will use
  294. data = {
  295. "bid": 0.001,
  296. "name": name
  297. }
  298. complete([
  299. "name",
  300. "bid",
  301. "title",
  302. "description",
  303. "email",
  304. "website",
  305. "thumbnail",
  306. "cover",
  307. "tags",
  308. "languages",
  309. "help",
  310. "save",
  311. "load",
  312. "create"
  313. ])
  314. while True:
  315. show_data(data)
  316. c = input(typing_dots())
  317. if not c:
  318. return
  319. elif c.startswith("name"):
  320. if " " in c:
  321. name = c[c.find(" ")+1:]
  322. else:
  323. name = input(" Name: ")
  324. if not name.startswith("@"):
  325. name = "@" + name
  326. data["name"] = name
  327. elif c.startswith("bid"):
  328. # Get the bid
  329. if " " in c:
  330. bid = c[c.find(" ")+1:]
  331. else:
  332. bid = input(" Bid: ")
  333. # Try to convert it a float
  334. try:
  335. bid = float(bid)
  336. except:
  337. pass
  338. # while bid is not a float, repeat until it is
  339. while type(bid) != type(10.0):
  340. center("Bid is not a number, try again", "bdrd")
  341. bid = input(" Bid: ")
  342. try:
  343. bid = float(bid)
  344. except:
  345. pass
  346. data["bid"] = bid
  347. elif c.startswith("title"):
  348. if " " in c:
  349. title = c[c.find(" ")+1:]
  350. else:
  351. title = input(" Title: ")
  352. data["title"] = title
  353. elif c.startswith("description"):
  354. c = c + ' '
  355. a = c[c.find(" "):]
  356. if len(a) > 1:
  357. description = file_or_editor(a, "Type the description here. Don't forget to save. Then return to FastLBRY.")
  358. else:
  359. description = input(" Description: ")
  360. data["description"] = description
  361. elif c.startswith("email"):
  362. if " " in c:
  363. email = c[c.find(" ")+1:]
  364. else:
  365. email = input(" Email: ")
  366. data["email"] = email
  367. elif c.startswith("website"):
  368. if " " in c:
  369. web_url = c[c.find(" ")+1:]
  370. else:
  371. web_url = input(" Website URL: ")
  372. data["website_url"] = web_url
  373. elif c.startswith("thumbnail"):
  374. if " " in c:
  375. thumb_url = c[c.find(" ")+1:]
  376. else:
  377. thumb_url = input(" Thumbnail URL: ")
  378. data["thumbnail_url"] = thumb_url
  379. elif c.startswith("cover"):
  380. if " " in c:
  381. cover_url = c[c.find(" ")+1:]
  382. else:
  383. cover_url = input(" Cover Image URL: ")
  384. data["cover_url"] = cover_url
  385. elif c.startswith("tags"):
  386. if " " in c:
  387. tags = c[c.find(" ")+1:]
  388. else:
  389. tags = input(" Enter the tags for the channel, separated by commas: ")
  390. tags = tags.split(",")
  391. # Stip each tag, so if the user types "tag1, tag2, tag3"
  392. # Resulting list would be: ["tag1", "tag2", "tag3"]
  393. for n, tag in enumerate(tags):
  394. tags[n] = tag.strip()
  395. data["tags"] = tags
  396. elif c.startswith("languages"):
  397. if " " in c:
  398. langs = c[c.find(" ")+1:]
  399. else:
  400. langs = input(" Enter the languages for the channel, separated by commas: ")
  401. langs = langs.split(",")
  402. for n, lang in enumerate(langs):
  403. langs[n] = lang.strip()
  404. data["languages"] = langs
  405. elif c == "help":
  406. markdown.draw("help/create-channel.md", "Create Channel Help")
  407. elif c.startswith("save"):
  408. if " " in c:
  409. pn = c[c.find(" ")+1:]
  410. else:
  411. pn = input(" Preset Name: ")
  412. # Create the preset folder is it's not there
  413. try:
  414. os.makedirs(settings.get_settings_folder()+"presets/channel")
  415. except:
  416. pass
  417. # Write the json file
  418. with open(settings.get_settings_folder()+"presets/channel/"+pn+'.json', 'w') as f:
  419. json.dump(data, f, indent=4, sort_keys=True)
  420. elif c.startswith("load"):
  421. if " " in c:
  422. pn = c[c.find(" ")+1:]
  423. else:
  424. pn = input(" Preset Name: ")
  425. # loading the json file
  426. try:
  427. name = data["name"]
  428. with open(settings.get_settings_folder()+"presets/channel/"+pn+'.json') as f:
  429. data = json.load(f)
  430. data["name"] = name
  431. except:
  432. center("There is no '"+pn+"' preset!", "bdrd")
  433. elif c == "create":
  434. command = ["flbry/lbrynet", "channel", "create", "--name="+data["name"], "--bid="+str(data["bid"])]
  435. for i in ["title", "description", "email", "website_url", "thumbnail_urL", "cover_url"]:
  436. if i in data:
  437. command.append("--"+i+"="+str(data[i]))
  438. for i in ["tags", "languages"]:
  439. if i in data:
  440. for j in data[i]:
  441. command.append("--"+i+"="+str(j))
  442. out = check_output(command)
  443. out = json.loads(out)
  444. if "message" in out:
  445. center("Error creating channel: "+out["message"], "bdrd")
  446. else:
  447. center("Successfully created "+name, "bdgr")
  448. return