From c5dc06962d12599879257d937771908debbe0d3e Mon Sep 17 00:00:00 2001 From: Jack Date: Tue, 24 May 2016 17:44:26 -0400 Subject: [PATCH 01/17] travis code signing --- .travis.yml | 32 +++++++++++++++++++++++++++++++ packaging/osx/certs/cert.cer.enc | 30 +++++++++++++++++++++++++++++ packaging/osx/certs/cert.p12.enc | 33 ++++++++++++++++++++++++++++++++ 3 files changed, 95 insertions(+) create mode 100644 .travis.yml create mode 100644 packaging/osx/certs/cert.cer.enc create mode 100644 packaging/osx/certs/cert.p12.enc diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..1f5f36cd7 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,32 @@ +matrix: + include: + - os: linux + sudo: required + dist: trust + language: generic +before_install: +- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update; brew install python; fi +- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then sudo pip install --upgrade pip virtualenv; + fi +- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then virtualenv $HOME/venv; source $HOME/venv/bin/activate; + fi +install: true +before_script: +- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then openssl aes-256-cbc -k "$ENCRYPTION_SECRET" + -in packaging/osx/certs/dist.cer.enc -d -a -out packaging/osx/certs/dist.cer; fi +- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then openssl aes-256-cbc -k "$ENCRYPTION_SECRET" + -in packaging/osx/certs/dist.p12.enc -d -a -out packaging/osx/certs/dist.p12; fi +- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then ./packaging/osx/add-key.sh; fi +script: +- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then bash packaging/ubuntu/ubuntu_package_setup.sh; + fi +- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew upgrade gmp; fi +- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then pip install git+https://github.com/metachris/py2app; + fi +- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then pip install json-rpc; fi +- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then cd packaging/osx/lbry-osx-app; ./setup_app.sh; + cd $TRAVIS_BUILD_DIR; fi +env: + global: + - secure: d3glJxXC0goiFETAP0JxMDEQoSNlh1fRDR76D3tjYY4Brxh2UgvUvpNJOAkFApynciA3n8kva4uBsv52jwnKG0VmCy/meaWowouhyi8ChdPpg7HZL84oC7rz3bZ2OA2iuYFPpAQrd6p7OMhmiCkeqhRKtW0YmOKn1F45kaIMnmq+bD0QK3IdP3QBdGz0rf6TQlNSQLSJtDAP0+HO+NaawJ1TQJXHUHA8mKnbOTRal4bH007uxfhvthXysm1QRQOfthGud2q/DN+f6RfCqF2Fv9l7NwR5BwVKluQYgqJjdhk9IOU7D+zW4Ne2fSt6V1PRAASAfyhtDiOA7B3ZUw2igimQ6rWyWao1csilq0RW9EmycOT8S7x5YgXltk0kVdNizdQeHCDII0mOxkIFBF0bs2ZgTgKasZrU5jGnEhV42eACl9CGeoT5/ots7an+zgCBoQ8c4HGkMQyKV4uNnvKD+gq1FPDuwlHEUHhbjy5uRI4kAVMzjsCJX7XBSumFELuwiOpE/XguiJAV/LvjYbN6OFemJ6HUOhep6kgq7Zcoxh+UnaMixiq6NQTSoLpffatSxCM9EG8uBoXZJBH45cS9aD0eP9zjV2COCC0Iom5BQFhkArgMVodYtcrNevPUA3AuvJOjdJpSwKB4fqaU/CDqThw/ieZQlzaZqWIM5RnHf6Y= + - secure: n08IQBOLaipKzAwS5aQM+JfFtvLSCrWqFFztTMeElmck6IJOVnPlDvPYRCrRmrXtgFmJ1G6hEU5Vri086MkTaISq3V04ndquhz7nv93Pp1do42vWPmvmFHdMrhaOGPVtYZ4m4XfgNcA+NC79V9X2VQ+MoQgCRhcGlgGomxJBHc924bGxppkIrDMljvSipkR5v9g/UFGRPYJ6BIF0imWrdsHUfqhbZMdAxAmdlBAjx/ZUOmUyPWYttHGYEW4HpLwvkU2K+sJrt1emogvR7GT94CadtqjmRsiKh0Y2lHHhHkqV1J941T08p9hyKU6S4fHQqPHfpkHRiPeJQY8JiipCbuERn0YeGqsp5q0jRE6PFUxCWcvlWqc7XNihOMQAb5JQD8vF2Lvs3YYycFBgZkIOaZQFbemqkx2pnQMjVF7GCZp4u+p5yo8iBeImYFYdkqIBHqPqsKxM9aBfe05XhAmi/6jUP0L2xikVFvJZDNxKP2uyqXVbSMR8KF8v1eaYnsSL3vzBolwkn96zaj2E/lItHaNeNIXvutAsGuy2ybTXeabm5rTaUr+5cmdqx4JqmyGM3DinGFik/fzpLUJCd4GaC1n1taLy6aUUW2oH46QRqLLAAjZ4AWao92Eb4cnFiVxxBqVG3efWaoEt+uW7/hzqG27iieoVqCXs3Hd5g4bCYyo= diff --git a/packaging/osx/certs/cert.cer.enc b/packaging/osx/certs/cert.cer.enc new file mode 100644 index 000000000..4dc96d9ba --- /dev/null +++ b/packaging/osx/certs/cert.cer.enc @@ -0,0 +1,30 @@ +U2FsdGVkX1/oLoj7zhPd0imh2T8RhLpKzZLk4EHQV0GUJ1g8nwGvxWov+CUMnmjh +Y+LNdomGBYWoUilhe4JaWDUYepwIXn6+TvuWBdVEMGpJhXGbmIf+ncMXo6AP8Fh/ +g9x79SE4RJxFj3utc02B2ivVehoQno5sEvNSZMVml5n9skJoJUBbAsbp1p+7Hm5j +p2Z7UI7/qiih6TmszX5KQvOl/DPezVNksn1c1mUShuaBTjxbprGlr/LvtboR3mAd +8DN4yTGLLJAQ2+FNftM4rAedrr6db2AhQ8WxRmiwTfdubnEoC6zYySFq2kmdjj3S +gPnK0atx+ZihMp+S+GqMnvfAHEtb0vqxoq6nFcSIvcQVxKPyzu5E1kMriY4Oq3xr +K6ebc1NKJHjh7niaUmR3NImBx2h1LAAf/hcKRH2+ATEVczGtI1AsSGgGhUM34eGH +7G+m7+bIkgb8AtlaIGS/VVHsIZCNSgzwZJoNd3hD6ZV65Hb2yeT6Hhos88/05iFT +ewYasa73TqFm5SJHRwt4d1W9WVIJKJPDJ910p+V+NZVUsKOx34+vMNrjCrqW9p9x +gQnza2V/F6blIHTbSzIGc+MFbeHYBO80d+v5jVxheL8z6ollDVts1SyJ5rKJBY6c +quvSgmc/ltE0dqRxLOQJ9mAFbayuMIUP6CbRkPXp8GfE55UtUJkDilalzcpCPrUC +YJpuAI61INOQZZPEVKWW8L68/tLY+oEwWpexQX7xs4FUCblIFf20T3XE2lVuBHf9 +Bp9k7cD2m4mNrbzWOJuqrVt1pr176l9+VSP/ESdDFbmPch2FHl8HK8kgfJvkV+iB +kudmAmzI9DTUpWd5lJp6Fr/rLCMjslFDs37zMg4/E5ikKFSDNeYMtgPZhCwM83kh +OAktow4QAzh3RdbVZMFxaKk9nbiGPuBEsgvraPjb2gY8U34RC9R2FINIuTnJttLK +q7CKFTdbJIf+TIIgzfNu/c978adsK/qS68iltyyx8WFflcybnlqVgja192Ptqw1M +PXBQkH4mUrAeWDfmCPPh/mhO67Bau5u9Wzv/qZ2RXcX0dgXOoMa2sO6ZpR2SzxCJ +/XZwXnElMl+pvojLURDOV16fMPpjMCbzCN+hQabiTASqFNCsz4C9hmOquNh2t+V9 +8xvU/bnOM+/SMhahjYnvdhmRMcY+5Wv32ZnKATq88dq4T7/OZI7q3IsROZ7MnucT +x4vADvcFOfOdtPK35IFfMTfl+Ri3q7REIHMts2WEwXddf8CUiVeIaf8NgrWYW0hP +f9DQbMGKFcqqCHlKrQkv2dBKX/qEbIzN7T7535Ly68zyFuBS252gsLO7nrf+CLEZ +AROOfmt2jv0BvQ4MI5dslzsXFAU11tS36gOZ303R+NJVVqySkza964h2rH5M1F7i +A5p7w/l0OVV7r6aXkmsrIcsUZuY7QnZJORQ1MxNtK20weKfrqs90nMTklUVPc4V8 +LnAW6AYem0ZaeDHn2kx947sglMYxf0h/mFECGhif9hfDTErw7TkSJ26t9ByuEyEf +vGpp3P4iTXHUx7HSh7L4KDva6CP6slGjFMAFUEETn7N5uX3VEYeztMBdHLz0XHZc +PcgVZ8kytXVTEg95upvWmliEbQqWRsy6sr9PanaN1QY6re6RLlYj4pOWVm8qgCXU +IJVTWkROMlYZTWCibCsTsY8fk8aNObZamHjzZGvnU8nEGTx7xQJS8i0r3NM1j2Ka +ehBA+WfXbTplI/Fq8Z2Nrb/O39hQpGbXp4ERsEmVbK0twwsqVNehI0CdobxmGsd5 +E9Afb9iL97DTXsna1Il6FXnHvj3iAQsxxaNLIaj0sN1GaQd9N1mbxThlFNOM3wry +jI8TKCWEfLRQzykkcR3sMg== diff --git a/packaging/osx/certs/cert.p12.enc b/packaging/osx/certs/cert.p12.enc new file mode 100644 index 000000000..40828aff6 --- /dev/null +++ b/packaging/osx/certs/cert.p12.enc @@ -0,0 +1,33 @@ +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 +vOtCHu46xXuBJbDmz2mslw== From 7fca1f865d4de45de0221f6451ed8bc58623b5a5 Mon Sep 17 00:00:00 2001 From: Jack Date: Tue, 24 May 2016 17:50:01 -0400 Subject: [PATCH 02/17] update from master --- .gitmodules | 3 + INSTALL.md | 36 ++++-- README.md | 25 ++-- lbrynet/lbrynet_gui/lbry.png | Bin 10651 -> 18944 bytes packaging/osx/add-key.sh | 25 ++++ packaging/osx/lbry-osx-app | 1 + packaging/ubuntu/lbry | 4 +- packaging/ubuntu/postinst_append | 28 ++++ packaging/ubuntu/ubuntu_package_setup.sh | 157 ++++++++++++++++++----- setup.py | 2 +- 10 files changed, 229 insertions(+), 52 deletions(-) create mode 100644 .gitmodules create mode 100755 packaging/osx/add-key.sh create mode 160000 packaging/osx/lbry-osx-app create mode 100755 packaging/ubuntu/postinst_append diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..a8c2e8de6 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "packaging/osx/lbry-osx-app"] + path = packaging/osx/lbry-osx-app + url = https://github.com/jobevers/lbry-osx-app.git diff --git a/INSTALL.md b/INSTALL.md index 6dc552bcf..23491e60c 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -1,19 +1,35 @@ -#### Installing lbrynet on Linux +#### Installing the LBRY app -------------------------- -The following packages are necessary (the following are their names on Ubuntu): -libgmp3-dev build-essential python2.7 python2.7-dev python-pip +Installing LBRY is simple. You can get a dmg installer for OS X (Mavericks and up) or a .deb for linux [here](https://lbry.io/get). -To install them on Ubuntu: -sudo apt-get install libgmp3-dev build-essential python2.7 python2.7-dev python-pip +##### OS X +Just drag and drop LBRY.app into your applications folder (replacing any older versions). When it's running you'll have a LBRY icon in your status bar. -python setup.py build bdist_egg -sudo python setup.py install -``` +##### Linux +Double click the .deb file and follow the prompts. The app can be started by searching "LBRY", and it can be turned off by clicking the red 'x' in the browser interface. -this will install all of the libraries and a few applications +On both systems you can also open the UI while the app is running by going to lbry://lbry in Firefox or Safari, or localhost:5279 in Chrome. -For running the file sharing application, see [RUNNING](RUNNING.md) + + +#### Installing LBRY command line +-------------------------- + +##### OS X +You can install LBRY command line by running `curl -sL https://rawgit.com/lbryio/lbry-setup/master/lbry_setup_osx.sh | sudo bash` in a terminal. This script will install lbrynet and its dependancies, as well as the app. You can start LBRY by either starting the app or by running `lbrynet-daemon` from a terminal. + +##### Linux +On Ubuntu or Mint you can install the prerequisites and lbrynet by running + + ``` + sudo apt-get install libgmp3-dev build-essential python2.7 python2.7-dev python-pip + git clone https://github.com/lbryio/lbry.git + cd lbry + sudo python setup.py install + ``` + +To start LBRY, run `lbrynet-daemon` in a terminal. #### On windows: diff --git a/README.md b/README.md index dc8b5c81f..cd1693811 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,15 @@ +[![Build Status](https://travis-ci.org/lbryio/lbry.svg?branch=master)](https://travis-ci.org/lbryio/lbry) + # LBRYnet LBRYnet is a fully decentralized network for distributing data. It consists of peers uploading and downloading data from other peers, possibly in exchange for payments, and a distributed hash table, used by peers to discover other peers. +## Installation + +Download the [latest release](https://github.com/lbryio/lbry/releases/latest) or see [INSTALL.md](INSTALL.md) for manual installation. + ## Overview On LBRYnet, data is broken into chunks, and each chunk is specified by its sha384 hash sum. This @@ -18,9 +24,9 @@ help peers find each other. For example, an application for which clients don't necessary chunks may use some identifier, chosen by the application, to find clients which do know all of the necessary chunks. -## Running +## For Developers -LBRYnet comes with an file sharing application, called 'lbrynet-console', which breaks +LBRY comes with an file sharing application, called 'lbrynet-console', which breaks files into chunks, encrypts them with a symmetric key, computes their sha384 hash sum, generates a special file called a 'stream descriptor' containing the hash sums and some other file metadata, and makes the chunks available for download by other peers. A peer wishing to download the file @@ -28,22 +34,19 @@ must first obtain the 'stream descriptor' and then may open it with his 'lbrynet download all of the chunks by locating peers with the chunks via the DHT, and then combine the chunks into the original file, according to the metadata included in the 'stream descriptor'. -To install and use this client, see [INSTALL](INSTALL.md) and [RUNNING](RUNNING.md) +For detailed instructions, see [INSTALL.md](INSTALL.md) and [RUNNING.md](RUNNING.md). -## Installation +Documentation: doc.lbry.io (may be out of date) -See [INSTALL](INSTALL.md) - -## Developers - -Documentation: doc.lbry.io Source code: https://github.com/lbryio/lbry -To contribute to the development of LBRYnet or lbrynet-console, contact jimmy@lbry.io +To contribute, [join us on Slack](https://lbry-slackin.herokuapp.com/) or contact josh@lbry.io. Pull requests are also welcome. ## Support -Send all support requests to jimmy@lbry.io +Please open an issue and describe your situation in detail. We will respond as soon as we can. + +For private issues, contact josh@lbry.io. ## License diff --git a/lbrynet/lbrynet_gui/lbry.png b/lbrynet/lbrynet_gui/lbry.png index 16c7c4683affa3e73c5bcf4fbd0b6bbb0778f275..6599574065ccfcc176cdd5e8726ca783f85f7097 100644 GIT binary patch literal 18944 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

U2Ho%8h$Z1A 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 O6N9I#pUXO@geCykjvm1P literal 10651 zcmYLv1z1$k);1!YLnGZtr*wx5Dc#)&2m?xYO1FTNNGk|P3rLrgN_T^FO2@y)d;jnH zJUTuz=j?O#+AH4oT`NjmO#vH&3W#cS9c?g#0Vf$<8j<#;Jop|TP5Oi;3Bmxu zN5v(fijrf5{AN^Fg;gc?^?aT(KiG?A{5-YVnrpoBim zwIw_eL5dfkJ3Cm6Q)56v59>aD;Zb6@oPGoq^F3aTk>sllloX0C=P7le#z#jFqwz-Y zJUOkNKCZl-b1f-9e5>$FE*8 z0);q+f4}4PMb&Io3D7`rF=H}f2m60Q;_28j)Uu`E+xo&V(ZS=T9(Y-=m zzL;wul0a?kjWZV<#X8r0_wQ$?6Mb?>SlD=L>HbAM*<>Z|uk0CpYiD#yXXj+FvE>?} zb33Y@-bC)ycWK8j1KzxTWAV%#Wp8NsXRRUueCx|8Vh56bZzt}hh1`$nDz`orRKztg zjaB`$q%y3xmxgbk&VL6jCQde$pUP#a)ym8AXUDE!pqqfFDmL~|witR$ImfU1H;(Yd z)N6$Tnp5K@?jQAYdG()VhBt(Y50zwDzUY;f8^Cux?07u#C0zY!_jt9{GR;?+lJk8< z1_nroqqms?d*Kr^9Q19eoI|5saI8UM3Qsw=8^_U%mC2i%$;goQzEpGPYb;p*~W5E8=>Pl1GP%4(Jv+| z_EvGQuE-=Pl!gJvTiES{VG=p~VXg!I~0Z-Hr_7-Dh?flkmP9b_qK8TGt_++elelWDAX{j}kTH`BHA*Dp; zS*>=a2a0o5|13sLi3$_g8I&TAASdx{)jrJYl)2fH`ubu-BDNPr<_q!q9MGQ@9c|aJeRwa9euRL#( zzf^8eVOFBIu`x}jEOnbJez~S2YL7;d0Xfjdw|$+v-#3VzQrVLA?D zn$&~h;@mI$%}q_4kAzql2AVTQnt1>2PJ~YkWdhfZ@_DnHtO9OiOlY~i(chpB!qof1 zOP*dFfwE~d6na7DThtgom*PRkUN!Q3gIP zch#Nr=A2>@1totOMq5+;?A0`6NmSJB$JBGh0m$9KCZ?E0V!m5V4_`Th(^H+C;Sc>{#Z(R9xqpPsrmMi;h9lxHTYjETHr? zGkTckYJU@qK=NT{ls~ijOFWLTle3Vb=8)ZpaZ4C93TungoxD-7(98_P5&Zf>kBw>p zZc=n#&CK4gSSk^uL9uxuZ-i2I>#cPf89Ld?8Tah-vA$_iHA~h7MMRA%AslOvP=2}= zngpM?$3Sej46W1VP^YCH z$#vp)Dbss@xq;Q{2PjiXuAxJt>kMVKAY7njmjL?GpOt!jEAGbKcMr}s3S3oj)j>@romg>{S5^+k@d zCkB*4$F6UY|5D!B;{DKcm8#>VkFSOOLqP56lhc(hR5_(Jy!7nYEiO~H$pn#i2ZV`Y z>sz*_Q~~Fh?P>E%nQ_X>%9uEl?_BF@igLan)HCk4*B9nwqa2iJZL2+sl9dpMl$T0w ziGlI11?aDAM1knFw`0oyui&H#?Ot289J&7aYqB@nLCeC$;mg+|QDlbrO8513mU2uC z@Iv~PFe`SCHxb)m$F(VrnG4%brs-nr41ZPfz9%Eg;BlZMHZYoedMZvWPeq$jI zWM!hx9iJ=FbrKqJ^;(Pl;&r6@)53*8dT@&Jz~LGW7D0sQurILCz;083+k;GRW2pYk zQGx#UEJqN;+VkeLJ_>Pvlq3wad9AeHIf(du56L+xp*jJN8sA({)l^UXnh3uMxvI(% zO!p|khi!Js!;@j)6-LQjEGa*zgpO)-pNMGj9}TQ&@vi-qrk&2L&)c46ilqn!&hzW5 zxBG08(vKoX;kT*Kq&Ej*C3jC9x7C70Vjd%$TT^tmbiSeI7IagwrLepdMS{euITl7j~ z33r(a$H86g;x;S`Eb4+V1S#{^SoKUyJlKspOEt`X{$j%3-_(v0LKG6JHzbKJ8nNcr zeBmz=6&hpR8%HM4ei4t8!RO4n&-$#ev=E60T@k*Mv3BwAzz<48#d1^MS;|65dE+VV zMCuK+n5@d0*_e@I&^A`D2GghCCx(TI&!ZeX8yoBLbLSfp%HyIO)G8=LIE9Xi`;-i&nW^4`F6r2T$S6?^k4;82Ba=YO+8r8 zk<#y9gWH-EKVm#I669D=X7%@WVr_kVYClF9uKcc}kb@Qq4ZxNcK>^3BhUJ`~g&FX! z`8yoNSZ572I`2*%{XJi1z(}U~Bnu4vHuZytRswdeYKt5{k%FSGEGrU)gxLo&J<_U0R? z1Hb!!_Cd5r8}jmZMdN*uW9xIgmHEDXYAz-D34zk+nB8cVxC8U*Md8QWzm6LuXrHwd zR4nmj8ft*0d-~DX{9AtZHHub<$rO2A+>6)uui~Y*kaL;pa=0dtAX+fS4+=M3&Ts`e zDBXc-=n(ef)KQKQLCq&31J>WNqK6Ub5W^q6(;+)eLEOQg3HwaUQ8hG1X%&fGL?mBIWK7&`$pm25 zf7vFL-Rze+!@wUVGd;}L<*9~Kn~YZXv8s`DH&hk|GTuE#*hCqYE?ii9V_CWc`ML~;l0jzl1wyLh#`M9U81HQ*%x5{JJQ0nO zcO^6!Q=%J>&hqxw-yrGdPt?}ZN-;5F->8-lLONzCQ=vyyz#+S2$%O6d*1kupvvk)- zf`c2BWSspa+(EI0rsnW6oF{$pRBJ zqWAp?qd<6s#S4@<>t3zi_zrwMJARkhRA4^=z(Opv^pSq!P+QD?8MuVOgR@xm{Znfy zH*{;e*??QJrlKFhsNW-`KkI^!={^ch9m-JWf@dnyRw6F5R?A6S-^Y*3kIuUj1N@0Z z(p@mDS1u&EEA;Zq%Fun;Fum9_Ua&=}vo(4tC>beZ0uCq?BVWf`;(`%YB!=>L`>`4I z=*#q3dNdmwXLD}+^TMwjmm{H`h5SUnqCogvrq>YIs=SB7m-roTZK-81q%`;=qi*J- zgBCl?v&Wi>&~M9_=2S%$?$?Iz-@{LGWib@7938FOCH-q=9Sb3b1)m);aq~&ZvZ!72 zH}^=K%Yi;je;?AZm%uwdWH`esfhQK`_X~l8__W1b9nM4(0*Qdz%VQ&NjZDnK_%Ard z`%cyEo!M|BTjz*J)&+MAoi2h}W|do}STVG1gm1x`m=ub-!c5JjWUEq2B+tk=O-d0R z%KSB>Uex_W4j=^S0u=0*ZvxCx(6oZtYq^=dAuoH*DF@JI>RV@#bMMIk>N@&++}Xa^ z>cux2oze7zE}uCINdud6Yuz#}kml9q29Kkbo&qXoy+BpN*Y*7twirOTAUeyV4KCFZ zPmx5&&iR}@_p0*scUB@MudkJ0l&r5Dz1ScnjLH_{BariV@9clYu3EpHwe$2r2|!Qi zt|8$9cKj;u&VK6J*SOM^dh-5AmnN`2y4mv(lJ^x=^t{Mo1b}I4=Q932F`28Y zXLK0VqAADsm2KdOZFVm%uf0BM-JB~0`d_jodIoYy{Jp>PW!PVcG`Ybw-&|L8t*ZYh zR)%CpA?%F-l8?InZJ0q;9JxUE#Yj9r|6hL?GcRJS?~^R9`U3tBT?|BE*?y8C0Yjva zo3yw48KEoI5vCw7uTMf6Yh@Q;HTxxVfS>)ZYiN9Zy^;wRRi4!Md$J?I8{x!D6PL1g z9A>rRWEF=K5mEd2?aLZ@;sii+7HXfx!=ikPOQufbqEN`txi>)TqX@5H;tzVd>= z$uBI&@`)q28cvAO*+tUK#03lPAK~puCPxieJ?|QLF89VQiWPQ$LR5Z^E4O%eGc;RX z@EP;s%A<-;a4n4xII{XlE!K-X@f+K#v^wSV(nXG;^vla*dQkIauYXx(2)$&;?x71u zp~Cyo2e5H94+B;i>P+!=HuwRMR_+^69kT?z^!4BV{%ZHY7W7g{+Vm{zS=U(6LMisr- zS8y9h8N{*2i?wyC*u52p;-mF>EVbLaLTpu zE6kx;LNuhR42x(Rssji{cy{dE$UGMFNUqI$91T<37ddpE_jtilG!Vbf0eVauEm70o zUJK7%_~s^Y56a1q$$FUNk3r!p`fEnvgSmR01Hl$=u0)IzOe#7})}HUO3P&W7blWp^ zZ1DA-2&8&^_`O(c+79aOeBk9-;)dDu z`Z;hB)G>m!)j=sTvOb0?j+&FrpDo@07MhFZ4(LV$@V_(VwEaEB4 z?TIvUL3+lu4>H%$xai3@X{benP~?P|XgFwHzuQ*fGt(;u>D9HqZbkkMdvy?+>`DLB zmK?+npX1m+=H_krC+NOTqRqilRE(W{wKjqF?wZbREP{1F)&W^l8m7on&%|Ka_nrUt z>YT&wj`Rsxry_d@s7tj*-rWX!~dJE zL$LPI!JV(1l64}(VdtP;rOC>01q$6LF>L6)XVr+x5OQp0$zJ= zEcgM5DdA0U$3J>wl&;HRNMk}~#Nl09E+xS%LnpsCVl5VUoLnOT6vB9U-FH>l!l51_ zsQ)z5!Wl_v8Hu7~5%gk@$t-BeR;OTPY+8!uu#D*DcVwtg@)b)*H|90dHxt$P>d6c}! z4x%>B{xH?ZbGW%jLKL^JC@SRNR>u3q!?JO9CRyrJD7y{r$29_tvvqVke}0E?G?0k^ z#1AQ_VFuI+ZGmd=Y_1k3!g*zPV_>i@6Ofik)bM2(tXk$~0+9COb%ya@6O6&*VU)gO z5XKlgAC)gE1rgYAy%w-w-fogcufJqK6IniQ^()TmHgI~3nnZNCc~e@7wi_zrdUlp+ zxV;3-11jX_BrzyWUiFQX2p|=3DFwVxxf!*9yIq%oQSN3CH84oupDD#4<^wG!GhjpZ%5?U^uSGbvxhocuNOmVtqx#;buzVb6a%-hD&(9Plgv&_l;K0$&4YWy_?>^t6>U@lohNzS zL}iTkY~KRQ%!TVU&rW;um0MCv)BoYB+rax_hXLN~Ca>eHHc=3cVHIYBa#Y!vg&~~J zpSu#ra4SX?7$)RMgo3L4WudXdfEv*347-;h;qQp;f0dUhMXmjeVs}Erk=IZ}uMr@B z<`i^DNy%+DtGzg!9cO^(iLiqzwL39J#Xuh|VY94ZVyRCq_=d1o|ABf!!#Nv*&*# zL#B(V#ON^aAe0J2%gjj8=h#nfC&o@ZA1<~kB@ExYyb&>}v`DQ>j?- zu@;n-Ma^h2bxB)?!||GD95^D78C0}5ULX9V`o3t|GE_b)e_{(t>a@}OE~^#9rhxkg zr+eh)??-Wyn|Xd4BhFop!P-B8$gXCYWK2|b`6K+Z9v&52+U|YLYz$QPF&~p^SV6zx zfnQo3$JDd?;6^?5_HBPjvqpGvLqb|PL#}r;2U?o`!FKSuGxn!5U#5CY^`lEz5rGO@ zVNWl2TBI@JOipevG%9xNf*xzGHlhF;29y+*3Ml8+ef*IS`f0)SmOWZHcP2MgJl!rU z7puIwp>Bk#1s+t?FU+BNO#!P<({n0t{^B$^(YbmWKd+si&y-JG96;jUTA0=6zc+f7 zW|0+Bo`KEbhTCh5p0~JK!P-^KYczC4yz3-l>w8^59>D%H!o{8ikrhoNTCz5B@cQ*OVsDV#G9^=hNRi?T{aSL3IR z2p;plPSJ)t66;lW*u+4H_=B2G`Ww6g!FI?ZyD zY$NZrw8?s?afcGcKG?k}xss=&s2-kGai96|wbj$f;oxBJ)Y?CG2VwD_6$lc~zgx&a%X;E=xX zs=p`F5$rgC0^Q<6k)QOvpb^szG4ecgJMY*QbVTuWRMsDTUsg-kbnyW}gBSjoUL0}^ z@ED)h`<({V4hp>0Ec_Hesv4RdcbrFiaPwHT`93q))Mix|i12EDz-snjXn>%4dP4p{ z$#r5yLnCQ^X<0{y3Un=l60q*Wp036XZ>s(^cX22iNB0Hn_jxKil`k)j+xr`f^$)?W z^+W{;FwbIqV**Hj?fOS4nszZQ0szWWZYm2umO|MY;nmnfwB8ZbjQRy!aML@j&GGDLrJ z<|}5l|B^X(?xRJ~L zt_tvfJG^=HNz6jW8|YPZtA&XNv%?O~SyU#??FLSv)m;&*ZfZb@LtV9{YIY&7t*x!< z|AGJN)Y;oJj+&U*67+%NbFShXnnQ2SJ1|^cytbdyrEMU2HdFE}sZS8Ovk1y*+!_s< zWGZ=o^oj#ozg1=Op{iNp9p)bCA~9DAq9T& zpmTb=yoxl2zD9I!{87ZWw)iB`h{iaY0WcbiYW!@Gz~Gu5&B}uk!VnygM!4+cM@-)5z->rr zVxm8qTXe>PM5HlSEP@s;18$)8tap(=Ih)~g!ii~Ll#R?>Li}=e90J=KspCwT{m>g~ z!;z9rE(l-Z_WRi(-LK${p(pz079)dnvL|$NIVWZH*j?;qelt*ZMU{nJ z1;7MUt#~wUCPwNuiz^zEkL{!KxQo> zQvAK>4?29ywe`bO0?8$JGWx@-W+uz?Mh#W7T#4RfO7DmcQgtYXkUP_$p#IZ}>2}`w zhmC5a85c&EI-d$N5%gMB#}IH4NLK=>8lHR}#s1SL&b!+sS|_f@JIbUQ@dx&OFF-Dq z@2y_T4)1dW;_H6|gH#a`mZ>`DS#nbI@CN3Hv)#ewro&OOXx01`l|0<-oj>h8lPK_Z zIgUJf7+cbRDH7-f2oppBH{4XoK}2C+el}o!v#^;<3c_Erv!44c6q- z5?b~GO+qudu-9z$&o&?MI-0Fs)uuoN_-FvkeZ@wVniKJk|JgI1rTJI`0e;XFsDN*c zP>fR!2<{HK*#GH0c4;uIZp@{f(7gyMh)_rSH9rH?<)P+dccMRnAn$g>zyc+Y68p&Ix6d0ow=Kf*hIw%(NB3*{{Ga}KG$@7afm$QOr)pk2@#wKT#j zdBt)1I=#6RKT38O2?Q4c4Z8piOw@CwhdR(z-~f=_&K|X)=2e+!22EC3x$M+!*?mCf z%*!Ild$FmXm*C(-_oLB77am17VzlZjd?GFPaH;O?- z;?Z2P2XBR@ZsxtXFIYM@|L&%2&ytOUt!uLiZSNlfd+c)&QrW;!pmJ5-Vas1IVWR-)IEEhCf1*;8g=0q{`D82x|m?8xOWP>mYG{ymm|JYcYS_32Z+{OY5 zPfZJ9wDo`FU0&5&>-i==zgF>IHAZ?OZxW@nWf_1P4@ZV)lo3B(AC9As)YHXM^MqVX z3GG9ffO5?zomyNrwUZs!d2!|1*hq&A}G#r(ywLk$0*qM z=jwYfxNvO;vJ|NpAVv65bU)btr$N-YEvRLSN4^P`e(Gi1U#<#9dW8P98B&D6TuCx$ zaDgO4*S1V+xAqE%4b0VOV^3@*u8G56`8?lc)-F(OK9!~jcBD)!I@!$T4UYFX`0NezK zYI(rEwJ*^XlAdjg@9S&w3AXID? z-)Py!49%!P9US6&R^~xoxzy5e{Q9Nn>Zg_Qmxs<#9@2+%u<`)2IXclz7 zIGLvt(l_d`>^)<>Tl{i|dZcVkI2P2&6i2q=%dBAAV8DQbj~0cLhanl>(@T4?$uZJf zu~hzKuY$uu+;LMc@W6J_1jq$a017wvFhSZuv3TeGO;%Ye1^$AKkO2@tbRb;xDwmd> z=7+O(Eb@zx0pPk_d-!jQ-odcrRgQ>dQ38q28>^J0xPPzc_t=}Ye>6;&393vR2U_Jb;Ee0{tF}`f&NJ-L4CKGXDXe?oop)2 z91KzV4@971?)WukCq&&0>O=L6@$KMD5`1NCMTWMeZ*hQc#b=B440zrA-FpNl(+5BQ z9h9~X1DvN&v2{p4r@a8vKAoU@2OpD(3Z)4yje)*eYK{cl83>-09wBSq#Y~sK7KlJy zWxxqR`WR0)FxZxWR)&Sjo3I+1U0LS2Z;u2PKjS%2D*+eC&^1>?#d#e0(vlGc-K>C+AdRXASL5Ag0WtKJ&GPsDa@phNWUKlmq&kNjWJo zjITyW1%gx!RFhG`WEy=Ew~;p7XTV6CH+-%T9saVATSD-OB$Eh*Qh_l+^;jsB0({EJ lz#oNlK}QRNn5{lM7KLBRY Installed\n\nRun LBRY now?" --title="LBRY Installed" --ok-label=" Yes" \ + --cancel-label=" No" --icon-name="system-software-install" + +case $? in + 0) RUN=1 # yes + ;; + 1) RUN=0 # no + ;; + *) RUN=0 # timeout or escape or whatever + ;; +esac + +if [ $RUN = 1 ]; then + xdg-open "lbry://lbry" +else + zenity --info --text="LBRY Installed\n\nNo problem. You can run LBRY later by going to + +lbry://lbry + +in your browser." --title="LBRY Installed" --icon-name="system-software-install" +fi + +) & diff --git a/packaging/ubuntu/ubuntu_package_setup.sh b/packaging/ubuntu/ubuntu_package_setup.sh index 4be10c8a7..f35a78b72 100755 --- a/packaging/ubuntu/ubuntu_package_setup.sh +++ b/packaging/ubuntu/ubuntu_package_setup.sh @@ -1,37 +1,124 @@ #!/bin/bash -# Tested on fresh Ubuntu 14.04 install. - -# wget https://raw.githubusercontent.com/lbryio/lbry/master/packaging/ubuntu/ubuntu_package_setup.sh -# bash ubuntu_package_setup.sh master - set -euo pipefail -BRANCH=${1:-master} +function HELP { + echo "Build a debian package for lbry" + echo "-----" + echo "When run without any arguments, this script expects the current directory" + echo "to be the main lbry repo and it builds what is in that directory" + echo + echo "Optional arguments:" + echo + echo "-c: clone a fresh copy of the repo" + echo "-b : use the specified branch of the lbry repo" + echo "-w : set the webui branch" + echo "-d : specifiy the build directory" + echo "-h: show help" + echo "-t: turn trace on" + exit 1 +} -BUILD_DIR="lbry-build-$(date +%Y%m%d-%H%M%S)" -mkdir "$BUILD_DIR" +CLONE=false +BUILD_DIR="" +BRANCH="" +WEB_UI_BRANCH="master" + +while getopts :hctb:w:d: FLAG; do + case $FLAG in + c) + CLONE=true + ;; + b) + BRANCH=${OPTARG} + ;; + w) + WEB_UI_BRANCH=${OPTARG} + ;; + d) + BUILD_DIR=${OPTARG} + ;; + t) + set -o xtrace + ;; + h) + HELP + ;; + \?) #unrecognized option - show help + echo "Option -$OPTARG not allowed." + HELP + ;; + :) + echo "Option -$OPTARG requires an argument." + HELP + ;; + esac +done + +shift $((OPTIND-1)) + + +SUDO='' +if (( $EUID != 0 )); then + SUDO='sudo' +fi + +if [ "$CLONE" = false ]; then + if [ `basename $PWD` != "lbry" ]; then + echo "Not currently in the lbry directory. Cowardly refusing to go forward" + exit 1 + fi + SOURCE_DIR=$PWD +fi + +if [ -z "${BUILD_DIR}" ]; then + if [ "$CLONE" = true ]; then + # build in the current directory + BUILD_DIR="lbry-build-$(date +%Y%m%d-%H%M%S)" + else + BUILD_DIR="../lbry-build-$(date +%Y%m%d-%H%M%S)" + fi +fi + +mkdir -p "$BUILD_DIR" cd "$BUILD_DIR" +if [ -z ${TRAVIS+x} ]; then + # if not on travis, its nice to see progress + QUIET="" +else + QUIET="-qq" +fi + # get the required OS packages -sudo add-apt-repository -y ppa:spotify-jyrki/dh-virtualenv -sudo apt-get update -sudo apt-get install -y build-essential git python-dev libffi-dev libssl-dev libgmp3-dev dh-virtualenv debhelper +$SUDO apt-get ${QUIET} update +$SUDO apt-get ${QUIET} install -y --no-install-recommends software-properties-common +$SUDO add-apt-repository -y ppa:spotify-jyrki/dh-virtualenv +$SUDO apt-get ${QUIET} update +$SUDO apt-get ${QUIET} install -y --no-install-recommends \ + build-essential git python-dev libffi-dev libssl-dev \ + libgmp3-dev dh-virtualenv debhelper wget python-pip fakeroot # need a modern version of pip (more modern than ubuntu default) -wget https://bootstrap.pypa.io/get-pip.py -sudo python get-pip.py -rm get-pip.py -sudo pip install make-deb - -# check out LBRY -git clone https://github.com/lbryio/lbry.git --branch "$BRANCH" +$SUDO pip install --upgrade pip +$SUDO pip install make-deb # build packages +# +# dpkg-buildpackage outputs its results into '..' so +# we need to move/clone lbry into the build directory +if [ "$CLONE" == true]; then + cp -a $SOURCE_DIR lbry +else + git clone https://github.com/lbryio/lbry.git +fi ( - cd lbry - make-deb - dpkg-buildpackage -us -uc + cd lbry + if [ -n "${BRANCH}" ]; then + git checkout "${BRANCH}" + fi + make-deb + dpkg-buildpackage -us -uc ) @@ -41,8 +128,20 @@ git clone https://github.com/lbryio/lbry.git --branch "$BRANCH" PACKAGE="$(ls | grep '.deb')" ar vx "$PACKAGE" mkdir control data -tar -xvzf control.tar.gz --directory control -tar -xvJf data.tar.xz --directory data +tar -xzf control.tar.gz --directory control + +# The output of the travis build is a +# tar.gz and the output locally is tar.xz. +# Instead of having tar detect the compression used, we +# could update the config to output the same in either spot. +# Unfortunately, doing so requires editting some auto-generated +# files: http://linux.spiney.org/forcing_gzip_compression_when_building_debian_packages +tar -xf data.tar.?z --directory data + +PACKAGING_DIR='lbry/packaging/ubuntu' + +# set web ui branch +sed -i "s/^WEB_UI_BRANCH='[^']\+'/WEB_UI_BRANCH='$WEB_UI_BRANCH'/" "$PACKAGING_DIR/lbry" # add files function addfile() { @@ -52,16 +151,16 @@ function addfile() { cp "$FILE" "data/$TARGET" echo "$(md5sum "data/$TARGET" | cut -d' ' -f1) $TARGET" >> control/md5sums } -PACKAGING_DIR='lbry/packaging/ubuntu' addfile "$PACKAGING_DIR/lbry" usr/share/python/lbrynet/bin/lbry addfile "$PACKAGING_DIR/lbry.desktop" usr/share/applications/lbry.desktop -#addfile lbry/packaging/ubuntu/lbry-init.conf etc/init/lbry.conf + +cat "$PACKAGING_DIR/postinst_append" >> control/postinst # repackage .deb -sudo chown -R root:root control data -tar -cvzf control.tar.gz -C control . -tar -cvJf data.tar.xz -C data . -sudo chown root:root debian-binary control.tar.gz data.tar.xz +$SUDO chown -R root:root control data +tar -czf control.tar.gz -C control . +tar -cJf data.tar.xz -C data . +$SUDO chown root:root debian-binary control.tar.gz data.tar.xz ar r "$PACKAGE" debian-binary control.tar.gz data.tar.xz # TODO: we can append to data.tar instead of extracting it all and recompressing diff --git a/setup.py b/setup.py index e4a9432f0..baf069fa7 100644 --- a/setup.py +++ b/setup.py @@ -33,7 +33,7 @@ gui_data_files = ['close2.gif', 'lbry-dark-242x80.gif', 'lbry-dark-icon.xbm', 'l gui_data_paths = [os.path.join(base_dir, 'lbrynet', 'lbrynet_gui', f) for f in gui_data_files] setup(name='lbrynet', - description='A fully decentralized network for distributing data', + description='A fully-decentralized content marketplace', version=__version__, maintainer='Jimmy Kiselak', maintainer_email='jimmy@lbry.io', From eb0dd827b1f46a66f0038fe7ed8124f2d23412bf Mon Sep 17 00:00:00 2001 From: Jack Date: Tue, 24 May 2016 17:54:44 -0400 Subject: [PATCH 03/17] delete old unused app and move uri handler to lbry-osx-app --- .../lbrynet_daemon/Apps/LBRYOSXStatusBar.py | 108 ------------------ lbrynet/lbrynet_daemon/Apps/LBRYURIHandler.py | 60 ---------- .../{Apps => daemon_scripts}/__init__.py | 0 setup_uri_handler.py | 25 ---- 4 files changed, 193 deletions(-) delete mode 100644 lbrynet/lbrynet_daemon/Apps/LBRYOSXStatusBar.py delete mode 100644 lbrynet/lbrynet_daemon/Apps/LBRYURIHandler.py rename lbrynet/lbrynet_daemon/{Apps => daemon_scripts}/__init__.py (100%) delete mode 100644 setup_uri_handler.py diff --git a/lbrynet/lbrynet_daemon/Apps/LBRYOSXStatusBar.py b/lbrynet/lbrynet_daemon/Apps/LBRYOSXStatusBar.py deleted file mode 100644 index 5ca433f72..000000000 --- a/lbrynet/lbrynet_daemon/Apps/LBRYOSXStatusBar.py +++ /dev/null @@ -1,108 +0,0 @@ -import rumps -import xmlrpclib -import os -import webbrowser -import subprocess -import argparse - - -class DaemonStatusBarApp(rumps.App): - def __init__(self): - icon_path = 'app.icns' - if os.path.isfile(icon_path): - rumps.App.__init__(self, name="LBRY", icon=icon_path, quit_button=None, - menu=["Open", "Preferences", "View balance", "Quit"]) - else: - rumps.App.__init__(self, name="LBRY", title="LBRY", quit_button=None, - menu=["Open", "Preferences", "View balance", "Quit"]) - - @rumps.timer(1) - def alert_daemon_start(self): - daemon = xmlrpclib.ServerProxy("http://localhost:7080/") - try: - start_msg = daemon.is_running() - if isinstance(start_msg, str): - rumps.notification(title='LBRY', subtitle='', message=str(start_msg), sound=True) - update_info = daemon.check_for_new_version() - update_msg = "" - for p in update_info: - if not p[0]: - update_msg += p[1] + "\n" - if update_msg: - update_msg += "\n Try running the installer again to fix this" - rumps.notification(title='LBRY', subtitle='', message=update_msg, sound=True) - except: - pass - - @rumps.clicked('Open') - def get_ui(self): - daemon = xmlrpclib.ServerProxy("http://localhost:7080/") - try: - daemon.is_running() - webbrowser.get('safari').open("lbry://lbry") - except: - try: - rumps.notification(title='LBRY', subtitle='', message="Couldn't connect to lbrynet daemon", sound=True) - except: - rumps.alert(title='LBRY', message="Couldn't connect to lbrynet daemon") - - @rumps.clicked("Preferences") - def prefs(self): - daemon = xmlrpclib.ServerProxy("http://localhost:7080/") - try: - daemon.is_running() - webbrowser.get('safari').open("lbry://settings") - except: - rumps.notification(title='LBRY', subtitle='', message="Couldn't connect to lbrynet daemon", sound=True) - - @rumps.clicked("View balance") - def disp_balance(self): - daemon = xmlrpclib.ServerProxy("http://localhost:7080/") - try: - balance = daemon.get_balance() - r = round(float(balance), 2) - try: - rumps.notification(title='LBRY', subtitle='', message=str("Your balance is %.2f LBC" % r), sound=False) - except: - rumps.alert(title='LBRY', message=str("Your balance is %.2f LBC" % r)) - - except: - try: - rumps.notification(title='LBRY', subtitle='', message="Couldn't connect to lbrynet daemon", sound=True) - except: - rumps.alert(title='LBRY', message="Couldn't connect to lbrynet daemon") - - @rumps.clicked('Quit') - def clean_quit(self): - daemon = xmlrpclib.ServerProxy("http://localhost:7080/") - try: - daemon.stop() - except: - pass - rumps.quit_application() - - -def main(): - parser = argparse.ArgumentParser(description="Launch lbrynet status bar application") - parser.add_argument("--startdaemon", - help="true or false, default true", - type=str, - default="true") - args = parser.parse_args() - - if str(args.startdaemon).lower() == "true": - daemon = xmlrpclib.ServerProxy('http://localhost:7080') - try: - daemon.is_running() - except: - subprocess.Popen("screen -dmS lbrynet bash -c " - "'PYTHONPATH=$PYTHONPATH:`cat /Users/${USER}/Library/Application\ Support/lbrynet/.python_path`; " - "PATH=$PATH:`cat /Users/${USER}/Library/Application\ Support/lbrynet/.lbry_bin_path`; " - "lbrynet-daemon --update=False'", shell=True) - - status_app = DaemonStatusBarApp() - status_app.run() - - -if __name__ == '__main__': - main() \ No newline at end of file diff --git a/lbrynet/lbrynet_daemon/Apps/LBRYURIHandler.py b/lbrynet/lbrynet_daemon/Apps/LBRYURIHandler.py deleted file mode 100644 index f6990cfea..000000000 --- a/lbrynet/lbrynet_daemon/Apps/LBRYURIHandler.py +++ /dev/null @@ -1,60 +0,0 @@ -import os -import json -import webbrowser -import subprocess -import sys - -from time import sleep -from jsonrpc.proxy import JSONRPCProxy - -API_CONNECTION_STRING = "http://localhost:5279/lbryapi" -UI_ADDRESS = "http://localhost:5279" - - -class LBRYURIHandler(object): - def __init__(self): - self.started_daemon = False - self.daemon = JSONRPCProxy.from_url(API_CONNECTION_STRING) - - def handle_osx(self, lbry_name): - try: - status = self.daemon.is_running() - except: - os.system("open /Applications/LBRY.app") - sleep(3) - - if lbry_name == "lbry" or lbry_name == "": - webbrowser.open(UI_ADDRESS) - else: - webbrowser.open(UI_ADDRESS + "/?watch=" + lbry_name) - - def handle_linux(self, lbry_name): - try: - status = self.daemon.is_running() - except: - cmd = r'DIR = "$( cd "$(dirname "${BASH_SOURCE[0]}" )" && pwd )"' \ - r'if [-z "$(pgrep lbrynet-daemon)"]; then' \ - r'echo "running lbrynet-daemon..."' \ - r'$DIR / lbrynet - daemon &' \ - r'sleep 3 # let the daemon load before connecting' \ - r'fi' - subprocess.Popen(cmd, shell=True) - - if lbry_name == "lbry" or lbry_name == "": - webbrowser.open(UI_ADDRESS) - else: - webbrowser.open(UI_ADDRESS + "/?watch=" + lbry_name) - - -def main(args): - if len(args) != 1: - args = ['lbry://lbry'] - - name = args[0][7:] - if sys.platform == "darwin": - LBRYURIHandler().handle_osx(lbry_name=name) - else: - LBRYURIHandler().handle_linux(lbry_name=name) - -if __name__ == "__main__": - main(sys.argv[1:]) diff --git a/lbrynet/lbrynet_daemon/Apps/__init__.py b/lbrynet/lbrynet_daemon/daemon_scripts/__init__.py similarity index 100% rename from lbrynet/lbrynet_daemon/Apps/__init__.py rename to lbrynet/lbrynet_daemon/daemon_scripts/__init__.py diff --git a/setup_uri_handler.py b/setup_uri_handler.py deleted file mode 100644 index e9ba6749c..000000000 --- a/setup_uri_handler.py +++ /dev/null @@ -1,25 +0,0 @@ -from setuptools import setup -import os - -APP = [os.path.join('lbrynet', 'lbrynet_daemon', 'Apps', 'LBRYURIHandler.py')] -DATA_FILES = [] -OPTIONS = {'argv_emulation': True, - 'packages': ['jsonrpc'], - 'plist': { - 'LSUIElement': True, - 'CFBundleIdentifier': 'io.lbry.LBRYURIHandler', - 'CFBundleURLTypes': [ - { - 'CFBundleURLTypes': 'LBRYURIHandler', - 'CFBundleURLSchemes': ['lbry'] - } - ] - } - } - -setup( - app=APP, - data_files=DATA_FILES, - options={'py2app': OPTIONS}, - setup_requires=['py2app'], -) \ No newline at end of file From 6b39e549f7ca44dc5f6b5b455ded0ea4695fc985 Mon Sep 17 00:00:00 2001 From: Jack Date: Wed, 25 May 2016 21:02:30 -0400 Subject: [PATCH 04/17] add developer id to travis.yml --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 1f5f36cd7..5215f0df5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,3 +30,4 @@ env: global: - secure: d3glJxXC0goiFETAP0JxMDEQoSNlh1fRDR76D3tjYY4Brxh2UgvUvpNJOAkFApynciA3n8kva4uBsv52jwnKG0VmCy/meaWowouhyi8ChdPpg7HZL84oC7rz3bZ2OA2iuYFPpAQrd6p7OMhmiCkeqhRKtW0YmOKn1F45kaIMnmq+bD0QK3IdP3QBdGz0rf6TQlNSQLSJtDAP0+HO+NaawJ1TQJXHUHA8mKnbOTRal4bH007uxfhvthXysm1QRQOfthGud2q/DN+f6RfCqF2Fv9l7NwR5BwVKluQYgqJjdhk9IOU7D+zW4Ne2fSt6V1PRAASAfyhtDiOA7B3ZUw2igimQ6rWyWao1csilq0RW9EmycOT8S7x5YgXltk0kVdNizdQeHCDII0mOxkIFBF0bs2ZgTgKasZrU5jGnEhV42eACl9CGeoT5/ots7an+zgCBoQ8c4HGkMQyKV4uNnvKD+gq1FPDuwlHEUHhbjy5uRI4kAVMzjsCJX7XBSumFELuwiOpE/XguiJAV/LvjYbN6OFemJ6HUOhep6kgq7Zcoxh+UnaMixiq6NQTSoLpffatSxCM9EG8uBoXZJBH45cS9aD0eP9zjV2COCC0Iom5BQFhkArgMVodYtcrNevPUA3AuvJOjdJpSwKB4fqaU/CDqThw/ieZQlzaZqWIM5RnHf6Y= - secure: n08IQBOLaipKzAwS5aQM+JfFtvLSCrWqFFztTMeElmck6IJOVnPlDvPYRCrRmrXtgFmJ1G6hEU5Vri086MkTaISq3V04ndquhz7nv93Pp1do42vWPmvmFHdMrhaOGPVtYZ4m4XfgNcA+NC79V9X2VQ+MoQgCRhcGlgGomxJBHc924bGxppkIrDMljvSipkR5v9g/UFGRPYJ6BIF0imWrdsHUfqhbZMdAxAmdlBAjx/ZUOmUyPWYttHGYEW4HpLwvkU2K+sJrt1emogvR7GT94CadtqjmRsiKh0Y2lHHhHkqV1J941T08p9hyKU6S4fHQqPHfpkHRiPeJQY8JiipCbuERn0YeGqsp5q0jRE6PFUxCWcvlWqc7XNihOMQAb5JQD8vF2Lvs3YYycFBgZkIOaZQFbemqkx2pnQMjVF7GCZp4u+p5yo8iBeImYFYdkqIBHqPqsKxM9aBfe05XhAmi/6jUP0L2xikVFvJZDNxKP2uyqXVbSMR8KF8v1eaYnsSL3vzBolwkn96zaj2E/lItHaNeNIXvutAsGuy2ybTXeabm5rTaUr+5cmdqx4JqmyGM3DinGFik/fzpLUJCd4GaC1n1taLy6aUUW2oH46QRqLLAAjZ4AWao92Eb4cnFiVxxBqVG3efWaoEt+uW7/hzqG27iieoVqCXs3Hd5g4bCYyo= + - secure: Unv3cSrgeT6fdHbWWXqBAH2WRpudiX1aPG90HFT2zwfdvXBTS8zUSTmsQJu0UJjsYUjErqjjwMNrzh0HFn1MaIc31aJQurC2CuKXZvzViBXh1abr5WBYzXEvkXuwgzhpcyNcg9SU2Qmhmxtdb1r1WV+HNnTofQahrWVCmUJh8mCunrXnFRMaTwW0qBfUpW9UvMaZ4N9RWdkkEAMV0at/kuP2MnYgW0EWA/42BV7N92HtWxAyvmiHyk2EjQ3cQ9avb4/6C2vFIhiFaYzU3ZR5VtT/XQxiuRRtVYj9aG4RpXhcXMg1LTTqCJ9JJTvcpQX75gpKz3pLr7rA3hVoV5nueHLpyGwoZU6iy+8auCA/6WnP+xGAgL/JRq9ozisr8NVD5BX+BrQED3FAxH9uARM1LVR24PphErctqzdDTvjkvdj9rHROdlD5DfSRuroNjwsRrrRTENruekWyYp0W3pORQQTO/A0wE+/YWfKR941j9w+Q1GdYWcvDnkPzTnL2o7D76N9REsy2jpIIT9dqsOcgp1fvCYaTN1k5GXlmgRydsRWBnelr0vD1uQxEyGhBzCrPwrCJz2U5VZDMmoCBPEMwZOLaRNkLbGX0Qn3gQqhKLeM/SYeJSuuxTO0WVyQvheNJIaE+crhZrk8sB5SrdN9hGGD4NYYUXrdcgEt91K78/BE= From 8922fd6dde76dc4c413f566ebe5142fa52aa1fd3 Mon Sep 17 00:00:00 2001 From: Jack Date: Wed, 25 May 2016 22:28:45 -0400 Subject: [PATCH 05/17] add startup scripts -populate blockchainname.db on first run from older version --- lbrynet/__init__.py | 2 +- lbrynet/core/LBRYcrdWallet.py | 5 ++ lbrynet/lbrynet_daemon/LBRYDaemon.py | 78 +++++++++++++++++-- lbrynet/lbrynet_daemon/LBRYDaemonServer.py | 51 +----------- .../daemon_scripts/Autofetcher.py | 68 ++++++++++++++++ .../daemon_scripts/migrateto025.py | 33 ++++++++ 6 files changed, 179 insertions(+), 58 deletions(-) create mode 100644 lbrynet/lbrynet_daemon/daemon_scripts/Autofetcher.py create mode 100644 lbrynet/lbrynet_daemon/daemon_scripts/migrateto025.py diff --git a/lbrynet/__init__.py b/lbrynet/__init__.py index 26dafe45d..3d80f11a2 100644 --- a/lbrynet/__init__.py +++ b/lbrynet/__init__.py @@ -4,5 +4,5 @@ import logging logging.getLogger(__name__).addHandler(logging.NullHandler()) -version = (0, 2, 4) +version = (0, 2, 5) __version__ = ".".join([str(x) for x in version]) diff --git a/lbrynet/core/LBRYcrdWallet.py b/lbrynet/core/LBRYcrdWallet.py index 8ec200611..17fd0e229 100644 --- a/lbrynet/core/LBRYcrdWallet.py +++ b/lbrynet/core/LBRYcrdWallet.py @@ -295,6 +295,11 @@ class LBRYWallet(object): d.addCallback(self._get_stream_info_from_value, name) return d + def get_txid_for_name(self, name): + d = self._get_value_for_name(name) + d.addCallback(lambda r: None if 'txid' not in r else r['txid']) + return d + def get_stream_info_from_txid(self, name, txid): d = self.get_claims_from_tx(txid) diff --git a/lbrynet/lbrynet_daemon/LBRYDaemon.py b/lbrynet/lbrynet_daemon/LBRYDaemon.py index 0a10dff9d..320ab6a51 100644 --- a/lbrynet/lbrynet_daemon/LBRYDaemon.py +++ b/lbrynet/lbrynet_daemon/LBRYDaemon.py @@ -151,6 +151,7 @@ class LBRYDaemon(jsonrpc.JSONRPC): self.waiting_on = {} self.streams = {} self.known_dht_nodes = KNOWN_DHT_NODES + self.first_run_after_update = False self.platform_info = { "processor": platform.processor(), "python_version: ": platform.python_version(), @@ -197,7 +198,9 @@ class LBRYDaemon(jsonrpc.JSONRPC): 'use_upnp': True, 'start_lbrycrdd': True, 'requested_first_run_credits': False, - 'cache_time': DEFAULT_CACHE_TIME + 'cache_time': DEFAULT_CACHE_TIME, + 'startup_scripts': [], + 'last_version': {'lbrynet': lbrynet_version, 'lbryum': lbryum_version} } if os.path.isfile(self.daemon_conf): @@ -234,6 +237,20 @@ class LBRYDaemon(jsonrpc.JSONRPC): self.session_settings = settings_dict + if 'last_version' in missing_settings.keys(): + self.session_settings['last_version'] = None + + if self.session_settings['last_version'] != self.default_settings['last_version']: + self.session_settings['last_version'] = self.default_settings['last_version'] + f = open(self.daemon_conf, "w") + f.write(json.dumps(self.session_settings)) + f.close() + + self.first_run_after_update = True + log.info("First run after update") + if lbrynet_version == '0.2.5': + self.session_settings['startup_scripts'].append({'script_name': 'migrateto025', 'run_once': True}) + self.run_on_startup = self.session_settings['run_on_startup'] self.data_rate = self.session_settings['data_rate'] self.max_key_fee = self.session_settings['max_key_fee'] @@ -252,6 +269,7 @@ class LBRYDaemon(jsonrpc.JSONRPC): self.start_lbrycrdd = self.session_settings['start_lbrycrdd'] self.requested_first_run_credits = self.session_settings['requested_first_run_credits'] self.cache_time = self.session_settings['cache_time'] + self.startup_scripts = self.session_settings['startup_scripts'] if os.path.isfile(os.path.join(self.db_dir, "stream_info_cache.json")): f = open(os.path.join(self.db_dir, "stream_info_cache.json"), "r") @@ -394,6 +412,10 @@ class LBRYDaemon(jsonrpc.JSONRPC): self.announced_startup = True self.startup_status = STARTUP_STAGES[5] log.info("[" + str(datetime.now()) + "] Started lbrynet-daemon") + if len(self.startup_scripts): + log.info("Scheduling scripts") + reactor.callLater(3, self._run_scripts) + # self.lbrynet_connection_checker.start(3600) if self.first_run: @@ -608,6 +630,9 @@ class LBRYDaemon(jsonrpc.JSONRPC): def _shutdown(self): log.info("Closing lbrynet session") log.info("Status at time of shutdown: " + self.startup_status[0]) + self.internet_connection_checker.stop() + self.version_checker.stop() + self.connection_problem_checker.stop() d = self._upload_log(name_prefix="close", exclude_previous=False if self.first_run else True) d.addCallback(lambda _: self._stop_server()) @@ -1017,19 +1042,31 @@ class LBRYDaemon(jsonrpc.JSONRPC): f.close() return defer.succeed(True) - def _resolve_name(self, name): + def _resolve_name(self, name, force_refresh=False): def _cache_stream_info(stream_info): + def _add_txid(txid): + self.name_cache[name]['txid'] = txid + return defer.succeed(None) + self.name_cache[name] = {'claim_metadata': stream_info, 'timestamp': self._get_long_count_timestamp()} - d = self._update_claim_cache() + d = self.session.wallet.get_txid_for_name(name) + d.addCallback(_add_txid) + d.addCallback(lambda _: self._update_claim_cache()) d.addCallback(lambda _: self.name_cache[name]['claim_metadata']) + return d - if name in self.name_cache.keys(): - if (self._get_long_count_timestamp() - self.name_cache[name]['timestamp']) < self.cache_time: - log.info("[" + str(datetime.now()) + "] Returning cached stream info for lbry://" + name) - d = defer.succeed(self.name_cache[name]['claim_metadata']) + if not force_refresh: + if name in self.name_cache.keys(): + if (self._get_long_count_timestamp() - self.name_cache[name]['timestamp']) < self.cache_time: + log.info("[" + str(datetime.now()) + "] Returning cached stream info for lbry://" + name) + d = defer.succeed(self.name_cache[name]['claim_metadata']) + else: + log.info("[" + str(datetime.now()) + "] Refreshing stream info for lbry://" + name) + d = self.session.wallet.get_stream_info_for_name(name) + d.addCallbacks(_cache_stream_info, lambda _: defer.fail(UnknownNameError)) else: - log.info("[" + str(datetime.now()) + "] Refreshing stream info for lbry://" + name) + log.info("[" + str(datetime.now()) + "] Resolving stream info for lbry://" + name) d = self.session.wallet.get_stream_info_for_name(name) d.addCallbacks(_cache_stream_info, lambda _: defer.fail(UnknownNameError)) else: @@ -1221,6 +1258,31 @@ class LBRYDaemon(jsonrpc.JSONRPC): requests.post(URL, json.dumps({"text": msg})) return defer.succeed(None) + def _run_scripts(self): + if len([k for k in self.startup_scripts if 'run_once' in k.keys()]): + log.info("Removing one time startup scripts") + f = open(self.daemon_conf, "r") + initialsettings = json.loads(f.read()) + f.close() + t = [s for s in self.startup_scripts if 'run_once' not in s.keys()] + initialsettings['startup_scripts'] = t + f = open(self.daemon_conf, "w") + f.write(json.dumps(initialsettings)) + f.close() + + for script in self.startup_scripts: + if script['script_name'] == 'migrateto025': + log.info("Running migrator to 0.2.5") + from lbrynet.lbrynet_daemon.daemon_scripts.migrateto025 import run as run_migrate + run_migrate(self) + + if script['script_name'] == 'Autofetcher': + log.info("Starting autofetcher script") + from lbrynet.lbrynet_daemon.daemon_scripts.Autofetcher import run as run_autofetcher + run_autofetcher(self) + + return defer.succeed(None) + def _render_response(self, result, code): return defer.succeed({'result': result, 'code': code}) diff --git a/lbrynet/lbrynet_daemon/LBRYDaemonServer.py b/lbrynet/lbrynet_daemon/LBRYDaemonServer.py index 85494d21c..5843bca06 100644 --- a/lbrynet/lbrynet_daemon/LBRYDaemonServer.py +++ b/lbrynet/lbrynet_daemon/LBRYDaemonServer.py @@ -189,50 +189,6 @@ class HostedLBRYFile(resource.Resource): call.cancel() -class MyLBRYFiles(resource.Resource): - isLeaf = False - - def __init__(self): - resource.Resource.__init__(self) - self.files_table = None - - def delayed_render(self, request, result): - request.write(result.encode('utf-8')) - request.finish() - - def render_GET(self, request): - self.files_table = None - api = jsonrpc.Proxy(API_CONNECTION_STRING) - d = api.callRemote("get_lbry_files", {}) - d.addCallback(self._get_table) - d.addCallback(lambda results: self.delayed_render(request, results)) - - return server.NOT_DONE_YET - - def _get_table(self, files): - if not self.files_table: - self.files_table = r'My LBRY files' - self.files_table += r'' - self.files_table += r'' - self.files_table += r'' - self.files_table += r'' - self.files_table += r'' - self.files_table += r'' - return self._get_table(files) - if not len(files): - self.files_table += r'
Stream nameCompletedToggleRemove
' - return self.files_table - else: - f = files.pop() - self.files_table += r'' - self.files_table += r'%s' % (f['stream_name']) - self.files_table += r'%s' % (f['completed']) - self.files_table += r'Start' if f['stopped'] else r'Stop' - self.files_table += r'Delete' - self.files_table += r'' - return self._get_table(files) - - class LBRYDaemonServer(object): def __init__(self): self.data_dir = user_data_dir("LBRY") @@ -336,12 +292,9 @@ class LBRYDaemonServer(object): def _setup_server(self, ui_ver, wallet): self._api = LBRYDaemon(ui_ver, wallet_type=wallet) self.root = LBRYindex(self.ui_dir) - self.root.putChild("css", static.File(os.path.join(self.ui_dir, "css"))) - self.root.putChild("font", static.File(os.path.join(self.ui_dir, "font"))) - self.root.putChild("img", static.File(os.path.join(self.ui_dir, "img"))) - self.root.putChild("js", static.File(os.path.join(self.ui_dir, "js"))) + for d in [i[0] for i in os.walk(self.ui_dir) if os.path.dirname(i[0]) == self.ui_dir]: + self.root.putChild(os.path.basename(d), static.File(d)) self.root.putChild("view", HostedLBRYFile(self._api)) - self.root.putChild("files", MyLBRYFiles()) self.root.putChild(API_ADDRESS, self._api) return defer.succeed(True) diff --git a/lbrynet/lbrynet_daemon/daemon_scripts/Autofetcher.py b/lbrynet/lbrynet_daemon/daemon_scripts/Autofetcher.py new file mode 100644 index 000000000..cd7ed02cb --- /dev/null +++ b/lbrynet/lbrynet_daemon/daemon_scripts/Autofetcher.py @@ -0,0 +1,68 @@ +import json +import logging.handlers +import sys +import os + +from appdirs import user_data_dir +from twisted.internet.task import LoopingCall +from twisted.internet import reactor + + +if sys.platform != "darwin": + log_dir = os.path.join(os.path.expanduser("~"), ".lbrynet") +else: + log_dir = user_data_dir("LBRY") + +if not os.path.isdir(log_dir): + os.mkdir(log_dir) + +LOG_FILENAME = os.path.join(log_dir, 'lbrynet-daemon.log') + +if os.path.isfile(LOG_FILENAME): + f = open(LOG_FILENAME, 'r') + PREVIOUS_LOG = len(f.read()) + f.close() +else: + PREVIOUS_LOG = 0 + +log = logging.getLogger(__name__) +handler = logging.handlers.RotatingFileHandler(LOG_FILENAME, maxBytes=2097152, backupCount=5) +log.addHandler(handler) +log.setLevel(logging.INFO) + + +class Autofetcher(object): + """ + Download name claims as they occur + """ + + def __init__(self, api): + self._api = api + self._checker = LoopingCall(self._check_for_new_claims) + self.best_block = None + + def start(self): + reactor.addSystemEventTrigger('before', 'shutdown', self.stop) + self._checker.start(5) + + def stop(self): + log.info("Stopping autofetcher") + self._checker.stop() + + def _check_for_new_claims(self): + block = self._api.get_best_blockhash() + if block != self.best_block: + log.info("Checking new block for name claims, block hash: %s" % block) + self.best_block = block + transactions = self._api.get_block({'blockhash': block})['tx'] + for t in transactions: + c = self._api.get_claims_for_tx({'txid': t}) + if len(c): + for i in c: + log.info("Downloading stream for claim txid: %s" % t) + self._api.get({'name': t, 'stream_info': json.loads(i['value'])}) + + +def run(api): + fetcher = Autofetcher(api) + fetcher.start() \ No newline at end of file diff --git a/lbrynet/lbrynet_daemon/daemon_scripts/migrateto025.py b/lbrynet/lbrynet_daemon/daemon_scripts/migrateto025.py new file mode 100644 index 000000000..9283b48aa --- /dev/null +++ b/lbrynet/lbrynet_daemon/daemon_scripts/migrateto025.py @@ -0,0 +1,33 @@ +from twisted.internet import defer + + +class migrator(object): + """ + Re-resolve lbry names to write missing data to blockchain.db and to cache the nametrie + """ + + def __init__(self, api): + self._api = api + + def start(self): + def _resolve_claims(claimtrie): + claims = [i for i in claimtrie if 'txid' in i.keys()] + r = defer.DeferredList([self._api._resolve_name(claim['name'], force_refresh=True) for claim in claims], consumeErrors=True) + return r + + def _restart_lbry_files(): + def _restart_lbry_file(lbry_file): + return lbry_file.restore() + + r = defer.DeferredList([_restart_lbry_file(lbry_file) for lbry_file in self._api.lbry_file_manager.lbry_files if not lbry_file.txid], consumeErrors=True) + r.callback(None) + return r + + d = self._api.session.wallet.get_nametrie() + d.addCallback(_resolve_claims) + d.addCallback(lambda _: _restart_lbry_files()) + + +def run(api): + refresher = migrator(api) + refresher.start() From e7a580fd3a3be55025abe386c638690b363c58f2 Mon Sep 17 00:00:00 2001 From: Jack Date: Sun, 29 May 2016 23:18:30 -0400 Subject: [PATCH 06/17] add lbry_ui_manager --- lbrynet/conf.py | 4 +- lbrynet/lbrynet_daemon/LBRYDaemon.py | 127 +++++++----- lbrynet/lbrynet_daemon/LBRYDaemonServer.py | 160 +++------------ lbrynet/lbrynet_daemon/LBRYUIManager.py | 219 +++++++++++++++++++++ 4 files changed, 330 insertions(+), 180 deletions(-) create mode 100644 lbrynet/lbrynet_daemon/LBRYUIManager.py diff --git a/lbrynet/conf.py b/lbrynet/conf.py index dfae3d274..526dc711d 100644 --- a/lbrynet/conf.py +++ b/lbrynet/conf.py @@ -36,8 +36,10 @@ UI_ADDRESS = "http://" + API_INTERFACE + ":" + str(API_PORT) PROTOCOL_PREFIX = "lbry" DEFAULT_WALLET = "lbryum" +WALLET_TYPES = ["lbryum", "lbrycrd"] DEFAULT_TIMEOUT = 30 DEFAULT_MAX_SEARCH_RESULTS = 25 DEFAULT_MAX_KEY_FEE = 100.0 DEFAULT_SEARCH_TIMEOUT = 3.0 -DEFAULT_CACHE_TIME = 3600 \ No newline at end of file +DEFAULT_CACHE_TIME = 3600 +DEFAULT_UI_BRANCH = "master" diff --git a/lbrynet/lbrynet_daemon/LBRYDaemon.py b/lbrynet/lbrynet_daemon/LBRYDaemon.py index 320ab6a51..0d3535012 100644 --- a/lbrynet/lbrynet_daemon/LBRYDaemon.py +++ b/lbrynet/lbrynet_daemon/LBRYDaemon.py @@ -31,13 +31,14 @@ from lbrynet.core.Error import UnknownNameError, InsufficientFundsError from lbrynet.lbryfile.StreamDescriptor import LBRYFileStreamType from lbrynet.lbryfile.client.LBRYFileDownloader import LBRYFileSaverFactory, LBRYFileOpenerFactory from lbrynet.lbryfile.client.LBRYFileOptions import add_lbry_file_to_sd_identifier +from lbrynet.lbrynet_daemon.LBRYUIManager import LBRYUIManager from lbrynet.lbrynet_daemon.LBRYDownloader import GetStream from lbrynet.lbrynet_daemon.LBRYPublisher import Publisher from lbrynet.core.utils import generate_id from lbrynet.lbrynet_console.LBRYSettings import LBRYSettings from lbrynet.conf import MIN_BLOB_DATA_PAYMENT_RATE, DEFAULT_MAX_SEARCH_RESULTS, KNOWN_DHT_NODES, DEFAULT_MAX_KEY_FEE, \ - DEFAULT_WALLET, DEFAULT_SEARCH_TIMEOUT, DEFAULT_CACHE_TIME -from lbrynet.conf import API_CONNECTION_STRING, API_PORT, API_ADDRESS, DEFAULT_TIMEOUT, UI_ADDRESS + DEFAULT_WALLET, DEFAULT_SEARCH_TIMEOUT, DEFAULT_CACHE_TIME, DEFAULT_UI_BRANCH +from lbrynet.conf import DEFAULT_TIMEOUT, WALLET_TYPES from lbrynet.core.StreamDescriptor import StreamDescriptorIdentifier, download_sd_blob from lbrynet.core.Session import LBRYSession from lbrynet.core.PTCWallet import PTCWallet @@ -129,7 +130,7 @@ class LBRYDaemon(jsonrpc.JSONRPC): isLeaf = True - def __init__(self, ui_version_info, wallet_type=DEFAULT_WALLET): + def __init__(self, root, wallet_type=DEFAULT_WALLET): jsonrpc.JSONRPC.__init__(self) reactor.addSystemEventTrigger('before', 'shutdown', self._shutdown) @@ -139,9 +140,10 @@ class LBRYDaemon(jsonrpc.JSONRPC): self.connected_to_internet = True self.connection_problem = None self.query_handlers = {} - self.ui_version = ui_version_info.replace('\n', '') self.git_lbrynet_version = None self.git_lbryum_version = None + self.ui_version = None + self.ip = None self.wallet_type = wallet_type self.first_run = None self.log_file = LOG_FILENAME @@ -152,20 +154,6 @@ class LBRYDaemon(jsonrpc.JSONRPC): self.streams = {} self.known_dht_nodes = KNOWN_DHT_NODES self.first_run_after_update = False - self.platform_info = { - "processor": platform.processor(), - "python_version: ": platform.python_version(), - "platform": platform.platform(), - "os_release": platform.release(), - "os_system": platform.system(), - "lbrynet_version: ": lbrynet_version, - "lbryum_version: ": lbryum_version, - "ui_version": self.ui_version, - } - try: - self.platform_info['ip'] = json.load(urlopen('http://jsonip.com'))['ip'] - except: - self.platform_info['ip'] = "Could not determine" if os.name == "nt": from lbrynet.winhelpers.knownpaths import get_path, FOLDERID, UserHandle @@ -261,7 +249,7 @@ class LBRYDaemon(jsonrpc.JSONRPC): self.search_timeout = self.session_settings['search_timeout'] self.download_timeout = self.session_settings['download_timeout'] self.max_search_results = self.session_settings['max_search_results'] - self.wallet_type = self.session_settings['wallet_type'] if self.session_settings['wallet_type'] == wallet_type else wallet_type + self.wallet_type = self.session_settings['wallet_type'] if self.session_settings['wallet_type'] in WALLET_TYPES else wallet_type self.delete_blobs_on_remove = self.session_settings['delete_blobs_on_remove'] self.peer_port = self.session_settings['peer_port'] self.dht_node_port = self.session_settings['dht_node_port'] @@ -319,6 +307,7 @@ class LBRYDaemon(jsonrpc.JSONRPC): self.sd_identifier = StreamDescriptorIdentifier() self.stream_info_manager = TempLBRYFileMetadataManager() self.settings = LBRYSettings(self.db_dir) + self.lbry_ui_manager = LBRYUIManager(root) self.blob_request_payment_rate_manager = None self.lbry_file_metadata_manager = None self.lbry_file_manager = None @@ -392,7 +381,7 @@ class LBRYDaemon(jsonrpc.JSONRPC): log.error(failure) return jsonrpclib.Fault(self.FAILURE, "error") - def setup(self): + def setup(self, branch=DEFAULT_UI_BRANCH, user_specified=False): def _log_starting_vals(): d = self._get_lbry_files() d.addCallback(lambda r: json.dumps([d[1] for d in r])) @@ -437,6 +426,7 @@ class LBRYDaemon(jsonrpc.JSONRPC): self.connection_problem_checker.start(1) d = defer.Deferred() + d.addCallback(lambda _: self.lbry_ui_manager.setup(branch=branch, user_specified=user_specified)) d.addCallback(lambda _: self._initial_setup()) d.addCallback(lambda _: threads.deferToThread(self._setup_data_directory)) d.addCallback(lambda _: self._check_db_migration()) @@ -455,9 +445,29 @@ class LBRYDaemon(jsonrpc.JSONRPC): return defer.succeed(None) + def _get_platform(self): + r = { + "processor": platform.processor(), + "python_version: ": platform.python_version(), + "platform": platform.platform(), + "os_release": platform.release(), + "os_system": platform.system(), + "lbrynet_version: ": lbrynet_version, + "lbryum_version: ": lbryum_version, + "ui_version": self.lbry_ui_manager.loaded_git_version, + } + if not self.ip: + try: + r['ip'] = json.load(urlopen('http://jsonip.com'))['ip'] + self.ip = r['ip'] + except: + r['ip'] = "Could not determine" + + return r + def _initial_setup(self): def _log_platform(): - log.info("Platform: " + json.dumps(self.platform_info)) + log.info("Platform: " + json.dumps(self._get_platform())) return defer.succeed(None) d = _log_platform() @@ -545,10 +555,13 @@ class LBRYDaemon(jsonrpc.JSONRPC): return defer.succeed(True) def _stop_server(self): - if self.lbry_server_port is not None: - self.lbry_server_port, p = None, self.lbry_server_port - return defer.maybeDeferred(p.stopListening) - else: + try: + if self.lbry_server_port is not None: + self.lbry_server_port, p = None, self.lbry_server_port + return defer.maybeDeferred(p.stopListening) + else: + return defer.succeed(True) + except AttributeError: return defer.succeed(True) def _setup_server(self): @@ -630,17 +643,21 @@ class LBRYDaemon(jsonrpc.JSONRPC): def _shutdown(self): log.info("Closing lbrynet session") log.info("Status at time of shutdown: " + self.startup_status[0]) - self.internet_connection_checker.stop() - self.version_checker.stop() - self.connection_problem_checker.stop() + if self.internet_connection_checker.running: + self.internet_connection_checker.stop() + if self.version_checker.running: + self.version_checker.stop() + if self.connection_problem_checker.running: + self.connection_problem_checker.stop() d = self._upload_log(name_prefix="close", exclude_previous=False if self.first_run else True) d.addCallback(lambda _: self._stop_server()) + d.addErrback(lambda err: True) d.addCallback(lambda _: self.lbry_file_manager.stop()) - d.addErrback(lambda err: log.info("Bad server shutdown: " + err.getTraceback())) + d.addErrback(lambda err: True) if self.session is not None: d.addCallback(lambda _: self.session.shut_down()) - d.addErrback(lambda err: log.info("Bad session shutdown: " + err.getTraceback())) + d.addErrback(lambda err: True) return d def _update_settings(self, settings): @@ -819,7 +836,8 @@ class LBRYDaemon(jsonrpc.JSONRPC): log.info("Using PTC wallet") d = defer.succeed(PTCWallet(self.db_dir)) else: - d = defer.fail() + log.info("Requested unknown wallet '%s', using default lbryum" % self.wallet_type) + d = defer.succeed(LBRYumWallet(self.db_dir)) d.addCallback(lambda wallet: {"wallet": wallet}) return d @@ -1392,10 +1410,11 @@ class LBRYDaemon(jsonrpc.JSONRPC): "remote_lbryum": most recent lbryum version available from github """ + platform_info = self._get_platform() msg = { - 'platform': self.platform_info['platform'], - 'os_release': self.platform_info['os_release'], - 'os_system': self.platform_info['os_system'], + 'platform': platform_info['platform'], + 'os_release': platform_info['os_release'], + 'os_system': platform_info['os_system'], 'lbrynet_version': lbrynet_version, 'lbryum_version': lbryum_version, 'ui_version': self.ui_version, @@ -2113,25 +2132,18 @@ class LBRYDaemon(jsonrpc.JSONRPC): # # return d - def jsonrpc_check_for_new_version(self): + def jsonrpc_log(self, message): """ - Checks local version against versions in __init__.py and version.py in the lbrynet and lbryum repos + Log message Args: - None + message: message to be logged Returns: - true/false, true meaning that there is a new version available + True """ - def _check_version(): - if (lbrynet_version >= self.git_lbrynet_version) and (lbryum_version >= self.git_lbryum_version): - log.info("[" + str(datetime.now()) + "] Up to date") - return self._render_response(False, OK_CODE) - else: - log.info("[" + str(datetime.now()) + "] Updates available") - return self._render_response(True, OK_CODE) - - return _check_version() + log.info(message) + return self._render_response(True, OK_CODE) def jsonrpc_upload_log(self, p=None): """ @@ -2140,7 +2152,7 @@ class LBRYDaemon(jsonrpc.JSONRPC): Args, optional: 'name_prefix': prefix to indicate what is requesting the log upload 'exclude_previous': true/false, whether or not to exclude previous sessions from upload, defaults on true - Returns + Returns: True """ @@ -2171,3 +2183,22 @@ class LBRYDaemon(jsonrpc.JSONRPC): d.addCallback(lambda _: self._log_to_slack(p['message'])) d.addCallback(lambda _: self._render_response(True, OK_CODE)) return d + + def jsonrpc_configure_ui(self, p): + """ + Configure the UI being hosted + + Args, optional: + 'branch': a branch name on lbryio/lbry-web-ui + 'path': path to a ui folder + """ + + if 'path' in p.keys(): + d = self.lbry_ui_manager.setup(user_specified=p['path']) + elif 'branch' in p.keys(): + d = self.lbry_ui_manager.setup(branch=p['branch']) + else: + d = self.lbry_ui_manager.setup() + d.addCallback(lambda r: self._render_response(r, OK_CODE)) + + return d \ No newline at end of file diff --git a/lbrynet/lbrynet_daemon/LBRYDaemonServer.py b/lbrynet/lbrynet_daemon/LBRYDaemonServer.py index 5843bca06..948c3ea2e 100644 --- a/lbrynet/lbrynet_daemon/LBRYDaemonServer.py +++ b/lbrynet/lbrynet_daemon/LBRYDaemonServer.py @@ -18,7 +18,7 @@ from txjsonrpc.web import jsonrpc from zope.interface import implements from lbrynet.lbrynet_daemon.LBRYDaemon import LBRYDaemon -from lbrynet.conf import API_CONNECTION_STRING, API_ADDRESS, DEFAULT_WALLET, UI_ADDRESS +from lbrynet.conf import API_CONNECTION_STRING, API_ADDRESS, DEFAULT_WALLET, UI_ADDRESS, DEFAULT_UI_BRANCH if sys.platform != "darwin": @@ -149,22 +149,23 @@ class HostedLBRYFile(resource.Resource): self._producer = None resource.Resource.__init__(self) - def makeProducer(self, request, stream): - def _save_producer(producer): - self._producer = producer - return defer.succeed(None) - - range_header = request.getAllHeaders()['range'].replace('bytes=', '').split('-') - start, stop = int(range_header[0]), range_header[1] - log.info("[" + str(datetime.now()) + "] GET range %s-%s" % (start, stop)) - path = os.path.join(self._api.download_directory, stream.file_name) - - d = stream.get_total_bytes() - d.addCallback(lambda size: _save_producer(LBRYFileStreamer(request, path, start, stop, size))) - d.addCallback(lambda _: request.registerProducer(self._producer, streaming=True)) - # request.notifyFinish().addCallback(lambda _: self._producer.stopProducing()) - request.notifyFinish().addErrback(self._responseFailed, d) - return d + # todo: fix LBRYFileStreamer and use it instead of static.File + # def makeProducer(self, request, stream): + # def _save_producer(producer): + # self._producer = producer + # return defer.succeed(None) + # + # range_header = request.getAllHeaders()['range'].replace('bytes=', '').split('-') + # start, stop = int(range_header[0]), range_header[1] + # log.info("[" + str(datetime.now()) + "] GET range %s-%s" % (start, stop)) + # path = os.path.join(self._api.download_directory, stream.file_name) + # + # d = stream.get_total_bytes() + # d.addCallback(lambda size: _save_producer(LBRYFileStreamer(request, path, start, stop, size))) + # d.addCallback(lambda _: request.registerProducer(self._producer, streaming=True)) + # # request.notifyFinish().addCallback(lambda _: self._producer.stopProducing()) + # request.notifyFinish().addErrback(self._responseFailed, d) + # return d def render_GET(self, request): if 'name' in request.args.keys(): @@ -182,125 +183,22 @@ class HostedLBRYFile(resource.Resource): request.finish() return server.NOT_DONE_YET - def _responseFailed(self, err, call): - call.addErrback(lambda err: err.trap(error.ConnectionDone)) - call.addErrback(lambda err: err.trap(defer.CancelledError)) - call.addErrback(lambda err: log.info("Error: " + str(err))) - call.cancel() + # def _responseFailed(self, err, call): + # call.addErrback(lambda err: err.trap(error.ConnectionDone)) + # call.addErrback(lambda err: err.trap(defer.CancelledError)) + # call.addErrback(lambda err: log.info("Error: " + str(err))) + # call.cancel() class LBRYDaemonServer(object): - def __init__(self): - self.data_dir = user_data_dir("LBRY") - if not os.path.isdir(self.data_dir): - os.mkdir(self.data_dir) - self.version_dir = os.path.join(self.data_dir, "ui_version_history") - if not os.path.isdir(self.version_dir): - os.mkdir(self.version_dir) - self.config = os.path.join(self.version_dir, "active.json") - self.ui_dir = os.path.join(self.data_dir, "lbry-web-ui") - self.git_version = None - self._api = None - self.root = None - - if not os.path.isfile(os.path.join(self.config)): - self.loaded_git_version = None - else: - try: - f = open(self.config, "r") - loaded_ui = json.loads(f.read()) - f.close() - self.loaded_git_version = loaded_ui['commit'] - self.loaded_branch = loaded_ui['branch'] - version_log.info("[" + str(datetime.now()) + "] Last used " + self.loaded_branch + " commit " + str(self.loaded_git_version).replace("\n", "")) - except: - self.loaded_git_version = None - self.loaded_branch = None - - def setup(self, branch="master", user_specified=None): - self.branch = branch - if user_specified: - if os.path.isdir(user_specified): - log.info("Using user specified UI directory: " + str(user_specified)) - self.branch = "user-specified" - self.loaded_git_version = "user-specified" - self.ui_dir = user_specified - return defer.succeed("user-specified") - else: - log.info("User specified UI directory doesn't exist, using " + branch) - else: - log.info("Using UI branch: " + branch) - self._git_url = "https://api.github.com/repos/lbryio/lbry-web-ui/git/refs/heads/%s" % branch - self._dist_url = "https://raw.githubusercontent.com/lbryio/lbry-web-ui/%s/dist.zip" % branch - - d = self._up_to_date() - d.addCallback(lambda r: self._download_ui() if not r else self.branch) - return d - - def _up_to_date(self): - def _get_git_info(): - response = urlopen(self._git_url) - data = json.loads(response.read()) - return defer.succeed(data['object']['sha']) - - def _set_git(version): - self.git_version = version - version_log.info("[" + str(datetime.now()) + "] UI branch " + self.branch + " has a most recent commit of: " + str(self.git_version).replace("\n", "")) - - if self.git_version == self.loaded_git_version and os.path.isdir(self.ui_dir): - version_log.info("[" + str(datetime.now()) + "] local copy of " + self.branch + " is up to date") - return defer.succeed(True) - else: - if self.git_version == self.loaded_git_version: - version_log.info("[" + str(datetime.now()) + "] Can't find ui files, downloading them again") - else: - version_log.info("[" + str(datetime.now()) + "] local copy of " + self.branch + " branch is out of date, updating") - f = open(self.config, "w") - f.write(json.dumps({'commit': self.git_version, - 'time': str(datetime.now()), - 'branch': self.branch})) - f.close() - return defer.succeed(False) - - d = _get_git_info() - d.addCallback(_set_git) - return d - - def _download_ui(self): - def _delete_ui_dir(): - if os.path.isdir(self.ui_dir): - if self.loaded_git_version: - version_log.info("[" + str(datetime.now()) + "] Removed ui files for commit " + str(self.loaded_git_version).replace("\n", "")) - log.info("Removing out of date ui files") - shutil.rmtree(self.ui_dir) - return defer.succeed(None) - - def _dl_ui(): - url = urlopen(self._dist_url) - z = ZipFile(StringIO(url.read())) - names = [i for i in z.namelist() if '.DS_exStore' not in i and '__MACOSX' not in i] - z.extractall(self.ui_dir, members=names) - version_log.info("[" + str(datetime.now()) + "] Updated branch " + self.branch + ": " + str(self.loaded_git_version).replace("\n", "") + " --> " + self.git_version.replace("\n", "")) - log.info("Downloaded files for UI commit " + str(self.git_version).replace("\n", "")) - self.loaded_git_version = self.git_version - return self.branch - - d = _delete_ui_dir() - d.addCallback(lambda _: _dl_ui()) - return d - - def _setup_server(self, ui_ver, wallet): - self._api = LBRYDaemon(ui_ver, wallet_type=wallet) - self.root = LBRYindex(self.ui_dir) - for d in [i[0] for i in os.walk(self.ui_dir) if os.path.dirname(i[0]) == self.ui_dir]: - self.root.putChild(os.path.basename(d), static.File(d)) + def _setup_server(self, wallet): + self.root = LBRYindex(os.path.join(os.path.join(data_dir, "lbry-ui"), "active")) + self._api = LBRYDaemon(self.root, wallet_type=wallet) self.root.putChild("view", HostedLBRYFile(self._api)) self.root.putChild(API_ADDRESS, self._api) return defer.succeed(True) - def start(self, branch="master", user_specified=False, wallet=DEFAULT_WALLET): - d = self.setup(branch=branch, user_specified=user_specified) - d.addCallback(lambda v: self._setup_server(v, wallet)) - d.addCallback(lambda _: self._api.setup()) - + def start(self, branch=DEFAULT_UI_BRANCH, user_specified=False, wallet=DEFAULT_WALLET): + d = self._setup_server(self._setup_server(wallet)) + d.addCallback(lambda _: self._api.setup(branch, user_specified)) return d diff --git a/lbrynet/lbrynet_daemon/LBRYUIManager.py b/lbrynet/lbrynet_daemon/LBRYUIManager.py new file mode 100644 index 000000000..6c48d018e --- /dev/null +++ b/lbrynet/lbrynet_daemon/LBRYUIManager.py @@ -0,0 +1,219 @@ +import os +import logging +import shutil +import sys +import json + +from urllib2 import urlopen +from StringIO import StringIO +from twisted.web import static +from twisted.internet import defer +from lbrynet.conf import DEFAULT_UI_BRANCH +from lbrynet import __version__ as lbrynet_version +from lbryum.version import ELECTRUM_VERSION as lbryum_version +from zipfile import ZipFile +from appdirs import user_data_dir + +if sys.platform != "darwin": + data_dir = os.path.join(os.path.expanduser("~"), ".lbrynet") +else: + data_dir = user_data_dir("LBRY") + +if not os.path.isdir(data_dir): + os.mkdir(data_dir) +version_dir = os.path.join(data_dir, "ui_version_history") +if not os.path.isdir(version_dir): + os.mkdir(version_dir) + +log = logging.getLogger(__name__) +log.addHandler(logging.FileHandler(os.path.join(data_dir, 'lbrynet-daemon.log'))) +log.setLevel(logging.INFO) + + +class LBRYUIManager(object): + def __init__(self, root): + self.data_dir = user_data_dir("LBRY") + self.ui_root = os.path.join(self.data_dir, "lbry-ui") + self.active_dir = os.path.join(self.ui_root, "active") + self.update_dir = os.path.join(self.ui_root, "update") + + if not os.path.isdir(self.data_dir): + os.mkdir(self.data_dir) + if not os.path.isdir(self.ui_root): + os.mkdir(self.ui_root) + if not os.path.isdir(self.ui_root): + os.mkdir(self.ui_root) + if not os.path.isdir(self.ui_root): + os.mkdir(self.ui_root) + + self.config = os.path.join(self.ui_root, "active.json") + self.update_requires = os.path.join(self.update_dir, "requirements.txt") + self.requirements = {} + self.ui_dir = self.active_dir + self.git_version = None + self.root = root + + if not os.path.isfile(os.path.join(self.config)): + self.loaded_git_version = None + else: + try: + f = open(self.config, "r") + loaded_ui = json.loads(f.read()) + f.close() + self.loaded_git_version = loaded_ui['commit'] + self.loaded_branch = loaded_ui['branch'] + self.loaded_requirements = loaded_ui['requirements'] + except: + self.loaded_git_version = None + self.loaded_branch = None + self.loaded_requirements = None + + def setup(self, branch=DEFAULT_UI_BRANCH, user_specified=None): + self.branch = branch + if user_specified: + if os.path.isdir(user_specified): + log.info("Checking user specified UI directory: " + str(user_specified)) + self.branch = "user-specified" + self.loaded_git_version = "user-specified" + self.ui_dir = user_specified + d = self.migrate_ui(source=user_specified) + d.addCallback(lambda _: self._load_ui()) + return d + else: + log.info("User specified UI directory doesn't exist, using " + branch) + else: + log.info("Checking for updates for UI branch: " + branch) + self._git_url = "https://api.github.com/repos/lbryio/lbry-web-ui/git/refs/heads/%s" % branch + self._dist_url = "https://raw.githubusercontent.com/lbryio/lbry-web-ui/%s/dist.zip" % branch + + d = self._up_to_date() + d.addCallback(lambda r: self._download_ui() if not r else self.branch) + return d + + def _up_to_date(self): + def _get_git_info(): + response = urlopen(self._git_url) + data = json.loads(response.read()) + return defer.succeed(data['object']['sha']) + + def _set_git(version): + self.git_version = version.replace('\n', '') + if self.git_version == self.loaded_git_version: + log.info("UI is up to date") + return defer.succeed(True) + else: + log.info("UI updates available, checking if installation meets requirements") + return defer.succeed(False) + + d = _get_git_info() + d.addCallback(_set_git) + return d + + def migrate_ui(self, source=None): + if not source: + requires_file = self.update_requires + source_dir = self.update_dir + delete_source = True + else: + requires_file = os.path.join(source, "requirements.txt") + source_dir = source + delete_source = False + + def _check_requirements(): + if not os.path.isfile(requires_file): + log.info("No requirements.txt file, rejecting request to migrate this UI") + return defer.succeed(False) + + f = open(requires_file, "r") + for requirement in [line for line in f.read().split('\n') if line]: + t = requirement.split('=') + if len(t) == 3: + self.requirements[t[0]] = {'version': t[1], 'operator': '=='} + elif t[0][-1] == ">": + self.requirements[t[0][:-1]] = {'version': t[1], 'operator': '>='} + elif t[0][-1] == "<": + self.requirements[t[0][:-1]] = {'version': t[1], 'operator': '<='} + f.close() + passed_requirements = True + for r in self.requirements: + if r == 'lbrynet': + c = lbrynet_version + elif r == 'lbryum': + c = lbryum_version + else: + c = None + if c: + if self.requirements[r]['operator'] == '==': + if not self.requirements[r]['version'] == c: + passed_requirements = False + log.info("Local version %s of %s does not meet UI requirement for version %s" % ( + c, r, self.requirements[r]['version'])) + else: + log.info("Local version of %s meets ui requirement" % r) + if self.requirements[r]['operator'] == '>=': + if not self.requirements[r]['version'] <= c: + passed_requirements = False + log.info("Local version %s of %s does not meet UI requirement for version %s" % ( + c, r, self.requirements[r]['version'])) + else: + log.info("Local version of %s meets ui requirement" % r) + if self.requirements[r]['operator'] == '<=': + if not self.requirements[r]['version'] >= c: + passed_requirements = False + log.info("Local version %s of %s does not meet UI requirement for version %s" % ( + c, r, self.requirements[r]['version'])) + else: + log.info("Local version of %s meets ui requirement" % r) + return defer.succeed(passed_requirements) + + def _disp_failure(): + log.info("Failed to satisfy requirements for branch '%s', update was not loaded" % self.branch) + return defer.succeed(False) + + def _do_migrate(): + if os.path.isdir(self.active_dir): + shutil.rmtree(self.active_dir) + shutil.copytree(source_dir, self.active_dir) + if delete_source: + shutil.rmtree(source_dir) + + log.info("Loaded UI update") + + f = open(self.config, "w") + loaded_ui = {'commit': self.git_version, 'branch': self.branch, 'requirements': self.requirements} + f.write(json.dumps(loaded_ui)) + f.close() + + self.loaded_git_version = loaded_ui['commit'] + self.loaded_branch = loaded_ui['branch'] + self.loaded_requirements = loaded_ui['requirements'] + return defer.succeed(True) + + d = _check_requirements() + d.addCallback(lambda r: _do_migrate() if r else _disp_failure()) + return d + + def _download_ui(self): + def _delete_update_dir(): + if os.path.isdir(self.update_dir): + shutil.rmtree(self.update_dir) + return defer.succeed(None) + + def _dl_ui(): + url = urlopen(self._dist_url) + z = ZipFile(StringIO(url.read())) + names = [i for i in z.namelist() if '.DS_exStore' not in i and '__MACOSX' not in i] + z.extractall(self.update_dir, members=names) + log.info("Downloaded files for UI commit " + str(self.git_version).replace("\n", "")) + return self.branch + + d = _delete_update_dir() + d.addCallback(lambda _: _dl_ui()) + d.addCallback(lambda _: self.migrate_ui()) + d.addCallback(lambda _: self._load_ui()) + return d + + def _load_ui(self): + for d in [i[0] for i in os.walk(self.active_dir) if os.path.dirname(i[0]) == self.active_dir]: + self.root.putChild(os.path.basename(d), static.File(d)) + return defer.succeed(True) \ No newline at end of file From 75052fc7739b276a06be04a5fba9fdcf9dc44c47 Mon Sep 17 00:00:00 2001 From: Jack Date: Mon, 30 May 2016 00:10:43 -0400 Subject: [PATCH 07/17] remove check_for_new_version vestige --- lbrynet/lbrynet_daemon/LBRYDaemon.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lbrynet/lbrynet_daemon/LBRYDaemon.py b/lbrynet/lbrynet_daemon/LBRYDaemon.py index 0d3535012..928f98f2a 100644 --- a/lbrynet/lbrynet_daemon/LBRYDaemon.py +++ b/lbrynet/lbrynet_daemon/LBRYDaemon.py @@ -110,7 +110,7 @@ CONNECTION_PROBLEM_CODES = [ ALLOWED_DURING_STARTUP = ['is_running', 'is_first_run', 'get_time_behind_blockchain', 'stop', 'daemon_status', 'get_start_notice', - 'version', 'check_for_new_version'] + 'version'] BAD_REQUEST = 400 NOT_FOUND = 404 From 41f8b5aee25ac0e8073b6c15f2f89795ded2dd77 Mon Sep 17 00:00:00 2001 From: Jack Date: Mon, 30 May 2016 02:05:16 -0400 Subject: [PATCH 08/17] add reveal() function and delete_target_file param for delete_lbry_file --- lbrynet/lbrynet_daemon/LBRYDaemon.py | 36 ++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/lbrynet/lbrynet_daemon/LBRYDaemon.py b/lbrynet/lbrynet_daemon/LBRYDaemon.py index 928f98f2a..bd8c75f62 100644 --- a/lbrynet/lbrynet_daemon/LBRYDaemon.py +++ b/lbrynet/lbrynet_daemon/LBRYDaemon.py @@ -1,5 +1,6 @@ import locale import os +import subprocess import sys import simplejson as json import binascii @@ -1094,7 +1095,7 @@ class LBRYDaemon(jsonrpc.JSONRPC): return d - def _delete_lbry_file(self, lbry_file): + def _delete_lbry_file(self, lbry_file, delete_file=True): d = self.lbry_file_manager.delete_lbry_file(lbry_file) def finish_deletion(lbry_file): @@ -1107,7 +1108,8 @@ class LBRYDaemon(jsonrpc.JSONRPC): d = self.lbry_file_manager.get_count_for_stream_hash(s_h) # TODO: could possibly be a timing issue here d.addCallback(lambda c: self.stream_info_manager.delete_stream(s_h) if c == 0 else True) - d.addCallback(lambda _: os.remove(os.path.join(self.download_directory, lbry_file.file_name)) if + if delete_file: + d.addCallback(lambda _: os.remove(os.path.join(self.download_directory, lbry_file.file_name)) if os.path.isfile(os.path.join(self.download_directory, lbry_file.file_name)) else defer.succeed(None)) return d @@ -1792,14 +1794,19 @@ class LBRYDaemon(jsonrpc.JSONRPC): confirmation message """ + if 'delete_target_file' in p.keys(): + delete_file = p['delete_target_file'] + else: + delete_file = True + def _delete_file(f): file_name = f.file_name - d = self._delete_lbry_file(f) + d = self._delete_lbry_file(f, delete_file=delete_file) d.addCallback(lambda _: "Deleted LBRY file" + file_name) return d - if p.keys()[0] in ['name', 'sd_hash', 'file_name']: - search_type = p.keys()[0] + if 'name' in p.keys() or 'sd_hash' in p.keys() or 'file_name' in p.keys(): + search_type = [k for k in p.keys() if k != 'delete_target_file'][0] d = self._get_lbry_file(search_type, p[search_type], return_json=False) d.addCallback(lambda l: _delete_file(l) if l else False) @@ -2201,4 +2208,23 @@ class LBRYDaemon(jsonrpc.JSONRPC): d = self.lbry_ui_manager.setup() d.addCallback(lambda r: self._render_response(r, OK_CODE)) + return d + + def jsonrpc_reveal(self, p): + """ + Open a folder in finder/file explorer + + Args: + 'path': path to be selected in finder + Returns: + True, opens finder + """ + + path = p['path'] + if sys.platform == "darwin": + d = threads.deferToThread(subprocess.Popen, ("open -R %s" % path), shell=True) + else: + d = threads.deferToThread(subprocess.Popen, ("xdg-open %s" % path), shell=True) + + d.addCallback(lambda _: self._render_response(True, OK_CODE)) return d \ No newline at end of file From 772866389f248e43f88a89c77e545119ffae0ea7 Mon Sep 17 00:00:00 2001 From: Jack Date: Mon, 30 May 2016 02:36:13 -0400 Subject: [PATCH 09/17] ui manager fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit load a ui when it’s supposed to be loaded --- lbrynet/lbrynet_daemon/LBRYUIManager.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lbrynet/lbrynet_daemon/LBRYUIManager.py b/lbrynet/lbrynet_daemon/LBRYUIManager.py index 6c48d018e..baa4b09f9 100644 --- a/lbrynet/lbrynet_daemon/LBRYUIManager.py +++ b/lbrynet/lbrynet_daemon/LBRYUIManager.py @@ -55,6 +55,8 @@ class LBRYUIManager(object): if not os.path.isfile(os.path.join(self.config)): self.loaded_git_version = None + self.loaded_branch = None + self.loaded_requirements = None else: try: f = open(self.config, "r") @@ -75,19 +77,22 @@ class LBRYUIManager(object): log.info("Checking user specified UI directory: " + str(user_specified)) self.branch = "user-specified" self.loaded_git_version = "user-specified" - self.ui_dir = user_specified d = self.migrate_ui(source=user_specified) d.addCallback(lambda _: self._load_ui()) return d else: log.info("User specified UI directory doesn't exist, using " + branch) + elif self.loaded_branch == "user-specified": + log.info("Loading user provided UI") + d = self._load_ui() + return d else: log.info("Checking for updates for UI branch: " + branch) self._git_url = "https://api.github.com/repos/lbryio/lbry-web-ui/git/refs/heads/%s" % branch self._dist_url = "https://raw.githubusercontent.com/lbryio/lbry-web-ui/%s/dist.zip" % branch d = self._up_to_date() - d.addCallback(lambda r: self._download_ui() if not r else self.branch) + d.addCallback(lambda r: self._download_ui() if not r else self._load_ui()) return d def _up_to_date(self): From c56b3e75eae1e5e73d65930de40efbe675c1535e Mon Sep 17 00:00:00 2001 From: Jack Date: Mon, 30 May 2016 02:42:33 -0400 Subject: [PATCH 10/17] add download_directory to lbry_file --- lbrynet/lbrynet_daemon/LBRYDaemon.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lbrynet/lbrynet_daemon/LBRYDaemon.py b/lbrynet/lbrynet_daemon/LBRYDaemon.py index bd8c75f62..5518bef1c 100644 --- a/lbrynet/lbrynet_daemon/LBRYDaemon.py +++ b/lbrynet/lbrynet_daemon/LBRYDaemon.py @@ -1217,7 +1217,8 @@ class LBRYDaemon(jsonrpc.JSONRPC): if status[0] == DOWNLOAD_RUNNING_CODE: d = f.status() d.addCallback(_get_file_status) - d.addCallback(lambda message: {'completed': f.completed, 'file_name': f.file_name, 'key': key, + d.addCallback(lambda message: {'completed': f.completed, 'file_name': f.file_name, + 'download_directory': f.download_directory,'key': key, 'points_paid': f.points_paid, 'stopped': f.stopped, 'stream_hash': f.stream_hash, 'stream_name': f.stream_name, @@ -1229,6 +1230,7 @@ class LBRYDaemon(jsonrpc.JSONRPC): 'message': message}) else: d = defer.succeed({'completed': f.completed, 'file_name': f.file_name, 'key': key, + 'download_directory': f.download_directory, 'points_paid': f.points_paid, 'stopped': f.stopped, 'stream_hash': f.stream_hash, 'stream_name': f.stream_name, 'suggested_file_name': f.suggested_file_name, 'upload_allowed': f.upload_allowed, 'sd_hash': f.sd_hash, 'total_bytes': size, From 16ef259ae129c502d8c12671affac67de0542994 Mon Sep 17 00:00:00 2001 From: Jack Date: Mon, 30 May 2016 02:53:36 -0400 Subject: [PATCH 11/17] add full_path to lbry_file --- lbrynet/lbrynet_daemon/LBRYDaemon.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lbrynet/lbrynet_daemon/LBRYDaemon.py b/lbrynet/lbrynet_daemon/LBRYDaemon.py index 5518bef1c..cd472d677 100644 --- a/lbrynet/lbrynet_daemon/LBRYDaemon.py +++ b/lbrynet/lbrynet_daemon/LBRYDaemon.py @@ -1218,7 +1218,9 @@ class LBRYDaemon(jsonrpc.JSONRPC): d = f.status() d.addCallback(_get_file_status) d.addCallback(lambda message: {'completed': f.completed, 'file_name': f.file_name, - 'download_directory': f.download_directory,'key': key, + 'download_directory': f.download_directory, + 'full_path': os.path.join(f.download_directory, f.file_name), + 'key': key, 'points_paid': f.points_paid, 'stopped': f.stopped, 'stream_hash': f.stream_hash, 'stream_name': f.stream_name, @@ -1231,6 +1233,7 @@ class LBRYDaemon(jsonrpc.JSONRPC): else: d = defer.succeed({'completed': f.completed, 'file_name': f.file_name, 'key': key, 'download_directory': f.download_directory, + 'full_path': os.path.join(f.download_directory, f.file_name), 'points_paid': f.points_paid, 'stopped': f.stopped, 'stream_hash': f.stream_hash, 'stream_name': f.stream_name, 'suggested_file_name': f.suggested_file_name, 'upload_allowed': f.upload_allowed, 'sd_hash': f.sd_hash, 'total_bytes': size, From 2c3b625b6b7010649c2007e04eb34c6d01ea1daa Mon Sep 17 00:00:00 2001 From: Jack Date: Mon, 30 May 2016 02:56:08 -0400 Subject: [PATCH 12/17] change full_path to path --- lbrynet/lbrynet_daemon/LBRYDaemon.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lbrynet/lbrynet_daemon/LBRYDaemon.py b/lbrynet/lbrynet_daemon/LBRYDaemon.py index cd472d677..7e87d4ff2 100644 --- a/lbrynet/lbrynet_daemon/LBRYDaemon.py +++ b/lbrynet/lbrynet_daemon/LBRYDaemon.py @@ -1219,7 +1219,7 @@ class LBRYDaemon(jsonrpc.JSONRPC): d.addCallback(_get_file_status) d.addCallback(lambda message: {'completed': f.completed, 'file_name': f.file_name, 'download_directory': f.download_directory, - 'full_path': os.path.join(f.download_directory, f.file_name), + 'path': os.path.join(f.download_directory, f.file_name), 'key': key, 'points_paid': f.points_paid, 'stopped': f.stopped, 'stream_hash': f.stream_hash, @@ -1233,7 +1233,7 @@ class LBRYDaemon(jsonrpc.JSONRPC): else: d = defer.succeed({'completed': f.completed, 'file_name': f.file_name, 'key': key, 'download_directory': f.download_directory, - 'full_path': os.path.join(f.download_directory, f.file_name), + 'path': os.path.join(f.download_directory, f.file_name), 'points_paid': f.points_paid, 'stopped': f.stopped, 'stream_hash': f.stream_hash, 'stream_name': f.stream_name, 'suggested_file_name': f.suggested_file_name, 'upload_allowed': f.upload_allowed, 'sd_hash': f.sd_hash, 'total_bytes': size, From da68bcf952c46f66c33d1c58a8935a5376424600 Mon Sep 17 00:00:00 2001 From: Jack Date: Mon, 30 May 2016 03:14:59 -0400 Subject: [PATCH 13/17] third time's the charm --- lbrynet/lbrynet_daemon/LBRYDaemon.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lbrynet/lbrynet_daemon/LBRYDaemon.py b/lbrynet/lbrynet_daemon/LBRYDaemon.py index 7e87d4ff2..32e811fc9 100644 --- a/lbrynet/lbrynet_daemon/LBRYDaemon.py +++ b/lbrynet/lbrynet_daemon/LBRYDaemon.py @@ -1219,7 +1219,7 @@ class LBRYDaemon(jsonrpc.JSONRPC): d.addCallback(_get_file_status) d.addCallback(lambda message: {'completed': f.completed, 'file_name': f.file_name, 'download_directory': f.download_directory, - 'path': os.path.join(f.download_directory, f.file_name), + 'download_path': os.path.join(f.download_directory, f.file_name), 'key': key, 'points_paid': f.points_paid, 'stopped': f.stopped, 'stream_hash': f.stream_hash, @@ -1233,7 +1233,7 @@ class LBRYDaemon(jsonrpc.JSONRPC): else: d = defer.succeed({'completed': f.completed, 'file_name': f.file_name, 'key': key, 'download_directory': f.download_directory, - 'path': os.path.join(f.download_directory, f.file_name), + 'download_path': os.path.join(f.download_directory, f.file_name), 'points_paid': f.points_paid, 'stopped': f.stopped, 'stream_hash': f.stream_hash, 'stream_name': f.stream_name, 'suggested_file_name': f.suggested_file_name, 'upload_allowed': f.upload_allowed, 'sd_hash': f.sd_hash, 'total_bytes': size, From f9c644b96494a9d5c3ed01f32e8db80320ec844f Mon Sep 17 00:00:00 2001 From: Jack Date: Mon, 30 May 2016 04:00:47 -0400 Subject: [PATCH 14/17] fix switching between --branch and --ui --- lbrynet/lbrynet_daemon/LBRYDaemon.py | 6 ++++-- lbrynet/lbrynet_daemon/LBRYDaemonControl.py | 13 ++++++++----- lbrynet/lbrynet_daemon/LBRYDaemonServer.py | 4 ++-- lbrynet/lbrynet_daemon/LBRYUIManager.py | 4 ++-- 4 files changed, 16 insertions(+), 11 deletions(-) diff --git a/lbrynet/lbrynet_daemon/LBRYDaemon.py b/lbrynet/lbrynet_daemon/LBRYDaemon.py index 32e811fc9..4d1494f80 100644 --- a/lbrynet/lbrynet_daemon/LBRYDaemon.py +++ b/lbrynet/lbrynet_daemon/LBRYDaemon.py @@ -382,7 +382,7 @@ class LBRYDaemon(jsonrpc.JSONRPC): log.error(failure) return jsonrpclib.Fault(self.FAILURE, "error") - def setup(self, branch=DEFAULT_UI_BRANCH, user_specified=False): + def setup(self, branch=DEFAULT_UI_BRANCH, user_specified=False, branch_specified=False): def _log_starting_vals(): d = self._get_lbry_files() d.addCallback(lambda r: json.dumps([d[1] for d in r])) @@ -427,7 +427,9 @@ class LBRYDaemon(jsonrpc.JSONRPC): self.connection_problem_checker.start(1) d = defer.Deferred() - d.addCallback(lambda _: self.lbry_ui_manager.setup(branch=branch, user_specified=user_specified)) + d.addCallback(lambda _: self.lbry_ui_manager.setup(branch=branch, + user_specified=user_specified, + branch_specified=branch_specified)) d.addCallback(lambda _: self._initial_setup()) d.addCallback(lambda _: threads.deferToThread(self._setup_data_directory)) d.addCallback(lambda _: self._check_db_migration()) diff --git a/lbrynet/lbrynet_daemon/LBRYDaemonControl.py b/lbrynet/lbrynet_daemon/LBRYDaemonControl.py index 1d7531331..72c8d2c1e 100644 --- a/lbrynet/lbrynet_daemon/LBRYDaemonControl.py +++ b/lbrynet/lbrynet_daemon/LBRYDaemonControl.py @@ -13,7 +13,8 @@ from twisted.internet import reactor, defer from jsonrpc.proxy import JSONRPCProxy from lbrynet.lbrynet_daemon.LBRYDaemonServer import LBRYDaemonServer -from lbrynet.conf import API_CONNECTION_STRING, API_INTERFACE, API_ADDRESS, API_PORT, DEFAULT_WALLET, UI_ADDRESS +from lbrynet.conf import API_CONNECTION_STRING, API_INTERFACE, API_ADDRESS, API_PORT, \ + DEFAULT_WALLET, UI_ADDRESS, DEFAULT_UI_BRANCH if sys.platform != "darwin": @@ -68,12 +69,11 @@ def start(): help="path to custom UI folder", default=None) parser.add_argument("--branch", - help="Branch of lbry-web-ui repo to use, defaults on master", - default="master") + help="Branch of lbry-web-ui repo to use, defaults on master") parser.add_argument('--no-launch', dest='launchui', action="store_false") parser.add_argument('--log-to-console', dest='logtoconsole', action="store_true") parser.add_argument('--quiet', dest='quiet', action="store_true") - parser.set_defaults(launchui=True, logtoconsole=False, quiet=False) + parser.set_defaults(branch=False, launchui=True, logtoconsole=False, quiet=False) args = parser.parse_args() if args.logtoconsole: @@ -104,7 +104,10 @@ def start(): if test_internet_connection(): lbry = LBRYDaemonServer() - d = lbry.start(branch=args.branch, user_specified=args.ui, wallet=args.wallet) + d = lbry.start(branch=args.branch if args.branch else DEFAULT_UI_BRANCH, + user_specified=args.ui, + wallet=args.wallet, + branch_specified=True if args.branch else False) if args.launchui: d.addCallback(lambda _: webbrowser.open(UI_ADDRESS)) diff --git a/lbrynet/lbrynet_daemon/LBRYDaemonServer.py b/lbrynet/lbrynet_daemon/LBRYDaemonServer.py index 948c3ea2e..393a87b72 100644 --- a/lbrynet/lbrynet_daemon/LBRYDaemonServer.py +++ b/lbrynet/lbrynet_daemon/LBRYDaemonServer.py @@ -198,7 +198,7 @@ class LBRYDaemonServer(object): self.root.putChild(API_ADDRESS, self._api) return defer.succeed(True) - def start(self, branch=DEFAULT_UI_BRANCH, user_specified=False, wallet=DEFAULT_WALLET): + def start(self, branch=DEFAULT_UI_BRANCH, user_specified=False, branch_specified=False, wallet=DEFAULT_WALLET): d = self._setup_server(self._setup_server(wallet)) - d.addCallback(lambda _: self._api.setup(branch, user_specified)) + d.addCallback(lambda _: self._api.setup(branch, user_specified, branch_specified)) return d diff --git a/lbrynet/lbrynet_daemon/LBRYUIManager.py b/lbrynet/lbrynet_daemon/LBRYUIManager.py index baa4b09f9..b4aa33cd5 100644 --- a/lbrynet/lbrynet_daemon/LBRYUIManager.py +++ b/lbrynet/lbrynet_daemon/LBRYUIManager.py @@ -70,7 +70,7 @@ class LBRYUIManager(object): self.loaded_branch = None self.loaded_requirements = None - def setup(self, branch=DEFAULT_UI_BRANCH, user_specified=None): + def setup(self, branch=DEFAULT_UI_BRANCH, user_specified=None, branch_specified=False): self.branch = branch if user_specified: if os.path.isdir(user_specified): @@ -82,7 +82,7 @@ class LBRYUIManager(object): return d else: log.info("User specified UI directory doesn't exist, using " + branch) - elif self.loaded_branch == "user-specified": + elif self.loaded_branch == "user-specified" and not branch_specified: log.info("Loading user provided UI") d = self._load_ui() return d From 4cd1cdf495e9e503fed34e8643c1e88203587cab Mon Sep 17 00:00:00 2001 From: Jack Date: Mon, 30 May 2016 04:37:34 -0400 Subject: [PATCH 15/17] escape spaces in paths given to reveal() --- lbrynet/lbrynet_daemon/LBRYDaemon.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lbrynet/lbrynet_daemon/LBRYDaemon.py b/lbrynet/lbrynet_daemon/LBRYDaemon.py index 4d1494f80..8c4389c89 100644 --- a/lbrynet/lbrynet_daemon/LBRYDaemon.py +++ b/lbrynet/lbrynet_daemon/LBRYDaemon.py @@ -2229,9 +2229,9 @@ class LBRYDaemon(jsonrpc.JSONRPC): path = p['path'] if sys.platform == "darwin": - d = threads.deferToThread(subprocess.Popen, ("open -R %s" % path), shell=True) + d = threads.deferToThread(subprocess.Popen, ("open -R %s" % path.replace(' ', '\ ')), shell=True) else: - d = threads.deferToThread(subprocess.Popen, ("xdg-open %s" % path), shell=True) + d = threads.deferToThread(subprocess.Popen, ("xdg-open %s" % path.replace(' ', '\ ')), shell=True) d.addCallback(lambda _: self._render_response(True, OK_CODE)) return d \ No newline at end of file From ba605e985a44a99365c7e5f37c23b58455bffd73 Mon Sep 17 00:00:00 2001 From: Jack Date: Mon, 30 May 2016 04:40:58 -0400 Subject: [PATCH 16/17] no shell=True --- lbrynet/lbrynet_daemon/LBRYDaemon.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lbrynet/lbrynet_daemon/LBRYDaemon.py b/lbrynet/lbrynet_daemon/LBRYDaemon.py index 8c4389c89..cf38ee02e 100644 --- a/lbrynet/lbrynet_daemon/LBRYDaemon.py +++ b/lbrynet/lbrynet_daemon/LBRYDaemon.py @@ -2229,9 +2229,9 @@ class LBRYDaemon(jsonrpc.JSONRPC): path = p['path'] if sys.platform == "darwin": - d = threads.deferToThread(subprocess.Popen, ("open -R %s" % path.replace(' ', '\ ')), shell=True) + d = threads.deferToThread(subprocess.Popen, ['open', '-R', path]) else: - d = threads.deferToThread(subprocess.Popen, ("xdg-open %s" % path.replace(' ', '\ ')), shell=True) + d = threads.deferToThread(subprocess.Popen, ['xdg-open', '-R', path]) d.addCallback(lambda _: self._render_response(True, OK_CODE)) return d \ No newline at end of file From c10e7b2b4196c5f06e7b972790032330d5b33728 Mon Sep 17 00:00:00 2001 From: Jack Date: Mon, 30 May 2016 15:39:27 -0400 Subject: [PATCH 17/17] fix import --- lbrynet/lbrylive/LBRYStdoutDownloader.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lbrynet/lbrylive/LBRYStdoutDownloader.py b/lbrynet/lbrylive/LBRYStdoutDownloader.py index 0c1fe8c60..8c413e129 100644 --- a/lbrynet/lbrylive/LBRYStdoutDownloader.py +++ b/lbrynet/lbrylive/LBRYStdoutDownloader.py @@ -1,7 +1,7 @@ import logging import sys -from lbrynet.lbrynet_console.plugins.LBRYLive.LBRYLiveStreamDownloader import LBRYLiveStreamDownloader +from lbrynet.lbrylive.client.LiveStreamDownloader import LBRYLiveStreamDownloader from lbrynet.core.BlobManager import TempBlobManager from lbrynet.core.Session import LBRYSession from lbrynet.core.client.StandaloneBlobDownloader import StandaloneBlobDownloader