diff --git a/.gitignore b/.gitignore index fd46c2e9..6c962a0d 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,5 @@ nbproject composer .ht* /vendor/ -.DS_Store \ No newline at end of file +.DS_Store +.php_cs.cache \ No newline at end of file diff --git a/INSTALL.md b/INSTALL.md index 9aad6779..07f25790 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -6,7 +6,7 @@ To run this project, you'll need to have either PHP7 or Docker installed, and be ## Running Locally -- Install PHP7 +- Install [PHP7](http://php.net/downloads.php) - Checkout the project - Run `./dev.sh` from the project root - Access [localhost:8000](http://localhost:8000) in your browser @@ -27,3 +27,4 @@ If `localhost:8000` returns the lbry.io website, it's running correctly. - Both the `dev.sh` and `docker.sh` scripts will initialise a configuration based on `data/config.php.example` if `data/config.php` does not exist. - Some pages and interactions rely on API keys that will not be available to you in your install. - To run remotely, simply install PHP and configure Apache or your server of choice to serve `web/index.php`. +- If dev.sh fails to start with missing php extensions, please install php-xml, php-curl, php-mbstring according to your OS instruction diff --git a/README.md b/README.md index 28200594..ff35c93f 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ The [lbry.io](https://lbry.io) website. This website uses barebones PHP along with Javascript and SCSS. -![lbry.io screenshot](https://spee.ch/@lbry/lbryio.png) +![lbry.io screenshot](https://spee.ch/b/new.png) ## Installation @@ -12,7 +12,11 @@ Please see [INSTALL](INSTALL.md) for comprehensive, easy-to-follow instructions Unless you are planning to contribute to the lbry.io website, this project serves little independent purpose. -To access a local copy of lbry.io, follow [INSTALL](INSTALL.md) and then access `localhost:8000` in your browser. +To access a local copy of lbry.io, follow [INSTALL](INSTALL.md) and then access `localhost:8000` in your browser. This will allow you to make changes to the website, test locally and then submit pull requests. + +## Running from Source + +Please see [INSTALL](INSTALL.md) for details on how to run from source. ## License @@ -22,9 +26,13 @@ This project is MIT licensed. For the full license, see [LICENSE](LICENSE). Contributions to this project are welcome, encouraged, and compensated. For more details, see [CONTRIBUTING](https://lbry.io/faq/contributing). +## Security + +We take security seriously. Please contact [security@lbry.io](mailto:security@lbry.io) regarding any security issues. Our PGP key is [here](https://keybase.io/lbry/key.asc) if you need it. + ## Contact -The primary contact for this project is Jeremy Kauffman (jeremy@lbry.io). +The primary contact for this project is [Jeremy Kauffman](https://github.com/kauffj) (jeremy@lbry.io). ## Additional Info and Links diff --git a/autoload.php b/autoload.php index d25383f7..cb52baeb 100644 --- a/autoload.php +++ b/autoload.php @@ -1,80 +1,71 @@ 2) { + throw new RuntimeException('Autoloader cannot handle 2 namespaces in the same file'); + } + + $prefix = isset($namespaces[1]) && count($namespaces[1]) ? reset($namespaces[1]) . '\\' : ''; + + foreach ($classes[1] as $class) { + $mapping[strtolower($prefix . $class)] = $path; + } + return $mapping; } - - static::$classes = []; - - $dir = new RecursiveDirectoryIterator(ROOT_DIR, RecursiveDirectoryIterator::SKIP_DOTS); - $ite = new RecursiveIteratorIterator($dir); - $pathIterator = new RegexIterator($ite, '/.*\.php$/', RegexIterator::GET_MATCH); - foreach($pathIterator as $paths) - { - foreach($paths as $path) - { - static::$classes += static::parseFile($path); - } - } - - if (ini_get('apc.enabled')) - { - apc_store($key, static::$classes); - } - } - - protected static function parseFile($path) - { - $mapping = []; - $classes = []; - - preg_match_all('~^\s*(?:namespace)\s+([^;]+)~mi', file_get_contents($path), $namespaces); - preg_match_all('~^\s*(?:abstract\s+|final\s+)?(?:class|interface)\s+(\w+)~mi', file_get_contents($path), $classes); - - if (isset($namespaces[1]) && count($namespaces[1]) > 2) - { - throw new RuntimeException('Autoloader cannot handle 2 namespaces in the same file'); - } - - $prefix = isset($namespaces[1]) && count($namespaces[1]) ? reset($namespaces[1]) . '\\' : ''; - - foreach ($classes[1] as $class) - { - $mapping[strtolower($prefix . $class)] = $path; - } - return $mapping; - } } ini_set('unserialize_callback_func', 'spl_autoload_call'); diff --git a/bootstrap.php b/bootstrap.php index f5cbeeff..b18d4471 100644 --- a/bootstrap.php +++ b/bootstrap.php @@ -2,4 +2,4 @@ define('ROOT_DIR', __DIR__); date_default_timezone_set('Etc/UTC'); -require ROOT_DIR . '/autoload.php'; \ No newline at end of file +require ROOT_DIR . '/autoload.php'; diff --git a/composer.phar b/composer.phar old mode 100755 new mode 100644 index 508f9cf2..4055d874 Binary files a/composer.phar and b/composer.phar differ diff --git a/content/bio/brinck-slattery.md b/content/bio/brinck-slattery.md index b93830cc..87c3804c 100644 --- a/content/bio/brinck-slattery.md +++ b/content/bio/brinck-slattery.md @@ -1,8 +1,8 @@ --- name: Brinck Slattery -role: Director of Marketing +role: Director of Communications email: brinck@lbry.io twitter: LazerLotus777 github: Feanor78 --- -Brinck’s range of experience includes national political campaigns, RV sales, crisis PR, SEO writing and optimization, construction labor, and digital marketing consulting. An inveterate music and media lover, Brinck remembers the pain of Kazaa and Limewire, and is seriously psyched about living in the future where it’s easy to compensate artists directly for their work. He lives in Manchester, NH, where he is training to compete in the Granite State Strongman Competition this summer. +Brinck’s range of experience includes national political campaigns, crisis PR, SEO writing and optimization, digital marketing, RV sales, construction labor, and a variety of other strange and interesting pursuits (feel free to ask). An inveterate music and media lover, Brinck remembers the pain of Kazaa and Limewire, and is seriously psyched about living in the future where it’s easy to compensate artists directly for their work. He lives in Manchester, NH, where he is training to compete in the Granite State Strongman Competition this summer. diff --git a/content/bio/james-biller.md b/content/bio/james-biller.md new file mode 100644 index 00000000..49f75d4b --- /dev/null +++ b/content/bio/james-biller.md @@ -0,0 +1,10 @@ +--- +name: James Biller +role: Marketing Intern +email: james@lbry.io +twitter: billerjames +github: jamesbiller +--- +James came to LBRY through Praxis, a company that offers apprenticeships to young entreprenrial types. +Originally brought on to help with recruiting YouTubers, James showed up to the office with a 3D printed LBRYopoly board and since then has started to promote the LBRY protocol use case for 3D printing. +He has a passion for philosophy, 3D printing, and writing fiction. diff --git a/content/bio/mark-beamer.md b/content/bio/mark-beamer.md new file mode 100644 index 00000000..46e15a6b --- /dev/null +++ b/content/bio/mark-beamer.md @@ -0,0 +1,11 @@ +--- +name: Mark Beamer +role: Elastic Search and Database Developer +email: mark@lbry.io +github: tiger5226 +--- + +Mark Beamer is an inveterate entrepreneur. Ever since he founded his first company (Beamer Lawn Service) at 16, he's pursued new and interesting business ideas. +While running his first computer business, Mark discovered the joys of coding - especially the joys of using coding to eliminate annoying tasks with automation. +His continued passion for software led him to close the business after 4 years and pursue a BS in computational science and mathematics. +Mark has been developing software ever since. He found LBRY summer of 2017 on a leisurely weekend, saw the coding maxims and was sucked in instantly. LBRY has been his focus since. diff --git a/content/bio/samuel-lbryian.md b/content/bio/samuel-lbryian.md index 2cebd8a0..84c1c321 100644 --- a/content/bio/samuel-lbryian.md +++ b/content/bio/samuel-lbryian.md @@ -2,5 +2,7 @@ name: Samuel Bryan role: Pen Name email: hello@lbry.io +twitter: lbryio +github: lbryio --- Much of our writing is a collaboration between LBRY team members, so we use SamueL BRYan to share credit. Sam has become a friend... an imaginary friend... even though we're adults... diff --git a/content/bounty/app-i18n.md b/content/bounty/app-i18n.md index 71689392..598d4622 100644 --- a/content/bounty/app-i18n.md +++ b/content/bounty/app-i18n.md @@ -8,7 +8,7 @@ date: '2016-07-01' The LBRY App is currently lacking support for internationalization. -To complete this bounty, [lbry-app](https://github.com/lbryio/lbry-app) must be modified as follows: +To complete this bounty, [lbry-desktop](https://github.com/lbryio/lbry-desktop) must be modified as follows: - Add [gettext](https://en.wikipedia.org/wiki/Gettext) internationalization calls to the React app - Store selected language in the user settings diff --git a/content/bounty/automatic-app-updates.md b/content/bounty/automatic-app-updates.md index 1d9c5223..cff8a617 100644 --- a/content/bounty/automatic-app-updates.md +++ b/content/bounty/automatic-app-updates.md @@ -8,7 +8,7 @@ date: '2017-07-22' The LBRY app currently does not update itself automatically, however electron does support this feature. -To complete this bounty, [lbry-app](https://github.com/lbryio/lbry-app) must be modified as follows: +To complete this bounty, [lbry-desktop](https://github.com/lbryio/lbry-desktop) must be modified as follows: - Detect when a new latest release is available, download it and prompt the user with a "Restart LBRY" / "I'll do it later" dialog to have the update take effect. - Parse the changelog for the latest release, if it contains `Security` items carry out the update automatically without presenting the "I'll do it later" option. diff --git a/content/bounty/bittorrent-support.md b/content/bounty/bittorrent-support.md index 03c7a09b..7d367aef 100644 --- a/content/bounty/bittorrent-support.md +++ b/content/bounty/bittorrent-support.md @@ -2,7 +2,7 @@ category: daemon title: Add Support for BitTorrent award: 10000 -status: available +status: completed date: '2016-07-01' --- diff --git a/content/bounty/bulk-upload.md b/content/bounty/bulk-upload.md index 2f42e24a..5fb6f4b4 100644 --- a/content/bounty/bulk-upload.md +++ b/content/bounty/bulk-upload.md @@ -6,7 +6,7 @@ status: available date: '2018-03-20' --- -This bounty is to create a utility that can bulk upload a series of videos to LBRY. +This bounty is to create a utility that can bulk upload a series of videos to LBRY. A LBRY term intern started implementing this in https://github.com/filipnyquist/lbrysync - this can be used as a base to fill in the rest of the required features. This utility is for semi-technical users who have a large amount of content they want to sync. A good example use case would be the videos from a conference. diff --git a/content/bounty/fourohfour-page.md b/content/bounty/fourohfour-page.md index 1d8088b1..8a719982 100644 --- a/content/bounty/fourohfour-page.md +++ b/content/bounty/fourohfour-page.md @@ -6,7 +6,7 @@ status: complete date: '2017-09-07' --- -Our [404 page](https://lbry.io/nothing-here) is very bland. We'd like something that's more +Our 404 page is very bland. We'd like something that's more - **helpful** - tell the user what happened, and give them some things to do next - **pretty** - we spent very little time on the one we have diff --git a/content/bounty/hardware-wallet.md b/content/bounty/hardware-wallet.md new file mode 100644 index 00000000..d209bef7 --- /dev/null +++ b/content/bounty/hardware-wallet.md @@ -0,0 +1,19 @@ +--- +category: code +title: Hardware Wallet Integration +award: 750 +status: available +date: '2018-05-25' +--- + +Since LBRY is a fork of Bitcoin Core, integrating into existing hardware wallet platforms should be fairly straightforward. Two of the most popular hardware wallet platforms are Ledger and Trezor. You can find all the LBRY specific blockchain modifications on our [LBRYcrd GitHub repo](https://github.com/lbryio/lbrycrd) and more specifically in the [chain params code](https://github.com/lbryio/lbrycrd/blob/master/src/chainparams.cpp). + +The Trezor integration is a bit more involved and thus includes a 30% bonus on top of the stated bounty amount. +- [Ledger GitHub](https://github.com/LedgerHQ/ledger-nano-s) - Ledger Nano GitHub respository with guidelines on how to develop. This will involve setting up their SDK and creating a Chrome app. +- [Trezor GitHub](https://github.com/trezor) - Please see Trezor's [developer guide](https://doc.satoshilabs.com/trezor-tech/) for more information on integrations. + +Status: +Ledger - In Progress: +https://github.com/LedgerHQ/ledger-live-common/pull/49 +https://github.com/LedgerHQ/blue-app-btc/pull/47 +https://github.com/LedgerHQ/lib-ledger-core/pull/24 diff --git a/content/bounty/http-support.md b/content/bounty/http-support.md index 8008e062..c23c6b3b 100644 --- a/content/bounty/http-support.md +++ b/content/bounty/http-support.md @@ -2,7 +2,7 @@ category: daemon title: Add Support for HTTP award: 3000 -status: available +status: completed date: '2016-11-01' --- diff --git a/content/bounty/lbry-binding.md b/content/bounty/lbry-binding.md new file mode 100644 index 00000000..98df2d6e --- /dev/null +++ b/content/bounty/lbry-binding.md @@ -0,0 +1,26 @@ +--- +category: code +title: LBRY Binding in Any Programming Language +award: 250 +status: available +date: '2018-05-25' +--- + +When the LBRY daemon is running, it provides a set of API calls available via a webserver running on localhost. + +We want to make it as easy as possible to interact with LBRY in every programming language. + +Completing this bounty involves creating a simple library to make it as easy as possible to interact with the LBRY protocol in a language we do not already have a binding for. Ideally, this this also include the LBRYcrd api, see php example below for details. + +An example of a binding can be found in the [php-api](https://github.com/lbryio/php-api) repo. + +A binding can typically be written using cURL in less than 100 lines of code. The repo should be well documented for the wrapper language, have install/setup instructions and tests. + +If you are pursuing this effort, please [email us](mailto:hello@lbry.io) so we can update this bounty with the current progress. + +Bounty Status: +Ruby - Completed +Lua - Completed +Java - Completed +Javascript - Completed +Rust - Started/Claimed diff --git a/content/bounty/mining-inflation.md b/content/bounty/mining-inflation.md new file mode 100644 index 00000000..f4c5ca92 --- /dev/null +++ b/content/bounty/mining-inflation.md @@ -0,0 +1,19 @@ +--- +category: code +title: Mining Inflation Chart +award: 300 +status: available +date: '2018-05-25' +--- + +LBRY Credits(LBC) are distributed via the blockchain mining process approximately every 150 seconds (2.5 minutes). There is a total supply of 1B credits, of which LBRY [controls 400M](https://lbry.io/faq/credit-policy) for funding, rewarding users and partnerships. The other 600M will be mined over a total of ~20 years from inception (June 2016). + +The [mining schedule](https://lbry.io/faq/block-rewards) details how these credits will be distributed. We are currently in the logarithmically decreasing phase of the mining process where, at the time of this writing (May 20, 2018), the current block reward is 359 LBC. You can find the current block reward by visiting the [explorer](https://explorer.lbry.io) page and clicking the latest block to find the first output. + +The goal of this bounty is to analyze and visualize the impact of mining inflation on coin supply. The end result would be the calculation code and web embeddable supply chart. Click [here](https://www.bitcoinmining.com/how-are-new-bitcoins-created/) to find an example. At a minimum, the x-axis should display dates and the y-axis is the cumulative LBC available. + +Hints: +- [Sample PHP Program](https://drive.google.com/open?id=19LXPIBhZnd-SEnQlrke2tb-ZwESrbK2D) to calculate estimated block reward at any given time. +- Use actual block times from https://explorer.lbry.io (or (Chainquery)[https://github.com/lbryio/chainquery) if it's avialable) for historical data + +Bonus: Overlay with circulating supply information from CoinMarketCap.com diff --git a/content/bounty/pkg-installer-for-osx.md b/content/bounty/pkg-installer-for-osx.md index 7ce88d8b..f83e4021 100644 --- a/content/bounty/pkg-installer-for-osx.md +++ b/content/bounty/pkg-installer-for-osx.md @@ -3,7 +3,7 @@ category: osx title: PKG Installer for OS X award: 1500 status: complete -pr: https://github.com/lbryio/lbry-app/pull/99 +pr: https://github.com/lbryio/lbry-desktop/pull/99 date: '2016-07-01' --- diff --git a/content/bounty/typos-and-edits.md b/content/bounty/typos-and-edits.md index 3cd08ffe..960c1a43 100644 --- a/content/bounty/typos-and-edits.md +++ b/content/bounty/typos-and-edits.md @@ -11,3 +11,5 @@ This bounty is awarded for finding and correcting a typo, misspelling, or poor p This includes everything from low-level help messages or comments in code to the copy of the website itself. Please initiate a pull request on GitHub to receive this bounty. + +Note: Bounty award may be higher or lower depending on the nature of the change. diff --git a/content/bounty/wallet-ui.md b/content/bounty/wallet-ui.md index 17151e8a..e02271ae 100644 --- a/content/bounty/wallet-ui.md +++ b/content/bounty/wallet-ui.md @@ -7,7 +7,7 @@ pr: https://github.com/lbryio/lbry-web-ui/pull/23 date: '2016-07-01' --- -Add a basic wallet interface to the LBRY Browser [`lbry-app`](https://github.com/lbryio/lbry-app). +Add a basic wallet interface to the LBRY Browser [`lbry-desktop`](https://github.com/lbryio/lbry-desktop). The interface must support: diff --git a/content/credit-reports/2016-Q2.md b/content/credit-reports/2016-Q2.md index a6c44d45..3ca8d7d3 100644 --- a/content/credit-reports/2016-Q2.md +++ b/content/credit-reports/2016-Q2.md @@ -17,7 +17,7 @@ Currently, we are engaging in the following programs: - 3-20 LBC for users invited to participate in the beta. This program is expected to continue well through this quarter. - 1-5 LBC for referrals. This program is expected to continue in parallel with above. - Up to 2000LBC for certain publishers to join LBRY. This program is new and experimental and subject to revision. -- A [bounty program](http://lbry.io/bounty) with awards varying from small awards to 10,000+ LBC. This program is new and experimental. +- A [bounty program](https://lbry.io/bounty) with awards varying from small awards to 10,000+ LBC. This program is new and experimental. - Tipping on our community chat for user contribution and participation. This program is expected to continue. We have allocated approximately 2,000,000 LBC across these categories, but do not anticipate actually awarding this much. diff --git a/content/credit-reports/2018-Q2.md b/content/credit-reports/2018-Q2.md new file mode 100644 index 00000000..5cfd4957 --- /dev/null +++ b/content/credit-reports/2018-Q2.md @@ -0,0 +1,38 @@ +--- +title: "Quarterly Credit Report: Second Quarter 2018" +sheet: https://docs.google.com/spreadsheets/d/1aRmrjTNfiKQwzW5WB_tcVJzHR-UXrCz6vHpINMfTVWs/edit?usp=sharing +category: policy +--- +## Summary +This quarter we moved no credits from cold storage. We spent 738,027 total community credits on line items detailed below. No operational credits were moved to markets. No institutional credits were moved or spent. +We anticipate comparable or larger total outlays in Q3 2018. Operational spending may increase, but not significantly, and community spending is likely to be higher. We will continue to incentivize new users and other beneficial behavior, which is likely to involve 300,000 to 1,500,000+ LBC. LBRY is also likely to form it’s first institutional partnership, with spending anticipated to be around 500,000 LBC. + +## Overview By Fund + +### Community Fund + +738,027 credits were spent from the community fund, in the following areas: + +| Category | Amount | +|---|---| +| Bounties | 48,606 | +| User Engagement | 275,000 | +| Community Management | 35,810 | +| Technical Contributions | 148,880 | +| New Publishers | 108,495 | +| Acquisition | 85,133 | +| Testing | 3103 | +| LBRY.fund | 33,333 | + +### Operational Fund + +* LBRY sold no LBC on the open market + +LBRY does not anticipate moving credits to market this quarter due to both market conditions and our favorable cash position. However, should market conditions or our needs change, we reserve the right to move credits to market as needed. + + +### Institutional Fund + +No activity this quarter. + +We may run our first institutional pilot programs this quarter. Any outlays from this fund this quarter will be minimal. diff --git a/content/faq/how-to-stop-lbry.md b/content/faq/ARCHIVE-how-to-stop-lbry.archive similarity index 100% rename from content/faq/how-to-stop-lbry.md rename to content/faq/ARCHIVE-how-to-stop-lbry.archive diff --git a/content/faq/claimtrie-implementation.md b/content/faq/ARCHIVED-claimtrie-implementation.archive similarity index 100% rename from content/faq/claimtrie-implementation.md rename to content/faq/ARCHIVED-claimtrie-implementation.archive diff --git a/content/faq/how-to-encrypt-wallet.md b/content/faq/ARCHIVED-how-to-encrypt-wallet.archive similarity index 95% rename from content/faq/how-to-encrypt-wallet.md rename to content/faq/ARCHIVED-how-to-encrypt-wallet.archive index 08579c4a..72cb2a62 100644 --- a/content/faq/how-to-encrypt-wallet.md +++ b/content/faq/ARCHIVED-how-to-encrypt-wallet.archive @@ -2,6 +2,8 @@ title: How do I encrypt my wallet? category: wallet --- +**THIS FAQ WAS MOVED TO LBRY.TECH *** + *Note: The below instructions are the [LBRYCrd Full Blockchain wallet](https://github.com/lbryio/lbrycrd) and not the default wallet that ships with the LBRY App. We are still working on an encryption solution for this.* You can use `lbrycrd-cli encryptwallet ` to encrypt your wallet. diff --git a/content/faq/how-to-run-lbry-with-lbrycrdd.md b/content/faq/ARCHIVED-how-to-run-lbry-with-lbrycrdd.archive similarity index 100% rename from content/faq/how-to-run-lbry-with-lbrycrdd.md rename to content/faq/ARCHIVED-how-to-run-lbry-with-lbrycrdd.archive diff --git a/content/faq/proof-algorithm.md b/content/faq/ARCHIVED-proof-algorithm.archive similarity index 100% rename from content/faq/proof-algorithm.md rename to content/faq/ARCHIVED-proof-algorithm.archive diff --git a/content/faq/to-log-to-console.md b/content/faq/ARCHIVED-to-log-to-console.archive similarity index 100% rename from content/faq/to-log-to-console.md rename to content/faq/ARCHIVED-to-log-to-console.archive diff --git a/content/faq/regtest-setup-guide.md b/content/faq/archived-regtest-setup-guide.archive similarity index 100% rename from content/faq/regtest-setup-guide.md rename to content/faq/archived-regtest-setup-guide.archive diff --git a/content/faq/backup-data.md b/content/faq/backup-data.md index 70cf9ff1..9b048377 100644 --- a/content/faq/backup-data.md +++ b/content/faq/backup-data.md @@ -31,7 +31,9 @@ To summarize, the main directories to consider when backing up or migrating a LB 6. Sign back into LBRY via your Email by going to the wallet (bank icon in the top right) and then accessing the Rewards tab 7. Verify wallet balance, Downloads, Published files and Rewards eligibility -*\*Please note: Do not run two instances of LBRY from the same wallet, this is unsupported* +*\*Note 1: Do not run two instances of LBRY from the same wallet, this is unsupported* + +*\*Note 2: If you get startup errors after this procedure: check if the `daemon_settings.yml` file from the `lbrynet` folder points to the correct LBRY file download directory. If it does not, edit the file with correct file path and restart the app. You can view and edit this file with any text editor.* ### I'm in need of some assistance, can you help? diff --git a/content/faq/content.md b/content/faq/content.md index 22ff1733..215ec10a 100644 --- a/content/faq/content.md +++ b/content/faq/content.md @@ -1,9 +1,9 @@ --- -title: What kind of content can I upload, and what about illegal or infringing content? +title: How does LBRY handle content and what am I allowed to upload? category: publisher --- -This guide provides answers to questions regarding what you may upload to the LBRY app and network, as well as how to report illegal or infringing content. To learn more about our DMCA producedures, please see our [DMCA article](https://lbry.io/faq/dmca). +This guide provides answers to questions regarding what you may upload to the LBRY app and network and how it differs from current centralized/status quo systems. To learn more about reporting infringing or illegal content and DMCA producedures, please see our [DMCA article](https://lbry.io/faq/dmca). ### What content can I legally upload to LBRY? @@ -11,18 +11,12 @@ You may upload content you created or own that does not infringe on the rights o ### Because LBRY is decentralized, doesn't this mean the content can’t be removed? -It is important to make a distinction between the LBRY protocol and any applications running on top when referring to censorship. The LBRY protocol is fully decentralized and censorship-resistant, with it storing metadata and naming on the blockchain, and facilitating data transfers over a peer to peer (P2P) network. +It is important to make a distinction between the LBRY protocol and any applications running on top when referring to censorship and the ability to block access to certain content. The LBRY protocol is fully decentralized and censorship-resistant - it provides permissionless access to [claiming of URLs](https://lbry.io/faq/naming) and indexing metadata on the blockchain, and facilitates data transfers over a peer to peer (P2P) network which consists of our own content severs and anyone running the LBRY protocol. This means infringing content may be stored on our servers, by the uploader and by anyone else who may have downloaded it. -However, LBRY also makes an app to demonstrate our protocol. Within our app, we will engage in non-arbitrary censorship, meaning only horrific or infringing content will be removed. As a U.S. company, LBRY Inc. and management of our app, and other services in our control, will follow all U.S. laws, including the CDA and DMCA. If someone made an app or website using the LBRY protocol in some other country, it would have to follow that country's laws, which aren’t necessarily the same as ours. Either app would read the same blockchain, though. +On the other hand, LBRY also makes an App and other services like [spee.ch](https://spee.ch) to demonstrate the protocol's capabilties. Within our app, we will engage in non-arbitrary censorship, meaning only horrific or infringing content will be blocked and removed from our content servers. As a U.S. company, LBRY Inc. and management of our app, and other services in our control, will follow all U.S. laws, including the CDA and DMCA. If someone made an app or website using the LBRY protocol in some other country, it would have to follow that country's laws, which aren’t necessarily the same as ours. Either app would read the same blockchain, though. LBRY Inc., makes no guarantee your content will be hosted on the network. The peer-to-peer network relies on hosts to seed content. If no user continues to host data - including you, it will not continue to be available on the network. -### Someone uploaded my content to LBRY without permission. How do I remove it? - -Reporting infringing/illegal content in the LBRY App is easy. You can [file a report here](https://lbry.io/dmca) or within the LBRY app. - -Open the LBRY App and navigate to the content you wish to report. Underneath the content, there are two buttons: "Download," and "Report." When you click on the Report button, you will be redirected to a web form to report the content to LBRY, Inc., who can remove the content link from the LBRY App. - ### What do I do if I see content that’s illegal or infringing in the LBRY App? -Please follow the FAQ above for reporting infringing content, and LBRY staff will review your report and take any appropriate action. +Please read our [DMCA FAQ](https://lbry.io/faq/dmca) for more information on reporting infringing content. diff --git a/content/faq/contributing.md b/content/faq/contributing.md index d1b8343e..812321ab 100644 --- a/content/faq/contributing.md +++ b/content/faq/contributing.md @@ -28,13 +28,13 @@ Whether you want to report an issue, contribute to the code, or help test the so | Component | Language | What Is It | Use This Repo For..| --- | --- | --- | --- | [lbry](https://github.com/lbryio/lbry) | Python | A daemon that runs in the background and allows your computer to speak LBRY. | Issues with downloading or uploading.

Anything related to output in `lbrynet.log`.

Issues unrelated to or deeper than the interface that does not deal with blockchain credits. | -| [lbry-app](https://github.com/lbryio/lbry-app) | JavaScript | A graphical browser for the LBRY protocol | Problems with or features missing from the browser interface.

Issues with using, installing or running the LBRY app **other** than network, connection, or performance issues. | -| [lbryum](https://github.com/lbryio/lbryum) | Python | Server for the thin wallet bundled with lbry/lbry-app | Issues related to credit/wallet functionality.

This is a fork of electrum. +| [lbry-desktop](https://github.com/lbryio/lbry-desktop) | JavaScript | A graphical browser for the LBRY protocol | Problems with or features missing from the browser interface.

Issues with using, installing or running the LBRY app **other** than network, connection, or performance issues. | +| [lbryum](https://github.com/lbryio/lbryum) | Python | Server for the thin wallet bundled with lbry/lbry-desktop | Issues related to credit/wallet functionality.

This is a fork of electrum. | [lbrycrd](https://github.com/lbryio/lbrycrd) | C++ | The LBRY blockchain and standalone wallet | Running a full node, or direct access to the LBRY blockchain.

(This wallet is not bundled with the application. You only want this if you downloaded/installed this package specifically.) | [lbry-schema](https://github.com/lbryio/lbryschema) | Protobuf, Python | The structure of the metadata stored in the LBRY blockchain. | You want to change the metadata LBRY stores about digital content. | | [lbryio](https://github.com/lbryio/lbry.io) | PHP | The lbry.io website. | Edits to the site, FAQ/KB requests or additions. -The vast majority of issues will be filed in either `lbry-app` or `lbry`. +The vast majority of issues will be filed in either `lbry-desktop` or `lbry`. ## Raising Issues {#raising-issue} @@ -86,13 +86,13 @@ If you're a writer, designer, or communicator, you can also contribute to LBRY. ### Writing {#writing} -If you want to update or edit existing written copy, it likely exists in either [lbry.io](https://github.com/lbryio/lbry.io) (the website) or [lbry-app](https://github.com/lbryio/lbry-app) (the browser). Try searching the respective repo for a string (in quotes) related to the copy that you want to adjust. You can likely figure out how to edit text via the GitHub interface. If not, you can point out issues to [Tom](mailto:tom@lbry.io). +If you want to update or edit existing written copy, it likely exists in either [lbry.io](https://github.com/lbryio/lbry.io) (the website) or [lbry-desktop](https://github.com/lbryio/lbry-desktop) (the browser). Try searching the respective repo for a string (in quotes) related to the copy that you want to adjust. You can likely figure out how to edit text via the GitHub interface. If not, you can point out issues to [Tom](mailto:tom@lbry.io). If you want to contribute new written copy, such as a blog post or other content, please contact [Jeremy](mailto:jeremy@lbry.io), or join or [chat](https://chat.lbry.io) and post a message in #general. ### Designing {#designing} -If you're a web designer, you can contribute to either [lbry.io](https://github.com/lbryio/lbry.io) (the website) or [lbry-app](https://github.com/lbryio/lbry-app) (the browser) by opening a pull request. +If you're a web designer, you can contribute to either [lbry.io](https://github.com/lbryio/lbry.io) (the website) or [lbry-desktop](https://github.com/lbryio/lbry-desktop) (the browser) by opening a pull request. If you're a graphic designer, creating engaging graphics, GIFs, explainers, HOWTOs, wallpapers, and other related graphical content is a huge help! You can submit or discuss contributions by emailing [Jeremy](mailto:jeremy@lbry.io) or joining the #design channel in our [chat](https://chat.lbry.io). @@ -108,7 +108,7 @@ Translations are not managed through Git or GitHub. Email [Josh](mailto:josh@lbr If you aren't a coder, or you're a lazy coder, one of the best ways you can contribute is testing! -Both `lbry` and `lbry-app` go through regular release cycles where new versions are shipped every few weeks. Testing release candidates or builds of a master is a great way to help us identify issues and ship bug-free code. +Both `lbry` and `lbry-desktop` go through regular release cycles where new versions are shipped every few weeks. Testing release candidates or builds of a master is a great way to help us identify issues and ship bug-free code. For any repos, you want to be a tester on, "Watch" the repo on GitHub. You will receive an email with release notes whenever a release candidate is out. diff --git a/content/faq/different-toaster.md b/content/faq/different-toaster.md index fb770ae1..845b78fb 100644 --- a/content/faq/different-toaster.md +++ b/content/faq/different-toaster.md @@ -8,4 +8,36 @@ LBRY transfers data via EM waves in the infrared spectrum* to deliver content an LBRY is also brave and little. So we’re basically, fundamentally the same as a toaster. -**Assumes fiber-optic transmission.* +\**Assumes fiber-optic transmission.* + +There happen to be a lot of technological advancements that have happened to toasters. These scientific feats apply to LBRY, as well. + +### Multithreading + +![Now that's a lot of threads!](https://spee.ch/dd1aa7f0db34d4bba810573d489c0fc857d0c492/t0.png) + +Properly-applied multithreading has allowed the distribution of workloads in LBRY, freeing up time that could have been spent waiting for required actions. + +### Multi-role, Multi-function + +![So many options, it's overwhelming.](https://spee.ch/d4e459e707ae446722faa86cf32940afc0fc206f/t1.png) + +LBRY can service many forms of data, such as videos, pictures, and sourdough. + +### Network Optimization + +![A solid 56KiB/s.](https://spee.ch/1038a070b1e43a41d40050f5a23e9a36a3884ecf/t2.png) + +In this day and age of cool acronyms (such as IEEE 802.11**ax**) and now-common internet integration into devices, LBRY too has joined the crowd. TLNI (Toaster-Level Network Integration) technology has been fused with LBRY, permitting speeds at least as fast as your dial-up modem. + +### Open-source + +![GNU-approved.](https://spee.ch/b4e7aefb242e78bfc164b42d27d251d576c04994/t3.png) + +Proprietary toasters may try to limit what you can put in them and your ability to look into the toaster's operation. LBRY is a different kind of toaster. Its full source code is available online at [its GitHub repos](https://github.com/lbryio). + +### Mobile + +![LBRY OTG](https://spee.ch/ffada396dd2af9569148f1fa8f87167f8ec1f88e/t4.png) + +Lightweight versions of TLNI and LBRY have been made and ported to Android. Take LBRY on the go! diff --git a/content/faq/dmca.md b/content/faq/dmca.md index 126b93f1..b89543e5 100644 --- a/content/faq/dmca.md +++ b/content/faq/dmca.md @@ -8,12 +8,22 @@ Please see our [Content FAQ](https://lbry.io/faq/content) for an explanation of The Digital Millennium Copyright Act (DMCA) is United States Copyright Law written to protect digital content against illegal use. When an owner finds their content used on the Internet without their permission, they can file a DMCA request with the website, app, or Internet Service Provider illegally hosting their content, asking for it to be removed. +### Someone uploaded my content to LBRY without permission. How do I report it? + +Reporting infringing/illegal content in the LBRY App is easy. You can [file a report here](https://lbry.io/dmca) or within the LBRY app. + +Open the LBRY App and navigate to the content you wish to report. Underneath the content, there are two buttons: "Download," and "Report." When you click on the Report button, you will be redirected to a web form to report the content to LBRY, Inc., who can remove the content link from the LBRY App. + ### How does LBRY handle and respond to a DMCA request or takedown notice? Upon receipt of a DMCA request from the content owner or legal representative, LBRY Inc will use its best judgment to determine validity. We will block access to content deemed infringing in any LBRY Inc. owned applications. LBRY Inc. will also remove any data related to the infringing content from servers within its control. Due to the immutable nature of the LBRY blockchain, a record and metadata of the infringing content may continue to exist if the original publisher does not remove it. The data may still exist on the original publisher's computer (and anyone who may have downloaded it prior to it being blocked) but will not be accessible through any LBRY Inc controlled applications. +### Where are DMCA requests recorded? + +You can view all of the DMCA requests LBRY receives on our [DMCA GitHub Repository](https://github.com/lbryio/dmca), categorized by year and dated by filename. + ### Can I dispute a DMCA complaint? Yes, you may. We can't give you legal advice, so check with your local attorney or legal group about how to do this. There are also good resources online to learn about DMCA counter-notices. The [EFF has published an excellent guide](https://www.eff.org/issues/intellectual-property/guide-to-youtube-removals) about how to deal with legal issues facing online content creators. If you have done the research or feel the content was blocked in error, [please reach out to us](mailto:help@lbry.io). diff --git a/content/faq/exchanges.md b/content/faq/exchanges.md index 70c9cb6a..02e0844e 100644 --- a/content/faq/exchanges.md +++ b/content/faq/exchanges.md @@ -3,14 +3,19 @@ title: Where can I buy or sell LBC? category: getstarted --- -We are listed on several exchanges. You can buy or sell credits at one of these: +We are listed on several exchanges. You can buy or sell LBRY Credits at one of these: +### Traditional Exchanges - [Bittrex](https://bittrex.com/Market/Index?MarketName=BTC-LBC) - [Poloniex](https://poloniex.com/exchange#btc_lbc) -- [Shapeshift](https://shapeshift.io) -- [Changelly](https://changelly.com/exchange/BTC/LBC/1) -- [BitSquare](https://bitsquare.io/) -- [Cryptopia](https://www.cryptopia.co.nz/Exchange/?market=LBC_BTC) - [Upbit](https://upbit.com/exchange?code=CRIX.UPBIT.BTC-LBC) +- [Cryptopia](https://www.cryptopia.co.nz/Exchange/?market=LBC_BTC) +- [BitSquare](https://bitsquare.io/) - [Coinspot](https://www.coinspot.com.au/buy/lbc) - [Trade by Trade](https://app.tradebytrade.com/exchange-one) + +### Instant Exchanges +- [Shapeshift](https://shapeshift.io) +- [Changelly](https://changelly.com/exchange/BTC/LBC/1) +- [ChangeNow](https://changenow.io/exchange?amount=1&from=btc&to=lbc) +- [Simple Swap](https://www.simpleswap.io/) diff --git a/content/faq/how-to-backup-wallet.md b/content/faq/how-to-backup-wallet.md index 6de143b0..4ad6aa22 100644 --- a/content/faq/how-to-backup-wallet.md +++ b/content/faq/how-to-backup-wallet.md @@ -2,13 +2,18 @@ title: How do I back up my LBRY wallet? category: wallet --- -The LBRY application relies on blockchain technology and the LBRY Credits (LBC) cryptocurrency in order to participate in the network. These LBC are stored in a wallet (data file on your PC) which is generated on each user's PC when they install LBRY. Think of your credits as digital cash on your PC. It is important to understand that the wallet is not stored on any LBRY servers and as such, users are responsible for its safeguarding and making sure a backup (copy of the wallet file) is available in the event that it is lost. Wallets should be backed up periodically to ensure the most up to date information is available. +The LBRY application relies on blockchain technology and the LBRY Credits (LBC) cryptocurrency in order to participate in the network. These LBC are stored in a wallet (data file on your PC) which is generated with each LBRY installation (think of your credits as digital cash on your PC). A wallet contains your funds, channel data, and claims (any uploads). It is important to understand that the wallet is not stored on any LBRY servers and as such, users are responsible for its safeguarding and making sure a backup (copy of the wallet file) is available in the event that it is lost. Wallets should be backed up periodically to ensure the most up to date information is available. ## How do I find my wallet? -The easiest way to find the location of your LBRY wallet is via the [LBRY app](https://lbry.io/get). Open LBRY and then go to your wallet (bank icon in the top right of the screen). In the Balance section, you will see a link for `Backup`, click this. If this link is grayed out, it usually means you don't have any credits (see section below on how to manually find the wallet). +The easiest way to find the location of your LBRY wallet is via the [LBRY app](https://lbry.io/get). Open LBRY and on the left side, you should see an option for wallet. +![wallet](https://spee.ch/d/wallet.jpeg) -When you click `Backup`, you will be shown the location of your `lbryum` directory that contains the wallet file. Navigate to this directory via your file explorer to locate your wallet. You can either choose to backup this whole directory, the wallets directory inside it or the `default_wallet` file itself inside the wallets directory. +Click on the Wallet tab to expand then click `Backup` numbered `1`. +![backup](https://spee.ch/f/backup.jpeg) +after clicking on backup, you will see a link for `Backup`, open the location of the link in your local machine and make a copy of the files stored in that location. If this link is grayed out, it usually means you don't have any credits (see section below on how to manually find the wallet). + +When you click `Backup`, you will be shown the location of your `lbryum` directory that contains the wallet file(numbered `2`). Navigate to this directory via your file explorer to locate your wallet. You can either choose to backup this whole directory, the wallets directory inside it or the `default_wallet` file itself inside the wallets directory. ## How do I backup my wallet? diff --git a/content/faq/how-to-change-email.md b/content/faq/how-to-change-email.md index 760fa79f..ebd5b1d1 100644 --- a/content/faq/how-to-change-email.md +++ b/content/faq/how-to-change-email.md @@ -3,9 +3,9 @@ title: How do I change my LBRY connected email? category: troubleshooting --- -In certain cases, you may want to change the email connected to your LBRY App. LBRY stores the email address along with an access token which is unique to each installation. To clear this token, shutdown LBRY and see instructions below for each operating system. +In certain cases, you may want to change the email connected to your LBRY App. LBRY stores the email address along with an access token which is unique to each user account. To clear this token, shutdown LBRY and see instructions below for each operating system. -Clearing out this token will allow you to change your email or reset your private access token. When you start up LBRY after clearing the token, you can reconnect an email address by going to **Settings** (gear icon in the top right) > **Help** > **Set Email** in the About section. After setting the email, your LBRY Rewards status should be transferred to the new account. If this does not happen, please reach out to us via [email](mailto:help@lbry.io) with your old/new email addresses. +Clearing out this token will allow you to change your email or reset your private access token. When you start up LBRY after clearing the token, you can reconnect an email address by going to **Help** > **Set Email** in the About section. After setting the email, your LBRY Rewards status should be transferred to the new account. If this does not happen, please reach out to us via [email](mailto:help@lbry.io) with your old/new email addresses. ## Windows 1. Open the Control Panel, find the Credentials Manager (may need change View By setting) diff --git a/content/faq/how-to-change-port.md b/content/faq/how-to-change-port.md index f1fd1cca..84ec1aad 100644 --- a/content/faq/how-to-change-port.md +++ b/content/faq/how-to-change-port.md @@ -5,6 +5,16 @@ category: setup If you see the error message `couldn't bind to port 3333`, it is likely that another process is already bound to that port. You will need to change the port before starting the daemon. The port number should not matter as long as it is available and not blocked by your ISP. +The most user friendly way to change the port permanently is to append the below line to the `daemon_settings.yml` in the `lbrynet` [directory](https://lbry.io/faq/lbry-directories). If it doesn't exist, create a new file named `daemon_settings.yml` and append: + + peer_port: 3334 + +Sample daemon_settings.yml (may vary by OS): + + {download_directory: c:\users\lbry, + peer:port: 3334} + +## Other Methods To change the port once during runtime, set the LBRY_PEER_PORT env variable. Here's one way to do this: LBRY_PEER_PORT=3334 ./lbrynet-daemon @@ -17,6 +27,4 @@ or via cli command lbrynet-cli settings_set --peer_port 3334 -Another way to change the port permanently is to append the below line to the `daemon_settings.yml` in the `lbrynet` [directory](https://lbry.io/faq/lbry-directories). If it doesn't exist, create a new file named `daemon_settings.yml` and append: - peer_port: 3334 diff --git a/content/faq/how-to-clean-install-preserve.md b/content/faq/how-to-clean-install-preserve.md index 51170525..fa7fcf57 100644 --- a/content/faq/how-to-clean-install-preserve.md +++ b/content/faq/how-to-clean-install-preserve.md @@ -21,7 +21,7 @@ Note: after a clean install, you may be prompted again for your email. This is n 2. `blobs.db` - reference data for the blob files which are used for hosting purposes 3. `lbryfile_info.db` - Downloads and Publishes data 4. `blockchainname.db` - Supports downloads data -8. Install the latest version of LBRY from: [Github App Page](https://github.com/lbryio/lbry-app/releases "Github App Page"). If prompted to allow through Windows Firewall, click Allow +8. Install the latest version of LBRY from: [Github App Page](https://github.com/lbryio/lbry-desktop/releases "Github App Page"). If prompted to allow through Windows Firewall, click Allow 9. LBRY should start immediately after install. If you kept your data, your balance and content would be reflected ## MacOS @@ -36,7 +36,7 @@ Note: after a clean install, you may be prompted again for your email. This is n 4. `blockchainname.db` - Supports downloads data 6. In Finder - click Go menu on top, choose "Go To Folder", type. ~/.lbryum and then click go 7. If performing a clean install, delete the entire contents of this folder **(!!THIS WILL DELETE YOUR WALLET!!)** and proceed to the next step, otherwise delete just the `blockchain_headers` file -8. Install the latest version of LBRY from: [Github App Page](https://github.com/lbryio/lbry-app/releases "Github App Page") +8. Install the latest version of LBRY from: [Github App Page](https://github.com/lbryio/lbry-desktop/releases "Github App Page") 9. Launch LBRY by starting it from the Applications folder. You can add it to your dock for easier access. If you kept your data and wallet, your balance and content would be reflected ## Ubuntu / Linux @@ -51,6 +51,6 @@ Note: after a clean install, you may be prompted again for your email. This is n 2. `blobs.db` - reference data for the blob files which are used for hosting purposes 3. `lbryfile_info.db` - Downloads and Publishes data 4. `blockchainname.db` - Supports downloads data -8. Install the latest version of LBRY from: [Github App Page](https://github.com/lbryio/lbry-app/releases "Github App Page") +8. Install the latest version of LBRY from: [Github App Page](https://github.com/lbryio/lbry-desktop/releases "Github App Page") 9. Click the Search button on the toolbar, type LBRY and then hit Enter to launch LBRY. You can pin it to your taskbar for easier access. If you kept your data, your balance and content would be reflected diff --git a/content/faq/how-to-cli.md b/content/faq/how-to-cli.md index 13f0b9fc..fb25edab 100644 --- a/content/faq/how-to-cli.md +++ b/content/faq/how-to-cli.md @@ -9,7 +9,7 @@ In recent versions of LBRY, the lbrynet-cli tool is no longer included with the ## Windows 1. Open a **Command Prompt** application window -1. Type `cd "C:\Program Files (x86)\LBRY\resources\static\daemon"` and click Enter +1. Type `cd "C:\Program Files\LBRY\resources\static\daemon"` ([32-bit located in Program Files(x86)]) and click Enter 1. Type `lbrynet-cli status` and click **Enter**. This will return the LBRYnet status data 1. See examples below or [LBRY CLI documentation](https://lbryio.github.io/lbry/cli/) for additional commands diff --git a/content/faq/how-to-find-lbry-log-file.md b/content/faq/how-to-find-lbry-log-file.md index cf6e2ff3..c96254ea 100644 --- a/content/faq/how-to-find-lbry-log-file.md +++ b/content/faq/how-to-find-lbry-log-file.md @@ -6,7 +6,15 @@ category: troubleshooting In certain cases, we may ask you to send us your log file(s). The current log file is titled `lbrynet.log` (or just `lbrynet` if you have file extensions hidden) and is archived each time the files reaches 2MB. Older log files are copied to `lbrynet.log.<#>`. Typically only the lbrynet.log file is required, but we may ask for the others depending on the situation. Since each Operating System has its own set of working directories, use the below guide in order to locate the log file(s). **lbrynet.log files may contain your IP address. While sharing this is not inherently dangerous, if you desire maximum privacy, please mask it before posting to public websites.** + + ### Find Logs via the LBRY App + You are able to open the log folder from the Help tab in the LBRY app. + From the LBRY App, click on Help. Next, click on "Open Log Folder" + ![log](https://spee.ch/a/helps.jpeg) + The folder will be highlighted, so just double click to open and here you will see "lbrynet.log". + +### Find Logs Manually ## Windows 1. Open File Explorer (Keyboard shortcut: Window Key + E) 1. Type `%localappdata%\lbry\lbrynet` (or `%appdata%\lbrynet` if you originally installed v0.14 or earlier) into the address bar and click Enter. diff --git a/content/faq/how-to-publish.md b/content/faq/how-to-publish.md index dc45437f..40c36a6b 100644 --- a/content/faq/how-to-publish.md +++ b/content/faq/how-to-publish.md @@ -4,94 +4,110 @@ category: publisher order: 1 --- -LBRY is a free, open, and community-driven digital marketplace which enables content sharing, monetization, discovery and consumption. Publishing in LBRY is the process by which you share your content on the network - you set the price per view (can be free too) which is paid directly to you. This process involves making a "claim" in the LBRY Blockchain which will be used to retrieve the content via a URL. Content can either be published anonymously or to a particular channel/identity which will group content together at a single location. Both channels and claims require a deposit(bid) of LBRY Credits (LBC) in order to reserve their location on the LBRY network. This deposit will be deducted from your balance as long as the claim is active. See our [naming](https://lbry.io/faq/naming) and [transaction](https://lbry.io/faq/transaction-types) documentation for more information about claims, bids and transactions. +LBRY is a free, open, and community-driven digital marketplace which enables content sharing, monetization, discovery and consumption. Publishing in LBRY is the process of sharing your content on the network. You set the price per view (can be free too) which is paid directly to you. This process involves making a "claim" in the LBRY blockchain which will be used to retrieve the content via a URL. Content can either be published anonymously or to a particular channel/identity which groups content in a single location. Both channels and claims require a deposit (bid) of LBRY Credits (LBC) in order to reserve their location on the LBRY network. This deposit will be deducted from your balance as long as the claim is active. See our [naming](https://lbry.io/faq/naming) and [transaction](https://lbry.io/faq/transaction-types) documentation for more information about claims, bids and transactions. Want to get your content featured on the Discover page? Check out [Community top bids](https://lbry.io/faq/community-top-bid). If you don't have LBRY yet, download it [here](https://lbry.io/get). -### How do I create a Channel? - -1. Open the LBRY app. -2. Once the application loads, click the `Publish` button in the top right of the screen. -![Click the Publish Button](https://spee.ch/de822faa6cda4989f68ec66abe5254bdd1ad031b/1111.jpeg) - -3. In the `Channel Name` section click the dropdown and select `New Channel` and declare the name you would like for your channel. For more details on different channel types, see our write up on [naming](https://lbry.io/faq/naming). -![Click the New Channel Dropdown](https://spee.ch/eb37ca6c6ea9d795bf2fc2a124f52b0084453c40/2222.jpeg) - -4. Once your name is selected, there is a `Deposit` section that is below. It requires a minimum bid of 0.0001 LBC (see more on deposits [here](https://lbry.io/faq/naming)). Please ensure that you have enough LBRY credits in your wallet to cover the bid amount. There is also a small network fee associated with the creation of a channel. -![Set the Deposit](https://spee.ch/31612026416cf1ea2c4b433aaa9835cd939a28be/3333.jpeg) - -5. Click `Create Channel` once you have entered your bid amount. You now own `lbry://@channelnameyoubidon#Claim_ID` and `lbry://@channelnameyoubidon` (the vanity name without a claim id) if you are the highest bidder. -![Create the Channel](https://spee.ch/f14f6ec87e3a962f0112142a766d8c7f67820568/4444.jpeg) - ### How do I Publish content? 1. Click the `Publish` button in the top right of the screen. -![Select Choose File](https://spee.ch/1f7bfc865a9a7b9cd6cb026a8e31343703fd57f8/Publishing001.png) +![Select Choose File](https://spee.ch/0/1-click-publish.jpeg) -1. Under the `Content` section click `Choose File`. -![Select the Content to Upload](https://spee.ch/7e53708abaab90b89c1e410cb2c3983c79b6b550/Publishing002.png) +2. Under the `Content` section click `Choose File`. +![Select the Content to Upload](https://spee.ch/5/choose-file-and-others.jpeg) -2. On your local machine, select the content you would like to upload to LBRY. LBRY accepts any HTML5 format for streaming video; the full list can be found [here](https://developer.mozilla.org/en-US/docs/Web/HTML/Supported_media_formats). This means a web-optimized MP4 is the best format. Other file types can also be uploaded, but won't be streamable via the LBRY app. +3. On your local machine, select the content you wish to upload to LBRY. LBRY accepts any HTML5 format for streaming video; the full list can be found [here](https://developer.mozilla.org/en-US/docs/Web/HTML/Supported_media_formats). This means a web-optimized MP4 is the best format. Other file types can also be uploaded, but won't be streamable via the LBRY app. + +4. Enter a `Title` and `Description` for your content. +![choose a file](https://spee.ch/2/311-choose-file-and-others.jpeg) + +5. Choose a `Thumbnail`or `Thumbnail URL` for your content. The `Thumbnail URL` is a hyperlink to an image file which will serve as a preview for your content. It can be any image/GIF URL, or you can use [spee.ch](https://www.spee.ch) to host it. The default size is 800x450, but the app will scale up/down. Images uploaded directly from your local machine as `Thumbnail` will be uploaded to [spee.ch](https://www.spee.ch). +![Choose the Thumbnail Image](https://spee.ch/6/5thumbnail.jpeg) + +6. Please make sure to check the option for mature audiences if your `Thumbnail`is NSFW. Otherwise just click on Upload. +![Select the Content to Upload](https://spee.ch/6/4-thumbnail44.jpeg) + +7. Under the `Price`, first, determine if you want to make your content free or set a price (in USD or LBC) per view. +![Set Price](https://spee.ch/4/5-choose-a-price2.png) + +8. You have an option to select/create the channel you would like to publish the content under. If one isn't selected, it will be posted anonymously. +![Select Channel or Anonymous](https://spee.ch/4/channel22.png) + +9. Type in the URL you want the content to be found under along with a minimum of 0.0001 LBC deposit for the upload (current limit, may change in the future). If you are trying to outbid a user-friendly/common URL, the system will suggest a minimum bid to take over the content at that vanity URL. There may be a delay for this takeover. Check out the `#content` channel on our [Discord chat](https://chat.lbry.io) to see this information (search for your URL). For more details regarding the URL or bid, check out our [naming document](https://lbry.io/faq/naming). +![Video URL and Deposit](https://staging.spee.ch/6/8content-urlf.jpeg) + +10. Next, there are selections for `Maturity`, `Language`, and `License.` The default values are set as follows: Maturity being unchecked, which means the content is safe for all audiences, Language is set to `English`, and the License is set to `None`. If a change is needed, click the dropdowns and select the appropriate choice. Please check the `Mature audience only` option if content is NSFW. +![publish](https://spee.ch/c/7-license-2-and-publish.jpeg) + +11. Read and agree to the terms of service. + +12. Click `Publish`. +![Click Publish](https://spee.ch/2/publish.jpeg) + +13. The file will process in the background and will be added to the LBRY Blockchain. Larger files will take longer to upload. Please leave LBRY running while your content is in the "pending confirmation" mode. Currently, this page will not automatically refresh. You can continue to use LBRY while the upload completes. -3. Enter a `Title`, `Thumbnail URL`, and `Description` for your content. The `Thumbnail URL` is a hyperlink to an image file which will serve as a preview for your content. It can be any image/GIF URL, or you can even use [spee.ch](https://www.spee.ch) to host it. The default size is 800x450, but the app will scale up/down. -![Enter File Information](https://spee.ch/d857e3040629145e0f5d70693c02b8016a9d45e6/Publishing003.png) -4. Next, there is a `Language` and `Maturity` which will default to `English` and `All Ages`. If a change is needed, click the dropdowns and select the appropriate choice. -![Enter Additional Metadata](https://spee.ch/a42fb51e56ab4809002982cea66b7fc44b938776/Publishing004.png) +### How do I create a Channel? -5. Under the `Access`, first, determine if you want to make your video Free or set a price (in USD or LBC) per view. Next, select the appropriate type of license for the content you are publishing. -![Set Access and License](https://spee.ch/35adbf43f6a8a6cd43fc67d18516ede2f74de86b/Publishing005.png) +1. Open the LBRY app. -6. You have an option to select/create the channel you would like to publish the channel under. If one isn't selected, it will be posted anonymously. -![Select Channel](https://spee.ch/d0c7fe044b0237017f0f5af00f79e3880aae201d/Publishing006.png) +2. Once the application loads, click the `Publish` button in the top right of the screen. +![Click the Publish Button](https://spee.ch/0/1-click-publish.jpeg) -7. Type in the URL you want the content to be found under along with a minimum of 0.0001 LBC deposit for the upload (current limit, may change in future). If you are trying to outbid a user-friendly/common URL, the system will suggest a minimum bid to take over the content at that vanity URL. There may be a delay for this takeover, check out the `#content` channel on our [Discord chat](https://chat.lbry.io) to see this information (search for your URL). For more details regarding the URL or bid, check out our [naming document](https://lbry.io/faq/naming). +3. Select a source file and then in the `Channel Name` section click the dropdown and select `New Channel` and declare the name you would like for your channel. For more details on different channel types, see our write up on [naming](https://lbry.io/faq/naming). +![Click the New Channel Dropdown](https://spee.ch/a/create-channel.jpeg) -8. Read and agree to the terms of service. -9. Click `Publish`. -10. The file will process in the background and will be added to the LBRY Blockchain. Larger files will take longer to upload, please leave LBRY running while your content is in the "pending confirmation" mode(currently, this page will not automatically refresh). You can continue using LBRY while the upload completes. +4. Once your name is selected, there is a `Deposit` section that is below numbered `3`. It requires a minimum bid of 0.0001 LBC (see more on deposits [here](https://lbry.io/faq/naming)). Please ensure that you have enough LBRY credits in your wallet to cover the bid amount. There is also a small network fee associated with the creation of a channel. +![Set the Deposit](https://spee.ch/a/create-channel.jpeg) + +5. Click `Create Channel` `numbered 4` once you have entered your bid amount. You now own `lbry://@channelnameyoubidon#Claim_ID` and `lbry://@channelnameyoubidon` (the vanity name without a claim id) if you are the highest bidder. +![Create the Channel](https://spee.ch/a/create-channel.jpeg) ### How do I delete my content and reclaim my deposit? -1. Click on the folder icon on the top right of the LBRY app. -![folder icon](https://spee.ch/5/lbryapp-folder-icon.jpeg) -2. Click on the `Published` tab. +1. Click My LBRY on the left side of the LBRY app. +![My LBRY](https://spee.ch/7/Mylbry.jpeg) + +2. Click on the `Publishes` tab. + 3. Select the content you want to remove from LBRY +![Content](https://spee.ch/c/contents.jpeg) + 4. Click `Remove`. If you don't see the remove button, try downloading the content locally again. -![remove](https://spee.ch/7/removebox.png) +![remove](https://spee.ch/4/delete.jpeg) + 5. There will be two options. `Abandon the claim for this URI` and `Delete this file from my computer`. Select the option that applies. Abandoning your claim will release the LBC back into your wallet (99% of the time you want to select this). -![abandon-delete box](https://spee.ch/7/removeabandonbox.png) +![abandon-delete box](https://spee.ch/1/abandon1.jpeg) **Warning: Deleting content is permanent. Please make sure this is what you want to do before confirming the deletion.** -6. Click `Remove`. If you abandoned your claim, you should see the deposit back in your balance shortly. +Click `Remove` numbered as `2`. If you abandoned your claim, you should see the deposit back in your balance shortly. ### How do I edit my existing Published content? -1. Click on the folder icon on the top right of the LBRY app. -2. Click on the `Published` tab. +1. Click on My LBRY the left side of the LBRY app. +![My LBRY](https://spee.ch/7/Mylbry.jpeg) + +2. Click on the `Publishes` tab. + 3. Select the content you want to update. +![Content](https://spee.ch/c/contents.jpeg) + 4. Click `Edit`. +![Edit](https://spee.ch/c/edit.jpeg) + 5. You can now edit your claim information. No need to re-select the file if it's the same one. -6. When you are done, re-confirm that you agree to the terms of service and click `Publish`. -or - -1. Go to the `Publish` page. -2. In the `Content URL` section, type in your content URL for the claim you would like to edit. -3. Below the URL you will see text saying "You already have a claim with this name." and a link that says `Use data from my existing claim`. Click this link in order to capture your previously published data. -4. You can now edit your claim information. No need to re-select the file if it's the same one. -5. When you are done, re-confirm that you agree to the terms of service and click `Publish`. +6. When you are done, re-confirm that you agree to the terms of service and click `Edit`. +![Agree Edit](https://spee.ch/b/agree.jpeg) ### Can someone tip me for my content? - -Yes, check out LBRY how tipping in LBRY works by going [here](https://lbry.io/faq/tipping). +Yes, check out LBRY how tipping in LBRY works by going [here](https://lbry.io/faq/tipping). ### Can I increase my bid amount? - -Yes, this is possible by sending [tips](https://lbry.io/faq/tipping) as support (additional bids) for your own claim. Since the claim is yours, you can withdraw the tips at your convenience. To increase your bid, go to the desired claim and click the `Support` option, enter an amount of LBC to add to the claim, and click `Send`. +Yes, the claim can be edited to increase the bid amount. Go into your published claim and click Edit. Then on the bid screen, enter your desired bid. Confirm everything else is correct and click Edit. An update will be created with your new LBC bid for this claim. ### How can I tell if someone is downloading my content? @@ -103,7 +119,7 @@ The in-app video player's streaming capabilities are limited to MP4 files which ### I shared my URL, but others can't download it. What's up? -Since LBRY uses a Peer to Peer network, it may require that your PC is accessible through the internet. LBRY also runs servers to assist in content hosting, but this process may fail if your PC cannot send it to us. By default, the sharing port is set to 3333. If your network is properly configured and LBRY is running, a port status check on 3333 should pass on this [port checking tool](https://www.canyouseeme.org). If it fails, you can check if UPnP is enabled on your router or forward port 3333 manually. If you need assistance, check out the [help page](https://lbry.io/faq/how-to-report-bugs) on how to reach us. +Since LBRY uses a Peer to Peer network, it may require that your PC is accessible through the internet. LBRY also runs servers to assist in content hosting, but this process may fail if your PC cannot send it to us. By default, the sharing port is set to 3333. If your network is properly configured and LBRY is running, a port status check on 3333 should pass on this [port checking tool](http://www.canyouseeme.org). If it fails, you can check if UPnP is enabled on your router or forward port 3333 manually. If you need assistance, check out the [help page](https://lbry.io/faq/how-to-report-bugs) on how to reach us. ### Where is my Channel and content saved locally? diff --git a/content/faq/how-to-run-lbry.md b/content/faq/how-to-run-lbry.md index ca61e1aa..901b1949 100644 --- a/content/faq/how-to-run-lbry.md +++ b/content/faq/how-to-run-lbry.md @@ -6,15 +6,15 @@ This guide will allow you to run the LBRY daemon which connects to the LBRY netw ## Windows 1. Open a **Command Prompt** application window -1. Type `cd "c:\Program Files (x86)\LBRY\resources\app\dist"` and click Enter +1. Type `cd "C:\Program Files\LBRY\resources\static\daemon"` ([32-bit located in Program Files(x86)]) and click Enter 1. Type `lbrynet-daemon` and click Enter. ## MacOS 1. Open a **Terminal** window -1. Type `cd /Applications/LBRY.app/Contents/Resources/app/dist` +1. Type `cd /Applications/LBRY.app/Contents/Resources/static/daemon` 1. Type `./lbrynet-daemon` and click Enter. ## Ubuntu / Linux 1. Open a **Terminal** window -1. Type `cd /opt/LBRY/resources/app/dist` or build location if running from source +1. Type `cd /opt/LBRY/resources/static/daemon` or build location if running from source 1. Type `./lbrynet-daemon` and click Enter. diff --git a/content/faq/incompatible-protocol-version.md b/content/faq/incompatible-protocol-version.md index d3a2f330..6fb4d75f 100644 --- a/content/faq/incompatible-protocol-version.md +++ b/content/faq/incompatible-protocol-version.md @@ -6,11 +6,13 @@ category: troubleshooting This warning happens when your LBRY browser did not install with the proper version of the software used to communicate with the LBRY network. The most likely cause of this error is an old version was running during the install process. This can usually be fixed by re-running the LBRY setup files after ensuring that no LBRY processes are running. +![Incompatible-daemon](https://spee.ch/b/incompatible-protocol.png) ### How to Fix +1. Click `Quit Daemon` in the LBRY app to kill the LBRY network process. 1. Restart your PC or ensure that any processes with "lbry" in the name are not running. -1. [Download](https://github.com/lbryio/lbry-app/releases) and re-install the latest version of LBRY. +1. [Download](https://github.com/lbryio/lbry-desktop/releases) and re-install the latest version of LBRY. 1. Start LBRY If you still receive this warning after completing the above steps, please [reach out to us](https://lbry.io/faq/how-to-report-bugs) for additional support. diff --git a/content/faq/lbry-basics.md b/content/faq/lbry-basics.md index 0975dafe..db83d5e7 100644 --- a/content/faq/lbry-basics.md +++ b/content/faq/lbry-basics.md @@ -9,13 +9,13 @@ The LBRY App allows you to view free and paid content, upload your digital media The purpose of this FAQ is to answer questions about some of the basic functionality available in the LBRY App. Please see our [other FAQ entries](https://lbry.io/faq) for additional information. ### What is the purpose of having my email connected to LBRY? -Emails are collected to authenticate and [uniquely identify](https://lbry.io/faq/identity-requirements) users so that they can be eligible for [LBRY Rewards](#rewards) and to stay up to date on the latest LBRY happenings. No other data is stored with your email login. All other data, including your [wallet](#wallet), [downloads](#data) and published content are stored locally on your computer. You can find your connected email by going to Settings (gear icon in the top right) > Help > Connected email. +Emails are collected to authenticate and [uniquely identify](https://lbry.io/faq/identity-requirements) users so that they can be eligible for [LBRY Rewards](#rewards), sync subscription data and to stay up to date on the latest LBRY happenings. No other data is stored with your email login. All other data, including your [wallet](#wallet), [downloads](#data) and published content are stored locally on your computer. You can find your connected email by going to Settings (gear icon in the top right) > Help > Connected email. ### How do I change my LBRY connected email? If you ever need to change your LBRY email address or sign out, please see [this guide](https://lbry.io/faq/how-to-change-email). If you sign into a new email and need to transfer your verification status, you'll need to [reach out to us](mailto:help@lbryio) in order to link your accounts. Please do not verify again to obtain rewards on a 2nd account; your Rewards account may be disabled for abuse. ### What if I want to run LBRY on multiple computers or different Windows accounts? -If you want to run the LBRY app on multiple PCs or Windows users, you can either choose not to sign in on the other computers/accounts or use a different email address for each. These additional accounts will not be eligible for LBRY Rewards as they are only allowed on a one account per household basis. If you log into a 2nd PC with the same email, the original PC will be signed out. Changing accounts back and forth on the same PC/user account will cause them to be merged. +If you want to run the LBRY app on multiple PCs or on other platforms like Android, you can sign in with the same email on all devices. Each installation will still have its separate wallet and download data (as mentioned above). Any rewards earned will be sent locally to the wallet where they are claimed. In the future, our goal to enable an opt-in wallet syncing service across devices. ### What are LBRY Rewards? {#rewards} [LBRY Rewards](https://lbry.io/faq/rewards) are used to distribute LBRY Credits(LBC) to new and existing users by allowing them to explore app functions and complete tasks which generate LBC as an award. In order to be eligible for Rewards, you need to [verify your identity](https://lbry.io/faq/identity-requirements) which uniquely identifies you as an LBRY user. @@ -23,14 +23,18 @@ If you want to run the LBRY app on multiple PCs or Windows users, you can either ### What is a wallet and how do I find it? {#wallet} A wallet is a secure, digital wallet used to store, send and receive cryptocurrencies like LBRY Credits(LBC). The LBRY App comes with its own wallet and is stored locally on your computer and nowhere else! **It is critical that you [backup your wallet data](https://lbry.io/faq/how-to-backup-wallet) in case you lose access to your PC or need to [migrate](https://lbry.io/faq/backup-data) to a new one.** -In the app, you can find your wallet in the top-right hand corner, next to the bank icon. Clicking it will bring you to the wallet overview page which shows your balance, available Rewards and recent transactions. -![Find wallet](https://spee.ch/6f82ff233910eebeb0f32f69710bd98c6a6bcb2a/walletaccess.png) +To find your wallet in the LBRY App, click on wallet on the left side of the App. Clicking it will bring you to the wallet overview page which shows your balance, available Rewards and recent transactions. +![Find wallet](https://spee.ch/4/wallet2.jpeg) The LBRY wallet is different from other cryptocurrencies because it also stores your shared content's metadata in the form of [claims](https://lbry.io/faq/naming) when using the [publishing features]((https://lbry.io/faq/how-to-publish). Claim related [wallet transactions](https://lbry.io/faq/transaction-types) ensure that the blockchain uniquely identifies your content and the payment/tips can be routed appropriately. ### Where do I find my LBC wallet address? -You can find your address by first clicking on the bank icon in the top right, then navigating to the Send/Receive tab. Your wallet holds multiple receiving addresses, and new ones can be generated by clicking "Get New Address". Your wallet balance is the sum total of all the LBC available in each of your addresses. -![Find address](https://spee.ch/6fff389043fadcf16ade8b0b8f6125834652e1c2/walletaddress.png) +You can find your address by first clicking on Wallet on the leftside of the LBRY APP. +![wallet](https://spee.ch/4/wallet21.jpeg) + +then navigating to the Send/Receive tab. +![wallet](https://spee.ch/0/snr.jpeg) +Your wallet holds multiple receiving addresses, and new ones can be generated by clicking "Get New Address". Your wallet balance is the sum total of all the LBC available in each of your addresses. ### Where can I get more LBRY Credits? The LBRY App is also integrated with [ShapeShift](https://lbry.io/faq/shapeshift) on the Get Credits tab of the wallet which allows you to convert cryptocurrencies into LBC or you can also [trade for LBC on exchanges](https://lbry.io/faq/exchanges). @@ -45,21 +49,30 @@ LBRY is a decentralized peer to peer protocol, meaning there are no big servers Please refer to our [publishing guide](https://lbry.io/faq/how-to-publish) as a reference to assist you through the publishing process. ### Where can I see my Downloaded and Published items? -Click the folder icon next to the Publish button to view downloaded files. Click the Published tab to view your published content. +Click My LBRY on the left side of the LBRY App, then click downloads to view downloaded files. +![download](https://spee.ch/8/downloads.jpeg) + +Click the Published tab to view your published content. +![published](https://spee.ch/9/pub.jpeg) ### How do I know if I'm sharing content and helping the LBRY network properly? The easiest way to confirm that you are sharing correctly is to determine if the port used for seeding, 3333, is open to the rest of the LBRY network. To do so, type 3333 into [this port checking tool](http://www.canyouseeme.org) and check the result. It if shows Open, you are all set. If it shows closed, you may need to check your router settings for UPnP options (set to enable) or forward ports 3333 TCP and 4444 UDP to your local computer running LBRY. Firewall and NAT settings may also affect the availability of this port. ### How can I search for content on LBRY? -Searching in LBRY is as easy as typing your search term(s) into the address bar at the top and waiting for the results to return (**don't click Enter!**). Clicking the Enter key will skip the search function and go directly to the URL typed - this is only helpful if you know the exact URL you are trying to view. We are still in the process of optimizing the search results; please bear with us if you are having trouble finding something! -![Search](https://spee.ch/f/search-faq.png) +Searching in LBRY is as easy as typing your search term(s) into the address bar at the top and waiting for the results to return (**don't click Enter!**). +![Search](https://spee.ch/2/search.jpeg) + +Clicking the Enter key will skip the search function and go directly to the URL typed (if it's valid) - this is only helpful if you know the exact URL you are trying to view. We are still in the process of optimizing the search results; please bear with us if you are having trouble finding something! +![Search](https://spee.ch/b/searh-2a.jpeg) ### How can I subscribe and view my favorite channels? -If you navigate to a channel page (LBRY URLs with an @ symbol in the front), you will see a **Subscribe @** button that manages your subscription. To view all your subscribed channels on one page, click the Subscriptions tab from the home page or the **@** symbol next to the LBRY address bar. -![Subscriptions](https://spee.ch/5/subs-faq.png) +If you navigate to a channel, or click on your favorite video, (LBRY URLs with an @ symbol in the front) click on subscribe right below the video and channel name. +![subscribe](https://spee.ch/1/sub.jpeg) +you will see a **@ Subscription** tap right under explore that manages your subscriptions. To view all your subscribed channels on one page, click the Subscriptions tab from the home page. +![Subscriptions](https://spee.ch/6/subs.jpeg) ### Content consistently fails to stream or download, what can I do? -Please see our [streaming guide](https://lbry.io/faq/unable-to-stream) if you consistently cannot download or stream content on LBRY. +Please see our [streaming guide](https://lbry.io/faq/unable-to-stream) if you consistently cannot download or stream content on LBRY. If you are having intermittent issues with download failures, try closing LBRY completely and downloading again. Some files on the network may just not be available for various reasons - we'll work on filtering these out in the future. ### Some files don't open in the LBRY app, what's going on? Currently, the LBRY in-app player supports MP4 videos, mp3s, images, GIFs, HTML and text files. Even though it doesn't support other formats within the app, the files can be externally opened by clicking the **Open** button or navigating to the file by clicking the **Downloaded to** file path on the content page. diff --git a/content/faq/referrals.md b/content/faq/referrals.md index 912ccc97..b44d4fa6 100644 --- a/content/faq/referrals.md +++ b/content/faq/referrals.md @@ -5,6 +5,8 @@ category: getstarted You can earn credits for referring others to use LBRY. In the latest version of the LBRY app, you can now view the status of your referrals and invite new users to LBRY. The Invites area can be found by going to your wallet (bank icon in the top right of the LBRY app) and then clicking Invites from the top menu. In the Invite History section, you will see all your referrals along with their status (whether it is claimable or not). You also have the ability to invite new users to LBRY via the "Invite a Friend" section. +*Note: Referral redemptions are currently limited to 1* + ### How many credits do I get and how do I see how many credits I've earned? New invites sent via the LBRY app are eligible for a 3 LBC reward amount. @@ -35,4 +37,4 @@ Currently, during the referral reward testing phase, there is a limit of 1 redem If you want to waste your time to receive no reward, be our guest. We will be monitoring the system closely and going to significant lengths to only let legitimate users in. -Rather than spending the time attempting to cheat and failing, we suggest you do productive work to earn LBRY credits. Join our [Discord chat](http://chat.lbry.io) and message Josh (@finer9) or Jeremy (@kauffj) for opportunities. +Rather than spending the time attempting to cheat and failing, we suggest you do productive work to earn LBRY credits. Join our [Discord chat](http://chat.lbry.io) and message or email [Tom](mailto:tom@lbry.io) (@jiggytom) or [Julie](mailto:julie@lbry.io) (@jsigwart) for opportunities. diff --git a/content/faq/rewards.md b/content/faq/rewards.md index c39067d2..f4a3a209 100644 --- a/content/faq/rewards.md +++ b/content/faq/rewards.md @@ -3,21 +3,24 @@ title: What are LBRY Rewards? category: getstarted --- -To provide a rich user experience and to [distribute](https://lbry.io/faq/credit-policy) LBRY Credits (LBC) into the hands of new and returning users, LBRY created a Rewards system where credits are earned by completing tasks throughout the application. Rewards are given to promote application testing, learning certain in-app skills, building the LBRY economy through purchasing and publishing content, and as a small "thank you" gift for being part of a revolutionary digital media platform. For qualified users, Rewards are issued automatically by the LBRY app into each users' [LBRY wallet](https://lbry.io/faq/how-to-backup-wallet). +To provide a rich user experience and to [distribute](https://lbry.io/faq/credit-policy) LBRY Credits (LBC) into the hands of new and returning users, LBRY created a Rewards system where credits are earned by completing tasks throughout the application. Rewards are given to promote application testing, learning certain in-app skills, building the LBRY economy through purchasing and publishing content, and as a small "thank you" gift for being part of a revolutionary digital media platform. For qualified users, Rewards are issued automatically by the LBRY app into each users' [LBRY wallet](https://lbry.io/faq/how-to-backup-wallet). **Note: There is a limit of 3 Reward redemptions per day**. +![rewards2](https://spee.ch/ca693e3a6ba9e5ff78274ddace3b1ae6886b6505/rewards-8-2-2018.jpeg) ### Verification requirements -In order to be eligible for LBRY Rewards, users must have a [verified account](https://lbry.io/faq/identity-requirements). If users choose not to verify themselves, LBRY works with full functionality, but they will not be able to earn any free credits from LBRY. **Rewards will only be granted on a 1 account per household basis and LBRY reserves the right to revoke Rewards privileges on any account if abuse is suspected or if VPN/shared connections are used.** +In order to be eligible for LBRY Rewards, users must have a verified account via [phone number](https://lbry.io/faq/phone) or [credit card](https://lbry.io/faq/identity-requirements) (there's also a manual verification method on [Discord](https://chat.lbry.io)). If users choose not to verify themselves, LBRY works with full functionality, but they will not be able to earn any free credits from LBRY. **Rewards will only be granted on a 1 account per household basis and LBRY reserves the right to revoke Rewards privileges on any account if abuse is suspected or if VPN/shared connections are used.** ### List of the current LBRY Rewards | Reward | Amount | Description | --- | --- | --- -| **Your First Nickel** | 3 LBC | A one-time welcome gift to learn basics of the application, wallet and if you want to buy some paid content -| **Go for a Stream** | 2 LBC | Awarded for streaming your very first video on LBRY -| **Channel Surfing** | 3 LBC | A one-time award for creating a Channel on LBRY via the Publish screen -| **Many Views** | 5 LBC | A one-time award for watching several videos on LBRY -| **First Publish** | 5 LBC | A one-time award for publishing your first piece of content to LBRY -| **Weekly LBRYCast** | 2 LBC | A weekly award for checking out featured content on LBRY. This content is marked with the red rocket logo and announced via email +| **Your First Nickel** | 6 LBC | A one-time welcome gift to learn basics of the application, wallet and if you want to buy some paid content +| **Go for a Stream** | 4 LBC | Awarded for streaming your very first video on LBRY +| **Channel Surfing** | 6 LBC | A one-time award for creating a Channel on LBRY via the Publish screen +| **First Publish** | 10 LBC | A one-time award for publishing your first piece of content to LBRY +| **Many Views** | 2- ???? LBC | A multi-level award for watching videos on LBRY. See descriptions in-app for levels/details! +| **Sub Sandwich** | 1 -2 LBC | A multi-level reward for subscribing to channels. +| **Weekly LBRYCast** | 4 LBC | A weekly award for checking out featured content on LBRY. This content is marked with the red rocket logo and announced via email +| **Welcome Back** | 6 LBC | Return to the LBRY app 24-48 hours following your first use of the app. This reward will self destruct after that time span. | **Referral** | 3 LBC | LBRY users can refer their friends via an email invitation and get rewarded when those users are verified. This reward is limited to one redemption at this time. For more information on referrals, click [here](https://lbry.io/faq/referrals) Rewards are added to the LBC wallet balance as they are completed. All the rewards can be listed by clicking on the tab marked "REWARDS" inside the LBC wallet, and they are also marked in the [transaction history](https://lbry.io/faq/transaction-types). Rewards redemption is tied to your account, but the credits themselves are stored in your wallet which is required to be [backed up](https://lbry.io/faq/how-to-backup-wallet) periodically. diff --git a/content/faq/shapeshift.md b/content/faq/shapeshift.md index 328750a7..105ce6d0 100644 --- a/content/faq/shapeshift.md +++ b/content/faq/shapeshift.md @@ -8,28 +8,32 @@ The ability to convert your cryptoassets into LBRY Credits (LBC) is available di *Note: ShapeShift is unavailable in New York and Washington (and possibly surrounding areas based on IP geolocation). You will see an `HTTP status code: 403` error if this happens. Please see [ShapeShift](https://shapeshift.io) for more information.* ## Convert Crypto to LBC -1. Open the LBRY app, access the Wallet (bank icon next to the Publish button) and click on **Get Credits** +1. Open the LBRY app, Click on the Wallet tab to expand. Click on **Get Credits** +![credit](https://spee.ch/f/credit.jpeg) -2. In the **Convert Crypto to LBC** section, choose from BTC, BCH, DASH, ETH, LTC or XMR to convert into LBC - +2. In the **Convert Crypto to LBC** section, choose from BCH, BTC, DASH, ETH, LTC or XMR to convert into LBC +![credits](https://spee.ch/3/rew.png) 3. Review the given rate of exchange and min/max amount. ShapeShift charges a small [fee](https://info.shapeshift.io/about) for the transaction 4. Enter the return address for the cryptoasset in case something were to go wrong with the process (if the address is not provided, you'll need to contact ShapeShift about your refund). - +![credits](https://spee.ch/d/creditk.jpeg) -5. Click **Begin Conversion** to start your request. You will now be presented with the deposit address for your conversion. - +5. Click **Begin Conversion** to start your request. +![beginconversion](https://spee.ch/d/reww.png) -6. Using your crypto wallet, send any amount between the min and max to the deposit address specified. Click **VIEW THE STATUS ON SHAPESHIFT.IO** to track in real-time. You can also bookmark this transaction for your records. We are not currently storing any information about the transaction after it confirms and you receive the LBC in your wallet. +6.You will now be presented with the deposit address for your conversion. +![depositaddress](https://spee.ch/7/depo.jpeg) + +7. Using your crypto wallet, send any amount between the min and max to the deposit address specified. Click **VIEW THE STATUS ON SHAPESHIFT.IO** to track in real-time. You can also bookmark this transaction for your records. We are not currently storing any information about the transaction after it confirms and you receive the LBC in your wallet. -7. Once your transaction is confirmed, you will be presented with the completion screen. Click **Done** to start a new conversion. +8. Once your transaction is confirmed, you will be presented with the completion screen. Click **Done** to start a new conversion. -8. Verify that LBC has been received on the **History** tab. - +9. Verify that LBC has been received on the **Transaction** tab under wallet. It should be updated in the transaction history. + -9. Thanks for acquiring some LBC! +10. Thanks for acquiring some LBC! ### I need help with my conversion, who can I reach out to? diff --git a/content/faq/startup-troubleshooting.md b/content/faq/startup-troubleshooting.md index a82a689f..cc483e11 100644 --- a/content/faq/startup-troubleshooting.md +++ b/content/faq/startup-troubleshooting.md @@ -17,23 +17,24 @@ LBRY operates on a couple of different ports, and if there are conflicts/firewal - Port 50001 - LBRY wallet connections happen over port 50001. LBRY may fail to start if this port is blocked by a firewall or network rules. ### This is my first time running LBRY, and it won't start -- Port 3333 already in use. This issue would reveal itself in the log file. You can see how to change this port [here](https://lbry.io/faq/how-to-change-port). If the port is properly forwarding correctly, you are able to successfully see port 3333 Open on this [port checker tool](https://www.canyouseeme.org). +- Port 3333 already in use. This issue would reveal itself in the log file. You can see how to change this port [here](https://lbry.io/faq/how-to-change-port). If the port is properly forwarding correctly, you are able to successfully see port 3333 Open on this [port checker tool](http://www.canyouseeme.org). - Port 50001 wallet connection fails. This issue would reveal itself in the log file. Typical things to check would be firewall/security settings that may block this connection. -- On Linux, LBRY may fail to start because of missing authentication capability. Please see [GitHub issue](https://github.com/lbryio/lbry-app/issues/386) or possible workaround below. +- On Linux, LBRY may fail to start(home page won't load, missing authentication token in Help) because of missing authentication capability. Please see [GitHub issue](https://github.com/lbryio/lbry-desktop/issues/386) or possible workaround below. - On Windows, LBRY may fail to start because of non-ASCII characters in your Windows username. Check your c:\users\ path to see if there are any such characters. Please see [GitHub issue](https://github.com/lbryio/lbry/issues/794) or workaround below. ### LBRY used to work previously, but now it won't start First and foremost, please ensure you are on the [latest version](https://lbry.io/get) of LBRY. Reinstalling the latest version may alleviate some start-up issues. Before installing, either make sure no LBRY/lbrynet processes or simply reboot your computer. - On Windows, if you get stuck on the "Starting daemon" green screen, the lbrynet-daemon file may be missing. The workaround is to rerun the [latest](https://lbry.io/get) LBRY installation file and try again. -- On older MAC installations, you may run into an issue with the daemon shutting down immediately. Please see [this GitHub issue](https://github.com/lbryio/lbry-app/issues/291) for troubleshooting. +- On older MAC installations, you may run into an issue with the daemon shutting down immediately. Please see [this GitHub issue](https://github.com/lbryio/lbry-desktop/issues/291) for troubleshooting. +- On older Linux/Mac installs you may see `Cannot read property 'match' of undefined`, install the [latest version](https://lbry.io/get) to fix this. - Other typical startup troubleshooting would be to ensure that LBRY or lbrynet-daemon is not already running in the background. If the processes cannot be killed, a restart of your computer may be required. ### Known startup issues and workarounds #### Stuck at blockchain sync {#sync} If you are stuck on the blockchain sync step or it shows a block count that doesn't decrease, you may need to clear your blockchain cache. To do so, Shut LBRY down completely by closing it from the system tray(check for running LBRY/lbrynet-daemon processes), delete the `blockchain_headers` file in the [lbryum folder](https://lbry.io/faq/lbry-directories) and then start LBRY again. -#### Linux auth_token requirements -Currently, LBRY requires an authorization token to be generated using the [keytar](https://github.com/atom/node-keytar) libraries. Please ensure libsecret and keytar are installed. On some distributions, LBRY won't run unless gnome-keyring is also installed/operational. See [GitHub issue](https://github.com/lbryio/lbry-app/issues/386) for more information. If you get a GLIBCXX_3.4.2 error, please see [this issue](https://github.com/lbryio/lbry-app/issues/423#issuecomment-327519486). +#### Linux auth_token requirements {#auth} +Currently, LBRY requires an authorization token to be generated using the [keytar](https://github.com/atom/node-keytar) libraries. Please ensure libsecret and keytar are installed. On some distributions, LBRY won't run unless gnome-keyring is also installed/operational. See [GitHub issue](https://github.com/lbryio/lbry-desktop/issues/386) for more information. If you get a GLIBCXX_3.4.2 error, please see [this issue](https://github.com/lbryio/lbry-desktop/issues/423#issuecomment-327519486). #### Windows user path has non-ASCII characters Currently, the LBRY app may fail to start because it does not support non-ASCII / non-English letters in the c:\Users\ directory where it tries to create your LBRY wallet, downloads and application data. As a workaround, you can manually set these directories in the `daemon_settings.yml` file within the [lbrynet folder](https://lbry.io/faq/lbry-directories). If this file does not exist in your [lbrynet folder](https://lbry.io/faq/lbry-directories), you can create one, or you can use [this sample](https://goo.gl/opybNE). @@ -43,4 +44,10 @@ This will configure your directories to the folders below, or you can create/edi lbryum_wallet_dir: 'c:\lbry\lbryum', download_directory: 'c:\lbry\Downloads'} ``` -After you are done inserting/editing the `daemon_settings.yml` configuration file, try rerunning LBRY. The settings file has to stay in the original location, and LBRY will create the new folders/data in the specified directories. `lbrynet`/`lbryum` folders should be copied there if you are migrating from a previous install. If you still receive this warning after completing the above steps, please [reach out to us](https://lbry.io/faq/how-to-report-bugs) for additional support. +Some Operating Systems may have this format: +``` +data_dir: 'c:\lbry\lbrynet' +lbryum_wallet_dir: 'c:\lbry\lbryum' +download_directory: 'c:\lbry\Downloads' +``` +After you are done inserting/editing the `daemon_settings.yml` configuration file, try re-running LBRY. The settings file has to stay in the original location, and LBRY will create the new folders/data in the specified directories. `lbrynet`/`lbryum` folders should be copied there if you are migrating from a previous install. If you still receive this warning after completing the above steps, please [reach out to us](https://lbry.io/faq/how-to-report-bugs) for additional support. diff --git a/content/faq/support.md b/content/faq/support.md index 31924a25..84d5a9bb 100644 --- a/content/faq/support.md +++ b/content/faq/support.md @@ -9,7 +9,7 @@ For live help, you can join [our chat](https://chat.lbry.io) and post in the #he ## Help via Email -You can also [email LBRY](mailto:help@lbry.io) with questions or issues. LBRY log files will help us better understand the issue you are experiencing, you can learn how to [find them here](https://lbry.io/faq/how-to-find-lbry-log-file) and attach with your email. +You can also [email LBRY](mailto:help@lbry.io) with questions or issues. LBRY log files will help us better understand the issue you are experiencing, you can learn how to [find them here](https://lbry.io/faq/how-to-find-lbry-log-file) and attach with your email. ### Reporting Issues @@ -17,8 +17,7 @@ To report an issue, you can do one of the following: 1. Send us an email to [help@lbry.io](mailto:help@lbry.io) with the details of your issue or bug report. If it's troubleshooting related, please attach [your LBRY log file](https://lbry.io/faq/how-to-find-lbry-log-file). -1. Go to the "Help" page of the app and then click the "Submit a Bug Report" button. You can access the help page from inside of "Settings". - -1. If you're a developer or otherwise technical and want to interact with LBRY developers directly, you're welcome to open an issue directly on GitHub. Please try to open network or protocol related issues [here](https://github.com/lbryio/lbry/issues) and interface, usability, and other application related issues [here](https://github.com/lbryio/lbry-app/issues). The penalty for getting this wrong is a mild shaming. We would appreciate a quick search to see if similar issues already exist, as well. - +1. Go to the "Help" page of the app and then click the "Submit a Bug Report" button. +![help](https://spee.ch/8/Fix-broken.png) +1. If you're a developer or otherwise technical and want to interact with LBRY developers directly, you're welcome to open an issue directly on GitHub. Please try to open network or protocol related issues [here](https://github.com/lbryio/lbry/issues) and interface, usability, and other application related issues [here](https://github.com/lbryio/lbry-desktop/issues). The penalty for getting this wrong is a mild shaming. We would appreciate a quick search to see if similar issues already exist, as well. diff --git a/content/faq/tipbot-discord.md b/content/faq/tipbot-discord.md new file mode 100644 index 00000000..26b17336 --- /dev/null +++ b/content/faq/tipbot-discord.md @@ -0,0 +1,72 @@ +--- +title: How do I use the Discord tipbot? +category: tipbots +order: 1 +--- + +## LBRY Discord Tipbot Information + +Tips, in LBRY Credits (LBC), are an integral part of our community because they allow us to reward members for their contributions - whether that's for sharing something insightful, providing feedback, completing bounties, testing our various apps or helping promote LBRY's vision and technology. You can earn them, share, or transfer them via simple commands on the Discord server. + +It is important to note that the LBC stored as a result of a tip is tied to your Discord account username and are stored on LBRY's wallet server. It is your responsibility to withdraw the tips to your LBRY App or other wallet like Coinomi. If you plan on storing LBC on the Discord server, it is a good idea to enable Two Factor Authentication (2FA) on your account. LBRY takes no responsibility for lost funds due to negligence + +Use the following commands to make amazing things happen. We recommend running them in the `#bot-sandbox` channel, unless you are tipping someone! + +### Help +This displays a list of tip commands and how to use them. +**Example:**   +`!tip help` or `!tips` + +![Tips](https://spee.ch/0/update-screenshot.jpeg) + +### Balance +Displays the balance of your Discord LBRY wallet. +**Example:** +`!tip balance` + +### Deposit +Displays your Discord LBRY wallet address. Useful if you want to receive LBC's directly to your Discord wallet. +**Example:** +`!tip deposit` + +### Withdraw +Use this to withdraw a chosen amount from your LBRY Discord wallet to another LBRY wallet such as the wallet in your LBRY app, Coinomi or to a LBC wallet on an exchange. +**Arguments:** +`!tip withdraw
` +**Example:** +`!tip withdraw bQ8N2xbbityGNyiijaUtZVHkN3KZys2ci 10` + +### Private Tips +Want to tip someone privately in a personal message? This will send a tip to your chosen username in a private personal message. +**Arguments:** +`!tip private ` +**Example:** +`!tip private @Electron#1111 10` + +### Multi Tips +This will send your set tip amount to all the users you list. +**Arguments:** +`!multitip ` +**Example:** +`!multitip @Electron#1111 @Proton#222 10` + +### Multi Tip Private +This will privately send your set tip amount to all the users you list in personal messages. +**Arguments:** +`!multitip private ` +**Example:** +`!multitip private @Electron#1111 @Proton#222 10` + +### Role Tips +Want to tip a Disocrd role? This will send a tip to your chosen role. +**Arguments:** +`!roletip ` +**Example:** +`!roletip @LBRY Team 10` + +### Private Role Tips +Want to tip a Disocrd role privately? This will send a tip to your chosen role in a private message. +**Arguments:** +`!roletip private ` +**Example:** +`!roletip private @LBRY Team 10` diff --git a/content/faq/tipbot-reddit.md b/content/faq/tipbot-reddit.md new file mode 100644 index 00000000..09211b8b --- /dev/null +++ b/content/faq/tipbot-reddit.md @@ -0,0 +1,57 @@ +--- +title: How do I use the Reddit tipbot? +category: tipbots +order: 2 +--- + +## LBRY Reddit Tipbot Information + +Tips, in LBRY Credits (LBC), are an integral part of our community because they allow us to reward members for their contributions - whether that's for sharing something insightful, providing feedback, completing bounties, testing our various apps or helping promote LBRY's vision and technology. You can earn them, share, or transfer them via simple [commands on Reddit](https://np.reddit.com/r/lbry/wiki/tipbot). +![Reddit-tip](https://spee.ch/1/reddit-tip.png) + +It is important to note that the LBC stored as a result of a tip is tied to your Reddit account and are stored on LBRY's wallet server. It is your responsibility to withdraw the tips to your LBRY App or other wallet like Coinomi. If you plan on storing LBC on Reddit, it is a good idea to enable Two Factor Authentication (2FA) on your account. LBRY takes no responsibility for lost funds due to negligence + +Use the following commands to make amazing things happen. + +### Balance +Displays the balance of your Reddit LBRY wallet. Performed via Reddit messaging. +**Example:** +`balance` +**Request:** +[Request Balance](https://reddit.com/message/compose?to=lbryian&subject=Balance&message=balance) + +### Deposit +Displays your Reddit LBRY wallet address. Useful if you want to receive LBC's directly to your wallet. Performed via Reddit messaging. +**Example:** +`Deposit` +**Request:** +[Request Wallet Address](https://www.reddit.com/message/compose?to=lbryian&subject=Deposit&message=deposit) + +### Withdraw +Use this to withdraw your balance from your LBRY Reddit wallet to another LBRY wallet such as the wallet in your LBRY app, or to a LBC wallet on an exchange. Performed via Reddit messaging. +**Arguments:** +`withdraw
` +**Request:** +[Request Withdraw](https://reddit.com/message/compose?to=lbryian&subject=Withdraw&message=withdraw%20%3Camount%3E%20%3Caddress) +**Example:** +`withdraw bLXasdadsa32432soas2sadsa 10` + +### Tip +Want to tip someone? This will send a tip to a chosen username from within a replying comment. +**Arguments:** +` u/lbryian` +`<$+amount> u/lbryian` +**Examples:** +LBC: `10 lbc u/lbryian` +USD: `5 usd u/lbryian` +USD: `$5 u/lbryian` + +**Note**: Only USD and LBC are currently supported. + +### Gild +Want to gild a post? This will command will gild the post you are replying to. +**Examples:** +`gild u/lbryian` +`u/lbryian gild` + +Find out more about Reddit Gilding [here](https://www.reddit.com/gilding/). diff --git a/content/faq/tipbot-twitter.md b/content/faq/tipbot-twitter.md new file mode 100644 index 00000000..ad543d21 --- /dev/null +++ b/content/faq/tipbot-twitter.md @@ -0,0 +1,70 @@ +--- +title: How do I use the Twitter tipbot? +category: tipbots +order: 3 +--- + +## LBRY Twitter Tipbot Information + +Tips, in LBRY Credits (LBC), are an integral part of our community because they allow us to reward members for their contributions - whether that's for sharing something insightful, providing feedback, testing our various apps or helping promote LBRY's vision and technology. You can earn them, share, or transfer them via simple Tweets which include tagging the tipbot Twitter account + command. Check out [this thread as an example](https://twitter.com/TomZarebczan/status/1015244426841677826). + + + + +It is important to note that the LBC stored as a result of a tip is tied to your Twitter account username and are stored on LBRY's wallet server. It is your responsibility to withdraw the tips to your LBRY App or [a standalone wallet](https://lbry.io/faq/standalone-wallet). If you plan on storing LBC on Twitter, it is a good idea to enable Two Factor Authentication (2FA) on your account. LBRY takes no responsibility for lost funds due to negligence + +Use the following commands to make amazing things happen. We recommend creating a new Tweet and starting out with tagging the [@LBC_TipBot](https://twitter.com/LBC_TipBot), followed by the desired command. If the tipbot account is already tagged in a thread, only command is required. Note: Make sure you keep your commands on one line. If they are on multiple lines, the command will not work. + +### Help +This displays a list of tip commands and how to use them. +[**Tweet Example:**](https://twitter.com/TomZarebczan/status/1015245364490833920) +`@LBC_TipBot help` + + + + + +### Balance +Displays the balance of your Twitter LBRY wallet. +[**Tweet Example:**](https://twitter.com/TomZarebczan/status/1015244426841677826) +`@LBC_TipBot balance` + + + + + +### Deposit +Displays your Twitter LBRY wallet address. Useful if you want to receive LBC's directly to your wallet. +[**Tweet Example:**](https://twitter.com/TomZarebczan/status/1015244855247888384) +`@LBC_TipBot deposit` + + + + +### Tip +Want to tip someone? This will send a tip to a chosen username. +**Arguments:** +`@LBC_TipBot tip ` +[**Tweet Example:**](https://twitter.com/TomZarebczan/status/1015245926691205120) +`@LBC_TipBot tip @TrendsPremium 10` + + + + +### Withdraw +Use this to withdraw your balance from your LBRY Twitter wallet to another LBRY wallet such as the wallet in your LBRY app, or to a LBC wallet on an exchange. +**Arguments:** +`@LBC_TipBot withdraw
amount` +[**Tweet Example:**](https://twitter.com/TrendsPremium/status/1015251227599364096) +`@LBC_TipBot withdraw bP8P5Dr9d3XcH5ibSsjeDYFU8vMWR8HHe3 5` + + + + +### Terms +Shows the terms and conditions +[**Tweet Example:**](https://twitter.com/TomZarebczan/status/1015245044415156225) +`@LBC_TipBot terms` + + + diff --git a/content/faq/tipping.md b/content/faq/tipping.md index 658459c8..fe1fbc83 100644 --- a/content/faq/tipping.md +++ b/content/faq/tipping.md @@ -9,9 +9,11 @@ Tips can be sent via the LBRY app or via the protocol's [`wallet_send`](https:// ### How do I send a tip? -Sending tips via the LBRY app is easy. Simply go to the page of the content you want to support and click "Support". Next, you'll be prompted for the tip amount in LBRY Credits (LBC). Once you enter a value, click "Send". Mahalo! +Sending tips via the LBRY app is easy. Simply go to the page of the content you want to support and click "Enjoying this? Send a tip". +![sendtip](https://spee.ch/0/tip.jpeg) - +Next, you'll be prompted for the tip amount in LBRY Credits (LBC). Once you enter a value, click "Send". Mahalo! +![sendtip](https://spee.ch/d/tip2.jpeg) **Note: This amount will show up in your transaction list and will be deducted from your balance.** @@ -21,6 +23,6 @@ When you receive a tip, the credits will come into your wallet, and you can see To have these credits show in your balance, they must be unlocked via the wallet Overview/History page. This is done by clicking the unlock icon next to `Tip` and then confirming your action on the following screen. Once the transaction is finalized, the icon will disappear. -Unlock tip: +Unlock tip: -Confirmation box: +Confirmation box: diff --git a/content/faq/transaction-types.md b/content/faq/transaction-types.md index 7a3e04d5..716e4290 100644 --- a/content/faq/transaction-types.md +++ b/content/faq/transaction-types.md @@ -5,16 +5,16 @@ category: wallet There are a number of transaction types which take place on the LBRY blockchain. The LBRY app displays these transactions in the **Overview** and **History** tabs on the Wallet page. -Many transaction types also have details associated with them such as the claim/channel name or if they came from an LBRY Reward. You can also see additional details by clicking the transaction ID and accessing them in the [LBRY block explorer](https://explorer.lbry.io). +Many transaction types also have details associated with them such as the claim/channel name or if they came from a LBRY Reward. You can also see additional details by clicking the transaction ID and accessing them in the [LBRY block explorer](https://explorer.lbry.io). | Type | Details | --- | --- | **Spends** | LBC is sent to another address or used to purchase content.
Also, revoked content/claimed tips show as Spends1 | **Receives** | LBC received at wallet address, an incoming content payment or LBRY Reward | **Publishes** | LBC claim associated with content publication.
Claims can be revoked via trash button2 -| **Channels** | LBC claim associated with Channel creation.
Channels can be revoked via trash button +| **Channels** | LBC claim associated with Channel creation.
Channel claims can be revoked via trash button | **Tips** | Tips sent or received. Received tips can be claimed via unlock button -| **Supports** | Claim support sent or received. Supports can be revoked via trash button +| **Supports** | Claim support sent or received. Support claims can be revoked via trash button | **Updates** | Update to previously published content3. Updated claims can be revoked via trash button 1 The amount shown in the transaction list only reflects the revoke/claim fee paid. See transaction details for the amount that is returned to your wallet. diff --git a/content/faq/unable-to-stream.md b/content/faq/unable-to-stream.md index e31e2cb9..c8fd38fc 100644 --- a/content/faq/unable-to-stream.md +++ b/content/faq/unable-to-stream.md @@ -15,6 +15,6 @@ Another common cause of this issue is the lack of access to port 4444 (UDP). LB 3. If you have a `daemon_settings.yml` file, add this line to it at the end(44444 is an example, can be changed): `dht_node_port: 44444`. 4. If you don't have the `daemon_settings.yml` file, you can create one or download/copy [this sample](https://goo.gl/a5uJq5) into the `lbrynet` folder. 5. Start LBRY and try to download a couple of items from the homepage. Be patient, if it doesn't work, leave the page and try again or a different video. -6. Depending on your network, you may need port 44444 UDP (the new port you just setup) forwarded. Also, for file sharing to work properly, you may need port 3333 (TCP) open/forwarded. This can be verified by using a [port checker](https://www.canyouseeme.org) on port 3333. 44444 will fail since it's UDP +6. Depending on your network, you may need port 44444 UDP (the new port you just setup) forwarded. Also, for file sharing to work properly, you may need port 3333 (TCP) open/forwarded. This can be verified by using a [port checker](http://www.canyouseeme.org) on port 3333. 44444 will fail since it's UDP If you continue to have issues streaming/downloading after completing the above steps, please [reach out to us](https://lbry.io/faq/how-to-report-bugs) for additional support. diff --git a/content/faq/what-is-lbry.md b/content/faq/what-is-lbry.md index 982a7c63..778d55b8 100644 --- a/content/faq/what-is-lbry.md +++ b/content/faq/what-is-lbry.md @@ -10,7 +10,7 @@ LBRY is first and foremost a new *protocol* that allows anyone to build apps tha What makes this all possible is the blockchain technology developed by the creator of Bitcoin. Do you have to understand any of this to use and enjoy LBRY? No. Does it still matter to users? Yes! ## Why Build A Protocol? -Building [protocols, not platforms](https://lbry.io/news/blockchain-is-love-blockchain-is-life), is the future of the free, open internet. Almost every tech giant today is a centralized service that sells users’ personal information and attention to advertisers. They spend a lot of money chasing their product (your personal information and time/attention), but at the end of the day, offer it up for free in exchange for access to the platform. +Building [protocols, not platforms](https://lbry.io/news/blockchain-is-love-blockchain-is-life), is the future of the free, open internet. Almost every tech giant today is a centralized service that sells users’ personal information and attention to advertisers. They spend a lot of money chasing their product (your personal information and time/attention), but at the end of the day, users (you) offer it up for free in exchange for access to the platform. We think users should own their content (and their privacy) instead of handing it over to a corporate giant and their advertising buddies. If you think we’re paranoid, there are dozens of examples of [companies abusing users](https://lbry.io/news/why-do-tech-giants-abuse-their-users) and acting against their interests. It’s not paranoia if they’re actually out to get you. diff --git a/content/faq/youtube.md b/content/faq/youtube.md index 28ab8809..d1c14a9d 100644 --- a/content/faq/youtube.md +++ b/content/faq/youtube.md @@ -10,18 +10,26 @@ LBRY offers an easy way for YouTubers to sync their content to the LBRY network, To sync your existing channel to LBRY and learn more about the program, use the [LBRY.io YouTube Sync page](https://lbry.io/youtube). +There is a size limit per video of 2GB. There is a total count limit of your most recent 1,000 videos. What this means, if that isn't clear, is that your most recent 1,000 videos that are smaller than (or equal to) 2GB in size will be synced to LBRY. + Authenticating your YouTube channel and other information puts your content into a queue to be automatically mirrored on the LBRY network. This serves as an alternative to moving your entire channel by yourself. The content, its title and description, as well as thumbnails and other metadata, will sync to your channel name. When it is done, you will receive a notice from LBRY indicating your channel is available to view. - When you sync your channel, you are also eligible to receive LBRY Credits in our Partner Program based on your subscriber count. Receiving these credits is subject to a one-year agreement. The exact agreement you make when you sync can be seen [here](https://lbry.io/faq/youtube-terms). The current rewards for syncing can be seen on the [sync page](https://lbry.io/youtube). +*Please note: users won't see their channel/content in their LBRY app under `My LBRY - Publishes` until LBRY sends their wallets/claims over - a more automated process is still being worked out. However, the content will be accessible through the LBRY Desktop app / spee.ch* + +Before you follow the next steps, it is helpful to know that creators who use YouTube Sync will need to coordinate with the LBRY team to deliver your wallet to you. The sync process stores your wallet in a secure location, as you may not have the LBRY app installed already. Reach out to [help@lbry.io](mailto:help@lbry.io) to set up a time for a team member to walk you through getting set up. + **How to Receive Your LBRY Credits** - Download the LBRY App at https://lbry.io/get - Run the LBRY App (this can take a while on your first start up) - Make sure the email saved in the application matches the email address on your [Sync Status Page](https://lbry.io/youtube/status) -- Click the Bank icon in the upper righthand corner -- Navigate to the Rewards section of your wallet +- Click on the wallet tab on the LBRY App `1` +![wallet](https://spee.ch/2/rewardsa.jpeg) + +- Navigate to the `Rewards` section of your wallet `2` +![reward](https://spee.ch/5/rewardsww.jpeg) - Scroll to the "YouTube Reward" claim button - If you met a particular subscriber threshold, you should receive the appropriate amount of credits. diff --git a/content/jobs/api-engineer.md b/content/jobs/api-engineer.md index 9e372d26..d9723177 100644 --- a/content/jobs/api-engineer.md +++ b/content/jobs/api-engineer.md @@ -2,9 +2,10 @@ title: API Engineer order: 5 status: active +location: remote (global) url: https://hire.withgoogle.com/public/jobs/lbryio/view/P_AAAAAADAAADFYg8lMqDBXz?trackingTag=joinUs --- -This job combines the sexiest language with a slightly less sexy objective for an overall attractiveness quotient of still pretty neat. +This job combines the coolest language with a slightly less cool objective for an overall attractiveness quotient of still pretty neat. Specifically, being an API engineer at LBRY involves creating and modifying web-based API endpoints in Go. These endpoints are used for everything from analytics and user databasing to reward disbursement, notifications, and more. diff --git a/content/jobs/application-engineer-lead.md b/content/jobs/application-engineer-lead.md index 2a137225..ac849cd0 100644 --- a/content/jobs/application-engineer-lead.md +++ b/content/jobs/application-engineer-lead.md @@ -1,17 +1,18 @@ --- title: Lead Application Engineer order: 2 -status: active +status: closed +location: remote (global) url: https://hire.withgoogle.com/public/jobs/lbryio/view/P_AAAAAADAAADNhIbg93Flmj?trackingTag=joinUs --- As the touch and interaction point for the vast majority of LBRY users, applications play a tremendous role in the success of the LBRY protocol. LBRY maintains three applications. All three use either ReactJS or React Native. - - [lbry-desktop](http://github.com/lbryio/lbry-app), an Electron based desktop browser and wallet. + - [lbry-desktop](http://github.com/lbryio/lbry-desktop), an Electron based desktop browser and wallet. - [lbry-android](https://github.com/lbryio/lbry-android), a browser and wallet for Android, currently in alpha. - [spee.ch](https://github.com/lbryio/spee.ch), a web-based image and video sharing and self-hosting tool that syncs to the LBRY network. This position would involve becoming the team leader of all three projects, which each currently have a single full-time engineer working on them. -Success at this position involves strength at both product vision and user-experience as well as code architecture and standards. Being able to showcase previous experience building an engaging and delightful application is a requirement for this position. \ No newline at end of file +Success at this position involves strength at both product vision and user-experience as well as code architecture and standards. Being able to showcase previous experience building an engaging and delightful application is a requirement for this position. diff --git a/content/jobs/blockchain-engineer.md b/content/jobs/blockchain-engineer.md index 60cd49d7..81287804 100644 --- a/content/jobs/blockchain-engineer.md +++ b/content/jobs/blockchain-engineer.md @@ -2,6 +2,7 @@ title: Blockchain Engineer order: 1 status: active +location: remote (global) url: https://hire.withgoogle.com/public/jobs/lbryio/view/P_AAAAAADAAADLZMs9Keowq0?trackingTag=joinUs --- This position involves working directly on the LBRY [blockchain](https://github.com/lbryio/lbrycrd), written in C++. diff --git a/content/jobs/lead-designer.md b/content/jobs/lead-designer.md index 281d06f3..e5f93ffb 100644 --- a/content/jobs/lead-designer.md +++ b/content/jobs/lead-designer.md @@ -1,10 +1,12 @@ --- title: Lead Designer order: 5 -status: active +status: paused +location: remote (global) +url: https://hire.withgoogle.com/public/jobs/lbryio/view/P_AAAAAADAAADDIkS8VwCnPN?trackingTag=joinUs --- Like designing things? Great, because this position involves designing all the things. -The Lead Designer at LBRY is responsible for design, look, feel, and aesthetics of [our logo](https://lbry.io/img/lbry-dark.svg), our applications ([1](http://github.com/lbryio/lbry-app) [2](https://github.com/lbryio/lbry-android) [3](http://github.com/lbryio/spee.ch)), our websites ([1](https://lbry.io) [2](https://beta.lbry.tech)), and [our CEO's hair](https://spee.ch/5/hair.jpg). +The Lead Designer at LBRY is responsible for design, look, feel, and aesthetics of [our logo](https://lbry.io/img/lbry-dark.svg), our applications ([1](http://github.com/lbryio/lbry-desktop) [2](https://github.com/lbryio/lbry-android) [3](http://github.com/lbryio/spee.ch)), our websites ([1](https://lbry.io) [2](https://beta.lbry.tech)), and [our CEO's hair](https://spee.ch/5/hair.jpg). We prefer to work with designers who can go from idea to implementation, including working directly in CSS. diff --git a/content/jobs/project-manager.md b/content/jobs/project-manager.md index 131ac019..da852c42 100644 --- a/content/jobs/project-manager.md +++ b/content/jobs/project-manager.md @@ -1,7 +1,8 @@ --- title: Project Manager order: 3 -status: active +status: closed +location: remote (global) url: https://hire.withgoogle.com/public/jobs/lbryio/view/P_AAAAAADAAADDIQ-YUHEtOA?trackingTag=joinUs --- Being a project manager at LBRY requires skillful facilitation and coaching of a menagerie of full-time engineers and community contributors. It demands the ability to break down big goals into practical plans and keep track of a wide variety of tasks and small details. diff --git a/content/jobs/protocol-engineer.md b/content/jobs/protocol-engineer.md index 50bb2e6a..21690420 100644 --- a/content/jobs/protocol-engineer.md +++ b/content/jobs/protocol-engineer.md @@ -2,6 +2,7 @@ title: Protocol Engineer order: 4 status: active +location: remote (global) url: https://hire.withgoogle.com/public/jobs/lbryio/view/P_AAAAAADAAADALc6v5NkAOf?trackingTag=joinUs --- The LBRY protocol consists of a [set of APIs](https://lbry.io/api) provided via a daemon. This daemon is comprised of several sub-components, and interacts with the blockchain, wallet, and other remote daemons that constitute the LBRY data network. diff --git a/content/jobs/web-developer.md b/content/jobs/web-developer.md index 701a1071..f0b20d1a 100644 --- a/content/jobs/web-developer.md +++ b/content/jobs/web-developer.md @@ -1,6 +1,7 @@ --- title: Web Developer order: 10 +location: remote (global) status: closed --- We're seeking someone to manage [this very website](https://github.com/lbryio/lbry.io), as well as other LBRY web properties and projects. diff --git a/content/news/101-airsoft.md b/content/news/101-airsoft.md index 8cbe37d2..36eada1f 100644 --- a/content/news/101-airsoft.md +++ b/content/news/101-airsoft.md @@ -1,4 +1,4 @@ ---- +--- author: reilly-smith title: 'Pew Pew Bang Bang' date: '2017-03-16 00:18:00' @@ -6,7 +6,7 @@ cover: 'airsoft-banner.png' --- One of the biggest perks of the early days of LBRY continues to be the discovery of content creators that never even occurred to me as a thing. -A niche stumbles into our inbox (this time with the [LBRY-YouTube sync tool here](https://api.lbry.io/yt/connect?type=sync)) and what you *thought* was a niche reveals itself as an entire world to explore. +A niche stumbles into our inbox (this time with the [LBRY-YouTube sync tool here](https://lbry.io/youtube)) and what you *thought* was a niche reveals itself as an entire world to explore. We’re going off the grid and the rails. Gear up, soldier, and enter Amped Airsoft. @@ -18,6 +18,6 @@ Imagine real-life Call of Duty warfare waged with non-lethal force on the forest Amped also publishes countless reviews of gear for your squadron, available [on their website](https://ampedairsoft.com/) for purchase. -Be a hero and join the Amped team on LBRY by syncing your channel today with https://api.lbry.io/yt/connect?type=sync +Be a hero and join the Amped team on LBRY by syncing your channel today with https://lbry.io/youtube -**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Have a burning desire to put your content more places? Email reilly@lbry.io for a ride on the wild side of publishing. +**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Have a burning desire to put your content more places? Email [reilly@lbry.io](mailto:reilly@lbry.io) for a ride on the wild side of publishing. diff --git a/content/news/102-montage.md b/content/news/102-montage.md index 6d3e88b4..336dd5ad 100644 --- a/content/news/102-montage.md +++ b/content/news/102-montage.md @@ -1,4 +1,4 @@ ---- +--- author: reilly-smith title: 'Ragequit the NoobTube' date: '2017-03-23 00:16:00' @@ -8,7 +8,7 @@ itshappening.jpg At least something’s happening. And that something is a surge of new channels joining LBRY as they flee the clutches of YouTube. -We made our own statue of liberty. She’s planted firmly at https://api.lbry.io/yt/connect?type=sync, and she welcomes all comers to the promised land of LBRY. Automatically. Without lifting a right trigger finger--or in most cases some WASD + left click. +We made our own statue of liberty. She’s planted firmly at https://lbry.io/youtube, and she welcomes all comers to the promised land of LBRY. Automatically. Without lifting a right trigger finger--or in most cases some WASD + left click. ![Montage of Gamers](/img/news/montage-inline.png) @@ -25,4 +25,4 @@ What does this seemingly gibberish string of words have to do with “Gaming?” gg no re. -**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Have a stream feed you want to add to LBRY? Email reilly@lbry.io to rage quit YouTube in style. +**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Have a stream feed you want to add to LBRY? Email [reilly@lbry.io](mailto:reilly@lbry.io) to rage quit YouTube in style. diff --git a/content/news/104-blesshay.md b/content/news/104-blesshay.md index 845eb8ac..6fe1ae2b 100644 --- a/content/news/104-blesshay.md +++ b/content/news/104-blesshay.md @@ -14,8 +14,8 @@ In the meantime, become the jack-of-all game dev trades at your own pace. Bless Hay Gaming is comprised of one Denmark native, Simon. Whether it’s programming, art design, or animation, Bless Hay shows you the basics of game development with a focus on 2D and sidescroller styles. -Add your channel on LBRY with Simon using our YouTube-LBRY API sync tool here: https://api.lbry.io/yt/connect?type=sync +Add your channel on LBRY with Simon using our YouTube-LBRY API sync tool here: https://lbry.io/youtube Or better yet, develop your own game and become the first interactive title published on LBRY. -**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Have a feed you want to add to LBRY? Email reilly@lbry.io to rage quit YouTube in style. +**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Have a feed you want to add to LBRY? Email [reilly@lbry.io](mailto:reilly@lbry.io) to rage quit YouTube in style. diff --git a/content/news/105-auto-diy.md b/content/news/105-auto-diy.md index 1aacebff..fd7520db 100644 --- a/content/news/105-auto-diy.md +++ b/content/news/105-auto-diy.md @@ -1,4 +1,4 @@ ---- +--- author: reilly-smith title: 'r/JustRolledIntoTheShop' date: '2017-04-06 00:12:00' @@ -21,7 +21,7 @@ If we ever finish DIY building the LBRY network, we think there might be a futur We agree, Mr. Smith. -DIY Auto Body and Paint joined LBRY via the YouTube-LBRY API tool: https://api.lbry.io/yt/connect?type=sync +DIY Auto Body and Paint joined LBRY via the YouTube-LBRY API tool: https://lbry.io/youtube Find more of Donnie’s work and courses at: http://www.collisionblast.com/ -**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Want to share your DIY madness? Email reilly@lbry.io to spread your knowledge. And maybe humblebrag, too. +**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Want to share your DIY madness? Email [reilly@lbry.io](mailto:reilly@lbry.io) to spread your knowledge. And maybe humblebrag, too. diff --git a/content/news/106-matt-sokol.md b/content/news/106-matt-sokol.md index fddbb9f4..733bac1f 100644 --- a/content/news/106-matt-sokol.md +++ b/content/news/106-matt-sokol.md @@ -15,4 +15,4 @@ A music industry veteran of ten years, Sokol shares an inspiring vision for the His chopped up vocals, abstract drum beats, and soothing guitars from his upcoming solo debut will be rolling onto his LBRY channel lbry://@heymattsokol as soon as he finishes doing the math for it. -**Not on LBRY yet?** [Download here](https://lbry.io/get). Musician looking for a way to avoid being shafted by The Man? Email reilly@lbry.io to actually make money on your music. +**Not on LBRY yet?** [Download here](https://lbry.io/get). Musician looking for a way to avoid being shafted by The Man? Email [reilly@lbry.io](mailto:reilly@lbry.io) to actually make money on your music. diff --git a/content/news/107-whoiscapital.md b/content/news/107-whoiscapital.md index 7c3f5e2e..fee64ff4 100644 --- a/content/news/107-whoiscapital.md +++ b/content/news/107-whoiscapital.md @@ -1,4 +1,4 @@ ---- +--- author: reilly-smith title: 'Get Lit on Rhymes' date: '2017-04-20 00:20:00' @@ -31,4 +31,4 @@ And keep up with his latest goings-on: *Today was a good day...* -**Not on LBRY yet?** [Download here](https://lbry.io/get). Musician looking for a way to avoid being shafted by The Man? Email reilly@lbry.io to actually make money on your music. +**Not on LBRY yet?** [Download here](https://lbry.io/get). Musician looking for a way to avoid being shafted by The Man? Email [reilly@lbry.io](mailto:reilly@lbry.io) to actually make money on your music. diff --git a/content/news/108-mayer-makes.md b/content/news/108-mayer-makes.md index 54a315e3..2c3e1a13 100644 --- a/content/news/108-mayer-makes.md +++ b/content/news/108-mayer-makes.md @@ -16,10 +16,10 @@ Mr. Mayer’s content is available in both Deutsch and English. A mix of vlogs, LBRY’s first 3D printing and DIY electronics channel can be found at: -**lbry://@MAYERMAKES** +[**lbry://@MAYERMAKES**](https://open.lbry.io/@MAYERMAKES) You can meet Clemens in person on May 6th and 7th at the [Munich Maker Festival](http://make-munich.de/). Maybe he’ll have some free LBRY Credits to give away… -**Not on LBRY yet?** [Download here](https://lbry.io/get). Have cool content to publish? Email reilly@lbry.io to share your creations with everyone on planet Earth before you forget. +**Not on LBRY yet?** [Download here](https://lbry.io/get). Have cool content to publish? Email [reilly@lbry.io](mailto:reilly@lbry.io) to share your creations with everyone on planet Earth before you forget. diff --git a/content/news/109-growing-green.md b/content/news/109-growing-green.md index 6a49e517..8a83abdc 100644 --- a/content/news/109-growing-green.md +++ b/content/news/109-growing-green.md @@ -1,4 +1,4 @@ ---- +--- author: reilly-smith title: 'Supergreen' date: '2017-05-04 00:19:30' @@ -16,8 +16,8 @@ But he’s moving onto greener pastures right here on LBRY. If you’ve been looking to build your own garden and don’t know where to start, this is bar none the best place on the internet to learn from the master. John Kohler’s gardening adventures and tips can soon be found at: -- lbry://@GrowingYourGreens +- [lbry://@GrowingYourGreens](https://open.lbry.io/@GrowingYourGreens) Don’t stay outside long, though. You’ll miss LBRY too much. -**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Have some tips and tricks of your own craft to share? Email reilly@lbry.io to **spread** your **knowledge** faster than Tai Lopez in a Lambo. +**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Have some tips and tricks of your own craft to share? Email [reilly@lbry.io](mailto:reilly@lbry.io) to **spread** your **knowledge** faster than Tai Lopez in a Lambo. diff --git a/content/news/110-julia-galef.md b/content/news/110-julia-galef.md index 542aa080..809b3868 100644 --- a/content/news/110-julia-galef.md +++ b/content/news/110-julia-galef.md @@ -1,4 +1,4 @@ ---- +--- author: reilly-smith title: 'Rationale' date: '2017-05-11 00:19:30' @@ -18,10 +18,10 @@ One of the most critical minds of our day, Julia co-founded the non-profit Cente Her vlog lectures, covering paradoxes, beliefs, morality, and everything under the philosophical sun, can be found on LBRY at: -lbry://@JuliaGalef +[lbry://@JuliaGalef](https://open.lbry.io/@JuliaGalef) (The Rationally Speaking podcast series will also be available @JuliaGalef--as well as other podcasts--when LBRY introduces its native audio player.) LBRY: now with free thinking caps. -**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Got wisdom and an audience? Email reilly@lbry.io to enlighten us all. +**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Got wisdom and an audience? Email [reilly@lbry.io](mailto:reilly@lbry.io) to enlighten us all. diff --git a/content/news/111-kokesh.md b/content/news/111-kokesh.md index 813ac89f..a131bd60 100644 --- a/content/news/111-kokesh.md +++ b/content/news/111-kokesh.md @@ -1,4 +1,4 @@ ---- +--- author: reilly-smith title: '...it isn’t free' date: '2017-05-11 00:19:30' @@ -16,7 +16,6 @@ The chronicles of Kokesh are now documented, boldly and uncensored at: lbry://@AdamKokesh -Have a message to share? Follow Adam’s lead and sync your YouTube channel with the LBRY sync API: https://api.lbry.io/yt/connect?type=sync +Have a message to share? Follow Adam’s lead and sync your YouTube channel with the LBRY sync API: https://lbry.io/youtube - -**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Feel like breaking the chains of YouTube? Email reilly@lbry.io to experience what liberty is really about. +**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Feel like breaking the chains of YouTube? Email [reilly@lbry.io](mailto:reilly@lbry.io) to experience what liberty is really about. diff --git a/content/news/112-epicenter.md b/content/news/112-epicenter.md index 2dad6cbc..1feb5140 100644 --- a/content/news/112-epicenter.md +++ b/content/news/112-epicenter.md @@ -14,8 +14,8 @@ Brian Fabian Crain, Sebastien Couture and Meher Roy of Epicenter TV host some of Founders, engineers, venture capitalists, and academics from Fred Ersham, Roger Ver, Peter Rizun, Vitalik Buterin, Gavin Andresen, Emin Gün Sirer… you won't want to miss a single interview, now available on LBRY in their entirety at: -- [lbry://@Epicenter](lbry://@Epicenter) +- [lbry://@Epicenter](https://open.lbry.io/@Epicenter) There *is* one person they haven’t interviewed yet, but the name is escaping me... -**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Wanna drop some crypto knowledge? Email reilly@lbry.io to make us all crypto rich. +**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Wanna drop some crypto knowledge? Email [reilly@lbry.io](mailto:reilly@lbry.io) to make us all crypto rich. diff --git a/content/news/113-trailers.md b/content/news/113-trailers.md index 70d89c89..2176e437 100644 --- a/content/news/113-trailers.md +++ b/content/news/113-trailers.md @@ -10,11 +10,11 @@ So, indulge yourself and watch 30 trailers in a row. That’s productive, right? ![LBRY Trailers](/img/news/trailers-inline.png) -If you’ve been on the fence about watching [It’s a Disaster](lbry://itsadisaster) or [Coherence](lbry://coherence) -- wonder no more. Trailers for current (or upcoming) LBRY movies from [Oscilloscope Labs](http://oscilloscope.net/films/), as well as theatrical and video game trailers courtesy of [IGN](http://www.ign.com/), are now available as their own channels right on the LBRY homepage, or at: +If you’ve been on the fence about watching [It’s a Disaster](https://open.lbry.io/itsadisaster) or [Coherence](https://open.lbry.io/coherence) -- wonder no more. Trailers for current (or upcoming) LBRY movies from [Oscilloscope Labs](http://oscilloscope.net/films/), as well as theatrical and video game trailers courtesy of [IGN](http://www.ign.com/), are now available as their own channels right on the LBRY homepage, or at: -- [lbry://@oscopelabs](lbry://@oscopelabs) -- [lbry://IGN-Trailers](lbry://@IGN-Trailers) +- [lbry://@oscopelabs](https://open.lbry.io/@oscopelabs) +- [lbry://IGN-Trailers](https://open.lbry.io/@IGN-Trailers) BRB another trailer for The Last Jedi is out... -**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Sharing something new? Email reilly@lbry.io to board the hype train. +**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Sharing something new? Email [reilly@lbry.io](mailto:reilly@lbry.io) to board the hype train. diff --git a/content/news/114-coinomiwallet.md b/content/news/114-coinomiwallet.md index 7d0a54f8..f2e9d326 100644 --- a/content/news/114-coinomiwallet.md +++ b/content/news/114-coinomiwallet.md @@ -7,7 +7,7 @@ date: '2017-06-02 00:14:00' ![LBRY Coinomi Screenshot](https://spee.ch/lbrycoinomi) -As our focus has been on building out a [great application](https://github.com/lbryio/lbry-app), we haven't put much emphasis on a standalone way to manage your LBRY credits. However, we have noticed the demand for easy backups and a simple, secure way to move credits between accounts. +As our focus has been on building out a [great application](https://github.com/lbryio/lbry-desktop), we haven't put much emphasis on a standalone way to manage your LBRY credits. However, we have noticed the demand for easy backups and a simple, secure way to move credits between accounts. That's why we're excited to announce integration with Coinomi! diff --git a/content/news/115-juicemedia.md b/content/news/115-juicemedia.md index 8bc40f45..34843b72 100644 --- a/content/news/115-juicemedia.md +++ b/content/news/115-juicemedia.md @@ -1,4 +1,4 @@ ---- +--- author: reilly-smith title: 'Pass the Juice' date: '2017-06-08 00:08:00' @@ -14,10 +14,10 @@ Corporate welfare, crony oil, coral reef decay, and other C-words like Corrupt P Donate to [their Patreon here](https://www.patreon.com/TheJuiceMedia), and look for your new favorite way to consume current events on LBRY, available soon at: -- lbry://@thejuicemedia +- [lbry://@thejuicemedia](https://open.lbry.io/@thejuicemedia) And add your channel to the LBRY queue like Juice Media, in a single click using this tool: -- https://api.lbry.io/yt/connect?type=sync +- https://lbry.io/youtube -**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Rapper, newscaster or Both? Email reilly@lbry.io to spit the facts. +**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Rapper, newscaster or Both? Email [reilly@lbry.io](mailto:reilly@lbry.io) to spit the facts. diff --git a/content/news/116-acousticlabs.md b/content/news/116-acousticlabs.md index 9116a6c8..9b677fbb 100644 --- a/content/news/116-acousticlabs.md +++ b/content/news/116-acousticlabs.md @@ -1,4 +1,4 @@ ---- +--- author: reilly-smith title: 'Strumming up a Storm' date: '2017-06-15 00:08:00' @@ -15,8 +15,8 @@ Acoustic Labs, otherwise known as composer GC Johnson, publishes a variety of mu GC has scored many films and multiple Netflix series. And he has some of the most epic fingerpicking videos online. (The ronroco versions are my personal favorite). Check him out soon on LBRY at: -- lbry://@AcousticLabs +- [lbry://@AcousticLabs](https://open.lbry.io/@AcousticLabs) If you're sick of Bandcamp or Spotify, LBRY could be your new outlet. -**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Have tabs for sale or girls to impress with your strumming? Email reilly@lbry.io to be super cool. Or sync your channel at: https://api.lbry.io/yt/connect?type=sync +**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Have tabs for sale or girls to impress with your strumming? Email [reilly@lbry.io](mailto:reilly@lbry.io) to be super cool. Or sync your channel at: https://lbry.io/youtube diff --git a/content/news/117-jordanbpeterson.md b/content/news/117-jordanbpeterson.md index 9e2e3601..89aba506 100644 --- a/content/news/117-jordanbpeterson.md +++ b/content/news/117-jordanbpeterson.md @@ -10,7 +10,7 @@ Jordan Peterson cuts through the mist and gets to the heart of, well, everyone ![Jordan B Peterson](/img/news/jordan-inline.jpg) -A professor and psychologist at the University of Toronto, Dr. Peterson has lectured, written and made videos covering some of the most deeply profound topics of our time in an accessible way. From [postmodernism](lbry://jp-Urd0IK0WEWU), to what the Bible [says about our relationship to authority](lbry://jp-R-GPAl-q2QQ), to [debating house bill C16](lbry://jp-KnIAAkSNtqo), he covers the root of humanity’s collective psychology in earnest. +A professor and psychologist at the University of Toronto, Dr. Peterson has lectured, written and made videos covering some of the most deeply profound topics of our time in an accessible way. From [postmodernism](https://open.lbry.io/jp-Urd0IK0WEWU), to what the Bible [says about our relationship to authority](https://open.lbry.io/jp-R-GPAl-q2QQ), to [debating house bill C16](https://open.lbry.io/jp-KnIAAkSNtqo), he covers the root of humanity’s collective psychology in earnest. A commenter on his Biblical series summarized his channel best: @@ -26,4 +26,4 @@ Check out some of the [best lectures on LBRY](https://open.lbry.io/%40JordanBPet You can learn more about Dr. Peterson, and [order his newest book](https://jordanbpeterson.com/12-rules-for-life/), here on his [website](https://jordanbpeterson.com/). -**Not on LBRY yet?** [Download it here](https://lbry.io/get). Know the meaning of life? Email reilly@lbry.io because he’s desperate to know. Or sync your channel at: https://api.lbry.io/yt/connect?type=sync +**Not on LBRY yet?** [Download it here](https://lbry.io/get). Know the meaning of life? Email[reilly@lbry.io](mailto:reilly@lbry.io) because he’s desperate to know. Or sync your channel at: https://lbry.io/youtube diff --git a/content/news/118-lukehall.md b/content/news/118-lukehall.md index 1e3fa169..725bfe4c 100644 --- a/content/news/118-lukehall.md +++ b/content/news/118-lukehall.md @@ -1,4 +1,4 @@ ---- +--- author: reilly-smith title: 'Young Gun' date: '2017-06-29 00:10:00' @@ -20,4 +20,4 @@ Please do yourselves the honor, and get your indie electronica on at: pieceofmind-mp3bundle -**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Can you make sick beats? Email reilly@lbry.io because he’s still listening to the same things he did at university eight years ago. Or sync your channel at: https://api.lbry.io/yt/connect?type=sync +**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Can you make sick beats? Email [reilly@lbry.io](mailto:reilly@lbry.io) because he’s still listening to the same things he did at university eight years ago. Or sync your channel at: https://lbry.io/youtube diff --git a/content/news/119-fee.md b/content/news/119-fee.md index dfb1260a..6548da8f 100644 --- a/content/news/119-fee.md +++ b/content/news/119-fee.md @@ -6,7 +6,7 @@ cover: 'fee-banner.jpg' --- The tenets of FEE, or the Foundation for Economic Education, are pretty similar to our own: to inspire, educate and connect. -Every video available via @FEEOrg consistently lives up to these ideals. +Every video available via @FEEOrg consistently lives up to these ideals. ![FEE](/img/news/fee-inline.jpg) @@ -15,8 +15,8 @@ Behind all of the fancy and often breathtaking technology that powers LBRY, it i - Do I like it? - What will I pay for it? -@FEEOrg takes simple economic ideas and applies them to big problems with incredible results. +@FEEOrg takes simple economic ideas and applies them to big problems with incredible results. If the economics of the LBRY world fascinate you, I highly recommend you check out everything this great channel has to offer. -**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Want to share your knowledge with the world? Email reilly@lbry.io to inspire us. Or sync your channel at: https://api.lbry.io/yt/connect?type=sync +**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Want to share your knowledge with the world? Email [reilly@lbry.io](mailto:reilly@lbry.io) to inspire us. Or sync your channel at: https://lbry.io/youtube diff --git a/content/news/120-slav.md b/content/news/120-slav.md index 5cd2afd6..f990e638 100644 --- a/content/news/120-slav.md +++ b/content/news/120-slav.md @@ -10,13 +10,13 @@ That’s exactly how I feel about this conspicuously popular-yet-underground cha ![Slav Records](/img/news/slav-inline.png) -@Slav Records are purveyors of house music with an emphasis on 90s-style beats. +@Slav Records are purveyors of house music with an emphasis on 90s-style beats. -The sickest album art and grooviest sounds instantly made @Slav my favorite music publisher on LBRY, second to none. +The sickest album art and grooviest sounds instantly made @Slav my favorite music publisher on LBRY, second to none. They are one of the biggest musical channels to take bitcoin donations--now it’s time to shower them with LBC. While moonwalking, of course. Sample some Slav Records below, courtesy of Spee.ch and served directly from the LBRY network. -**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Want to show us how to get down? Email reilly@lbry.io with your hottest beats. Or sync your channel at: https://api.lbry.io/yt/connect?type=sync +**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Want to show us how to get down? Email [reilly@lbry.io](mailto:reilly@lbry.io) with your hottest beats. Or sync your channel at: https://lbry.io/youtube diff --git a/content/news/121-makestuff.md b/content/news/121-makestuff.md index b1e49238..2f6be17d 100644 --- a/content/news/121-makestuff.md +++ b/content/news/121-makestuff.md @@ -1,4 +1,4 @@ ---- +--- author: reilly-smith title: 'The Maker’s Mark' date: '2017-07-21 00:03:00' @@ -10,7 +10,7 @@ One thing you can’t do (yet!) is sharing your tangible, physical works. But to ![I Like to Make Stuff](/img/news/makestuff-inline.jpg) -Bob of @ILikeToMakeStuff builds anything and everything for the fun of it, and you’re along for the ride. Stormtrooper helmet? Check. Outdoor tool bed? Check. Rube Goldberg machine? Easy peasy. +Bob of @ILikeToMakeStuff builds anything and everything for the fun of it, and you’re along for the ride. Stormtrooper helmet? Check. Outdoor tool bed? Check. Rube Goldberg machine? Easy peasy. If you’re like us, you spend a lot of time building things indoors. Not moving. At a desk. @@ -18,4 +18,4 @@ Bob shows you just how fun building cool things that you *actually* want to make Bob, LBRY. LBRYians, Bob. -**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Building something grand? Email reilly@lbry.io with further instructions. Or sync your channel at: https://api.lbry.io/yt/connect?type=sync +**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Building something grand? Email [reilly@lbry.io](mailto:reilly@lbry.io) with further instructions. Or sync your channel at: https://lbry.io/youtube diff --git a/content/news/122-jaylight.md b/content/news/122-jaylight.md index f60fca4c..2d279d6e 100644 --- a/content/news/122-jaylight.md +++ b/content/news/122-jaylight.md @@ -14,6 +14,6 @@ It’s a privilege to give you a sneak peek into this rising scene. Jay Light is one of the brightest young comics in Los Angeles. He’s competed on the Comedy Central edition of Roast Battle, and he’s opened for the likes of Theo Von, Dana Carvey and Dave Chappelle. And Kevin McNamara hasn’t been seen at The Comedy Store since Jay assaulted him. -Available now at lbry://light-versus-mcnamara, the battle also features the legendary Jeff Ross, Tony Hinchcliffe and Comedy Central's Roast Battle season one champion Mike Lawrence as the three judges. +Available now at lbry://light-versus-mcnamara, the battle also features the legendary Jeff Ross, Tony Hinchcliffe and Comedy Central's Roast Battle season one champion Mike Lawrence as the three judges. -**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Have comedy chops? Email reilly@lbry.io to make us laugh. Or sync your channel at: https://api.lbry.io/yt/connect?type=sync +**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Have comedy chops? Email [reilly@lbry.io](mailto:reilly@lbry.io) to make us laugh. Or sync your channel at: https://lbry.io/youtube diff --git a/content/news/124-rocketsaway.md b/content/news/124-rocketsaway.md index 1dcd0fc4..bfb56154 100644 --- a/content/news/124-rocketsaway.md +++ b/content/news/124-rocketsaway.md @@ -29,11 +29,11 @@ As you can tell from [several](https://lbry.io/news/slav) [previous](https://lbr @Recollection is a sister channel to @1791L / @AmericanLampoon with top notch music curation. From Nosaj Thing samples to Stranger Thing remixes, if you like mashups of cinematic soundscapes and chillwave remixes, this is the channel for you. Some of my favorites include: -- [Glocque - Slitted](lbry://rec-KFTZC3z4s-0) -- [Nosaj Thing - Aquarium](lbry://rec-KKMHHwCwZLU) -- [Max Richter - On The Nature Of Daylight (Euterpia Remix)](lbry://rec-7OG5Zb1s8Gc) -- [Thomas Bergersen - Final Frontier](lbry://rec-wL1MDPW-HSk) +- [Glocque - Slitted](https://open.lbry.io/rec-KFTZC3z4s-0) +- [Nosaj Thing - Aquarium](https://open.lbry.io/rec-KKMHHwCwZLU) +- [Max Richter - On The Nature Of Daylight (Euterpia Remix)](https://open.lbry.io/rec-7OG5Zb1s8Gc) +- [Thomas Bergersen - Final Frontier](https://open.lbry.io/rec-wL1MDPW-HSk) Check it out. Smash that Watch button. Listen to the illest (re)collection of sounds on LBRY. -**[Download LBRY today](https://lbry.io/get)**. Is your favorite channel not on LBRY? They’re eligible for $250 LBC. Help us feature what you want to see! Email reilly@lbry.io to make it happen. Or sync your own channel at: https://api.lbry.io/yt/connect?type=sync +**[Download LBRY today](https://lbry.io/get)**. Is your favorite channel not on LBRY? They’re eligible for $250 LBC. Help us feature what you want to see! Email [reilly@lbry.io](mailto:reilly@lbry.io) to make it happen. Or sync your own channel at: https://lbry.io/youtube diff --git a/content/news/125-rerez.md b/content/news/125-rerez.md index b2cc8491..2236d281 100644 --- a/content/news/125-rerez.md +++ b/content/news/125-rerez.md @@ -1,4 +1,4 @@ ---- +--- author: reilly-smith title: 'Game Smarter, Not Harder' date: '2017-08-10 00:21:00' @@ -16,8 +16,8 @@ That’s why I’m a huge fan of this week’s show because he consistently main [@Rerez](https://dir.block.ng/%40Rerez) is without question the best video games-related channel on LBRY. Whether you like in-depth critical reviews of games and hardware, or exploring the weirdest depths of game novelty–and in fact, if you like neither of those things!–he finds a way to make it interesting. -How do you make a [Playstation on Raspberry Pi](lbry://re-YzbCyOSJhho)? Is Superman 64 secretly [the greatest game](lbry://re-4EVL4u570T8) ever made? +How do you make a [Playstation on Raspberry Pi](https://open.lbry.io/re-YzbCyOSJhho)? Is Superman 64 secretly [the greatest game](https://open.lbry.io/re-4EVL4u570T8) ever made? Go to [lbry://@Rerez](https://open.lbry.io/%40Rerez) or check out the latest homepage update to find out. -**[Download LBRY today](https://lbry.io/get)**. Is your favorite channel not on LBRY? They’re eligible for $250 LBC. Help us feature what you want to see! Email reilly@lbry.io to make it happen. Or sync your own channel at: https://api.lbry.io/yt/connect?type=sync +**[Download LBRY today](https://lbry.io/get)**. Is your favorite channel not on LBRY? They’re eligible for $250 LBC. Help us feature what you want to see! Email [reilly@lbry.io](mailto:reilly@lbry.io) to make it happen. Or sync your own channel at: https://lbry.io/youtube diff --git a/content/news/126-casuallyexplained.md b/content/news/126-casuallyexplained.md index 5a9da067..a01301f7 100644 --- a/content/news/126-casuallyexplained.md +++ b/content/news/126-casuallyexplained.md @@ -1,4 +1,4 @@ ---- +--- author: reilly-smith title: 'Pls Explain, Thx' date: '2017-08-17 00:15:00' @@ -12,8 +12,8 @@ A regular on the front page of Reddit and one of the funniest channels in existe >*If you're looking for the answers to life's questions and Wikipedia was under scheduled maintenance, look no further, Casually Explained is here to help.* -[@CasuallyExplained](https://dir.block.ng/%40CasuallyExplained) will teach you how to understand the nuances of [intelligence and evolution](lbry://thespectrumofintelligence#300e83787c03a5edc6dd64c6697ab2dfb5d825e1), learn how to [find your special someone](lbry://findingtheone#da5856c57536f12917f62cedb06e1cd87288020e), and dole out generally phenomenal life advice in easy-to-digest three minute spans. Seeing as you’re very busy. +[@CasuallyExplained](https://dir.block.ng/%40CasuallyExplained) will teach you how to understand the nuances of [intelligence and evolution](https://open.lbry.io/thespectrumofintelligence#300e83787c03a5edc6dd64c6697ab2dfb5d825e1), learn how to [find your special someone](https://open.lbry.io/findingtheone#da5856c57536f12917f62cedb06e1cd87288020e), and dole out generally phenomenal life advice in easy-to-digest three minute spans. Seeing as you’re very busy. Go to lbry://@CasuallyExplained to learn more about literally anything. It’s possibly the best edutainment channel on LBRY. And please consume [Casually Explained merchandise](http://casuallyexplained.com/) responsibly. -**[Download LBRY today](https://lbry.io/get)**. Is your favorite channel not on LBRY? Help us feature what you want to see! Email reilly@lbry.io to make it happen. Or sync your own channel at: https://api.lbry.io/yt/connect?type=sync +**[Download LBRY today](https://lbry.io/get)**. Is your favorite channel not on LBRY? Help us feature what you want to see! Email [reilly@lbry.io](mailto:reilly@lbry.io) to make it happen. Or sync your own channel at: https://lbry.io/youtube diff --git a/content/news/127-lunduke.md b/content/news/127-lunduke.md index 058d4514..bb831844 100644 --- a/content/news/127-lunduke.md +++ b/content/news/127-lunduke.md @@ -1,4 +1,4 @@ ---- +--- author: reilly-smith title: 'Kernels of Truth' date: '2017-08-24 00:22:00' @@ -18,4 +18,4 @@ If you check out [lbry://@Lunduke](https://open.lbry.io/%40Lunduke), you’ll ca Please support [his patreon](https://www.patreon.com/bryanlunduke) or beg him for a LBRY address so you can tip him that sweet, sweet LBC. -**[Download LBRY today](https://lbry.io/get)**. Is your favorite channel not on LBRY? Help us feature what you want to see! Email reilly@lbry.io to make it happen. Or sync your own channel at: https://api.lbry.io/yt/connect?type=sync +**[Download LBRY today](https://lbry.io/get)**. Is your favorite channel not on LBRY? Help us feature what you want to see! Email [reilly@lbry.io](mailto:reilly@lbry.io) to make it happen. Or sync your own channel at: https://lbry.io/youtube diff --git a/content/news/128-redactedtonight.md b/content/news/128-redactedtonight.md index 777d8e22..218d37d7 100644 --- a/content/news/128-redactedtonight.md +++ b/content/news/128-redactedtonight.md @@ -1,4 +1,4 @@ ---- +--- author: reilly-smith title: 'Real Talk' date: '2017-08-31 00:19:00' @@ -20,4 +20,4 @@ From YouTube’s [war on independent media](https://open.lbry.io/rt-PgmzvCCXlBI) (Literally, there are no middlemen). -**[Download LBRY today](https://lbry.io/get)**. Is your favorite channel not on LBRY? Help us feature what you want to see! Email reilly@lbry.io to make it happen. Or sync your own channel at: https://api.lbry.io/yt/connect?type=sync +**[Download LBRY today](https://lbry.io/get)**. Is your favorite channel not on LBRY? Help us feature what you want to see! Email [reilly@lbry.io](mailto:reilly@lbry.io) to make it happen. Or sync your own channel at: https://lbry.io/youtube diff --git a/content/news/129-lbry-release-first-run-wallet-invites-rewards.md b/content/news/129-lbry-release-first-run-wallet-invites-rewards.md index fe489ae9..750d98fe 100644 --- a/content/news/129-lbry-release-first-run-wallet-invites-rewards.md +++ b/content/news/129-lbry-release-first-run-wallet-invites-rewards.md @@ -7,7 +7,7 @@ The latest LBRY app, v0.15 and the first update since open beta, is now availabl ## Release Notes -The release notes are below. These notes are copied from the [GitHub releases page](https://github.com/lbryio/lbry-app/releases), which is the official source of release notes. +The release notes are below. These notes are copied from the [GitHub releases page](https://github.com/lbryio/lbry-desktop/releases), which is the official source of release notes. For immediate notification of releases, you can watch the project on GitHub. diff --git a/content/news/131-eevblog.md b/content/news/131-eevblog.md index c7cabce5..96c55691 100644 --- a/content/news/131-eevblog.md +++ b/content/news/131-eevblog.md @@ -16,4 +16,4 @@ Plus–and I’m not saying that correlation equals causation–it’s been abou -**[Download LBRY today](https://lbry.io/get)**. Is your favorite channel not on LBRY? Help us feature what you want to see! Email reilly@lbry.io to make it happen. Or sync your own channel at: https://lbry.io/youtube +**[Download LBRY today](https://lbry.io/get)**. Is your favorite channel not on LBRY? Help us feature what you want to see! Email [reilly@lbry.io](mailto:reilly@lbry.io) to make it happen. Or sync your own channel at: https://lbry.io/youtube diff --git a/content/news/132-nurdrage.md b/content/news/132-nurdrage.md index 47f2df9c..4bb5f713 100644 --- a/content/news/132-nurdrage.md +++ b/content/news/132-nurdrage.md @@ -1,4 +1,4 @@ ---- +--- author: reilly-smith title: 'React. Or don't.' date: '2017-09-28 00:22:30' @@ -20,4 +20,4 @@ Donate LBRY Credits directly to the videos you like using the integrated tipjar Or become a Patreon donor here: https://www.patreon.com/NurdRage -**[Download LBRY today](https://lbry.io/get)**. Is your favorite channel not on LBRY? Help us feature what you want to see! Email reilly@lbry.io to make it happen. Or sync your own channel at: https://lbry.io/youtube +**[Download LBRY today](https://lbry.io/get)**. Is your favorite channel not on LBRY? Help us feature what you want to see! Email [reilly@lbry.io](mailto:reilly@lbry.io) to make it happen. Or sync your own channel at: https://lbry.io/youtube diff --git a/content/news/133-minutephysics.md b/content/news/133-minutephysics.md index 29fecd86..78c7374d 100644 --- a/content/news/133-minutephysics.md +++ b/content/news/133-minutephysics.md @@ -1,4 +1,4 @@ ---- +--- author: reilly-smith title: 'Real Particles Ride Waves' date: '2017-10-05 00:20:30' @@ -19,4 +19,4 @@ Donate LBRY Credits directly to the best MinutePhysics videos using the integrat And become a Patreon donor here: https://www.patreon.com/minutephysics -**[Download LBRY today](https://lbry.io/get)**. Is your favorite channel not on LBRY? Help us feature what you want to see! Email reilly@lbry.io to make it happen. Or sync your own channel at: https://lbry.io/youtube +**[Download LBRY today](https://lbry.io/get)**. Is your favorite channel not on LBRY? Help us feature what you want to see! Email [reilly@lbry.io](mailto:reilly@lbry.io) to make it happen. Or sync your own channel at: https://lbry.io/youtube diff --git a/content/news/135-kaze.md b/content/news/135-kaze.md index 27652910..6afee27e 100644 --- a/content/news/135-kaze.md +++ b/content/news/135-kaze.md @@ -10,7 +10,7 @@ One of the most persistent and fascinating art-remix communities is the mod comm ![KazeEmanuar](/img/news/kaze-inline.jpg) -[@KazeEmanuar](https://open.lbry.io/%40KazeEmanuar), recently featured on [IGN](open.lbry.io/@IGNonLBRY) for his [Super Mario 64 Online mod](http://www.ign.com/articles/2017/09/13/fan-made-super-mario-64-brings-online-play), has made a habit of taking arguably the most revolutionary game of the most iconic franchise in history and fusing it to the modern day, in all its blocky glory. +[@KazeEmanuar](https://open.lbry.io/%40KazeEmanuar), recently featured on [IGN](https://open.lbry.io/@IGNonLBRY) for his [Super Mario 64 Online mod](http://www.ign.com/articles/2017/09/13/fan-made-super-mario-64-brings-online-play), has made a habit of taking arguably the most revolutionary game of the most iconic franchise in history and fusing it to the modern day, in all its blocky glory. Words don’t do this madman justice. Feast your eyes on Super Mario 64 graphed onto the Legend of Zelda: Ocarina of Time overworld. @@ -19,4 +19,4 @@ Recently, Nintendo had Kaze’s Patreon and many videos deleted. **Please suppor Check out his work and guides here: https://sites.google.com/site/kazemario64/ -**[Download LBRY today](https://lbry.io/get)**. Is your favorite channel not on LBRY? Help us feature what you want to see! Email reilly@lbry.io to make it happen. Or sync your own channel at: https://lbry.io/youtube +**[Download LBRY today](https://lbry.io/get)**. Is your favorite channel not on LBRY? Help us feature what you want to see! Email [reilly@lbry.io](mailto:reilly@lbry.io) to make it happen. Or sync your own channel at: https://lbry.io/youtube diff --git a/content/news/136-johncleese.md b/content/news/136-johncleese.md index 956f0093..e978c27d 100644 --- a/content/news/136-johncleese.md +++ b/content/news/136-johncleese.md @@ -1,4 +1,4 @@ ---- +--- author: reilly-smith title: 'farting in our direction' date: '2017-10-19 00:22:30' @@ -21,4 +21,4 @@ Skeptical? Fear not. Proof! Please support John Cleese’s channel by tipping LBRY credits in-app to your favorite content. -**[Download LBRY today](https://lbry.io/get)**. Is your favorite channel not on LBRY? Help us feature what you want to see! Email reilly@lbry.io to make it happen. Or sync your own channel at: https://lbry.io/youtube +**[Download LBRY today](https://lbry.io/get)**. Is your favorite channel not on LBRY? Help us feature what you want to see! Email [reilly@lbry.io](mailto:reilly@lbry.io) to make it happen. Or sync your own channel at: https://lbry.io/youtube diff --git a/content/news/137-nilered.md b/content/news/137-nilered.md index f3bc2f5d..51682ab8 100644 --- a/content/news/137-nilered.md +++ b/content/news/137-nilered.md @@ -1,4 +1,4 @@ ---- +--- author: reilly-smith title: 'cut chemistry' date: '2017-10-26 00:15:30' @@ -17,8 +17,8 @@ Rounding out the top five science channels on LBRY is perhaps the most legendary [@NileRed](https://open.lbry.io/%40NileRed) (also available on spee.ch at https://spee.ch/@NileRed) is a regular Reddit front page sensation. Don’t believe me? As /u/deeexz [two years ago](https://www.reddit.com/r/chemistry/comments/3lgk8f/my_absolute_favourite_chemistryrelated_channel_on/?st=j98pwzbq&sh=a9b568e6) said, “My absolute favourite chemistry-related channel on YouTube.” And now, NileRed has nearly a quarter of a million subscribers. So, we should all be thanking deeexz. Get lit on chemistry via spee.ch: - + Please support NileRed by tipping LBRY credits in-app to your favorite chemical reactions. And also because tipping is a pretty, pretty, prettttty cool. -**[Download LBRY today](https://lbry.io/get)**. Is your favorite channel not on LBRY? Help us feature what you want to see! Email reilly@lbry.io to make it happen. Or sync your own channel at: https://lbry.io/youtube +**[Download LBRY today](https://lbry.io/get)**. Is your favorite channel not on LBRY? Help us feature what you want to see! Email [reilly@lbry.io](mailto:reilly@lbry.io) to make it happen. Or sync your own channel at: https://lbry.io/youtube diff --git a/content/news/14-lbry-gets-content-creators-out-of-precarious-position-daily-decrypts-amanda-b-johnson.md b/content/news/14-lbry-gets-content-creators-out-of-precarious-position-daily-decrypts-amanda-b-johnson.md index be71f023..27cae955 100644 --- a/content/news/14-lbry-gets-content-creators-out-of-precarious-position-daily-decrypts-amanda-b-johnson.md +++ b/content/news/14-lbry-gets-content-creators-out-of-precarious-position-daily-decrypts-amanda-b-johnson.md @@ -26,7 +26,7 @@ In Bitcoin, if one or a hundred or even a thousand "hosts" go offline, Bitcoin s This is what censorship-resistance looks like. As information itself becomes more integral – not only to our livelihoods but our lives – the most censorship-resistant tools will emerge as the preferable choice. The Daily Decrypt hopes to be among the first to host our content using such a tool. -![Amanda](http://tracesofreality.com/wp-content/uploads/2013/09/amanda-billyrock.jpg) +![Amanda](https://tracesofreality.com/wp-content/uploads/2013/09/amanda-billyrock.jpg) **Amanda B. Johnson** is the host of [The Daily Decrypt](https://www.youtube.com/channel/UCqNCLd2r19wpWWQE6yDLOOQ). She has written on cryptocurrencies for Bitcoin Magazine, CoinTelegraph, Bitcoin.com, and Liberty.me. diff --git a/content/news/140-3blue1brown.md b/content/news/140-3blue1brown.md index 07e7a55c..b40c629a 100644 --- a/content/news/140-3blue1brown.md +++ b/content/news/140-3blue1brown.md @@ -1,4 +1,4 @@ ---- +--- author: reilly-smith title: 'Derivative Originality' date: '2017-11-10 00:20:30' @@ -22,4 +22,4 @@ A math whiz, a South African, his South African friend, and a troupe of funny me Please support your favorite creators by tipping LBRY credits in-app. And remember to look for the red rocketship icon next to reward-eligible weekly videos. -**[Download LBRY today](https://lbry.io/get)**. Is your favorite channel not on LBRY? Help us feature what you want to see! Email reilly@lbry.io to make it happen. Or sync your own channel at: https://lbry.io/youtube +**[Download LBRY today](https://lbry.io/get)**. Is your favorite channel not on LBRY? Help us feature what you want to see! Email [reilly@lbry.io](mailto:reilly@lbry.io) to make it happen. Or sync your own channel at: https://lbry.io/youtube diff --git a/content/news/142-unbubbled.md b/content/news/142-unbubbled.md index 19976225..0716010e 100644 --- a/content/news/142-unbubbled.md +++ b/content/news/142-unbubbled.md @@ -16,4 +16,4 @@ And coming November 2017 is STEAL THIS SIDESHOW, a bi-weekly video podcast of th Please support your favorite creators by tipping LBRY credits in-app. -**[Download LBRY today](https://lbry.io/get)**. Is your favorite channel not on LBRY? Help us feature what you want to see! Email reilly@lbry.io to make it happen. Or sync your own channel at: https://lbry.io/youtube +**[Download LBRY today](https://lbry.io/get)**. Is your favorite channel not on LBRY? Help us feature what you want to see! [reilly@lbry.io](mailto:reilly@lbry.io) to make it happen. Or sync your own channel at: https://lbry.io/youtube diff --git a/content/news/143-nono.md b/content/news/143-nono.md index d9ef70d6..51b7ceb6 100644 --- a/content/news/143-nono.md +++ b/content/news/143-nono.md @@ -1,4 +1,4 @@ ---- +--- author: reilly-smith title: 'Nono LoL' date: '2017-11-24 00:20:30' @@ -10,12 +10,12 @@ But I’m just a camper and couldn’t solo mid to save my life, so what do I kn Introducing the *first* French-speaking channel to LBRY and the first LoL streamer… - + -Nono, a Season 1 LCS (League Championship Series) player in EU, shows some of his games, his e-sport events and Summoner's tips to a French-speaking audience, right here on LBRY at [lbry://@Nono](https://open.lbry.io/%40Nono) or spee.ch/@Nono. +Nono, a Season 1 LCS (League Championship Series) player in EU, shows some of his games, his e-sport events and Summoner's tips to a French-speaking audience, right here on LBRY at [lbry://@Nono](https://open.lbry.io/%40Nono) or [spee.ch/@Nono](https://spee.ch/@Nono). *Nono ex-joueur LCS, joueur de LoL au haut niveau depuis longtemps, propose ses conseils pour s'améliorer ! Autant rendre l’expérience que j'ai acquis sur le jeu, profitable à tous.* It’s high time that LBRY supported the most popular game on the planet. Please support your favorite streamers by tipping LBC in-app via the Support button. Because you can’t win a match without good supports. Did I mention we need a Healer? -**[Download LBRY today](https://lbry.io/get)**. Is your favorite channel not on LBRY? Help us feature what you want to see! Email reilly@lbry.io to make it happen. Or sync your own channel at: https://lbry.io/youtube +**[Download LBRY today](https://lbry.io/get)**. Is your favorite channel not on LBRY? Help us feature what you want to see! Email [reilly@lbry.io](mailto:reilly@lbry.io) to make it happen. Or sync your own channel at: https://lbry.io/youtube diff --git a/content/news/144-Howl-with-us.md b/content/news/144-Howl-with-us.md index 36eecc50..e8ff621f 100644 --- a/content/news/144-Howl-with-us.md +++ b/content/news/144-Howl-with-us.md @@ -1,4 +1,4 @@ ---- +--- author: samuel-lbryian title: 'Howl With Us' date: '2017-11-24 00:20:30' @@ -18,4 +18,4 @@ Use [this link](https://open.lbry.io/howl-lbry-movie-night) to watch **Howl** in See you at the movies! -**[Download LBRY today](https://lbry.io/get)**. Is your favorite channel or creator not on LBRY? Help us feature what you want to see! Email reilly@lbry.io to make it happen. Or sync your own channel at: https://lbry.io/youtube +**[Download LBRY today](https://lbry.io/get)**. Is your favorite channel or creator not on LBRY? Help us feature what you want to see! Email [reilly@lbry.io](mailto:reilly@lbry.io) to make it happen. Or sync your own channel at: https://lbry.io/youtube diff --git a/content/news/145-chronos.md b/content/news/145-chronos.md index 0372899e..a4590a87 100644 --- a/content/news/145-chronos.md +++ b/content/news/145-chronos.md @@ -10,7 +10,7 @@ Engaging that scrutiny is something we improve upon all the time. Especially in -Chronos Crypto ([lbry://@chronos](https://open.lbry.io/%40chronos)) is *easily* one of the most concise and informative crypto shows. From the simplest introductions (What is Bitcoin?) to [understanding forks](lbry://fork#c67fd5f39ab3db9fa9c2660692a4fce21dd743d7) or using desktop wallets [like Electrum](lbry://gettingstartedwithelectrum#d2403438d37b8501268e4fb982ad8c1ef55430b0), Chronos breaks down all you need to know to master cryptocurrency in digestible chunks. +Chronos Crypto ([lbry://@chronos](https://open.lbry.io/%40chronos)) is *easily* one of the most concise and informative crypto shows. From the simplest introductions (What is Bitcoin?) to [understanding forks](https://open.lbry.io/fork#c67fd5f39ab3db9fa9c2660692a4fce21dd743d7) or using desktop wallets [like Electrum](https://open.lbry.io/gettingstartedwithelectrum#d2403438d37b8501268e4fb982ad8c1ef55430b0), Chronos breaks down all you need to know to master cryptocurrency in digestible chunks. ![Steal This Sideshow](/img/news/stss-inline.png) @@ -18,4 +18,4 @@ And another week, another new show from Jamie King. His long-standing series STEAL THIS SHOW presented by Torrent Freak has a new companion piece: the video podcast [STEAL THIS SIDESHOW](https://open.lbry.io/%40stealthisshow), streamable only on LBRY and spee.ch. Look for regular episodes every 1-2 weeks. -**[Download LBRY today](https://lbry.io/get)**. Is your favorite channel not on LBRY? Help us feature what you want to see! Email reilly@lbry.io to make it happen. Or sync your own channel at: https://lbry.io/youtube +**[Download LBRY today](https://lbry.io/get)**. Is your favorite channel not on LBRY? Help us feature what you want to see! Email [reilly@lbry.io](mailto:reilly@lbry.io) to make it happen. Or sync your own channel at: https://lbry.io/youtube diff --git a/content/news/146-a-very-special-holiday-special.md b/content/news/146-a-very-special-holiday-special.md index 516b0cef..96cd2727 100644 --- a/content/news/146-a-very-special-holiday-special.md +++ b/content/news/146-a-very-special-holiday-special.md @@ -16,4 +16,4 @@ Join us on Tuesday 12/12 at 8 PM EST to watch along, comment, and spread good ti You can watch the movie [in-app with this link](https://open.lbry.io/rare-exports-movienight), or [on Spee.ch with this link](https://spee.ch/rare-exports-movienight). -And remember, the greatest gift of all the warm feeling you get in the cockles of your heart when you tell a friend to [download LBRY](http://lbry.io/get). +And remember, the greatest gift of all the warm feeling you get in the cockles of your heart when you tell a friend to [download LBRY](https://lbry.io/get). diff --git a/content/news/147-lbry-shifting-into-high-gear.md b/content/news/147-lbry-shifting-into-high-gear.md index 77591d33..564ee99f 100644 --- a/content/news/147-lbry-shifting-into-high-gear.md +++ b/content/news/147-lbry-shifting-into-high-gear.md @@ -14,8 +14,8 @@ You'll find all this (and a lot more) in LBRY v0.19 - **[get it here](https://lb ## Release Notes ### Added - * [Subscriptions](https://github.com/lbryio/lbry-app/issues/715). File and channel pages now show a subscribe button. A new "Subscriptions" tab appears on the homepage shows the most recent content from subscribed channels. - * [LBC acquisition widget](https://github.com/lbryio/lbry-app/issues/609). Convert other popular cryptos into LBC via a ShapeShift integration. + * [Subscriptions](https://github.com/lbryio/lbry-desktop/issues/715). File and channel pages now show a subscribe button. A new "Subscriptions" tab appears on the homepage shows the most recent content from subscribed channels. + * [LBC acquisition widget](https://github.com/lbryio/lbry-desktop/issues/609). Convert other popular cryptos into LBC via a ShapeShift integration. * [Flow](https://flow.org/) static type checking. This is a dev-only feature but will make development faster, less error-prone, and better for newcomers. ### Changed diff --git a/content/news/148-lbry-development-community-update-1.md b/content/news/148-lbry-development-community-update-1.md index 36ea05c8..d467b833 100644 --- a/content/news/148-lbry-development-community-update-1.md +++ b/content/news/148-lbry-development-community-update-1.md @@ -2,10 +2,11 @@ author: samuel-lbryian title: 'Development and Community Update' date: '2017-12-12 5:00:00' +category: community-update ---

-The LBRY community spoke, and we listened! This is the first of many posts that will keep the community up-to-date on project development and what’s going on in the LBRY community. +The LBRY community spoke, and we listened! This is the first of many posts that will keep the community up-to-date on project development and what’s going on in the LBRY community. To read all of our updates, please visit [our Development and Community Update archive](https://lbry.io/news/category/community-update). To skip the tech stuff and see what’s happened and what’s next in the LBRY community, click the link below. Otherwise, read on! diff --git a/content/news/149-stop-net-banality.md b/content/news/149-stop-net-banality.md index 6d7eb6c9..c353b187 100644 --- a/content/news/149-stop-net-banality.md +++ b/content/news/149-stop-net-banality.md @@ -16,4 +16,4 @@ The internet is for everyone, from timid lurkers to blustery blowhards. At LBRY, ### **(202) 596-7587** -And while you’re at it, do your part to end Net Banality: [download the LBRY app](http://lbry.io/get) and upload some interesting content! +And while you’re at it, do your part to end Net Banality: [download the LBRY app](https://lbry.io/get) and upload some interesting content! diff --git a/content/news/15-renowned-ip-attorney-kinsella-joins-lbry-cryptoapp-as-legal-advisor.md b/content/news/15-renowned-ip-attorney-kinsella-joins-lbry-cryptoapp-as-legal-advisor.md index 39aed2ea..2ea771f4 100644 --- a/content/news/15-renowned-ip-attorney-kinsella-joins-lbry-cryptoapp-as-legal-advisor.md +++ b/content/news/15-renowned-ip-attorney-kinsella-joins-lbry-cryptoapp-as-legal-advisor.md @@ -8,7 +8,7 @@ LBRY Inc., the startup behind a new blockchain-based content distribution platfo Stephan Kinsella has joined the executive team of LBRY Inc. as Legal Advisor, helping the company navigate the complex US and international copyright laws as they seek to radically upend the media industry. -![Stephan Kinsella](http://i.imgur.com/oKoXXO2.jpg?1) +![Stephan Kinsella](https://i.imgur.com/oKoXXO2.jpg?1) *

Stephan Kinsella, J.D. LL.M., Author and IP Attorney

* diff --git a/content/news/150-rubin-youtubeweek.md b/content/news/150-rubin-youtubeweek.md index 345e07d4..7b9ebd8e 100644 --- a/content/news/150-rubin-youtubeweek.md +++ b/content/news/150-rubin-youtubeweek.md @@ -15,4 +15,4 @@ We’ve partnered with The Rubin Report to bring you YouTube Week, a series of i This special series includes interviews with the Twitter-banned Bunty King; opiner of all the things Some Black Guy; skeptic extraordinaire Jaclyn Glenn; up-and-coming Aussie comedian Neel Kolhatkar; and parody rapper Rucka Rucka. Tip your favorite interviews with LBRY Credits and let us know who you want on LBRY next. -**[Download LBRY today](https://lbry.io/get)**. Is your favorite channel not on LBRY? Help us feature what you want to see! Email reilly@lbry.io to make it happen. Or sync your own channel at: https://lbry.io/youtube +**[Download LBRY today](https://lbry.io/get)**. Is your favorite channel not on LBRY? Help us feature what you want to see! Email [reilly@lbry.io](mailto:reilly@lbry.io) to make it happen. Or sync your own channel at: https://lbry.io/youtube diff --git a/content/news/151-boombust.md b/content/news/151-boombust.md index c1465aaf..233d45a1 100644 --- a/content/news/151-boombust.md +++ b/content/news/151-boombust.md @@ -8,10 +8,10 @@ The financial cycles, bubbles, and economies of scale: they’re the mythical st And depending on who you ask, the story isn’t always the same. - + On the heel’s of the television debut of LBRY, [Boom Bust](https://open.lbry.io/%40BoomBust) joins [Redacted Tonight](https://open.lbry.io/%40RedactedTonight) as the second RT America show to land on LBRY for your post-broadcast pleasure. Tracking the story of markets has never been more decentralized. Subscribe to Boom Bust using the new Subscriptions feature for the latest episodes. -**[Download LBRY today](https://lbry.io/get)**. Is your favorite channel not on LBRY? Help us feature what you want to see! Email reilly@lbry.io to make it happen. Or sync your own channel at: https://lbry.io/youtube +**[Download LBRY today](https://lbry.io/get)**. Is your favorite channel not on LBRY? Help us feature what you want to see! Email [reilly@lbry.io](mailto:reilly@lbry.io) to make it happen. Or sync your own channel at: https://lbry.io/youtube diff --git a/content/news/153-Blockchain-is-love-Blockchain-is-life.md b/content/news/153-Blockchain-is-love-Blockchain-is-life.md index 9982e8dc..53313936 100644 --- a/content/news/153-Blockchain-is-love-Blockchain-is-life.md +++ b/content/news/153-Blockchain-is-love-Blockchain-is-life.md @@ -49,4 +49,4 @@ It also aligns incentives: the developers have incentive to make the protocol ma For the first time ever, it’s economically feasible for companies to compete to create the best technology instead of capture and then abuse their users. That’s why we’re building LBRY, and it’s why we’re open source. Anyone who’s inspired to help us create a free, open internet is welcome to build apps on our protocol, or fork our project and strike out on their own if they think they can do it better than us. Real competition instead of a market in name only. -Want to join us? You can start by [downloading the LBRY app](http://lbry.io/get), trying it out, and letting us know if you have any questions or suggestions to improve it. You can also [join our chat](http://chat.lbry.io/) or consider a [more committed relationship](http://lbry.io/join-us). Together, we can build a better, freer internet. +Want to join us? You can start by [downloading the LBRY app](https://lbry.io/get), trying it out, and letting us know if you have any questions or suggestions to improve it. You can also [join our chat](https://chat.lbry.io/) or consider a [more committed relationship](https://lbry.io/join-us). Together, we can build a better, freer internet. diff --git a/content/news/154-how-ads-wrecked-entertainment.md b/content/news/154-how-ads-wrecked-entertainment.md index 8675f1c0..6633f7cf 100644 --- a/content/news/154-how-ads-wrecked-entertainment.md +++ b/content/news/154-how-ads-wrecked-entertainment.md @@ -108,4 +108,4 @@ If you follow even one of these steps, you’re already making the world better **WARNING: SHAMELESS SELF-PROMOTION AHEAD** -At [LBRY](https://lbry.io/), we’re betting on the idea that patron-supported art will be more satisfying to its intended audience and offer greater and more sustainable rewards to creators. If you want to help us build one of those uncharted territories for creators who are looking for a different way to share their work, [download our app](http://lbry.io/get) and let us know what you think. +At [LBRY](https://lbry.io/), we’re betting on the idea that patron-supported art will be more satisfying to its intended audience and offer greater and more sustainable rewards to creators. If you want to help us build one of those uncharted territories for creators who are looking for a different way to share their work, [download our app](https://lbry.io/get) and let us know what you think. diff --git a/content/news/156-sidequest.md b/content/news/156-sidequest.md index 4a6344c4..8456bdf8 100644 --- a/content/news/156-sidequest.md +++ b/content/news/156-sidequest.md @@ -1,4 +1,4 @@ ---- +--- author: reilly-smith title: 'Choose your own adventure' date: '2018-01-25 00:20:00' @@ -8,7 +8,7 @@ Analysis is a dish best served intelligently. You’ve seen his channel [@ColinsLastStand](https://open.lbry.io/%40ColinsLastStand) make its way to LBRY. But more recently, Mr. Moriarty has returned to his roots: the games industry. This is [@Sidequest](https://open.lbry.io/%40Sidequest) - + After a long stint as one of the independent staple voices at IGN.com, and later joining his former comrades at Kinda Funny Games, Colin Moriarty has again reinvented his quest to speak his mind about his favorite things. On the heels of his interview last year with [@TheRubinReport](https://open.lbry.io/%40TheRubinReport) to boot! @@ -16,4 +16,4 @@ Between Colin’s Last Stand and Sidequest, one of the most iconic games reviewe Subscribe to @Sidequest or follow him [via Spee.ch](https://spee.ch/@Sidequest). -**[Download LBRY today](https://lbry.io/get)**. Is your favorite channel not on LBRY? Help us feature what you want to see! Email reilly@lbry.io to make it happen. Or sync your own channel at: https://lbry.io/youtube +**[Download LBRY today](https://lbry.io/get)**. Is your favorite channel not on LBRY? Help us feature what you want to see! Email [reilly@lbry.io](mailto:reilly@lbry.io) to make it happen. Or sync your own channel at: https://lbry.io/youtube diff --git a/content/news/157-verified-awesome.md b/content/news/157-verified-awesome.md index 1389f1db..163843c5 100644 --- a/content/news/157-verified-awesome.md +++ b/content/news/157-verified-awesome.md @@ -12,4 +12,4 @@ LBRY v0.20 is live, and if you've been waiting in vain for verification, your da Users can now use SMS to verify that they're human. We've also automated the app upgrade process, updated the LBRY daemon, added a "dark" mode for evening viewing, and fixed an [Electron security issue](https://electronjs.org/blog/protocol-handler-fix). -You can read the [full release notes here](https://github.com/lbryio/lbry-app/releases/tag/v0.20.0). If you haven't downloaded the app yet, [what are you waiting for](lbry.io/get)? +You can read the [full release notes here](https://github.com/lbryio/lbry-desktop/releases/tag/v0.20.0). If you haven't downloaded the app yet, [what are you waiting for](lbry.io/get)? diff --git a/content/news/158-LBRY-in-2017-2018.md b/content/news/158-LBRY-in-2017-2018.md index 29404ba4..bae1f5cb 100644 --- a/content/news/158-LBRY-in-2017-2018.md +++ b/content/news/158-LBRY-in-2017-2018.md @@ -1,4 +1,4 @@ ---- +--- author: jeremy-kauffman title: 'Looking Back and Moving Forward: LBRY in 2017/2018' date: '2018-01-31 14:00:00' @@ -155,9 +155,9 @@ You can’t be prepared for *everything*, but you can build processes and princi Want to help LBRY build a free and open internet? Here’s how: -1. [Download the LBRY app](https://lbry.io/) and try it out. +1. [Download the LBRY app](https://lbry.io/get) and try it out. -2. If you have an idea that could make the app better (or if something breaks while you’re using it!) [open a new issue on Github](https://github.com/lbryio/lbry-app/issues) and tell us about it. +2. If you have an idea that could make the app better (or if something breaks while you’re using it!) [open a new issue on Github](https://github.com/lbryio/lbry-desktop/issues) and tell us about it. 3. Tell your friends and family about LBRY. With SMS Rewards verification in the [newest version of the app](https://lbry.io/news/verified-awesome), it’s easier than ever to earn LBC which can then be used to publish, view paid content or tip your favorite creators on LBRY. diff --git a/content/news/159-lbry-development-community-update-Jan-2018.md b/content/news/159-lbry-development-community-update-Jan-2018.md index ad695700..2f32c55c 100644 --- a/content/news/159-lbry-development-community-update-Jan-2018.md +++ b/content/news/159-lbry-development-community-update-Jan-2018.md @@ -3,8 +3,9 @@ author: tom-zarebczan title: 'Development and Community Update January 2018' date: '2018-02-01 4:00:00' cover: 'wooden-letters-banner.jpg' +category: community-update --- -In December, we published the [first ever LBRY Development and Community update](https://lbry.io/news/lbry-development-community-update-1), and we will continue this series at the end of each month to keep each and every LBRYian up to date on our quest to revolutionize content discovery, sharing and monetization! +In December, we published the [first ever LBRY Development and Community update](https://lbry.io/news/lbry-development-community-update-1), and we will continue this series at the end of each month to keep each and every LBRYian up to date on our quest to revolutionize content discovery, sharing and monetization! To read all of our updates, please visit [our Development and Community Update archive](https://lbry.io/news/category/community-update). Please take a moment to read our [Looking Back and Moving Forward: LBRY in 2017/2018](https://lbry.io/news/lbry-in-2017-2018) blog post! @@ -13,20 +14,20 @@ To skip the tech stuff and see what’s happened and what’s next in the LBRY c [Skip to **Community Happenings**](#com-updates) # Development Updates {#dev-updates} -All of our code is open source and available on [GitHub](https://github.com/lbryio). Are you a dev and want to find out more? Check out our [general contributing guide](https://lbry.io/faq/contributing) and our LBRY App specific contributing [document](https://github.com/lbryio/lbry-app/blob/master/CONTRIBUTING.md). +All of our code is open source and available on [GitHub](https://github.com/lbryio). Are you a dev and want to find out more? Check out our [general contributing guide](https://lbry.io/faq/contributing) and our LBRY App specific contributing [document](https://github.com/lbryio/lbry-desktop/blob/master/CONTRIBUTING.md). ### App and Protocol Quick Summary -As you may know from our previous update, the LBRY App is undergoing a redesign which is taking up a large chunk of the team’s time, but we still managed to ship multiple patch releases to version 0.19 that enabled users to continue enjoying the main functions of the LBRY app. We also finished off January with [version 0.20](https://github.com/lbryio/lbry-app/releases/tag/v0.20.0) which enables a more streamlined LBRY app update process, phone number verification for [LBRY Rewards](https://lbry.io/faq/rewards) that makes it easier for new users to earn some LBC, added automated dark theme mode, patched a security hole introduced by [Electron](https://electronjs.org/blog/protocol-handler-fix) and a bit of code refactoring. On the protocol side, we ran into a showstopper related to how files are handled and were forced to go back to the drawing board to re-engineer a better solution. Our protocol devs also had to deal with growing pains on [spee.ch](https://spee.ch) which continues to uncover invaluable insights into scaling the LBRY as it’s the biggest user of the protocol to date! +As you may know from our previous update, the LBRY App is undergoing a redesign which is taking up a large chunk of the team’s time, but we still managed to ship multiple patch releases to version 0.19 that enabled users to continue enjoying the main functions of the LBRY app. We also finished off January with [version 0.20](https://github.com/lbryio/lbry-desktop/releases/tag/v0.20.0) which enables a more streamlined LBRY app update process, phone number verification for [LBRY Rewards](https://lbry.io/faq/rewards) that makes it easier for new users to earn some LBC, added automated dark theme mode, patched a security hole introduced by [Electron](https://electronjs.org/blog/protocol-handler-fix) and a bit of code refactoring. On the protocol side, we ran into a showstopper related to how files are handled and were forced to go back to the drawing board to re-engineer a better solution. Our protocol devs also had to deal with growing pains on [spee.ch](https://spee.ch) which continues to uncover invaluable insights into scaling the LBRY as it’s the biggest user of the protocol to date! ### Documentation, Organization and Development Processes -A considerable amount of effort went into documenting an app side [contributing process](https://github.com/lbryio/lbry-app/blob/master/CONTRIBUTING.md) along with [tagging and organization](https://github.com/lbryio/lbry/wiki/Labels) of GitHub issues which required a careful review of all outstanding issues across multiple repositories. The result was a clearer and more organized depiction of what’s on the plate for our developers to work on but it also provides a more welcoming environment for contributors and potential LBRY developers to get started. +A considerable amount of effort went into documenting an app side [contributing process](https://github.com/lbryio/lbry-desktop/blob/master/CONTRIBUTING.md) along with [tagging and organization](https://github.com/lbryio/lbry/wiki/Labels) of GitHub issues which required a careful review of all outstanding issues across multiple repositories. The result was a clearer and more organized depiction of what’s on the plate for our developers to work on but it also provides a more welcoming environment for contributors and potential LBRY developers to get started. During the past month, we’ve also transitioned to a more agile development methodology we like to call [scrum-lite](https://github.com/lbryio/lbry/wiki/Development-Process-&-Standards) which enables the team to reassess and refocus our efforts on a bi-monthly basis. In addition to creating a more structured framework around development processes, this also forces us to break down large tasks into smaller, easier to tackle initiatives which allow for a transition from “I’m still working on it” to “completed part 1A, onto the next one!”. ![Scrum overview](https://spee.ch/b/AgileProjectManagementbyPlanbox.png) ### App Redesign -The LBRY App redesign is in full swing, and a good amount of progress has been made to date - you can follow along on [GitHub](https://github.com/lbryio/lbry-app/issues/848) for the latest status and updates. Not only does it include a facelift, but much of the underlying framework and navigation will be revamped as well. This also includes changes to the way searching is performed and redesigns to other pages like Wallet Overview, claims and file lists. +The LBRY App redesign is in full swing, and a good amount of progress has been made to date - you can follow along on [GitHub](https://github.com/lbryio/lbry-desktop/issues/848) for the latest status and updates. Not only does it include a facelift, but much of the underlying framework and navigation will be revamped as well. This also includes changes to the way searching is performed and redesigns to other pages like Wallet Overview, claims and file lists. ![App redesign early preview](https://spee.ch/1/app-redesign-preview.jpeg) @@ -44,7 +45,7 @@ Our recent [hiring efforts](https://lbry.io/join-us) exposed a weakness in LBRY A LBRY whitepaper is in the works which will formally document the LBRY ecosystem and protocol specifications. Its creation had been put off for a while since most agree that whitepapers are marketing gimmicks for projects without a product, but as LBRY continues to get more attention, having one will allow us to link a single technical resource into how LBRY functions as opposed to directing users to FAQ posts or other articles. ### LBRY Wallet Encryption -LBRY wallet encryption has been enabled in the latest versions of the daemon (not released yet). With the help of a few community members, we ran through initial tests, and they were successful at a basic level. In order to ensure we didn’t break anything, additional testing will need to be performed once an app is released that uses the new daemon. The final step would be to [integrate this feature](https://github.com/lbryio/lbry-app/issues/762) into the app. This would most likely happen after the app redesign and will come along with a new user walkthrough. +LBRY wallet encryption has been enabled in the latest versions of the daemon (not released yet). With the help of a few community members, we ran through initial tests, and they were successful at a basic level. In order to ensure we didn’t break anything, additional testing will need to be performed once an app is released that uses the new daemon. The final step would be to [integrate this feature](https://github.com/lbryio/lbry-desktop/issues/762) into the app. This would most likely happen after the app redesign and will come along with a new user walkthrough. ![Wallet Encryption command line](https://spee.ch/7/walletencryption.png) diff --git a/content/news/16-digging-into-lbry.md b/content/news/16-digging-into-lbry.md index 24e0536f..ea2bc765 100644 --- a/content/news/16-digging-into-lbry.md +++ b/content/news/16-digging-into-lbry.md @@ -4,12 +4,12 @@ title: 'Digging Into LBRY: Our Inspiration & The Future of Content' date: '2015-11-05 21:08:22' --- -In the past week, LBRY was featured in two major Bitcoin publications – CoinTelegraph LBRY: The Lovechild of Bitcoin, BitTorrent & Storj and Bitcoin.com LBRY: The Decentralized Sharing Platform. Both articles are lengthy interviews with our team. We've excerpted some of our favorite questions below. +In the past week, LBRY was featured in two major Bitcoin publications – CoinTelegraph LBRY: The Lovechild of Bitcoin, BitTorrent & Storj and Bitcoin.com LBRY: The Decentralized Sharing Platform. Both articles are lengthy interviews with our team. We've excerpted some of our favorite questions below. -

Digital content at your fingertips

+

Digital content at your fingertips

-

**CoinTelegraph: What was the inspiration for LBRY?** +

**CoinTelegraph: What was the inspiration for LBRY?** **Jeremy Kauffman:** LBRY was inspired by a number of things. It was clearly inspired by Bitcoin – seeing the power of the blockchain in reaching consensus without a central authority. It was also inspired by BitTorrent, which is an absolutely brilliant protocol with an unfortunately flawed incentive structure. @@ -19,7 +19,7 @@ It was inspired by the love of markets and an appreciation for how they facilita It is inspired by the fact that there has got to be a better way to find, buy, and sell something as simple as a number. -

**CoinTelegraph: What has been the biggest challenge in development? What about the greatest reward?** +

**CoinTelegraph: What has been the biggest challenge in development? What about the greatest reward?** **Kauffman:** One unique difficulty in developing a blockchain-based protocol/application is the difficulty in revising it. Great products are typically created through iteration. But by design, it’s nearly impossible to revise a cryptocurrency once it’s in the wild. To mitigate this, we’re trying to roll out LBRY slowly, as well as have several of its aspects be conventions rather than core properties. @@ -29,11 +29,11 @@ The greatest reward by far is the feeling that you are on to something absolutel **Mike Vine:** There isn’t really a system. There are many systems, and some are better than others. Services like Netflix and Hulu helped to break cable’s monopoly on TV content. YouTube allowed anyone to upload original content for a mass audience. But these are still centralized services. They are corporations that offer particular services according to their Terms of Use – or effectively, at their pleasure. -The risks of this came to the fore just last week with YouTube strong-arming publishers into its new “Red” premium service. Another example from a different industry is a few years back when Facebook was starting throttling Page owners’ access to their own followers – many of which they had paid to accumulate. The lesson is that if you’re using a system that has a higher authority, you’re always vulnerable. +The risks of this came to the fore just last week with YouTube strong-arming publishers into its new “Red” premium service. Another example from a different industry is a few years back when Facebook was starting throttling Page owners’ access to their own followers – many of which they had paid to accumulate. The lesson is that if you’re using a system that has a higher authority, you’re always vulnerable. LBRY is an open-source protocol, like the ones that form the backbone of the internet. LBRY Inc. supports the development of the protocol, but it doesn’t control it. With the advent of the blockchain era, I think the internet will fulfill its promise of radically decentralizing the flow of information. We hope LBRY will be a big part of that. -

**CoinTelegraph: The IPFS is probably the most similar concept to LBRY right now. Why do you think that incorporating a native blockchain with user payments is superior to IPFS's approach?** +

**CoinTelegraph: The IPFS is probably the most similar concept to LBRY right now. Why do you think that incorporating a native blockchain with user payments is superior to IPFS's approach?** **Jimmy Kiselak:** LBRY is about distributing data fast, and to do that it needs to be able to handle the fact that not all data is alike. IPFS relies on the same sort of planned sharing incentives as BitTorrent. @@ -45,7 +45,7 @@ We believe that if we don’t lay down rules for where data should be and who sh **Vine:** LBRY can actually accept torrent links as key values on the blockchain. So out of the gate, it will function as the world’s easiest torrent client. Instead of sending someone a complicated hyperlink, an independent artist can just say “Go to LBRY and type in hipster shorts to find my films.” But we expect LBRY’s built-in content hosting model to supplement or replace BitTorrent over time because of better incentives for everyone involved. -

**CoinTelegraph: Which use-case in particular do you believe would be LBRY's "killer app"? Listeners looking for new music? Indie cultists looking for obscure French films? Something else?** +

**CoinTelegraph: Which use-case in particular do you believe would be LBRY's "killer app"? Listeners looking for new music? Indie cultists looking for obscure French films? Something else?** **Kauffman:** We’re hesitant to speculate. LBRY is designed to gracefully extend BitTorrent, so at a minimum we expect LBRY to be a better BitTorrent client. @@ -57,7 +57,7 @@ LBRY’s hosts are motivated by what price people are paying for a given piece o With LBRY, artists have a turnkey publishing, distribution, and payment platform – all encapsulated in a neat little LBRY name like lbry://hipstershorts. Over time, LBRY’s killer app will be its lower cost and greater reach than centralized media. -

**CoinTelegraph: Do you envision content distributors, hosts, or miners being able to make a living by their LBRY work?** +

**CoinTelegraph: Do you envision content distributors, hosts, or miners being able to make a living by their LBRY work?** **Kauffman:** The “big five” of media (film, video games, TV, music, and books) see over US$2 trillion per year. We think LBRY can play a role in the distribution of all of this. This answer is a verbose “Yes!” @@ -65,5 +65,5 @@ With LBRY, artists have a turnkey publishing, distribution, and payment platform So devoting hard drive space or bandwidth to LBRY is like suddenly filling your car on daily commutes with paying customers who keep to themselves and get out where you tell them. It’s a pretty sweet deal. -

**Read the full CoinTelegraph interview here. +

**Read the full CoinTelegraph interview here. Read the full Bitcoin.com interview here.** diff --git a/content/news/161-nickatnyte.md b/content/news/161-nickatnyte.md index fc55b2fc..a747466d 100644 --- a/content/news/161-nickatnyte.md +++ b/content/news/161-nickatnyte.md @@ -1,4 +1,4 @@ ---- +--- author: reilly-smith title: 'Clash Cash' date: '2018-02-22 00:20:00' @@ -8,7 +8,7 @@ Are casual mobile games *really* casual? [@nickatnyte](https://open.lbry.io/%40nickatnyte) will reframe how seriously you can take the Clash series. - + A longtime Clash series veteran [@nickatnyte](https://open.lbry.io/%40nickatnyte) (and more at [@nickatnyte2](https://open.lbry.io/%40nickatnyte2)) Nick Nyte and his guests dive deeper into the casual/mobile genre than you ever thought possible, digging out every last bit of strategy there is to glean. diff --git a/content/news/162-colinslaststand.md b/content/news/162-colinslaststand.md index 978ab6f0..781b6b29 100644 --- a/content/news/162-colinslaststand.md +++ b/content/news/162-colinslaststand.md @@ -1,4 +1,4 @@ ---- +--- author: reilly-smith title: 'The Stand' date: '2018-03-01 00:20:00' @@ -8,10 +8,10 @@ This week, I’d like to reintroduce one of my personalities on LBRY. You caught Mr. Moriarty’s show Sidequest [in January](https://lbry.io/news/sidequest). Now it’s time for the main course. - + The prolific [Colin Moriarty](https://open.lbry.io/%40ColinsLastStand) (CLS, Sidequest, as well as podcast series Fireside Chats and now Knockback) has continued to forge his own path in games and news media with his own blend of political opinion, games criticism, and a wit that kills fluff pieces on site. He’s as no-nonsense as they come. Long form interviews, short-form reviews, and video essays on what Trump, Oprah, and reactionary thought mean for America, you’ll find no shortage of content from this IGN editor-turned-content-maverick. Become a subscriber to [Colin’s Patreon here](https://www.patreon.com/colinslaststand/posts). -**[Download LBRY today](https://lbry.io/get)**. Is your favorite channel not on LBRY? Help us feature what you want to see! Email reilly@lbry.io to make it happen. Or sync your own channel at: https://lbry.io/youtube +**[Download LBRY today](https://lbry.io/get)**. Is your favorite channel not on LBRY? Help us feature what you want to see! Email [reilly@lbry.io](mailto:reilly@lbry.io) to make it happen. Or sync your own channel at: https://lbry.io/youtube diff --git a/content/news/163-lbry-development-community-update-Feb-2018.md b/content/news/163-lbry-development-community-update-Feb-2018.md index faf55f97..4de926cc 100644 --- a/content/news/163-lbry-development-community-update-Feb-2018.md +++ b/content/news/163-lbry-development-community-update-Feb-2018.md @@ -3,8 +3,9 @@ author: tom-zarebczan title: 'Development and Community Update February 2018' date: '2018-03-01 18:00:00' cover: 'wooden-letters-banner.jpg' +category: community-update --- -In December, we published the [first ever LBRY Development and Community update](https://lbry.io/news/lbry-development-community-update-1) and followed up with another for [January 2018](https://lbry.io/news/lbry-development-community-update-jan-2018) - be sure to check both out if you missed them! We will continue these updates at the beginning of each month to keep each and every LBRYian up to date on our quest to revolutionize content discovery, sharing and monetization! +In December, we published the [first ever LBRY Development and Community update](https://lbry.io/news/lbry-development-community-update-1) and followed up with another for [January 2018](https://lbry.io/news/lbry-development-community-update-jan-2018) - be sure to check both out if you missed them! We will continue these updates at the beginning of each month to keep each and every LBRYian up to date on our quest to revolutionize content discovery, sharing and monetization! To read all of our updates, please visit [our Development and Community Update archive](https://lbry.io/news/category/community-update). Please take a moment to read our [Looking Back and Moving Forward: LBRY in 2017/2018](https://lbry.io/news/lbry-in-2017-2018) blog post and check out our [roadmap](https://lbry.io/roadmap). @@ -15,7 +16,7 @@ To skip the tech stuff and see what’s happened and what’s next in the LBRY c [Skip to **Community Happenings**](#com-updates) # Development Updates {#dev-updates} -All of our code is open source and available on [GitHub](https://github.com/lbryio). Are you a dev and want to find out more? Check out our [general contributing guide](https://lbry.io/faq/contributing) and our LBRY App specific contributing [document](https://github.com/lbryio/lbry-app/blob/master/CONTRIBUTING.md). Please be patient with us while we improve our technical documentation. Our plan is to create a technical reference site which will be developer focused at https://lbry.tech which will also include a StackExchange like a forum for Q&A/troubleshooting. +All of our code is open source and available on [GitHub](https://github.com/lbryio). Are you a dev and want to find out more? Check out our [general contributing guide](https://lbry.io/faq/contributing) and our LBRY App specific contributing [document](https://github.com/lbryio/lbry-desktop/blob/master/CONTRIBUTING.md). Please be patient with us while we improve our technical documentation. Our plan is to create a technical reference site which will be developer focused at https://lbry.tech which will also include a StackExchange like a forum for Q&A/troubleshooting. ### App and Protocol Summary As you may know from our previous updates, the LBRY app is undergoing a redesign which is taking up a large chunk of the team’s time but the app team is aiming to release a patch to version 0.20 which brings a few bug fixes, an updated LBRY protocol version and possibly a new feature or two (spee.ch thumbnail uploads from within the app, automated subscription downloads). @@ -29,14 +30,14 @@ After this round of protocol enhancements, the team will be focusing on improvin Stay tuned for an updated version of the LBRY app next week! In the meantime, you can download the current version [here](https://lbry.io/get?auto=1) if you don’t have it installed already! ### App Redesign -The LBRY App redesign continues to make great progress you can follow along on [GitHub](https://github.com/lbryio/lbry-app/issues/848) for the latest status and updates. The latest focus has been on refactoring the inner workings of the Publish page into a more optimized version using the React framework. We also have a community member, @btzr, who is helping out in migrating the dark theme into the new design. +The LBRY App redesign continues to make great progress you can follow along on [GitHub](https://github.com/lbryio/lbry-desktop/issues/848) for the latest status and updates. The latest focus has been on refactoring the inner workings of the Publish page into a more optimized version using the React framework. We also have a community member, @btzr, who is helping out in migrating the dark theme into the new design. Want to explore a web-based prototype of the new LBRY app design? Head over to [https://design.lbry.io](https://design.lbry.io) to get a preview of what’s to come. This page works best on a Desktop but may display correctly in landscape mode on mobile. *Please note: some of the design features like notifications and commenting are still experimental and most likely will not roll out with the first iteration of the re-design as they require additional support from the protocol to work properly.* ![App redesign early preview](https://spee.ch/1/app-redesign-preview.jpeg) ### Subscription Enhancements in Progress -A new round of enhancements to the Subscription feature is being developed. The goal is to increase interactivity, engagement and notification for users who subscribe to channels on LBRY. This will include an option to automatically download new content as it is added to a channel as well as notification features within the app and email. Have questions or comments, drop us a line on the [GitHub issue](https://github.com/lbryio/lbry-app/issues/994). +A new round of enhancements to the Subscription feature is being developed. The goal is to increase interactivity, engagement and notification for users who subscribe to channels on LBRY. This will include an option to automatically download new content as it is added to a channel as well as notification features within the app and email. Have questions or comments, drop us a line on the [GitHub issue](https://github.com/lbryio/lbry-desktop/issues/994). ### LBRY Mobile for Android LBRY recently worked with a user interface designer who came up with a very nice and simple design prototype, check out the video below for a preview. The process of incorporating this prototype into React Native code is underway. To solve delays deploying the app changes, we had to come up with a way to build the UI and daemon separately, such that the React Native app can be reloaded without having to rebuild the daemon each time (slow!). This enables testing the UI changes relatively quickly against any stable or development build of the daemon. There is a good chance that we will have a basic working mobile Android app in a few weeks! diff --git a/content/news/164-if-you-still-havent-found-what-youre-searching-for.md b/content/news/164-if-you-still-havent-found-what-youre-searching-for.md index 7e993b1d..1f260f61 100644 --- a/content/news/164-if-you-still-havent-found-what-youre-searching-for.md +++ b/content/news/164-if-you-still-havent-found-what-youre-searching-for.md @@ -15,4 +15,4 @@ Users will also get a popup notification when a new video is added to a channel We've upgraded the LBRY Protocol from version 0.18.2 to 0.19.1., which should improve reliability and help download performance and a number of other back-end processes. Get caught up on all changes in the release notes for [0.19.0](https://github.com/lbryio/lbry/releases/tag/v0.19.) and [0.19.1](https://github.com/lbryio/lbry/releases/tag/v0.19.1). -You can read the [full release notes for v0.21.2 here](https://github.com/lbryio/lbry-app/releases). And if you haven't downloaded the app yet, [what are you waiting for](https://lbry.io/get?auto=1)? +You can read the [full release notes for v0.21.2 here](https://github.com/lbryio/lbry-desktop/releases). And if you haven't downloaded the app yet, [what are you waiting for](https://lbry.io/get?auto=1)? diff --git a/content/news/165-lbry-development-community-update-Mar-2018.md b/content/news/165-lbry-development-community-update-Mar-2018.md index b41842d7..bbfacaea 100644 --- a/content/news/165-lbry-development-community-update-Mar-2018.md +++ b/content/news/165-lbry-development-community-update-Mar-2018.md @@ -3,9 +3,10 @@ author: tom-zarebczan title: 'Development and Community Update March 2018' date: '2018-04-03 18:00:00' cover: 'droid1.jpg' +category: community-update --- -Back in December, we published the [first ever LBRY Development and Community update](https://lbry.io/news/lbry-development-community-update-1) and followed up with another for [January 2018](https://lbry.io/news/lbry-development-community-update-jan-2018) and [February 2018](https://lbry.io/news/lbry-development-community-update-feb-2018) - be sure to check them all out if you missed it! We will continue these updates at the beginning of each month to keep each and every LBRYian up to date on our quest to revolutionize content discovery, sharing and monetization! +Back in December, we published the [first ever LBRY Development and Community update](https://lbry.io/news/lbry-development-community-update-1) and followed up with another for [January 2018](https://lbry.io/news/lbry-development-community-update-jan-2018) and [February 2018](https://lbry.io/news/lbry-development-community-update-feb-2018) - be sure to check them all out if you missed it! We will continue posting these updates at the beginning of each month to keep each and every LBRYian up to date on our quest to revolutionize content discovery, sharing and monetization! To read all of our updates, please visit [our Development and Community Update archive](https://lbry.io/news/category/community-update). If you haven’t already, please take a moment to read our [Looking Back and Moving Forward: LBRY in 2017/2018](https://lbry.io/news/lbry-in-2017-2018) blog post and check out our [roadmap](https://lbry.io/roadmap). @@ -14,12 +15,12 @@ To skip the tech stuff and see what’s happened and what’s next in the LBRY c [Skip to **Community Happenings**](#com-updates) # Development Updates {#dev-updates} -All of our code is open source and available on [GitHub](https://github.com/lbryio). Are you a dev and want to find out more? Check out our [general contributing guide](https://lbry.io/faq/contributing) and our LBRY App specific contributing [document](https://github.com/lbryio/lbry-app/blob/master/CONTRIBUTING.md). Please be patient with us while we improve our technical documentation. Our plan is to create a technical reference site which will be developer focused at https://lbry.tech, which is not live yet, but you can check out our progress on [GitHub](https://github.com/lbryio/lbry.tech). +All of our code is open source and available on [GitHub](https://github.com/lbryio). Are you a dev and want to find out more? Check out our [general contributing guide](https://lbry.io/faq/contributing) and our LBRY App specific contributing [document](https://github.com/lbryio/lbry-desktop/blob/master/CONTRIBUTING.md). Please be patient with us while we improve our technical documentation. Our plan is to create a technical reference site which will be developer focused at https://LBRY.tech, which is not live yet, but you can check out our progress on [GitHub](https://github.com/lbryio/lbry.tech). ### App and Protocol Summary We know it's been a long wait, but we're very happy with our latest app release. And the wait will be worth it when you experience the improved LBRY daemon - it's the first major protocol update since last November, and it's a big step in the right direction. You can read about specific enhancements in the changelogs ([0.19.0](https://github.com/lbryio/lbry/releases/tag/v0.19.0) and [0.19.1](https://github.com/lbryio/lbry/releases/tag/v0.19.1)) but in general we’ve noticed improvements in network availability and streaming speed (time from when play is clicked to when the download starts). -On top of that, it brought some much needed bug fixes, wallet encryption capabilities, ability to redownload content on updated claims and updating claims with large LBC deposits (now it uses the current bid, previously you had to have the balance in your wallet to cover it). The next steps for the protocol are to significantly improve network data announcement times, speed up startup times and rewrite of client/server side wallet functionality. +On top of that, it brought some much needed bug fixes, wallet encryption capabilities, ability to redownload content on updated claims and updating claims with large LBC deposits (now it uses the current bid, previously you had to have the balance in your wallet to cover it). The next steps for the protocol are to significantly improve network data announcement times, speed up startup times and rewrite client/server side wallet functionality. On the app side, the main feature was subscription enhancements which enabled in-app notifications (including alerts for new content on channels that users are subscribed to) and auto download for channels that users subscribe to. The update also improves search results (see section below for details), allows YouTubers to earn [Sync Rewards](https://lbry.io/youtube), saves the app state when closing to tray, and lets users export transactions to CSV. Bug fixes included correct sorting of published content, allowing lbry:// links to be opened when the app was closed on OSX and fixing them on Linux, fixing night mode start time and bringing Windows notifications back into a working state. @@ -42,21 +43,21 @@ Other enhancements include improved results with partial search terms and a bett If you run into search issues while testing the app or not happy with search results, give us some feedback on [GitHub](https://github.com/lbryio/lighthouse/issues)! ### App Redesign {#redesign-updates} -The LBRY app redesign has been merged into the master branch and is available to [run from source](https://github.com/lbryio/lbry-app) for those interested in checking out an early preview. We have identified a number of issues which block a release candidate from being built yet and they are marked with a [redesign label](https://github.com/lbryio/lbry-app/issues?q=is%3Aissue+is%3Aopen+label%3Aredesign) on the issues page. Before submitting any new design related issues, be sure to review those first. We are extremely excited and eager to get the redesign into the hands of community members, stay tuned! +The LBRY app redesign has been merged into the master branch and is available to [run from source](https://github.com/lbryio/lbry-desktop) for those interested in checking out an early preview. We have identified a number of issues which block a release candidate from being built yet and they are marked with a [redesign label](https://github.com/lbryio/lbry-desktop/issues?q=is%3Aissue+is%3Aopen+label%3Aredesign) on the issues page. Before submitting any new design related issues, be sure to review those first. We are extremely excited and eager to get the redesign into the hands of community members, stay tuned! ![App-preview](https://spee.ch/c/app-preview.jpeg) ### Subscription Next Steps -Channel subscriptions give users a reason to return to the LBRY app on a daily or weekly basis, so we will continue to focus development effort in this area. The next steps include global and per subscribed content auto download settings, notification badges, saving subscription data to LBRY’s API server for syncing across installations/devices and providing emails when new content is available. We also plan to experiment with several different rewards structure to incentivize use of subscription features. +Channel subscriptions give users a reason to return to the LBRY app on a daily or weekly basis, so we will continue to focus development effort in this area. The next steps include global and per subscription content auto download settings, notification badges, saving subscription data to LBRY’s API server for syncing across installations/devices and providing emails when new content is available. We also plan to experiment with several different rewards structure to incentivize use of subscription features. ### Cool App Features in the Pipeline {#features-update} Check out the below pull requests to get a sense of a few exciting features coming to the LBRY app. -[Spee.ch URL Links](https://github.com/lbryio/lbry-app/pull/1222) - This will allow creators and consumers to quickly share a https://spee.ch URL for content or channels they find in the LBRY app. This will be available for free content that’s an image/video/GIF and for all channels. +[Spee.ch URL Links](https://github.com/lbryio/lbry-desktop/pull/1222) - This will allow creators and consumers to quickly share a https://spee.ch URL for content or channels they find in the LBRY app. This will be available for free content that’s an image/video/GIF and for all channels. -[Ability to update thumbnail to spee.ch from Publish screen](https://github.com/lbryio/lbry-app/pull/1248) - A common frustration among publishers is having to upload their thumbnails on spee.ch separately or using another website to do so. With this enhancement, the LBRY app will allow direct thumbnail uploading to spee.ch which will significantly increase the publisher experience. +[Ability to update thumbnail to spee.ch from Publish screen](https://github.com/lbryio/lbry-desktop/pull/1248) - A common frustration among publishers is having to upload their thumbnails on spee.ch separately or using another website to do so. With this enhancement, the LBRY app will allow direct thumbnail uploading to spee.ch which will significantly increase the publisher experience. -[Auto Updating channels on the LBRY Discover page](https://github.com/lbryio/lbry-app/pull/1267) - Currently the LBRY discover page is fairly static besides the trending and community sections (or when we update the page with featured content). This change will allow us to specify a channel name instead of specific claims to include on the Discover page. The main benefit is that the Discover page will dynamically update when new content is posted to those channels! +[Auto Updating channels on the LBRY Discover page](https://github.com/lbryio/lbry-desktop/pull/1267) - Currently the LBRY discover page is fairly static besides the trending and community sections (or when we update the page with featured content). This change will allow us to specify a channel name instead of specific claims to include on the Discover page. The main benefit is that the Discover page will dynamically update when new content is posted to those channels! ### spee.ch Update Over the past few weeks, we’ve been converting the code base of spee.ch so that it can be imported and customized in a variety of applications. Keep an eye out for an announcement soon which will fully explain how you can use the spee.ch code base to implement your own content hosting web app that uses the LBRY network! Want to get a headstart? Check out the [spee.ch server section below](#speech-host) and subscribe to our [speech admin mailing list](https://lbry.io/speech-admin) to be notified when we schedule a live Google Hangouts demo which will walk through the process of setting up your own portal. @@ -64,9 +65,9 @@ Over the past few weeks, we’ve been converting the code base of spee.ch so tha In terms of spee.ch site performance, the publishing function has been running without a hitch over the last 2 weeks after the team implemented a few patches to deal with wallet issues. Currently, publishes take about 15-20 seconds but we are hoping to get that number close to 5 seconds with additional refactoring and enhancements to the publishing process. ### Protocol - Wallet Server and Client Development Efforts -We’ve found that elements of the LBRY wallet (LBRYum) and LBRY wallet server (LBRYum-server) are causing bottlenecks with claim resolution and publishing - two functions that are widely used throughout the LBRY app experience. Claim resolution occurs between the client and server to make sure that claim data is read correctly from the blockchain. Publishing is the process by which a claim id is generated and broadcast to the network - something the spee.ch server has been struggling with since there is lots of publishing activity. +We’ve found that elements of the LBRY wallet (LBRYum) and LBRY wallet server (LBRYum-server) are causing bottlenecks with claim resolution and publishing - two functions that are widely used throughout the LBRY app experience. Claim resolution occurs between the client and server to make sure that claim data is read correctly from the blockchain. Publishing is the process by which a claim ID is generated and broadcast to the network - something the spee.ch server has been struggling with since there is lots of publishing activity. -In order to fix the client issue, the lbryum framework is being re-written into the LBRY daemon with performance and stability in mind. On the server side, we are migrating away from the Electrum code base and into [ElectrumX](https://github.com/kyuupichan/electrumx) which will bring numerous performance and stability enhancements. There is significant development and testing to be done in both areas and we believe this will result in a much better App and protocol experience. +In order to fix the client issue, the LBRYum framework is being re-written into the LBRY daemon with performance and stability in mind. On the server side, we are migrating away from the Electrum code base and into [ElectrumX](https://github.com/kyuupichan/electrumx) which will bring numerous performance and stability enhancements. There is significant development and testing to be done in both areas and we believe this will result in a much better App and protocol experience. ### Blockchain - Preparing for a Hardfork There are two major changes/fixes to the LBRY blockchain in the pipeline that will require a hard fork of the network. Neither of these changes are controversial - both will improve the functionality of the blockchain. Our goal is to announce these at the end of April and allow for another month or two before the hardfork code will activate so that exchanges, miners and other services have time to update. @@ -83,35 +84,35 @@ The Q1 coming to a close, we wanted to provide an update on our [roadmap objecti The [YouTube Sync partner program is live and getting feedback from testers, see additional information below](#youtube-updates). The Meetup program has also been launched on our [Meet](https://lbry.io/meet) and [College](https://lbry.io/college) landing pages, see [below for more information](#meetup-update). -Significant progress has been made on the white paper and a first draft is very close to being shared publicly. It will be released as a navigational document, in an in-progress state, on our new lbry.tech (not live yet) developer resource site and we’ll be asking the community for their feedback. +Significant progress has been made on the white paper and a first draft is very close to being shared publicly. It will be released as a navigational document, in an in-progress state, on our new LBRY.tech (not live yet) developer resource site and we’ll be asking the community for their feedback. Wallet encryption has been implemented on the daemon but is not yet available in the LBRY app. If you want to use this feature ASAP, check out the [CLI](https://lbryio.github.io/lbry/cli/#wallet_encrypt) documentation - the wallet will need to be [unlocked](https://lbryio.github.io/lbry/cli/#wallet_unlock) manually before app startup. Feel free to reach out on Discord with any questions! -The UI redesign was recently merged on our lbry-app repo and we should have a release candidate to share with early testers this/next week. See [App Redesign development update above](#redesign-updates). +The UI redesign was recently merged on our lbry-desktop repo and we should have a release candidate to share with early testers this/next week. See [App Redesign development update above](#redesign-updates). And last but not least, the Search algorithm has undergone a number of optimizations (see [search update above](#search-updates)) which have added up to much better search results - give it a shot and let us know what you think! There's still a lot more work to do here when categories and tags come online, and we need your feedback! -A quick check-in on Q2 items shows that we are ahead of schedule with Android alpha being launched and progress being made on lbry.tech . Also, the ability to share free content on HTML websites via the new spee.ch sharing feature is mentioned in the [features update above](#features-update). +A quick check-in on Q2 items shows that we are ahead of schedule with Android alpha being launched and progress being made on LBRY.tech . Also, the ability to share free content on HTML websites via the new spee.ch sharing feature is mentioned in the [features update above](#features-update). ### LBRY Merchandise Store -Come visit our newly upgraded merchandise store! Following the launch in February, we’ve moved our swag shop to a new, easy to remember URL: [https://shop.lbry.io](https://shop.lbry.io). New t-shirt designs and products are frequently added, so please be sure to stop by! We’re very excited to offer some fresh items designed by our new marketing intern James Biller - if you like what you see, give him a shout out on [Twitter](https://twitter.com/BillerJames)! +Come visit our newly upgraded merchandise store! Following the launch in February, we’ve moved our swag shop to a new, easy to remember URL: [https://shop.LBRY.io](https://shop.lbry.io). New t-shirt designs and products are frequently added, so please be sure to stop by! We’re very excited to offer some fresh items designed by our new marketing intern James Biller - if you like what you see, give him a shout out on [Twitter](https://twitter.com/BillerJames)! -Help us grow our community and get the word out about LBRY: We will tip LBC if you tag us (@LBRYio) on Twitter or Facebook with a picture of you rocking LBRY swag. Please upload to spee.ch and share the post on Discord or Reddit to claim your reward. Thanks for your support in spreading LBRY love! +Help us grow our community and get the word out about LBRY: We will tip LBC if you tag us (@LBRYio) on Twitter or Facebook with a picture of you rocking LBRY swag. Please upload it to spee.ch and share the post on Discord or Reddit to claim your reward. Thanks for your support in spreading LBRY love! -### Youtube Sync Update {#youtube-updates} +### YouTube Sync Update {#youtube-updates} We've re-launched our [Youtube Sync](https://lbry.io/youtube) campaign under a new design, process and Rewards system that allows YouTubers, who meet our subscriber requirements, to sync their content to LBRY within a few clicks. Once the creator is queued, they can claim their sync Reward in the LBRY app by signing in with the same email as their sync status page. The program launched as a pilot with our internal Discord community/targeted YouTubers and will continue expanding to a full fledged outreach on Social media and other direct targeting. The LBC rewards mentioned below are a first trial run and may increase depending on feedback. -The most common issue creators run into, especially those who have used the LBRY app prior to going through the sync process, is that their YouTube channel email is mismatched with the LBRY app email. This will require a manual fix my emailing [our Helpdesk](mailto:help@lbry.io) at the moment, but we hope to straighten this out automatically in the coming weeks. Having trouble or want to find out more? First make sure to check out our [FAQ page](https://lbry.io/faq/youtube) or you can email [Reilly](mailto:reilly@lbry.io) with any specific sync questions that aren’t covered. +The most common issue creators run into, especially those who have used the LBRY app prior to going through the sync process, is that their YouTube channel email is mismatched with the LBRY app email. This will require a manual fix by emailing [our Helpdesk](mailto:help@lbry.io) at the moment, but we hope to straighten this out automatically in the coming weeks. Having trouble or want to find out more? First make sure to check out our [FAQ page](https://lbry.io/faq/youtube) or you can email [Reilly](mailto:reilly@lbry.io) with any specific sync questions that aren’t covered. -![Youtube Rewards](https://spee.ch/b/youtube-program.png) +![YouTube Rewards](https://spee.ch/b/youtube-program.png) ### Twitter LBC Tip Bot The Twitter TipBot bounty was solved internally by the team after open source code was discovered that provided 90% of the functionality we were looking for. With a few modifications, we set up a test server and were able to send tips! The TipBot is now being migrated to an official LBRY server which will allow it to be used all across Twitter world to tip LBC - users, creators, fans, and even projects with similar missions! Stay tuned on our Discord server and [@LBC_TipBot](https://twitter.com/@LBC_TipBot) for updates. -![Tipbot](https://spee.ch/d/lbrytipbot.png) +![TipBot](https://spee.ch/d/lbrytipbot.png) ### LBRY.tech Update -Initial legwork has begun on our developer/tech resource, lbry.tech. We don’t currently have an ETA on the launch, but it will be released as an early alpha version that includes an alpha draft of the LBRY white paper, all of which will be polished over time as we get feedback from the community. We weren’t joking about “release early, release often”! You can follow our progress on the [lbry.tech GitHub repository](https://github.com/lbryio/lbry.tech). +Initial legwork has begun on our developer/tech resource, LBRY.tech. We don’t currently have an ETA on the launch, but it will be released as an early alpha version that includes an alpha draft of the LBRY white paper, all of which will be polished over time as we get feedback from the community. We weren’t joking about “release early, release often”! You can follow our progress on the [LBRY.tech GitHub repository](https://github.com/lbryio/lbry.tech). ### Discord Role Self Assignment and New Member Flow In order to improve the experience for new and existing members of our Discord community, we will be implementing a better system that will allow users to self assign roles based on their activity, purpose and interests. It will also allow more granular controls over which roles have access to particular channels/areas, which will also hopefully improve the overall user experience. diff --git a/content/news/166-imineblocks.md b/content/news/166-imineblocks.md index 4b1bbeac..bcc304b5 100644 --- a/content/news/166-imineblocks.md +++ b/content/news/166-imineblocks.md @@ -4,7 +4,7 @@ title: 'Hey, mine your own business!' date: '2018-04-05 00:20:00' cover: 'imineblocks-banner.jpg' --- -What was once the subject of niche crypto-anarchist communities vis-a-vis Bitcoin has now grown into a worldwide virtual homesteading: mining. +What was once the subject of niche crypto-anarchist communities vis-à-vis Bitcoin has now grown into a worldwide virtual homesteading: mining. If you find a nascent project you like in crypto, chances are you can mine it. But what is the first step to mining? @@ -14,4 +14,4 @@ Learning the ins and outs of the PoW algorithms for your favorite coin is essent Subscribe to @imineblocks in the LBRY App or follow him [via Spee.ch](https://spee.ch/@imineblocks). -**[Download LBRY today](https://lbry.io/get)**. Is your favorite channel not on LBRY? Help us feature what you want to see! Email reilly@lbry.io to make it happen. Or sync your own channel at: https://lbry.io/youtube +**[Download LBRY today](https://lbry.io/get)**. Is your favorite channel not on LBRY? Help us feature what you want to see! Email [reilly@lbry.io](mailto:reilly@lbry.io) to make it happen. Or sync your own channel at: https://lbry.io/youtube diff --git a/content/news/167-lbry-development-community-update-Apr-2018.md b/content/news/167-lbry-development-community-update-Apr-2018.md new file mode 100644 index 00000000..3740cb6e --- /dev/null +++ b/content/news/167-lbry-development-community-update-Apr-2018.md @@ -0,0 +1,173 @@ +--- +author: samuel-lbryian +title: 'Development and Community Update April 2018' +date: '2018-05-04 13:45:00' +cover: 'coder-tank.jpg' +category: community-update +--- +Welcome to our LBRY Development and Community update! In this post we’ll show you what we’ve been up to and review our progress for the month of April. It was a busy month, starting off with the company All-Hands summit, and progress was made on our desktop and Android apps, LBRY Protocol improvements, several community updates, and team growth. To read all of our updates, please visit [our Development and Community Update archive](https://lbry.io/news/category/community-update). + +If you haven’t already, please take a moment to read our [Looking Back and Moving Forward: LBRY in 2017/2018](https://lbry.io/news/lbry-in-2017-2018) blog post and check out our [roadmap](https://lbry.io/roadmap). + +To skip the tech stuff, see what’s happened and what’s next in the LBRY community, click the link below. Otherwise, read on! + +[Skip to **Community Happenings**](#com-updates) + +# Development Updates {#dev-updates} +All of our code is open source and available on [GitHub](https://github.com/lbryio). Are you a dev and want to find out more? Check out our [general contributing guide](https://lbry.io/faq/contributing) and our LBRY App specific contributing [document](https://github.com/lbryio/lbry-desktop/blob/master/CONTRIBUTING.md). Please be patient with us while we improve our technical documentation. In the next month, we’ll be releasing a technical reference site which will be developer focused at [https://LBRY.tech](#lbry-tech). + +### App and Protocol Summary +Due to travel time for and participation at [LBRY’s All Hands](#all-hands) gathering, development activities were on the lighter side this past month. The app team released version [0.21.3](https://github.com/lbryio/lbry-desktop/releases/tag/v0.21.3) which included a small patch to block content deemed infringing according to DMCA laws (see next section for more information). Otherwise, the main focus on the app side has been on testing and bug squashing on the [redesign](#redesign-updates). Part of this effort was spent on separating out the [lbry-redux](https://github.com/lbryio/lbry-redux) code into its own repository so that the the mobile and desktop apps can re-use redux states and functionality. We ran into a few quirks during this process but we were able to get things running smoothly! + +On the Protocol side of the house, [version 0.20](https://github.com/lbryio/lbry/releases/tag/v0.20.0rc9) is undergoing refactoring and improvements to the way blob announcement is handled and other DHT enhancements around peer availability. We were able to bring the blob announce times from 1 or 2 per second, to 25 per second (our target is 100 per second!). With these changes, our team hopes to improve the consistency of content availability on the network. A fair amount of time was also spent identifying and fixing our reflector servers to ensure that content uploaded by users is passed correctly to our content nodes. Before these changes, certain content would not be reflected properly which resulted in unavailable content if the original uploader was not online. Finally, development efforts on both the client and server side of our [wallet functionality](#wallet) have been progressing nicely. + +### Content Blocking and DMCA +In early April, LBRY Inc. received its first set of legitimate DMCA take down requests for a fair amount of infringing content LBRY URLs. This can be seen as a positive development because it means LBRY is on the map as a legitimate content sharing platform! Due to the decentralized nature of LBRY, we cannot remove content metadata from the blockchain, but as a company creating services which use the data, we must follow local laws. This means we have to block access to infringing URLs and make sure the content itself is deleted from our hosts (even though it could still be hosted by the original uploader and by others who have downloaded it). The latest version of the app provided the ability to block infringing content. + +Our [Content](https://lbry.io/faq/content) and [DMCA](https://lbry.io/faq/dmca) FAQ pages cover what content is allowed to be uploaded to LBRY, how we handle this content and reporting/DMCA procedures. + +![DMCA](https://spee.ch/3/dmca-censored.jpeg) + +### LBRY for Android Update +We're getting closer to a feature-complete mobile app with search functionality recently added to the current alpha and wallet features in the pipeline. Once full wallet functionality is released with the next alpha, we will be transitioning to the Open Beta phase via Google Play Store. Open beta will allow us to release the Android app to a wider audience and get better feedback from testers. Rewards will be added in open beta and we may actually add some mobile-only rewards! + +Want to participate in LBRY for Android alpha testing? [Sign up here](https://lbry.io/android-alpha) and you’ll be added to the waitlist! Not interested in alpha yet but want to know when we are ready for the next phase, join our regular [Android mailing list](https://lbry.io/android) instead. You can see the current status of testing and issues on our [Android GitHub repository](https://github.com/lbryio/lbry-android/issues). + +![wallet-preview](https://spee.ch/9/wallet-android-preview.jpeg) + + ### spee.ch Update +The majority of our effort on the spee.ch codebase has been to convert the [spee.ch repository](https://github.com/lbryio/spee.ch) to hold the main server code, and create the [www.spee.ch repository](https://github.com/lbryio/www.spee.ch) which will be the main entry point for spee.ch implementations that can be cloned to [create your own version of spee.ch](https://lbry.io/speech-admin). We are planning to hold a kick off event which will explain and walk through how you can setup and build your own clone. Are you interested in this feature? Learn more by checking out the [spee.ch community section](#speech-host). + +### App Redesign {#redesign-updates} +Since our last update, we’ve squashed a number of critical bugs and the LBRY app redesign has moved into a release candidate phase where we’ve invited testers from our Discord community to perform more rigorous testing and gather additional feedback. Want to help out? Join us on [Discord](https://chat.lbry.io) and ask about becoming an early tester. In addition to previewing the redesign before its officially released, you can earn LBRY Credits by finding bugs or providing suggestions. + +We’ve identified a number of issues which are marked with the [redesign label](https://github.com/lbryio/lbry-desktop/issues?q=is%3Aissue+is%3Aopen+label%3Aredesign) on GitHub. Before submitting any new design related issues, be sure to review those first. We’ve also identified show stoppers, which prevent a public release, with the [release-blocker tag](https://github.com/lbryio/lbry-desktop/issues?q=is%3Aissue+is%3Aopen+label%3Arelease-blocker) so you can have a better idea when it’s ready for primetime! We believe that a full release is near once these bugs are squashed, but you never know what new issues could creep up. + +![App-preview](https://spee.ch/8/redesign-dark.jpeg) + +### Subscription Page Improvements +Currently, the subscriptions page is organized similar to the explore page where each channel has its own single row of 10 latest items. It becomes difficult to monitor your subscriptions for updated content in this fashion, so we will be changing the the page to look and behave similar to the Downloads/Published pages where channels are no longer grouped together, but instead, the latest content updates will be displayed at the top of the page. We will still show the 10 latest items per subscribed channel but they will be sorted by publish date across all subscriptions. + +We’re also working on syncing subscription data to the LBRY API server so that subscriptions are no longer lost if the LBRY cache is cleared or if you move to a new device (and we can sync to mobile too!). This will also allow us to recommend similar content / channels in the future. + +![new sub page](https://spee.ch/7/subs-new.png) + +### Upcoming Feature: Hyperlinks to LBRY In-App Areas +One of our new team members had a fantastic suggestion that would re-engage our users by providing hyperlinks to various areas in the LBRY app. Currently, we use the hyperlink functionality to provide users direct links to LBRY content and channel URLs (they open the app if you have it installed too!) via https://open.lbry.io but this new feature will allow us to hyperlink to areas such as Rewards, Publish, Wallet and Settings. One use case would be to send out an email encouraging publishing and provide the users a clickable URL that opens the app and brings them to the Publish feature. + +### Protocol - Wallet Server and Client Development Updates {#wallet} +Large chunks of progress have been made on both the [wallet server](https://github.com/lbryio/lbryum-server/issues/54) and [client](https://github.com/lbryio/lbry/issues/1155) redesign/reimplementation. Once the server side is implemented, we should see significant performance gains that will translate directly to an improved app experience as claim information will load much faster, especially for new users on a first run. + +During the process of developing the ElectrumX server to support LBRY claims, we were able to identify several improvements required on the full blockchain or LBRYcrd codebase. Initial testing is underway as we currently have a test server running these changes along with the enhanced ElectrumX code that accepts wallet connections from the current LBRY wallet (and it will also support the new client when it’s ready). + +The client side, which is a Twisted based implementation of the wallet, is still in development but is able to create and receive basic transactions. The next phase is to support claim related transactions. The wallet was architected in such a way (see below) that it will be able to support multiple LBC wallets and even Bitcoin wallets if and when we’d decide to support bitcoin payments in the app. + +![wallet diagram](https://spee.ch/8/wallet-diagram.jpeg) + +### Chainquery SQL Databasing on the Blockchain +This is the first time we are mentioning the [Chainquery project for LBRY](https://github.com/lbryio/chainquery) in our communications as it’s mostly flown under the radar because of its early development status. Chainquery will provide a SQLized view of the LBRY blockchain which can be used across many projects where we currently need such data such as Lighthouse (search server), API server, LBRY app and spee.ch. Currently, these projects either run their own implementation of blockchain views or don’t take advantage of having the information readily available through a simple query, but rather have to rely on direct communication with the blockchain. Chainquery will slowly be integrated into these services to provide quick and easy access to claim/channel information, payment data and transactions. + +### Blockchain - Hardfork Update +In our previous [hardfork update](https://lbry.io/news/lbry-development-community-update-mar-2018), we mentioned two upgrades to the blockchain - removing claim expiration and removing case sensitivity. After further discussion with the team, we have decided to perform these upgrades one by one in order to simplify the number of code changes which should result in a smoother transition. Since claim expiration is the more critical of the two, it will go first. We will be activating these changes and simulating the hard fork on our testnet in the coming days, and if all goes to plan, we will announce the date of the main net hardfork. + +Based on feedback received when we first mentioned a hard fork, we want to stress that forks are standard means of upgrading blockchain functionality and should not be compared to other contentious hard forks like Bitcoin or Ethereum. Other blockchain projects go through this same process and LBRY will continue to evolve the blockchain code base through such upgrades. + +# Community Happenings {#com-updates} +If you aren’t part of our Discord community yet, [join us](https://chat.lbry.io) anytime and say hello! Our community allows LBRYians to interact with the team directly and for us to engage users in order to grow the LBRY platform. + +### Upcoming Events +If you’re in the DC area, make sure to get registered for the [Startup Society Summit](https://www.startupsocieties.com/startup-societies-summit-gmu-2018/) at George Mason University next week (May 9th and 10th). Natalie Mitchell, LBRY’s Director of Talent and Branding, will be speaking on how free market startup cities and blockchain technology can help rebuild Puerto Rico in the aftermath of Hurricane Maria. Stop by and say hello! + +### LBRY is hiring! New Jobs Posted +The team saw amazing growth in March as 21 team members came together for our second All Hands meetup at LBRY’s home base in Manchester, NH. We’re delighted to announce we’re seeking [even more members to join our crew,](https://lbry.io/join-us) so if you’ve dreamed of working at the front of bleeding edge technology, we have these opportunities available: + +Blockchain Engineer +Lead Application Engineer +Project Manager +Protocol Engineer +API Engineer +Lead Designer (UI/UX, Graphics) + +And if you don’t see an opportunity that’s an exact fit, there are many other ways to get involved in the project and earn LBC or cash. We’re also offering a $5k finder’s fee for successful hires for our job listings. [Click for more info](https://lbry.io/join-us). + +### LBRY All-Hands Recap {#all-hands} +The LBRY Spring 2018 All-Hands Summit was a great success. Our team of 21 came from around the world to Manchester, NH, for a week of food, bonding, break-out sessions, and jumping competitions. [Our CTO won.](https://spee.ch/4/natalie-grin-jumping.jpeg) + +A great deal of the time we spent together was focused on cross-team operations and communications strategies. We’re growing fast, and our processes and systems need to grow at pace. We have already begun to implement a number of the takeaways from these meetings and believe we will gain a lot from more efficient and clear communication across teams. + +This year’s all-hands schedule was full, and we accomplished just about everything we’d hoped to while together --but the most important achievement was getting to know our newer team members and connecting in-person with people we work with daily, but mostly see on screens. + +![LBRY Team Growth](https://spee.ch/c/LBRYteam.jpeg) + + +### Roadmap Check-in +We’ve recently made a few updates to our [Roadmap](https://lbry.io/roadmap) which include a status indicator (Complete/In Progress/Planned), new items and updated timelines to reflect current priorities and goals. New items include `YouTube Sync Automation`, `Speech Multisite` and `Wallet Server/Client Improvements` - all of which are mentioned in this update. We’ve had to re-assess our priorities and updated future events on the roadmap respectively. This is and will continue to be an evolving roadmap so schedule/goal adjustments are to be expected. + +![roadmap](https://spee.ch/f/roadmap-update.jpeg) + +### Q1 Credit Report +Due to funding needs being met, no credits were moved from cold storage during the first quarter. The majority of credit outlays were for technical contributions and community - both things we love to reward! As usual, you can see both the [summary report](https://lbry.io/credit-reports/2018-q1) and [detailed spreadsheet](https://docs.google.com/spreadsheets/d/17GIrc-cmoXEY9x3NYhtxd45g2mHnuJ7cjUy_TfmhLMA/edit?usp=sharing) on our website. + +### LBRY Merchandise Store +We’ve added some brand new designs to our [swag store!](https://shop.lbry.io). New t-shirt designs and products are frequently added, so please be sure to stop by! We’re very excited to offer some fresh items designed by our new marketing intern James Biller - if you like what you see, give him a shout out on [Twitter](https://twitter.com/BillerJames)! + +Earn LBC! Help us grow our community and get the word out about LBRY: We will tip LBC if you tag us (@LBRYio) on Twitter or Facebook with a picture of you rocking LBRY swag. Please upload it to spee.ch and share the post on Discord or Reddit to claim your reward. Thanks for your support in spreading LBRY love! + +![Give me LBRY](https://spee.ch/0/givemeliberty.png) + +### YouTube Sync Updates {#youtube-updates} +Since the launch of our revamped YouTube Sync program in March, we are coming close to 10,000 total syncs and YouTubers have claimed a total of 40,000 LBRY Credits. + +We are also refining our YouTube Sync process so that we can automate the syncing of new channels as well as updates to existing channels. This will allow for much more content to be uploaded to LBRY by running our sync process continuously and shorten the time between agreement to sync and having YouTubers’ content available on LBRY. + +![YouTube Rewards](https://spee.ch/0/youtube-update.jpeg) + +### Twitter LBC Tip Bot +The [LBRY Tipbot for Twitter](https://twitter.com/@LBC_TipBot) was tested internally by the team along with the help of community members, and is now ready for mainstream action! You can read our [Tipbot FAQ](https://lbry.io/faq/tipbot-twitter) for more information on how to use the tipbot. Hint: use the `lbryian` keyword and make sure to keep your entire command on a single line. + +![Tipbot](https://spee.ch/b/tipbot-gerbil.jpeg) + +### LBRY.tech Update {#lbry-tech} +We’ve recently hired additional help to shape our LBRY.tech technical resource and its design and development is progressing quickly. Developers will be able to interact with a live LBRY daemon directly from the website and access various technical content like documentation on how to contribute, how to develop and other technical resources. Follow along the development on our GitHub repository and check out an early preview below (disregard the poor design, it’s getting a facelift!). Our goal is to launch an early version of this site by the end of May, which will include the long awaited LBRY whitepaper in draft form. + +![lbry.tech preview](https://spee.ch/d/lbrytech-early-design.jpeg) + +### Discord Role Self Assignment and New Member Flow +With our Discord community growing and in order to improve the experience for new members, we have implemented a system allowing users to self-assign roles based on their activity, purpose, and interests. It also allows more granular controls over which roles have access to particular channels/areas. + +When you join our Discord, you will first see the #welcome-to-lbry channel and have access to the #general, #help, and #verification areas. If you wish to join other channels that relate to international channels, mining, or trading channels, you will need to use the #role-assign channel to give yourself access to the roles you’re interested in. A full description of the roles along with general info about the LBRY Discord server may be found in channel #welcome-to-lbry. + +![discord](https://spee.ch/9/welcome-to-lbry.jpeg) + +### Meetups and College Campus Initiatives - Get Involved! {#meetup-update} +![Hack the Heights](https://spee.ch/6/hackheights.png) + +Natalie Mitchell was delighted to attend Boston College’s second *Hack the Heights* hackathon event on behalf of LBRY. Students from all Boston area Colleges came together to rapidly “hack” together creative projects and compete for prizes. + +LBRY is proud to be working with local colleges to encourage students to become involved in blockchain projects: through computer programming, business of blockchain, and creative design. Babson College is using LBRY as a case study in their Blockchain and Cryptocurrency class, and LBRY actively provides internships for local college students. + +On the Meetup side of things, we’ve been hard at work creating a community hub for meetup resources, to be rolled out in May. The new hub will include slideshows and other resources to present to your group, a forum to talk to other Ambassadors and ask questions, and interact with LBRY staff. + +Does free speech matter to you? Would you like to get involved with cutting edge blockchain technology and earn a few cryptocoins in the process? We’re [looking for ambassadors](https://lbry.io/meet) to spread the word on college campuses and at live blockchain or video related meetups - if you’d like to bring LBRY to your group, we’ll help you with resources, swag, and LBC for your members! + +### LBRY-C Collaboration +The independent LBRY-C community group has begun work on a website to use the LBRY protocol called *Liberum.* + +*Liberum* is a message board that prides itself on anonymity, privacy and free speech, built on top of the LBRY protocol. It is created for those users wanting to engage in and discuss their passions in an open space without fear of unjust censorship. + +It's here for those that want civil discourse as well as to discuss interests amongst their fellows. The project has just begun but for those who are interested in helping grow the project, you are welcome to join the team on their [Discord Server](https://discord.gg/ytY4NeJ). + +### Want to Run a Spee.ch Clone or Web Server Hosting LBRY content? {#speech-host} +LBRY is looking for users or communities that are interested in hosting a spee.ch like website or even a web portal into their own LBRY content (think of this as your own local YouTube page!). We would be more than happy to walk you through the process which would include customization and tweaking it to your liking! Does this sound interesting to you or maybe someone you know? Sign up on or share our [speech admin mailing list](https://lbry.io/speech-admin) to be notified when LBRY will host a live session. The purpose of this will be to learn how to run your own spee.ch install, how spee.ch works, and have live help and discussion. + +We are also looking for communities, subreddits and websites that could make use of spee.ch’s image sharing features. This includes researching plugins to applications like Wordpress that would make such an integration easier for users. If you know any that might benefit, drop us a line on Discord or have them reach out to our speech developer, [Bill](mailto:bill@lbry.io). + +![speech](https://spee.ch/0/spech-adminnew.jpeg) + +### 3D Printing Community on LBRY +We are in the process of creating a landing page on LBRY.io specifically to attract users and creators of 3D printing models. We believe that LBRY is a perfect fit for creators as our platform will allow them to charge for their models or receive micropayments in terms of tips (Thingiverse allows payments, but there are large minimums). + + +[Back to **Development Updates**](#dev-updates) + +Thanks for supporting LBRY - stay tuned for more news and updates! And if you haven’t downloaded the [LBRY app](https://lbry.io/get?auto=1) yet, what are you waiting for? diff --git a/content/news/168-we-are-hiring-our-boss.md b/content/news/168-we-are-hiring-our-boss.md new file mode 100644 index 00000000..752e8998 --- /dev/null +++ b/content/news/168-we-are-hiring-our-boss.md @@ -0,0 +1,48 @@ +--- +author: samuel-lbryian +title: 'We\'re Hiring Our Boss' +date: '2018-05-11 10:00:00' +cover: 'hirebossheader.jpg' +--- +# We're Hiring our Boss +Hello world, we are three self-taught programmers who are hiring our boss here at LBRY. + +We aren’t writers, but we are three developers who are able to get things done (like writing this post). The purpose of this blog post is to tell you who we are, what we are looking for, and why it would be awesome to be our boss. + +If you’re just hearing about LBRY for the first time, it is an open-source, blockchain-based, decentralized digital media protocol. We're the team that builds applications that use the protocol. That is, we build the ways most people interact with and use LBRY. + +## Who we are +

+
Sean Yesmunt

I'm Sean, I joined LBRY in November to work on the LBRY desktop app after making a few small contributions on my own. Before that I was working with React and Node on Walmart.com for 1.5 years after teaching myself to code. I am very interested in user experience, and creating simple apps that are easy to use.

+
Bill Bittner

I’m Bill. I found out about LBRY while exploring blockchain applications and was quickly hooked by the vibrant dev community that had already sprung up around their alpha build. I love building web apps and exploring new technologies, so it wasn’t long before I jumped in and started to build on top of the LBRY network.

+
Akinwale Ariwodola

I’m Akinwale. I discovered LBRY when my interest in cryptocurrencies got rekindled in early 2017 and I was looking for a coin to mine. I found the project really exciting which led to my decision to start contributing to the desktop app. One thing led to another, and I eventually started working on getting the Android version up and running.

+
+## What we're looking for +As self-taught developers, we are able to complete and implement features, but we would really like mentorship in the planning and decision making before we start coding. In other words, we are confident in our cutting skills, and are looking for a seasoned veteran who can help us measure twice. Someone who can tell us, "These are the best practices and this is the reason why." +If you’re wondering what we’re wondering about, these are some of our recent discussion topics: + +* App architecture (How data flows) +* React performance +* React Native performance +* Electron performance +* How to model the data. What should the application state look like? +* Efficient styling rules and patterns +* Knowledge about JavaScript packaging +* Testing (What is worth testing) +* Authentication +* Devops (CI/CD) +* Load handling and scalability + +## Why being our boss would be awesome +We are working on the intersection of blockchain and web technologies, and that’s a pretty cool thing. Being on the forefront of this space is ripe with new challenges, but working on problems that not many people have worked on before is what gets us excited to continue to learn and build. + +I (Sean) work on the LBRY Electron app, which is built for macOS, linux, and windows. It starts up the LBRY daemon and interacts with it locally, as well as an API server that handles authentication and rewards. This app is a showcase for what can be done on the LBRY blockchain and we will continue to build features as the the functionality becomes available. We get a lot of community contributions which is also very cool. + +Bill (me) works on the spee.ch web app, which is a portal for users to upload and retrieve content from the LBRY network from the browser. Initially conceived as a simple imgur alternative on the blockchain, the project has grown immensely both in how, and how much, it is used. As the user base has grown from a handful of visits to hundreds of thousands of requests per day, we have embarked on a journey to build out a multi-site ecosystem where developers can create their own spee.ch-like websites using a common code base. + +I (Akinwale) work on the LBRY Android app, which is an interesting combination of technologies: React Native (the frontend), Python (the LBRY daemon) and Java (for access to the native platform API). It is similar in functionality to the Electron app with shared code across platforms, and we’d love to be able to present virtually the same experience to the user for both platforms. + +We love Node, React, and all web technologies and we are looking forward to working with someone who shares the same excitement! + diff --git a/content/news/169-what-is-chainquery.md b/content/news/169-what-is-chainquery.md new file mode 100644 index 00000000..66d9fbc6 --- /dev/null +++ b/content/news/169-what-is-chainquery.md @@ -0,0 +1,57 @@ +--- +author: mark-beamer +title: 'What is Chainquery' +date: '2018-05-24 14:20:00' +cover: 'database2.jpg' +--- + +### Chainquery makes LBRY blockchain data significantly more accessible and useable + +By default, data from the LBRY blockchain is basically a raw dump of data in a compressed format: + +![Raw Data](https://spee.ch/@lbry/lbrycliexample.png) +*In addition to extracting this data, you’d have to run several hundred thousand commands to get the same data returned by a single command below.* + +Since blockchain data is raw, compressed, and not accessibly structured, most blockchains have specifically designed methods to create more usable forms of the data. + +In the case of LBRY, Chainquery is both the easiest and most feature-rich method of doing that to date. + +# What does Chainquery do? + +Chainquery takes all the raw data in the blockchain, decompresses and extracts it, and stores it in a relational database, all in real-time. This allows the rich features of SQL to be applied to live blockchain data. + +This is all rather abstract, so let’s look at an example. Suppose we wanted to determine the 10 LBRY URLs with the largest number of staked credits. Without Chainquery, this would require making direct calls to the LBRY blockchain API, iterating over each claim, and storing the 10 largest. In addition to being non-trivial to write, it would also be quite slow. + +With Chainquery, it’s about a hundred characters of performant SQL: + +![Top Channels](https://spee.ch/@lbry/topchannels.png) + +Virtually any application that is interested in displaying or operating off of data stored in the LBRY blockchain can benefit from Chainquery. + +You can see the full schema for Chainquery [here](https://github.com/lbryio/chainquery/blob/master/db/chainquery_schema.sql). + +# Why is Chainquery exciting? + +Chainquery is exciting, anyone in the world interested in building on top of LBRY, as well as internal LBRY team members, can do so much, much more efficiently. + +LBRY team members will be able to save significant time when working on any features that depend at any point on blockchain data. No longer do we have to develop complex data handling or transformations for a new feature that works around the specific formats coming out of the blockchain. We can now “Chainquery it”. Indeed, Chainquery will see internal use in at least four projects: [search](https://github.com/lbryio/lighthouse), [explorer](https://github.com/lbryio/block-explorer), rewards, and [spee.ch](https://spee.ch)! + +For our community and users, this means features will come faster and work better. We will be less encumbered by the raw and compressed format of the blockchain and not re-implement similar functionality multiple times over. + +Additionally, community contributors and other technical users can use Chainquery to build something on top of LBRY. Anyone can run their own Chainquery instance and access blockchain information via SQL queries. + +# How do I use Chainquery? + +Most LBRY users do not need to use Chainquery directly and instead will simply benefit from the increased engineering capabilities and performance it allows us internally. + +However, if you’re interested in using it directly or poking around with the data, all of our code is open-source and available on GitHub. Detailed setup instructions are inside of the repository. + +Additionally, at least for now, we’ve provided a public, readonly MySQL server you can connect to. This is a generic SQL API that is accessible from an HTTP GET and returns JSON data. You can even use it in your browser! + +Get the latest block information: + +[https://chainquery.lbry.io/api/sql?query=SELECT * FROM block ORDER BY height DESC LIMIT 1](https://chainquery.lbry.io/api/sql?query=SELECT%20*%20FROM%20block%20ORDER%20BY%20height%20DESC%20LIMIT%201) + +Get all claims and their details for the name “lbry”: + +[https://chainquery.lbry.io/api/sql?query=SELECT * FROM claim WHERE name = “lbry”](https://chainquery.lbry.io/api/sql?query=SELECT%20*%20FROM%20claim%20WHERE%20name%20=%20%27lbry%27) diff --git a/content/news/170-forking-for-asic-resistance.md b/content/news/170-forking-for-asic-resistance.md new file mode 100644 index 00000000..350128f1 --- /dev/null +++ b/content/news/170-forking-for-asic-resistance.md @@ -0,0 +1,73 @@ +--- +author: kay-kurokawa +title: 'Forking for ASIC Resistance: A Monero Case Study' +date: '2018-05-31 13:45:00' +cover: 'ASICS.jpg' +--- +# Forking for ASIC Resistance: A Monero Case Study + +Designing ASIC resistant proof-of-work blockchains, and particularly hard-forking to achieve such ASIC-resistance is a contentious new issue in the cryptocurrency space. ASIC chips are custom manufactured computing devices designed specifically for a particular blockchain or hashing algorithm. As such, they are far more efficient at mining than commodity hardware such as CPUs or GPUs. + +Forking to prevent such resistance, referred to as an AAHF (Anti-ASIC Hard Fork) for the rest of this article, changes the mining algorithm on a blockchain so that ASICs tailored to the old algorithm can no longer mine effectively. AAHFs aren’t just theory. Recently Monero[ executed one](https://cointelegraph.com/news/monero-hard-fork-appears-successful-as-devs-shun-bitmains-asic-miners) and[ Zcash](https://forum.z.cash/t/let-s-talk-about-asic-mining/27353/459) is pondering whether to do the same. At [LBRY](https://lbry.io), we’ve received requests to hard fork due to the release of a[ Baikal miner](https://www.baikalminer.com/product10.php) appearing on the market (the miner is likely a FPGA machine, not an ASIC, however). + +This article is a case study on the recent Monero AAHF. The Monero hard fork that occurred on April 6th was interesting in that it: + +A) Set the Monero dev/community against the much maligned ASIC manufacturer Bitmain + +B) Resulted in the chain splintering into various alternative projects that took over the old pre-fork chain (you can read about this[ here](https://bitcoinmagazine.com/articles/monero-just-hard-forked-and-it-resulted-four-new-projects/)) + +The goals of this article is to look at verifiable data instead of speculating about the nature of the fork, and to see what kind of lessons we can learn from it. + +## Effects on Hashrate + +First, let’s look at Monero’s hash rate before and after the hard fork. In the below graph, you can see the hashrate for Monero in green. The black line is the hashrate for the the various alt-coin splinter projects that took over Monero’s old chain after the hard fork (from henceforth called Monero Original)*. + +*Note that according to GPU miners that I’ve talked to, the pre-fork and post-fork Monero POW algorithm is equivalent in computational difficulty thus the hash rate before and after the fork should be comparable. + +![Monero hash rates](https://lbry.io/img/monero_hash.png) +
+Green: Monero hash rate, Black: Monero Original hash rate + +Source:Blkdat.com +
+ +One possible interpretation of this graph is that the total Bitmain ASIC hashrate is around 500 Megahash/sec. This matches up with the amount that seemed to drop off from Monero post-fork, and also matches up with the amount that remained on the Monero Original chain post-fork. However, we can’t say this with certainty that the above interpretation is correct since it is impossible to associate hashrate to a specific type of miner. + +Regardless of what the total hashrate of Bitmain’s ASIC miners is, losing almost 50% of the hashrate post fork should be a concern for Monero. The recent 51%[ attack of Bitcoin Gold](https://www.ccn.com/bitcoin-gold-hit-by-double-spend-attack-exchanges-lose-millions/) illustrates the very real connection that exists between hash rate and security. + +## ASIC Capabilities + +The primary argument for an AAHF is that ASIC manufacturing results in more mining centralization by pushing out the commodity hardware miners. To verify this claim, we need to look at the capability of these ASIC miners and compare them to commodity hardware. Below are the respective specs for the Bitmain Monero miner and a top of the line AMD GPU miner: + +* Bitmain X3: 220 KH/s, 550 Watts, 0.4KH/s per Watt, Retail value: 1900$ + +* AMD HD 7990: 1.1 KH/s, 110 Watts, 0.01 KH/s per Watt, Retail value: 900$ + +We see that the Bitmain miner is 220 times more powerful than a single top of the line AMD GPU unit. More importantly, it is 40 times more energy efficient at mining. It is clear that commodity GPUs are outclassed by these custom miners, but it’s also important to note that there are a whole lot of GPUs out there in the world. Consider that AMD shipped 19.6 million discrete GPUs in[ 2017 alone](https://www.extremetech.com/gaming/264836-cryptocurrency-miners-bought-776m-gpus-2017-mostly-amd). AMD does not release sale numbers for specific models, but if we assume that all 19.6 million of the GPUs sold were of the cheap 400 series variety, this adds up to 7.84 GH/s (a 400 series runs about 0.4 KH/s on the Monero network). This is 15 times larger than the 0.5 GH/s that we estimated to be the Bitmain ASICs total hashrate and the current Monero hashrate. + +The point of this calculation is to show that while AMD/NVIDIA may not produce profitable miners, the total hash rate they produce is immense. ASIC producers like Bitmain may be able to obtain monopolies on profitable mining, but they have not monopolized mining. If Bitmain tries to perform a 51% attack, the Monero community will likely be able to fight it off using commodity GPUs. If we assume that 0.5 GH/s is the correct estimate for the Bitmain ASICs total hash rate, this will require 1.25 million AMD 400 series GPUs. Assuming 150 watts of power consumption per unit and 12 cents per kilowatt-hour we get the energy costs to be 22,500$ per hour. The numbers will obviously be better if we use a higher end GPU. + +## Aftermath for Users + +Software upgrades by nature are attack vectors. Some users will end up downloading a compromised version of the upgrade which may for example send all their coins to a hacker’s address. We can actually get download numbers for[ Monero 0](http://www.somsubhra.com/github-release-stats/?username=monero0&repository=monero0) and[ Monero Original](http://www.somsubhra.com/github-release-stats/?username=XmanXU&repository=monero-original). They are two projects that took over the old chain after the Monero fork and released their binaries on GitHub (GitHub tracks download numbers through their API). + +About 1000 users total have downloaded either Monero Original or Monero 0 binaries and have presumably used them. I’m not suggesting that these binaries are malware but they are unsigned binaries from anonymous developers. Needless to say, there are significant risks involved in running such software. It is worth considering whether it is worth exposing users to such attack vectors when hard forking. + +Other users may not even be aware that the Monero network has hard forked and may be transacting on the old network unaware of what is happening. It is impossible to tell whether the transactions happening on the Monero Original chain are intentional or accidental but the below graph shows that there is still a small amount of transactions occurring on the Monero Original chain (note that the Monero Original chain is traded on hitbtc.com so the transactions below could all be intentional). + +![Monero transaction rates](https://lbry.io/img/monero_trans.png) +
+Green: number of transactions on Monero, Black: number of transactions on Monero Original + +Source:Blkdat.com +
+## Conclusions + +There are several causes for concern regarding Monero’s AAHF. + +The first concern is that the AAHF may have been unnecessary in the first place because the Monero community underestimated the total amount of hash rate that can be produced through commodity hardware. If the community had concerns about Bitmain abusing their powers, they certainly could have fought back without resorting to a hard fork. + +The second concern is that the AAHF created attack vectors that could be exploited against its users. The lowered hash rate can be used to 51% attack the chain, and the software update necessary for the hard fork may have left users on the wrong chain or exposed them to malware. + +It remains to be seen how these concerns will work out for Monero in the future. So far, things has gone smoothly as the Monero price has been stable and there have been no noticeable network disruptions for the users. The market for the most part has deemed this AAHF to be a success. However, this AAHF is likely just the opening battle in a war to determine who gets to control the Monero network. Bitmain, and other ASIC manufacturers, will not be undeterred if there is money to be made. The next time this battle is fought, these concerns are going to be revisited again. + diff --git a/content/news/171-hf1807.md b/content/news/171-hf1807.md new file mode 100644 index 00000000..894f7e0e --- /dev/null +++ b/content/news/171-hf1807.md @@ -0,0 +1,35 @@ +--- +author: alex-grintsvayg +title: 'HF1807: A Hard Fork on July 9th, 2018' +date: '2018-06-05 07:00:00' +--- + +**Update 2018-07-11 9:51am EST**: The fork is live. Block 400155 occurred on July 9 at 11:40am EST. Block 401019 (the block with the first difference between forks) occurred on July 11 at 2:43am EST. The prefork chain halted at that block, while the postfork chain is proceeding normally (at block 401361 at the time of writing). + +**Update 2018-07-11 2:35pm EST**: Some users have reported that blocks are coming in slowly or not at all. If you were running an old version (< version 0.12.2.0) until block 401019 or after, and then updated, you will be stuck on the old chain. To check if you are stuck on the old chain, run the command, `getblockhash 401019` to check the blockhash on block 401019. If the output is not equal to 40c852eea4184aa5597eb075328921a3e600eb9f930561cc0c463a63276c609b you are on the wrong chain. To fix this, you will need to reindex. Run lbrycrdd with the reindex option: `./lbrycrdd -reindex` . You may also need to clear your ban list with command `clearbanned` to unban any post fork nodes. + +## What's Changing + +LBRY is announcing hard fork HF1807. This fork will extend how long claims are valid before they expire. +Currently, claims expire after about 1.5 years unless they are updated. After the fork takes effect, claims will expire after 10 years. +Existing claims that have not expired will have their expiration extended to 10 years. +Any claims that expire before the fork takes effect will stay expired. We will republish them manually to preserve access to that content. + +Only claim expiration is changing. The block rewards, token distribution, hash algorithms, etc. are all staying the same. + +The fork will take effect at block 400155, which will occur on July 9th, 2018 at around noon EST. + +## What You Should Do + +For most people, nothing needs to be done. The fork will take effect transparently and won't change your experience of LBRY. + +**If you are running a full node or mining pool, upgrade to the latest version of lbrycrd as soon as possible**. +Pre-built binaries are available on [the releases page](https://github.com/lbryio/lbrycrd/releases). You will need version 0.12.2.0 or higher. + +We will post updates about this fork to this page. If you want to be notified of news about this fork and future forks, please join the [fork mailing list](/forklist). + +## Fork Details + +The fork is explained in detail in [this pull request](https://github.com/lbryio/lbrycrd/pull/137), including the relevant code. +Please review the details and the code if you are able to. +We pay significant bounties for any bugs found in blockchain code, especially fork-related code. Email grin@lbry.io if you wish to discuss. diff --git a/content/news/172-lbry-development-community-update-May-2018.md b/content/news/172-lbry-development-community-update-May-2018.md new file mode 100644 index 00000000..e77f57f0 --- /dev/null +++ b/content/news/172-lbry-development-community-update-May-2018.md @@ -0,0 +1,145 @@ +--- +author: samuel-lbryian +title: 'Development and Community Update May 2018' +date: '2018-06-07 12:00:00' +cover: 'lbryopoly-min.jpg' +category: community-update +--- +Welcome to our LBRY Development and Community update! In this post we’ll show you what we’ve been up to and review our progress for the month of May. This month we made progress on our desktop app redesign and Android app alpha, LBRY Protocol improvements (work in progress), LBRY tech resource website, and several community initiatives. Scroll down to learn about new rewards coming to the app as well as a couple contests for you to earn up to $300 USD in LBRY credits. To read all of our updates, please visit [our Development and Community Update archive](https://lbry.io/news/category/community-update). + +If you want to see what we have planned for LBRY, check out our [roadmap](https://lbry.io/roadmap). + +To skip the tech stuff, see what’s happened and what’s next in the LBRY community, click the link below. Otherwise, read on! + +[Skip to **Community Happenings**](#com-updates) + +# Development Updates {#dev-updates} +All of our code is open source and available on [GitHub](https://github.com/lbryio). Are you a developer and want to find out more? Check out our [general contributing guide](https://lbry.io/faq/contributing) and our LBRY App specific contributing [document](https://github.com/lbryio/lbry-desktop/blob/master/CONTRIBUTING.md). Please be patient with us while we improve our technical documentation. In June, we’ll be releasing [LBRY.tech](#lbry-tech), a technical reference / guide website which will be developer and contributor focused to drive more apps and services on top of LBRY. + +### App and Protocol Summary +The app team released a few patches on the pre-redesign version of the desktop app, you can find all the release notes on our [GitHub release page](https://github.com/lbryio/lbry-desktop/releases). The fixes/features in these patches allow us to rename the lbry-desktop repo to lbry-desktop, send emails with a link to open LBRY to various pages like the Rewards section of the app, prevent errors when opening invalid URLs, give us the ability to roll out new rewards like [Welcome Back to LBRY](#wb-reward), and provide a long-awaited daemon upgrade to [0.19.3](https://github.com/lbryio/lbry/releases/tag/v0.19.3) which enables faster blockchain sync. On the app redesign side of the fence, we’ve progressed to a 2nd round of community testing and are close to a final release, [read more below](#redesign-updates). + +On the protocol side of the house, our engineers have been deep in the weeds with [version 0.20](https://github.com/lbryio/lbry/releases/tag/v0.20.0rc9) tuning our DHT implementation in order to increase consistency and availability of P2P data. We’ve discovered and fixed a number of issues which should make the overall network run smoother. Peers now know when other peers are offline and drop them from their routing tables - something that was not being done previously. We’ve also improved the process to allow peers to connect to multiple hosts when searching for data. We are also making sure that these changes are backwards compatible with nodes on older versions, and expect to put out a release in the next week or two. For wallet server and client updates, see [below](#wallet). + +### App Redesign {#redesign-updates} +The LBRY desktop app redesign is in its second round of community testing after numerous bugs were fixed and a few new features like improved [markdown support](#md), [autoplay](#autoplay) and a [view on web](#view-web) button. The left hand menu was also reorganized so that “My LBRY” only contains Downloads and Publishes, and the Settings option was moved up to first class to join Explore/Subscriptions/My LBRY/Publish/Help. + +Our goal is to release the app redesign by the end of June, barring the discovery of any other critical bugs. The main bugs left to be worked out include issues with getting the subscriptions page to load in some cases and editing of claims (properly putting them into pending status and checking for confirmation). + +![redesign](https://spee.ch/7/redesign-update-May-2018.jpeg) + +### Welcome Back Reward {#wb-reward} +If you are eligible for [LBRY Rewards](https://lbry.io/faq/rewards), you may have noticed a new reward pop up, as well as an email notification! We’ve deployed a new, time sensitive reward that must be claimed within 48 hours for existing LBRY app users. New users who sign up for LBRY will also be eligible for this reward automatically (to be turned on ASAP!) if they revisit the LBRY app a day after claiming their first reward. + +![welcome back](https://spee.ch/7/welcome-back2.jpeg) + +### Pop-out Player +A new community contributor, dan1d on Discord, has resurrected our efforts to bring a pop-out player to the LBRY app. Based on initial testing, he’s getting quite close to a finished 1.0 version of this and you can follow along with his [PR on GitHub](https://github.com/lbryio/lbry-desktop/pull/1523) to learn more. The last known bug that exists is a hiccup where the player goes back to a “waiting for metadata” state when switching from the claim page to pop out mode. If this can be fixed, we hope to include it with the app redesign release. The next steps in v2.0 would be to make it draggable to other parts of the screen. + +![popout](https://spee.ch/b/pop-out.png) + +### Improved Markdown Support {#md} +Long time community developer, btzr on Discord, has made yet another great addition to the LBRY app - enhanced markdown support along with confirmation prompts when clicking URLs. This will enable creators to spice up the descriptions of their content, see below for a great example from community creator Mibo! This will be available in the LBRY app redesign. + +![markdown](https://spee.ch/0/markdown-preview.jpeg) + +### Autoplay Free and Downloaded media {#autoplay} +A local Manchester, NH contributor, Travis, added the ability to autoplay free and downloaded media files to the LBRY app. This means that videos will start automatically and photos/other media will open when entering the claim page. By default, this setting will be shipped in an off state and users will be able to enable it on content pages or in the settings area. Looking to contribute? We want to make the styling fancier, and it’s a great place for a new developer to start - see this [GitHub issue](https://github.com/lbryio/lbry-desktop/issues/1539). + +![autoplay](https://spee.ch/0/lbry-autoplay-redesign.jpeg) + +### View on Web Button {#view-web} +Travis has also added a View on Web button which is shown on channel pages as well as content pages for certain media types (videos/images/gifs). This button will open up the content on [spee.ch](https://spee.ch) so it can be easily shared via the web. The button on the channel pages will lead to the channel page on speech, such as [@TheRubinReport](https://spee.ch/@TheRubinReport). + +![view on web](https://spee.ch/0/view-on-web.jpeg) + +### LBRY for Android Update +LBRY for Android underwent a product review with management recently and a number of [issues were identified](https://github.com/lbryio/lbry-android/issues?q=is%3Aissue+label%3A"product+review"). The latest updates to the alpha include a search function, ability to open other file types like images, html and text and, opening of lbry:// URLS via hyperlinks. Want to give alpha testing a shot? [Sign up here](https://lbry.io/android-alpha)! +![Android Search](https://spee.ch/0/Lbry-android-search.jpeg) + +### spee.ch Update +We can’t believe another month has passed! Since the last update, we’ve been working to streamline the spee.ch code base and creating tools so that anyone who wants to run their own version of spee.ch can do so with a few easy configurations. If you are interested in running your own version of spee.ch, visit the #speech discord channel or try the [quick start guide](https://github.com/lbryio/www.spee.ch/blob/master/quickstart.md) at [lbryio/www.spee.ch](https://github.com/lbryio/www.spee.ch) + +### Protocol - Wallet Server and Client Development Updates {#wallet} +On the server side, we’ve released [version 1.1.0](https://github.com/lbryio/lbryumx/releases/tag/v1.1.0) of lbryumx, which is an electrumx clone with improvements for the LBRY blockchain. We have been testing this internally on the LBRY desktop app as well as on our spee.ch and YouTube sync servers. So far the results have been promising and we hope to include this with the next release of the LBRY protocol to replace our two (lbryum8/9 which the app uses) SPV wallet servers. + +On the client side, a good amount of progress has been made and integration testing was setup to test various scenarios when connecting to lbryumx. The next step is to internally test the client side wallet with the LBRY app, which should begin in the 2nd week of June. If all goes well, a release will be right around the corner! Together with lbryumx, we expect a large performance increase when resolving claims and publishing - two of the main features used in the LBRY protocol. + +### Protocol - Componentizing the Daemon +LBRY’s Daemon is our silent, unsung hero - and we’re taking big steps in refactoring the way it works to improve performance in its startup code. The components that are currently initialized in the Daemon.py file will be separated and put in a different modules in order to make the code more organized and flexible. + +Down the road, this will offer many benefits such as running only the components which services require and not others. One example would be an implementation that needs to only act as a seed server - you can just run the lbrynet-daemon with the DHT component and turn off others. By employing this technique, you could run multiple instances by using less system resources overall. Another example might be a service that only needs the wallet to run - one would enable that functionality and turn off data sharing features. + +### Chainquery SQL Databasing on the Blockchain +Chainquery is here! The need for Chainquery originally came along when we noticed we relied on the same type of blockchain data across many projects/services at LBRY and thus the project was born! While this new feature will be invisible to most users, it gives people developing apps on the LBRY protocol a powerful new set of tools to work with our blockchain. If you want to dive into the details, take a look at [our blog post](https://lbry.io/news/what-is-chainquery). + +At LBRY, we are in the process of integrating Chainquery into our internal services. The first service to integrate with Chainquery was our [Lighthouse search](https://github.com/lbryio/lighthouse) which occurred a few weeks ago. Previous to this, Lighthouse relied on its own database, which had some flaws when keeping up with claim updates. By relying on Chainquery for current blockchain data, Lighthouse now has the most up to date claim and channel information when presenting search results. The next couple projects to integrate Chainquery will most likely be spee.ch and our internal API service which provides authentication, analytics and rewards. + +![chainquery](https://spee.ch/@lbry/topchannels.png) + +### Blockchain - Hard fork Update +LBRY will undergo a hard fork on 7/9/18. There won’t be any noticeable changes for most users, but miners and exchanges will need to make sure they are running [version 0.12.2.0 of the LBRYcrd full blockchain wallet ](https://github.com/lbryio/lbrycrd/releases/tag/v0.12.2.0) to ensure they are on the most up to date chain. Read [this blog post](https://lbry.io/news/hf1807) for the whole story. + +# Community Happenings {#com-updates} +If you aren’t part of our Discord community yet, [join us](https://chat.lbry.io) anytime and say hello! Our community allows LBRYians to interact with the team directly and for us to engage users in order to grow the LBRY platform. Also follow us on [Twitter](https://twitter.com/lbryio), [Facebook](https://facebook.com/lbryio), and also on [Reddit.](https://www.reddit.com/r/lbry) + +### Help Create the Stack Exchange Community for LBRY +Please check out the newly proposed LBRY Protocol site on Stack Exchange, and help get it to the next step. Example questions and up votes are needed! +[View the LBRY Stack Exchange site here](https://area51.stackexchange.com/proposals/118455/lbry-protocol) + +Stack Exchange Q&A site proposal: LBRY Protocol - Digital Content Distribution + + +### Education Donations +At LBRY, we believe in the importance of quality education, and think that every child should have the opportunity to pursue the type of education that fits them best and allows them to succeed. We’re proud to announce that we’ve donated $2,500 in funding to the [Children’s Scholarship Fund of New Hampshire](https://nh.scholarshipfund.org/), a non-profit organization that provides scholarships to empower low-income New Hampshire families to choose the schools that best fit their children’s needs, regardless of their income or zip code. + +If you’re inspired to help a child get the education they need, you can [donate to the CSFNH here](https://nh.scholarshipfund.org/support/individual-donor/). + +### LBRY-C Collaboration +We’re excited to announce our first grant from The LBRY Fund is going to the LBRY-C group, who are working on creating some amazing new community resources and applications for use on the LBRY protocol. They are receiving 100,000 LBC to fund their projects. You can learn more about them and participate in their projects by [visiting their website.](https://lbry.community) +Going through the process with LBRY-C has made us realize the need for a structured method for other individuals and organizations to request community and institutional funds. We are creating a program to provide a streamlined process in the near future. + +### LBRY is hiring! New Jobs Posted +[Come join team Content Freedom!](https://lbry.io/join-us) We have the following positions open: Blockchain Engineer, Lead Application Engineer, Project Manager, Protocol Engineer, and API Engineer. We’d love for you to join us, or if you have a friend you think would be interested, we pay a $5,000 bounty if we hire them. + +### Roadmap Check-in +Since our last update, we’ve completed the spee.ch Multisite roadmap task and the others have not changed at the moment. By next month’s update, we hope to complete the UI Redesign, sharing of free content on the web (via the [View on Web button](#view-web), the LBRY tech resource website, and the release of the white paper. A good amount of progress has been made on YouTube Sync automation but we will keep this pending until we can close the entire feedback loop of the process. + +### Spee.ch Multisite Launched +The spee.ch codebase has been released so you can create your own fully customized web-based applications using the LBRY Protocol. Spee.ch is an image and video sharing application that publishes and displays content using the LBRY blockchain. You can download Spee.ch Multisite from our [GitHub repo here](https://github.com/lbryio/www.spee.ch) and follow along with the [quickstart guide](https://github.com/lbryio/www.spee.ch/blob/master/quickstart.md) for best results. + +LBRY provided a live streamed technical event to work with people interested in installing Spee.ch multisite, and even provided testing servers! The event was well attended, and by the end of the session, participants had launched their own spee.ch server and learned to customize the interface. + +### LBRY Merchandise Shop +New patriotic and free speech inspired designs have been added to [our shop](https://shop.lbry.io), in time for Independence day. + +![Satoshi Jefferson t-shirt](https://spee.ch/c/jefferson.png) + +**LBRY T-shirt design contest** +Calling graphic designers! We are hosting a t-shirt design contest for the LBRY Merchandise Shop. Use your imagination to design LBRY logowear or another creative concept illustrating your vision of LBRY. First and second place winners will respectively get $100 and $50 in LBC plus $25 in credit to use in the LBRY Shop! [You can find the details here.](https://lbry.io/shirt-contest) + +### YouTube Sync Updates {#youtube-updates} +Since we initiated our new YouTube Sync program, we’ve mirrored more than 500,000 videos to LBRY’s decentralized content network! If your favorite creator hasn’t joined the party yet, send them [this link](https://lbry.io/youtube) and tell them to get on it! + +Our developers are hard at work fully automating this process. We are able to sync new channels as they come in and provide updates to YouTubers via email when their videos are queued and finally synced. Although we still run through the occasional hiccup, the process is almost flawless! + +We do still have a backlog to sync through, so those YouTubers will be getting their completion emails as soon as we are caught up. The final step of this process will be handing over control of the channel and claims to the YouTubers. We are still working out the details on how to complete this as there are two distinct options - handing over control of the wallet or sending the claims/channels to their wallet. + +### 3D Printing Community on LBRY +We are now accepting design submissions for our [Chess Set Design contest](https://lbry.io/3d-contest)! Winners will receive $150 in LBC; $100 for Grand Prize, $50 Runner-up. + +Earn $5 in LBC just for uploading your chess set to the LBRY network. Email the lbry:// address to [james@lbry.io](mailto:james@lbry.io) to enter and claim your reward! Winners will be decided by Tom, Julie, and James. We’re looking for a set designed with originality and/or craftsmanship! + +You can also get paid to print out our featured Cryptocurrency Chess Set and/or Genius Chess Set by simply posting a picture of it to social media and tagging LBRY! Details at the bottom of the contest page. + +Want to keep in touch regarding future 3D printing updates, [subscribe here](https://lbry.io/3d-printing)! + +### LBRY.tech Update {#lbry-tech} +The LBRY tech resource website underwent a huge facelift in terms of design as well as content. You can follow along with progress on our [GitHub repository](https://github.com/lbryio/lbry.tech). Next steps before launch include finishing up the Build section, determining a single API documentation resource across our projects and moving technical documentation from lbry.io and other GitHub locations. + +![lbrytech preview1](https://spee.ch/5/lbrytech-preview1.jpeg) +![lbrytech preview2](https://spee.ch/7/lbrytech-preview2.jpeg) + +[Back to **Development Updates**](#dev-updates) + +Thanks for supporting LBRY - stay tuned for more news and updates! And if you haven’t downloaded the [LBRY app](https://lbry.io/get?auto=1) yet, what are you waiting for? diff --git a/content/news/173-lbry-fund-launches.md b/content/news/173-lbry-fund-launches.md new file mode 100644 index 00000000..4b358ae4 --- /dev/null +++ b/content/news/173-lbry-fund-launches.md @@ -0,0 +1,30 @@ +--- +author: brinck-slattery +title: 'LBRY.fund is Live' +date: '2018-06-14 12:00:00' +cover: 'gradient.png' +--- +We are very excited to announce the launch of [LBRY.fund](https://lbry.fund/), a new site that gives LBRY developers and community members the opportunity to apply for grants from a pool of 200 million LBC to help them pursue independent projects. + +![LBRY.fund home page](https://spee.ch/7/lbryfund.png) + +You can get all the details in the copy of our press release below. We can’t build LBRY without the LBRY Community, and we can’t wait to see what you come up with in your grant applications! + +## LBRY Inc. Announces Launch of LBRY.fund, Awards First Community Grant to LBRY-C + +Manchester, NH - 6/14/18 + +LBRY Inc., creators of the LBRY media sharing system and LBC utility token, today announced the launch of [LBRY.fund](https://lbry.fund/). The 200 million LBC in the fund (worth $36.4 million at time of publication) will be distributed to applicants making innovative contributions to the overall ecosystem. + +"The LBRY Fund is designed to empower people around the world to contribute to the adoption and use of the LBRY Protocol," said Julie Sigwart, LBRY Community Director. “Proposals can be submitted for any project that promotes the use of the LBRY ecosystem. Some project examples could include coding, marketing, meetups, or graphics and media creation.” + + + + +The first grant recipient will be LBRY-C, a decentralized community organization of LBRY fans and developers. LBRY-C will receive 100,000 LBC over three months for their planned community and outreach efforts. + +"It's great to have the opportunity to receive funding from the LBRY Fund, so that we can develop applications, build a stronger community, and work on securing the integrity of the LBRY protocol." said Torgeir Flø, LBRY-C. + +Interested groups and individuals can apply for grant funding at [LBRY.fund](https://lbry.fund). Developers, content creators, marketers, and anyone who thinks they have something unique and compelling to offer are all welcome. The LBRY.fund grant program will accept applications on a rolling basis, and there is no deadline for application. + +LBRY Inc. is the creator of the LBRY protocol, a blockchain-based, peer to peer method for devices to communicate and discover verified versions of any kind of digital media, anywhere in the world. They’ve also built the [LBRY app](https://lbry.io/get), a desktop viewer for content on the LBRY network, as the first implementation of their new protocol. diff --git a/content/news/174-lbry-desktop-redesign.md b/content/news/174-lbry-desktop-redesign.md new file mode 100644 index 00000000..80842a16 --- /dev/null +++ b/content/news/174-lbry-desktop-redesign.md @@ -0,0 +1,37 @@ +--- +author: sean-yesmunt +title: 'LBRY Austen: A Developers Perspective' +date: '2018-06-25 17:00:00' +cover: 'redesign.png' +--- +About 4 months ago a user by the name of @nizuka told us he had been working on a redesign for our desktop app and posted a link to a demo he made in one of our Slack channels (RIP LBRY slack, checkout our [Discord](https://chat.lbry.io/)). This wasn’t solicited and I’m not sure we had any immediate plans for a redesign at that time, but It was awesome. I think pretty much right away, everyone knew we should start working towards a new app design. + +![Redesign first draft](https://spee.ch/f/redesign-rough-draft.png) + +This happened shortly after I had joined LBRY full time. My main task since I started has been working with Nicolas to implement this new design. Initially we had a few brainstorming meetings to discuss what we could improve on his initial demo. He began coming back with some killer mockups and the project was officially underway. Once we were happy with the design, Nicolas created a web demo to share to the community. + + + +While all of this was happening, I had started working on some of the initial refactoring inside our app. There were a few React components that had tons of internal state and it was making it tough to add new features and reproduce bugs. The publish page was one of the worst offenders. We have moved most of this logic into redux and which has allowed us to do some cool things such as remembering where you were in the publish process if you leave the screen, and it has made it a lot easier to prefill the publish form if you are editing claims. We plan to re-work the UX for publishing, and this was a needed first step. + +Another piece we wanted to improve was our CSS architecture. The app is now (mostly) conforming to BEM naming patterns, which has made it really easy to ensure styles are the same for similar components across the app. + +As the redesign was taking place, we added Flow to the project. We are slowly adding types to more components and functions, and we quickly saw the benefits of doing so. We aren’t all the way there, but we are continuing to increase Flow coverage as we add new features. + +This project has been incredibly fun and rewarding to work on, but it has not been without issues. It was hard to know when to stop refactoring older components and when to stop and move on. I found it very easy to keep diving deeper into the code until I realized maybe my time would be better spent continuing to work on the actual redesign. These were changes that made parts of the code easier to work with for the future, but I think this could have been finished sooner if I focused on actual design changes more. Another issue I quickly ran into was how to keep the project up to date. The LBRY app is constantly being worked on by many contributors (team members, and community members) and I realized it would be a pain to constantly keep resolving merge conflicts as I tried to keep my branch up to date. I eventually decided just to wait until I was ready to merge my initial PR into master, and solve the 80+ conflicts then. I’m sure this could be handled better. + +Here are a few photos to see some of the changes we've made. + + + +![Home page light](https://spee.ch/9/redesign-home.png) + +![Home page dark](https://spee.ch/f/redesign-home-dark.png) + +![Send & receive credits page](https://spee.ch/d/redesign-send-credits.png) + +![Wallet page](https://spee.ch/9/redesign-wallet.png) + +This redesign has been a large project with tons of help from LBRY team members and several community members. We had many code contributions from the community and a lot of help with early testing. The first release is far from our final design, but we see it as a good first step. We would rather release early and release often. Is there something about the redesign that you don’t like? Have you run into any issues? Please let us know! We will even pay you with some LBC if you are interested in fixing the issue. We love community feedback and contributions, that is how this entire redesign project got started. :) + +[Download LBRY Austen Here](https://lbry.io/get?auto=1) diff --git a/content/news/175-austen.md b/content/news/175-austen.md new file mode 100644 index 00000000..c08a84ae --- /dev/null +++ b/content/news/175-austen.md @@ -0,0 +1,77 @@ +--- +author: samuel-lbryian +title: 'A is for Austen' +date: '2018-06-25 17:00:00' +cover: 'austen-min.jpg' +--- + +# LBRY Austen: A New Chapter in the LBRY story + +We're very excited to announce the launch of the newest version of our app, Austen! + +LBRY Austen is loaded with new features and an entirely redesigned look to improve user experience in the app. As you may have noticed, this app version has a name instead of a number - from now on, each major upgrade of the LBRY app will be named alphabetically after writers and thinkers who’ve helped shape this incredible modern world where we're able to build a truly decentralized, peer to peer content network. + +![LBRY app gif](https://spee.ch/7/lbry-redesign-full.gif) + +## About The Austen Release + +LBRY Austen is packed with new features and a totally overhauled design. Here are the details: + +### 1. Thumbnail Upload Button + +We’ve added a nifty little button that integrates [spee.ch](https://spee.ch) to keep your thumbnails on the decentralized LBRY blockchain with your content. Gone are the days of having to open a web browser to copy and paste image links for thumbnails. This feature should be working, but may experience intermittent interruptions if spee.ch publishing is disabled. + +### 2. "View On Web" Button + +Another useful button that we’ve added is the "View on Web" button which enables you to watch free content in your web browser via spee.ch and share links to content via messaging and social media. This feature should be working, but may experience intermittent interruptions if spee.ch publishing is disabled. + +### 3. Autoplay Option + +We have now added an option to autoplay free and downloaded media. The default toggle setting is set to "off" but you can enable it in the Content Settings. + +### 4. QR Code on Send & Receive Page + +We don’t speak QR code, but computers do. We decided to add a QR code for the public address (that’s the one you share with people for receiving tips and funds) on the Send & Receive Page. Those pixels will save you the time of typing in strings of random characters, after all, that’s time you could be watching or publishing content on LBRY! + +### 5. Publish ‘Pending…’ + +It’s nice to know the status of your published content. Beneath the title you’ll now see ‘Pending…’ if your publish isn’t yet confirmed. Once your published content is confirmed on the blockchain, ‘Pending…’ disappears. Call it magic. + +### 6. Subscription Page Redesign + +People like to keep *up to date* with their favorite creators, so we’ve redesigned the Subscription page to show the latest content from their subscriptions instead of showing them per channel. Don’t worry, if you want to look at the latest content from each channel all you do is click on the channel name: channel pages work the same way. + +### 7. Improved Markdown Support + +Although a video is worth a million words, we made some improvements that allow creators to enhance their descriptions with enhanced markdown syntax. **Pretty** *cool* [right?](http://commonmark.org/help/) + +### 8. Confirmation Prompt + +Money is power, and so is knowledge. Knowing what's happening with your LBC is ultra powerful. Now when you send credits from the LBRY app wallet you’ll be prompted with a confirmation screen to review the destination address and amount + +### 9. LBRY Daemon Upgrade + +We've included a major upgrade to the LBRY daemon in this app release. The LBRY daemon is the behind-the-scenes program that lets computers communicate with one another on the LBRY network - this upgrade should enhance system stability and reliability. + +# A Is for Austen + +**So why Austen?** + +Jane Austen is one of the most prolific novelists of all time, best known for her works *Pride and Prejudice* and *Sense and Sensibility.* + +Austen’s literature was controversial in 19th century England - as a woman in a time when women couldn't own property let alone enter public conversations about culture, morals, and rights, Austen raised her contemporaries' hackles by suggesting that maybe women can be more than roommates owned by their husband. + +**What happened to her novels?** + +They were banned. If YouTube existed at the time, they would have helped, but in this case the censorship came from parents of unmarried women, worried that her novels would turn their daughters into insane hussies. Fortunately for us, the generations since her novels' publication have seen the value in her work and removed it from the censorship bonfire. + +Jane Austen’s father George believed in education for all of his children. Pushing back against the status quo, his daughters were encouraged to explore his library and immerse themselves in the great works. This early education and respect led Austen to become one of the most well-known novelists in history. + +Austen once said, *"What is right to be done cannot be done too soon."* + +Decentralizing content to provide controversial ideas with the freedom to flourish is the right thing to do, and it's never too soon to do it. + +Help us build the decentralized platform that provides creators with true freedom - download LBRY Austen today! + +[Download LBRY Austen Here](https://lbry.io/get?auto=1) + diff --git a/content/news/176-lbry-development-community-update-June-2018.md b/content/news/176-lbry-development-community-update-June-2018.md new file mode 100644 index 00000000..42a8a387 --- /dev/null +++ b/content/news/176-lbry-development-community-update-June-2018.md @@ -0,0 +1,149 @@ +--- +author: samuel-lbryian +title: 'Development and Community Update June 2018' +date: '2018-07-06 16:00:00' +cover: 'fireworks.jpg' +category: community-update +--- +Welcome to the June 2018 LBRY Development and Community update! In this post we’ll show you what we’ve been up to and review our progress for the month of June. This month we launched the [LBRY app redesign](#summary), made enhancements to the LBRY protocol, continued to move forward with our Android app alpha testing, and began several new community initiatives, including the [debut of the lbry.fund, created to fund projects promoting or using the LBRY protocol.](#lbry-fund) + +Scroll down to learn about new [rewards updates](#reward) as well as a couple contests where you can earn up to $300 USD in LBRY credits. To read all of our previous updates, please visit our [Development and Community Update archive](https://lbry.io/news/category/community-update). + +If you want to see what we have completed recently and what’s planned for LBRY, check out our [roadmap](https://lbry.io/roadmap). + +## UPDATE - LBRY Community Contests +If you’re a designer or 3D printing expert, we have two contests that might be right up your alley - read on for details! + +### LBRY T-shirt design contest +Calling graphic designers! We are hosting a t-shirt design contest for the LBRY Merchandise Shop. Use your imagination to design LBRY logowear or another creative concept illustrating your vision of LBRY. First and second place winners will respectively get $100 and $50 in LBC plus $25 in credit to use in the LBRY Shop! [You can find the details here.](https://lbry.io/shirt-contest) + +### 3D Printed Chess Set Contest +We are now accepting design submissions for our [Chess Set Design contest](https://lbry.io/3d-contest)! Winners will receive $150 in LBC; $100 for Grand Prize, $50 Runner-up. + +Earn $5 in LBC just for uploading your chess set to the LBRY network. Email the lbry:// address to [james@lbry.io](mailto:james@lbry.io) to enter and claim your reward! Winners will be decided by Tom, Julie, and James. We’re looking for a set designed with originality and/or craftsmanship! + +You can also get paid to print out our featured Cryptocurrency Chess Set and/or Genius Chess Set by simply posting a picture of it to social media and tagging LBRY! Details at the bottom of the contest page. + +Want to keep in touch regarding future 3D printing updates, [subscribe here](https://lbry.io/3d-printing)! + +To skip the tech stuff, see what’s happened and what’s next in the LBRY community, click the link below. Otherwise, read on! + +[Skip to **Community Happenings**](#com-updates) + +# Development Updates {#dev-updates} + +### App and Protocol Summary {#summary} +We are extremely excited to announce that the LBRY app redesign, codename Austen, was launched on June 26th! You can read [our blog post](https://lbry.io/news/austen) which details the most important updates in the new app and introduces the LBRY app naming system. Full release notes can be found on our [GitHub page](https://github.com/lbryio/lbry-desktop/releases/tag/v0.22.0) + +For a deeper perspective of this process from the eyes of our lead developer Sean, check our his [blog post here](https://lbry.io/news/lbry-desktop-redesign). + +On the protocol side of the house, we released [version 0.20](https://github.com/lbryio/lbry/releases/tag/v0.20.2) along with the redesign launch that included a tuned DHT implementation in order to increase consistency and availability of P2P data. Once more peers came online, we discovered and fixed a number of issues which should make the overall network run smoother - these are included with the latest app patch and [daemon version 0.20.3](https://github.com/lbryio/lbry/releases/tag/v0.20.3). In our benchmarks, we are at or just above 95% availability when downloading well seeded content from the homepage. We’ve identified a few more issues when multiple downloads are performed simultaneously and are currently looking for a solution. + +![redesign](https://spee.ch/7/lbry-redesign-full.jpg) + +### New Rewards {#reward} +To celebrate LBRY’s second birthday and the redesign launch, we’re DOUBLING all in-app rewards for new users as well as existing ones that haven’t claimed rewards (this included the weekly LBRYCast also too)! Returning users (those we have earned LBRY rewards prior to the redesign launch) will also receive a 50 LBC reward when they upgrade to LBRY Austen. New app users can now claim over 40 LBC in rewards by completing tasks in the app, check out our [rewards page](https://lbry.io/faq/rewards) for the latest details and amounts. In the coming weeks, we will introducing tiered rewards, stay tuned! + +![redesign](https://spee.ch/0/redesign-reward.jpg) + +### File Renderer enhancements and 3D/PDF Support! +Community member [btzr](https://github.com/btzr-io) has been working on two PRs that greatly improve the LBRY app’s ability to support additional file types. First, the [File Renderer PR](https://github.com/lbryio/lbry-desktop/pull/1576) refactors the current code which will allow passing certain file types to different viewer types (i.e. if it’s a PDF, use a PDF viewer). To showcase this feature, he’s also added a [3D file viewer](https://github.com/lbryio/lbry-desktop/pull/1558) which currently supports previewing of STL files in-app! This will also be expanded to OBJ files in the future. + +![3D](https://spee.ch/b/3d-support.jpg) + +![PDF](https://spee.ch/3/pdf-support.gif) + +### Search Result Setting +A big limitation in previous LBRY versions was related to search results - it would only return 10. With the recent improvements in protocol speed, we’ve added a configurable setting on the search page. Users can now control how many results are returned from their searches. Next up in terms of search improvements will be additional filters like searching inside channels and narrowing down file types. + +![search](https://spee.ch/d/search-results.jpg) + +### Channels on Homepage, with CryptoCandor! +We are currently experimenting with a more dynamic LBRY Explore page with [@CryptoCandor’s](https://open.lbry.io/@CryptoCandor) channel which is updated as soon as she uploads new content. This is different from our previous approach where the rest of the homepage is static LBRY URLs as opposed to a specific channel. Once we improve [the UX around this experience](https://github.com/lbryio/lbry-desktop/issues/1717), we will be adding more channels to use this scheme. + +![Candor](https://spee.ch/2/cryptocandor-channel.jpg) + +### Easier way to find your wallet, Open Directory Button! +We currently have a [GitHub PR](https://github.com/lbryio/lbry-desktop/pull/1638) in progress by a community member which will add a Open Wallet Directory button to the wallet backup page. This will make it easier for users to locate their wallet file. The change also includes an easy copy button to copy/paste the wallet directory. + +![Button](https://spee.ch/e/wallet-backup-button.jpg) + +### LBRY for Android Update +The recent updates to LBRY for Android Alpha include the addition of a welcome page on startup, ability to view channels, a separate trending page, and blockchain sync status on startup. Behind the scenes, ongoing work includes the derivation of a unique device ID for authentication to our API server in order to support things like rewards and wallet syncing. Initial work as also begin on moving rewards logic to a common area so it can be shared with the desktop app. Want to give alpha testing a shot? [Sign up here](https://lbry.io/android-alpha)! + +![Android Sync](https://spee.ch/8/android-lbry-sync.jpg) + +### spee.ch Update +We’ve gone back to the basics with spee.ch and began re-focusing on core components such as social sharing and knocking out existing bugs. A recent change to channel pages fixes the sorting order to match what’s in the LBRY app. We’ve also spent some time debugging why publishing on the spee.ch server is taking longer than expected (average of 20-30 seconds). Our findings show that the new LBRY wallet will help speed this up so we are looking forward to that being released! Finally, we had to impose some API limits and other spam preventative measures… thank you, spammers, for keeping us on our toes! + +Interested in running your own spee.ch server or clone? Check out the [quick start guide](https://github.com/lbryio/www.spee.ch/blob/master/quickstart.md) and GitHub repository at [lbryio/www.spee.ch](https://github.com/lbryio/www.spee.ch). + +![speech publish time](https://spee.ch/4/speech-timing.jpg) + +### Protocol - Improved Speed {#speed} +[Version 0.20.2](https://github.com/lbryio/lbry-desktop/releases/tag/v0.22.0) of the protocol greatly improved the LBRY app experience by speeding up resolve calls throughout the app. Resolve calls are used anytime the app needs information about a channel or claim, such as when the homepage is loaded or when you go into a channel. These changes brought the initial homepage load from about a minute down to 4 seconds!! This significantly improves the first run experience for new users as well as the channel browsing speeds. The fix was a combination of optimizing SQL queries along with adapting a faster cryptographic library which sped up verification of data. We hope this is one of many places that can still be further optimized in the LBRY ecosystem. + +![speed](https://spee.ch/a/protocol-timing.jpg) + +### Protocol - LBRY Reflector Hosting {#reflector} +As some of you may be aware, LBRY Inc helps with the hosting of content that’s published to LBRY. Currently we employ a single server which accepts the data and forwards it to multiple content hosts. Over time, and especially with larger volumes of uploads coming from the YouTube Sync program, we’ve realized this solution won’t cut it anymore. Our engineers are working on a new process which would host all the blob files on a single Amazon S3 data storage server and multiple satellite nodes that would be responsible for announcing the content via the LBRY P2P protocol. This should allow for a larger throughput and less maintenance since all the data will be in a single place. + +### Protocol - Wallet Server and Client Development Updates {#wallet} +On the server side, our 2 ElectrumX servers (lbryumx1.lbry.io and lbryumx2.lbry.io) are getting their first extended action with the launch of the redesigned app (which included the protocol update for the wallet servers). We’ve been monitoring performance and so far so good! If you experience any wallet issues, please [let us know](https://lbry.io/faq/support). + +On the client side, we’ve begun testing the wallet migration process from the old style wallet to the new one. The old wallet included all transaction data in the default_wallet data file which was inefficient. The new wallet will store only seed and channel information in the default_wallet file, which a SQL database will store all transaction and claim information. Left on the todo list is signing claims with channel certificates and double checking that all the current API commands are implemented. In the next couple of weeks we should have a test version for the community to try out. We are excited to see improvement in publishing times, especially on larger wallets, like spee.ch’s, with this update! + +![SQL Wallet](https://spee.ch/1/wallet-sql.jpg) + +### Blockchain - Hard fork Update - ALL SYSTEMS GO! +LBRY will undergo a [hard fork on 7/9/18](https://lbry.io/news/hf1807), which is just a few days from the time of this blog post. There won’t be any noticeable changes for most users, but miners and exchanges will need to make sure they are running [version 0.12.2.0 of the LBRYcrd full blockchain wallet ](https://github.com/lbryio/lbrycrd/releases/tag/v0.12.2.0) to ensure they are on the most up to date chain. We’ve done our due diligence to reach out to known exchanges, miners and other service providers (i.e. Coinomi/Changelly/ShapeShift) informing them of the update. + +The LBRY blockchain team is already preparing for the next set of upgrades for a 2nd hardfork - this time it revolves around case sensitivity of claim and channel names. See the current [GitHub PR](https://github.com/lbryio/lbrycrd/pull/159) for details. We may include another blockchain upgrade to go along with this, stay tuned! + +# Community Happenings {#com-updates} +If you aren’t part of our Discord community yet, [join us](https://chat.lbry.io) anytime and say hello! Our community allows LBRYians to interact with the team directly and for us to engage users in order to grow the LBRY platform. Also follow us on [Twitter](https://twitter.com/lbryio), [Facebook](https://facebook.com/lbryio), and also on [Reddit.](https://www.reddit.com/r/lbry) + +### LBRY.fund Launches {#lbry-fund} +June was an exciting month as we released our LBRY.fund program to fund projects from you -- our community! So far, we’ve received great proposals from users around the globe telling us how they would like to promote or build applications using the LBRY protocol. We’re hearing pitches from film studios, YouTubers, media organizations, Meetup groups, animators, hackathon organizers, software companies, and a wide variety of other creative people with unique ideas! It is awe-inspiring for us to hear ideas to use the LBRY protocol in ways we would never imagined, and we thank you as you inspire us. Keep the ideas coming, and we’ll be announcing first grant recipients soon! [Visit the LBRY.fund.](https://lbry.fund) + +![LBRY.fund](https://spee.ch/2/lbry-fund.png) + +### Help Create the Stack Exchange Community for LBRY +We’ve had great response and progress on the LBRY Protocol site on Stack Exchange, but in order for the community to move onto the Commitment phase, we need more folks to help vote for example questions and then commit to use the site. Please give us a hand: +[View the LBRY Stack Exchange site here](https://area51.stackexchange.com/proposals/118455/lbry-protocol) + +Stack Exchange Q&A site proposal: LBRY Protocol - Digital Content Distribution + +### LBRY is hiring! New Jobs Posted +[Come join team Content Freedom!](https://lbry.io/join-us) We have the following positions open: Blockchain Engineer, Protocol Engineer, and API Engineer. We’d love for you to join us, or if you have a friend you think would be interested, we pay a $5,000 bounty if we hire them. + +### Roadmap Check-in +We are happy to mark UI Redesign, URL embedding of free content, and LBRY.fund marked as completed on the [roadmap](https://lbry.io/roadmap). We’ve recently added *LBRY Content Mirroring 2.0* to the Q3 roadmap, check the latest update [here](#reflector). + +### Youtube Sync Updates {#youtube-updates} +Since we initiated our new YouTube Sync program, we’ve mirrored more than 500,000 videos to LBRY’s decentralized content network! If your favorite creator hasn’t joined the party yet, send them [this link](https://lbry.io/youtube) and tell them to get on it! + +We’ve had to scale back on YouTube sync publishing so that our [content hosting infrastructure](#reflector) can be upgraded to support the large amounts of data being uploaded. Pardon us for the delay if you are waiting for your content to be synced or updated, we hope to be back up and running in full steam within a couple weeks! + +### Twitter TipBot Update +Our [Twitter Tipbot](https://twitter.com/@LBC_Tipbot) received a small revamp which fixes a number of bugs we were running into. It can now be called without having to use the `lbryian` keyword. See the latest commands on [our FAQ page](https://lbry.io/faq/tipbot-twitter)! + + + + +### LBRY Merchandise Shop +New patriotic and free speech inspired designs have been added to our shop as well as [tank tops for the summer](https://shop.lbry.io/collections/mens-unisex/products/lbry-logo-tank-top) - check out the entire offering at our [merchandise shop](https://shop.lbry.io)! + +![Satoshi Jefferson t-shirt](https://spee.ch/c/jefferson.png) + +### LBRY.tech Update {#lbry-tech} +LBRY.tech is in the middle of a major overhaul - when we’re done, it will be easier to use and more accessible to developers who want to learn about LBRY. You can follow along with progress on our [GitHub repository](https://github.com/lbryio/lbry.tech). Next steps before launch include determining a single API documentation resource across our projects. We’ve recently added a [glossary with LBRY/Blockchain related terms](https://lbry.tech/glossary.html) and moved [several technical documents](https://github.com/lbryio/lbry.tech/tree/master/content/resources) over to the lbry.tech repositoy. The 2nd step of the tour, which allows for developers to publish a meme on the blockchain, has been completed. + +![lbrytech publish](https://spee.ch/9/lbry-tech-publish.jpg) + +# Want to develop on the LBRY protocol? +All of our code is open source and available on [GitHub](https://github.com/lbryio). Are you a developer and want to find out more? Check out our [general contributing guide](https://lbry.io/faq/contributing) and our LBRY App specific contributing [document](https://github.com/lbryio/lbry-desktop/blob/master/CONTRIBUTING.md). Please be patient with us while we improve our technical documentation. In the next few weeks we’ll be releasing [lbry.tech](#lbry-tech), a technical reference / guide website which will be developer and contributor focused to drive more apps and services on top of LBRY. + +[Back to **Development Updates**](#dev-updates) + +Thanks for supporting LBRY - stay tuned for more news and updates! And if you haven’t downloaded the [LBRY app](https://lbry.io/get?auto=1) yet, what are you waiting for? diff --git a/content/news/177-app-release-023.md b/content/news/177-app-release-023.md new file mode 100644 index 00000000..d9195afa --- /dev/null +++ b/content/news/177-app-release-023.md @@ -0,0 +1,47 @@ +--- +author: brinck-slattery +title: 'LBRY Desktop App Has Leveled Up' +date: '2018-07-25 13:00:00' +cover: 'lbryopoly-min.jpg' +--- +# LBRY v0.23 is live! + +We heard your feedback, and we think you’ll love the new LBRY v0.23! View all the changes in this version on our [release notes](https://github.com/lbryio/lbry-desktop/releases/tag/v0.23.0). This release also includes an updated [LBRY Protocol](https://github.com/lbryio/lbry/releases/tag/v0.20.4) which will help with download avialability and result in less false positives on Antivirus software since it is now digitally signed! + + + + + +### More than just video + +Just a reminder - the LBRY protocol can do a lot more than video. We’ve added 3D file viewing support (STL/OBJ formats) and a PDF preview window directly inside the LBRY Desktop app experience. Now creators can more easily share or sell their 3D files without middlemen taking a cut. + +![3d support](https://spee.ch/0512125bc89b5a70c1e7abed595dfca297a824b3/3d-chess.jpeg) + +### Preview thumbs before you Publish + +We’ve included a new feature for publishers in this update as well! Now you can preview what your thumbnail will look like before you publish! Using either the spee.ch thumbnail upload feature or when entering a URL, the app will now show a preview of the thumbnail. + +![thumb](https://spee.ch/64d74fe70b627fff863d54b6204ee9ff7fb91baf/thumb-preview.jpeg) + +### Safer for work than ever (unless you don’t want it to be) + +We’ve also made some changes to the app to make it more family-friendly for people who have not chosen to view NSFW content. This means the Community Bid section will not be shown unless you choose to view NSFW content in the app since we don’t [control the content posted there](https://lbry.io/faq/community-top-bid). Also, previews of NSFW videos will not show up at all on channel pages, subscriptions or search results. If you’re just here for the hentai, everything’s still available - you just have to make it clear that you want to see it. + +![content settings](https://spee.ch/49727978e4ae0d60a8127056da46905f12b0ee7a/content-settings.jpeg) + +### Faster than a speeding bullet + +Our developers have been spending a lot of time and effort on speeding up the in-app experience. The tweaks to channel caching and navigation between channels/claims in this update will show just what a great job they’ve done, significantly reducing load times in-app. + +### Other Additions/Changes + +There are a number of other changes in this update as well, including: +- Abandons transactions now show in wallet history +- Download failure errors no longer show up if you leave the claim page +- Emoji text support has been enabled in claim descriptions +- Auto update no longer leaves setup files behind + +Your app will prompt you to download the upgrade next time you open it, or you can just [download the newest version directly](https://lbry.io/get?auto=1). + + diff --git a/content/news/178-lbry-development-community-update-july-2018.md b/content/news/178-lbry-development-community-update-july-2018.md new file mode 100644 index 00000000..abf863b4 --- /dev/null +++ b/content/news/178-lbry-development-community-update-july-2018.md @@ -0,0 +1,226 @@ +--- +author: samuel-lbryian +title: 'Development and Community Update July 2018' +date: '2018-08-08 08:08:08' +cover: 'lex-amit.jpg' +category: community-update +--- +Welcome to the July 2018 LBRY Development and Community update! In this post we’ll show you what we’ve been up to and review our progress for the month of July. We had a very busy month - new releases on the Protocol, new features in the LBRY app, project grants via the LBRY.fund, and wallet encryption progress are among the highlights. + +You asked for more ways to earn LBC credits in the app, so we’ve added multi-level rewards. Scroll down to learn about new [rewards updates](#reward). + +To read all of our previous updates, please visit our [Development and Community Update archive](https://lbry.io/news/category/community-update). + +If you want to see a condensed view of what we have completed recently and what’s planned for LBRY, check out our [roadmap](https://lbry.io/roadmap). + +## UPDATE - LBRY Community Contests + + + + +We have winners in our t-shirt and 3D chess set contests! We are also excited to announce new Video and Networking contests hosted by our community partners, LBRY-C! + +7,000 LBC (yes, seven-thousand LBC!) will be awarded in two different contests. Please visit [LBRY.community](https://lbry.community/contest-august-2018) for full details. + +### LBRY T-shirt design contest +Congratulations to the winners of our [summer t-shirt contest](https://lbry.io/shirt-contest)! First place goes to Usman Yaqub and second place is Stephen Firth. They win $100 and $50 in LBC respectively, plus $25 in credit to use in the LBRY Shop! Buy the winning design here: [Men’s](https://shop.lbry.io/collections/mens-unisex/products/lbry-sublimation-t-shirt) or [Women’s](https://shop.lbry.io/products/ladies-lbry-sublimation-t-shirt)! + +![First place](https://spee.ch/19848de763d3f31fafa686217f84d55a34ec6c22/LBRY-SuBLiMaTioN-Model.png) + +![Second place](https://spee.ch/80ce070534a3841e7bdd86e157086ab2f422632d/Dove-Side-by-Side-Final-01.png) + + +### 3D Printed Chess Set Contest +We were blown away by the submissions for our Chess Set Design contest! Congratulations to Greg Broas and Zeycus who take home $100 in LBC for the Grand Prize, and $50 in LBC for the Runner-up. Download the winning chess set here: [lbry://gregsintricatechessa](https://open.lbry.io/gregsintricatechessa) & here: [lbry://gregsintricatechessb](https://open.lbry.io/gregsintricatechessa) + +![chess set](https://spee.ch/7c892f0dc7e34392787ac1e8061c97d4fe270cdd/Gregs-Chess-Coupled-01.png) + +Even though the contests are over, you can still earn $5 in LBC just for uploading your chess set to the LBRY network. Email the lbry:// address to [james@lbry.io](mailto:james@lbry.io) to enter and claim your reward! You can also get paid to print out our featured Cryptocurrency Chess Set by simply posting a picture of it to social media and tagging LBRY! Details at the bottom of the contest page. + +Want to keep in touch regarding future 3D printing updates, [subscribe here](https://lbry.io/3d-printing)! + +To skip the tech stuff, see what’s happened and what’s next in the LBRY community, click the link below. Otherwise, read on! + +[Skip to **Community Happenings**](#com-updates) + +# Development Updates {#dev-updates} + +### App and Protocol Summary +After releasing our redesigned LBRY Desktop app at the end of June, the app team has been hard at work adding additional features and squashing bugs. In our [June update](https://lbry.io/news/lbry-development-community-update-june-2018) we previewed 3D viewing and PDF support, both of which were implemented in version 0.23.0 along with changes to how we handle mature content on the Explore page, channel caching, and thumbnail preview - continue below to check out the changes or read the [release blog post](https://lbry.io/news/app-release-023). + +[Version 0.23.1](https://github.com/lbryio/lbry-app/releases/tag/v0.23.1), a minor patch to re-enable ShapeShift integration, was released just over a week ago - after ShapeShift fixed a wallet issue on their end. Version 0.24.0 is just around the corner with features including -- at a minimum, the first version of wallet encryption in-app, recommended content on file pages, a document viewer, browsing history and improving the search experience. Keep reading below for a preview! + +On the protocol side of the house, we shipped [version 0.20.4](https://github.com/lbryio/lbry/releases/tag/v0.20.4) along with the latest app release. This version brings more stability and CPU usage improvements, especially when running the daemon for extended periods of time. The protocol team has prioritized startup speed/status, blob mirroring support and other DHT performance improvements in 0.21.0. You can read the latest list of changes in the [release notes for the latest Release Candidate](https://github.com/lbryio/lbry/releases/) and further down in the [Protocol update section](#protocol). + +### New Multi-Level Rewards {#reward} +Since everyone loves their hard earned LBC by way of [LBRY Rewards](https://lbry.io/faq/rewards), we’ll cover recent reward additions and changes! We’ve implemented a new multi-level reward structure in order to incentivize user engagement over time. + +The “Many Views” reward was converted to use this new feature. Users who originally claimed this reward will now start at Level 2: “The Browser”, with 5 being the max level. Only new views within the app will count towards your progress. A new subscription reward was also added, with 2 incentive levels. Any previous subscriptions won’t count, so get out there and subscribe to your favorite creators! Next up for multi-level rewards will most likely be related to tipping, so keep an eye out in your LBRY app for new rewards. + +![multilevel](https://spee.ch/34dee02a49fefd7301307f4b95948a0cd8ae7990/rewards-sub-download.jpeg) + +### Channel Page Caching +Our developers have been spending a lot of time and effort on speeding up the in-app experience in 0.23. The tweaks in this update to channel caching and navigation between channels/claims will show just what a great job they’ve done, significantly reducing load times in-app. Previously, the channel pages would reload each time they were accessed. As a next step, we will pre-cache latter pages in a channel, so that when you go to the next page, it’s already all loaded up for your viewing pleasure! + +![Channel caching](https://spee.ch/bd9cae0f865a7e0f8a915397de005e0acb577890/channel-caching.gif) + +### Download Error Suppression +We also improved when those annoying download failure errors are shown. If you’ve used LBRY before, we’re sure you’ve run into it! If you start a stream and leave the page, we’ll no longer bug you with a pop-up alert. This also previously happened if you started another video and one failed before -- yep, we’ll suppress that one too. As the LBRY network grows stronger and more reliable, we’ll have to worry about this less and less, but for now, we think this improves the user experience! + +### Thumbnail Preview +We’ve included a new feature in 0.23 for publishers: you can preview what your thumbnail will look like before you publish! Using either the spee.ch thumbnail upload feature, or when entering a URL, the app now shows a thumbnail preview. With 0.24, if you enter an incorrect URL, you’ll see a red alert image. + +![thumb](https://spee.ch/8f9077826c54a3855cbd6dc873fb655e330fb6e4/thumb-checking.gif) + +### Abandon Transactions now on Transaction Page +If you have published content on LBRY before, there may have been a time when you decided to delete the claim -- for whatever reason. After doing this, your balance would magically update to show any LBC deposit you received back from your claim. This process is now no longer a mystery -- claim abandon transactions are now shown in the LBRY app as the value (+) returned to your wallet! + +![abandon transaction](https://spee.ch/8ce03eaa90e3a401727aed3db5c638bf11182ec4/abandon-tx.jpeg) + +### NSFW Changes - Hide Tiles + Community Top Bids +We’ve also made some changes to the app to make it more family-friendly for people who choose not to enable NSFW (mature) content. This means the Community Bid section will not be shown unless you choose to view NSFW content in the app as we don’t [control the content posted there](https://lbry.io/faq/community-top-bid). Also, previews of NSFW videos will not show up at all on channel pages, subscriptions or search results. If you’re just here for the hentai, everything’s still available - you just have to enable NSFW content in your settings. + +![content settings](https://spee.ch/77e02036642ec2b2d9aebbb57b1194bbf1dac3ab/content-settings-top-bid.jpeg) + +### Recommended Content on File Pages +In the next LBRY Desktop app update, we’re extremely excited to introduce a new feature that recommends related content on LBRY, all based on textual search rankings. Currently, the recommended content uses the title of the current file to search and display up to 20 results on the right side of the app. In the future, we will use the description as well as tags (when the LBRY protocol implements them) to increase the accuracy of related content. We are still debating whether related videos should autoplay, especially since hard drive space management is not a strong suite of the protocol at the moment. + +![Recommended Content 1.0](https://spee.ch/cab352cbdc61044687b1a6bb4b7460d5991cba11/recommended-content-1-0.jpeg) + +### Wallet Encryption via the App {#encrypt} +With the next app release, users will be able to easily encrypt their wallet via a few simple steps. This feature was added a few months ago to the protocol and now it’s finally exposed to users in the Settings area. If you turn this setting on, you’ll be prompted with a message asking for a secure password and a written confirmation of your action. *As you know, if this password is ever lost not even LBRY will be able to get access back to your wallet.* As a first go around, the wallet will most likely need to be unlocked at startup. + +![Encryption](https://spee.ch/1b5ed4120cac0ca2d36ee9a8a630dc31d1babfc5/wallet-encryption-1-0.jpeg) + +### Subscription Fixes and Enhancements +We’ve recently identified a number of subscription-related bugs and inconsistencies which we hope to patch up before the next app release. We believe subscriptions will play a major role in engaging users within the LBRY app, so we want the experience to be as seamless as possible. + + One major issue identified is the lack of a setting for auto download - the app currently tries to automatically pre-download new videos from your subscribed channels without an option to turn this off. As a first fix for this, we’ll be adding a global setting and then determine if per channel settings further improve the user experience. This auto download feature only currently works if the app is open, but we also want it to function if the user had their app shutdown. The goal would still be to download the 1 latest piece of content posted, similar to other streaming platforms. Finally, we’ll be adding auto claiming of the subscription reward after subscribing to enough channels. + +### Document Viewer +Community member [btzr](https://github.com/btzr-io) comes through again with another awesome addition to the LBRY app - a document viewer for files like docx, markdown, HTML, txt, csv, json and formatting for over 100 coding languages like javascript, xml and python. These file types will also automatically open the document viewer if you have them downloaded. This also means there’s less and less of a reliance on the most prominent file viewer, render-media, in the LBRY app so we can slowly focus on replacing it with a more versatile video player. + +![document viewer](https://spee.ch/31248a3319f26e4a1a2844f6048b05c390255152/colors.png) + +### Browsing History +Wouldn’t it be nice to know which content you’ve visited while exploring the LBRY app? Good news! A browsing history feature is currently a [work in progress](https://github.com/lbryio/lbry-desktop/pull/1846) by community member [Travis,](https://github.com/daovist) which will store each file you’ve visited, and whether it was actually played/opened. We’ll be changing the tile view to a more detailed list view in order to improve the display of this type of information. You’ll have the ability to clear items one by one and also with a clear all button. Check out an early preview below! + +![file history](https://spee.ch/b7ddafc76342c79c250853da32625d0b2f71afe0/view-history-1-0.jpeg) + +### Behind the scenes - Electron Builder / Electron Upgrade / Daemon Download / Watch Script +There’s a few awesome behind the scenes developments we also wanted to mention. We’ve recently upgraded Electron-Builder which creates our executables and enables autoupdate. This fixed a few bugs we were seeing, especially one where old auto update downloads would not get cleaned up automatically. + +The latest version also enabled digital signing of any files included with our setup, which now means that the LBRY Daemon is finally signed on Windows! This should lead to fewer complaints by AntiVirus software which normally flags files like this as potentially dangerous. In the next app version, we are also upgrading the Electron version to 2.0.6 (jumping from 1.8.7) - this is pretty much the chrome subsystem that runs our app. + +We’ve also implemented a new watch script to make it easier for developers to automatically view code changes as they are working in the lbry-redux repository with the LBRY app open in development mode. Previously, it was a constant struggle to make changes appear on the desktop app after they were made in lbry-redux locally. Now a script will take care of all that for you! + +### Protocol - 0.21 Features and Progress {#protocol} +Protocol version 0.21 is undergoing final testing internally and almost ready to be shipped! Much of the work done includes the initialization, handling and status of the various components making up the LBRY protocol. These enhancements ensure that component dependencies are decoupled and started as soon as possible, thus speeding up the overall daemon startup process and giving application developers better control over component. This also includes a new UPnP component that the team coded from scratch to replace the poorly supported miniupnpc plugin which helps automate port forwarding on many home routers automatically. We’ll be logging information for this new addition but failing back to miniupnpc until we are comfortable that it will work on many different networks/routers. + +Two enhancements related to file availability and performance are also included in 0.21 - “greedy” search and blob mirrors. Greedy search is an improvement on the original implementation of how nodes find each other on the network when looking for data. This implementation is more aggressive and should be able to traverse more peers if it can’t find the content immediately, thus improving the chances of a successful download. Blob mirroring is a new feature that will accompany the P2P layer by simultaneously trying to download blobs directly from our reflector infrastructure (see more on this below). Both blob mirroring and the P2P layer will attempt to download blob files, and whichever is able to respond the quickest will complete that portion of the download. In initial tests, the P2P layer still wins out, but that will vary per user and what their connectivity to the network. + + +### Protocol - LBRY Reflector Hosting {#reflector} +In our [previous update](https://lbry.io/news/lbry-development-community-update-june-2018#reflector), we mentioned that LBRY’s hosting infrastructure was getting a major overhaul and we’ve made progress towards this goal. The server which holds the blobs has been configured and the reflector service, which communicates with the LBRY protocol, is accepting blobs from our YouTube sync process. These blobs will be immediately available for protocol versions that use blob mirroring (0.21+) and the next step is to finish configuring the P2P layer which will make them available via the DHT. The final step will be to point the daemon at this new reflector so that any uploads from users will also go here - then we can slowly sunset the old one. + +### Protocol - Wallet Client Development Updates {#wallet} +We can see the light at the end of the tunnel for the launch of the new LBRY wallet! Final integration into the LBRY protocol is being completed and is being extensively tested on our staging spee.ch server to ensure that all required API calls are implemented correctly. The wallet also successfully migrates existing LBRY wallets to a new format, including any channels that users may have. The new format is much smaller and cleaner than the previous wallet that used to store transaction information (now in a rebuildable database). This wallet will be launched along with version 0.30, see the next section for more details. + +![wallet format](https://spee.ch/0aca7b923f92a5c1d1513ac172ab25c95083ba69/wallet-sample.jpeg) + +### Protocol - Version 0.30 - Python 3.x Upgrade and Wallet Implementation +In order to more effectively integrate new wallet functionality into the rest of the LBRY protocol, we decided it was finally time to migrate away from Python 2.7 and into Python 3, which is a major version bump that requires lots of rewrites and changes to application logic. Portions of our code were already compatible with both versions, except for the largest and arguably the most important component - the peer to peer layer. After version 0.21 is released, the plan is to migrate all the current changes from our [lbryum-refactor branch](https://github.com/lbryio/lbry/commits/lbryum-refactor) (which already has lots of progress on the python 3 port), into the Master branch and continue the development there. That will force the team to use the latest code and to work out all the kinks! + +### LBRY for Android Update +We are still continuing a lot of the behind the scenes work to integrate our mobile platform with our user authentication and rewards services. The Android team also has been working closely with the Protocol team to ensure that the next release supports overall performance improvements on a cell phone device - this includes the faster resolve changes implemented last month, blockchain sync status and ability to download content through a mirroring solution rather than P2P. Other app improvements include improving hyperlink display/behavior, video player enhancements, keeping the device awake when playing videos, and support for Android 8.0 (Oreo). + +We should have a much smoother version ready for testing with the 0.21 Protocol in a couple weeks so stay tuned and [sign up](https://lbry.io/android-alpha) to become an alpha tester if you haven’t already! + +![welcome screen](https://spee.ch/6fb914c11530370bb2ee671e8c9792c7dbd8d854/lbry-android-welcome.png) + +### spee.ch Update +We’ve been testing the new wallet integration on our staging spee.ch server and it’s almost ready for prime time. With this update, we expect spee.ch publishing times to go down considerably - hopefully down to just a few seconds from 30s+. In other news, thumbnail publishing from the LBRY app revealed some issues with different URL types and their previews which were addressed recently. If you are sharing a spee.ch link on social media, you should use the first URL provided (without an extension) but if you are embedding it in a post, you’ll want the full path with the file extension. We’ve also improved the way certain content displays on the spee.ch page, especially around images/scaling. + +Interested in running your own spee.ch server or clone? Check out the [quick start guide](https://github.com/lbryio/www.spee.ch/blob/master/quickstart.md) and GitHub repository at [lbryio/www.spee.ch](https://github.com/lbryio/www.spee.ch). + +### Blockchain - Hard Fork Success! +LBRY went through a successful [hard fork on 7/9/18](https://lbry.io/news/hf1807). It was not until a few days after that we were able to confirm that consensus was maintained by miners after the upgrade. Exchanges and other services handled the update fairly well also, but we did run into a hiccup or two with some nodes needing to be re-indexed (which we provided instructions for in the blog post). There already is another [hard fork upgrade](https://github.com/lbryio/lbrycrd/pull/159) related to case sensitivity on claims/channels in the queue and we are determining the best timing to execute it. + + + + +The next priorities for the Blockchain team include cleaning up the claimtrie code base in order to increase maintainability and to provide a smoother transition to upstream changes from Bitcoin. These changes include features like SegWit and HD Addresses which were enabled on later versions of Bitcoin. Segwit is especially important since it would allow us to run Lighting Network, which is a 2nd layer scaling solution for microtransactions and a perfect use case for data payments on the P2P network. + +# Community Happenings {#com-updates} +If you aren’t part of our Discord community yet, [join us](https://chat.lbry.io) anytime and say hello! Our community allows LBRYians to interact with the team directly and for us to engage users in order to grow the LBRY platform. Also follow us on [Twitter](https://twitter.com/lbryio), [Facebook](https://facebook.com/lbryio), [Reddit](https://www.reddit.com/r/lbry), [Instagram](https://www.instagram.com/lbryio), and [Telegram](https://www.instagram.com/lbryio/). + +### Q2 2018 Credit Report +This quarter we moved no credits from cold storage. We spent 738,027 total community credits on line items detailed in the Q2 report. No operational credits were moved to markets. No institutional credits were moved or spent. We anticipate comparable or larger total outlays in Q3 2018. Operational spending may increase, but not significantly, and community spending is likely to be higher. We will continue to incentivize new users and other beneficial behavior, which is likely to involve 300,000 to 1,500,000+ LBC. LBRY is also likely to form it’s first institutional partnership, with spending anticipated to be around 500,000 LBC. [Read the details here.](https://lbry.io/credit-reports/2018-q2) + +### Roadmap Check-in +Things are fairly quiet on the [roadmap](https://lbry.io/roadmap) front. We’ve moved the `Wallet Encryption in the LBRY App` goal to `In Progress` as it’s almost ready to be released, see [above](#encrypt) for the update. Large amounts of progress have been made on [YouTube Sync automation](#youtube-updates) as well as [Wallet improvements](#wallet) - both of which we hope to check off as complete by the end of this month. + +### LBRY.fund Awards New Community Grants {#lbry-fund} +The LBRY.fund had an amazing month in July, and we’re proud to fund some familiar and some new faces in the LBRY community. +*Approved/Funded projects include:* + +#### CryptoCandor LBRY Review: +CryptoCandor, one of LBRY’s featured content creators, brings us a full review about the LBRY protocol and project. View her review on [spee.ch](https://spee.ch/3d/LBRY). + +![CryptoCandor](https://spee.ch/ef8cb7be92327f2af7b16917847fdcbfedf0692f/cryptocandor-lbry.jpg) + + + + + +#### FONK World: +David Heath and Mike Little are teaming up to create an animated humorous video guide to LBRY called “FONKn' LBRY.” The animation is intended to appeal to a larger audience and explain the workings of the LBRY protocol in a whimsical way. + +![FONK World](https://spee.ch/36c08e0463b8247c8a53990f9634c4ad473cc9d1/fonk-lbry-sm.jpg) + + + + +#### LBRY Pi TV: +[Discord](https://chat.lbry.io) user Madiator2011 is creating a plug and play device to view LBRY video content on television. At its core, the device uses a Raspberry Pi computer to stream LBRY videos. You can watch the progress and follow along by visiting [Madiator2011’s Github repository](https://github.com/kodxana/LBRY-Pi-TV). + +#### UNH Hackathon: +LBRY is partnering with the University of New Hampshire to present a hackathon for UNH students this fall! Prizes range up to $1000 worth of LBC. Date and additional details to be announced soon. + +#### Matt Sokol Development Grant: +Musician and LBRY contributor Matt Sokol approached us with a unique LBRY.fund request: he asked us to fund him to work on a special project using a custom [Spee.ch Multisite](https://github.com/lbryio/spee.ch) installation to host beautifully typeset classical books. We said yes! If you’d like to hear some of Matt’s music, you can find it on the LBRY app: [lbry://@heymattsokol.](https://open.lbry.io/@heymattsokol) + + + + +Do you have an idea for a project using or promoting the LBRY Protocol? We may pay you to create your vision. [Visit the LBRY.fund for details.](https://lbry.fund) + +![LBRY.fund](https://spee.ch/2/lbry-fund.png) + +### LBRY Stack Exchange Community Enters Commitment Phase! +The Stack Exchange LBRY Protocol site grew last month, and has now entered the Commitment phase! The real work begins now: we need 200 people to commit to use our Stacke Exchange site, and 100 of them must be active participants on other Stack Exchange forums (have 200+ reputation). Please give us a hand: +[View the LBRY Stack Exchange site here](https://area51.stackexchange.com/proposals/118455/lbry-protocol) +Stack Exchange Q&A site proposal: LBRY + +### LBRY is hiring! +[Come join team Content Freedom!](https://lbry.io/join-us) We have the following positions open: Blockchain Engineer, Protocol Engineer, and API Engineer. We’d love for you to join us, or if you have a friend you think would be interested, we pay a $5,000 bounty if we hire them. + +### Youtube Sync Updates {#youtube-updates} +After being put on a forced hold due to a much needed work on our infrastructure, we were able to restart syncing several channels in the queue - both for existing and new YouTubers. +Enhancements to the code made it possible to publish several videos concurrently, rather than 1 at a time, which allows us to process large channels in a matter of minutes. Thanks to the continuous effort from the Protocol team, publishing has gotten much smoother and the whole process requires less and less manual intervention. + +We also expanded our fleet of workers to 8 servers making it possible to keep up with the incoming queue of new channels, In order to support this more distributed effort, we implemented a solution that allows us to track every single video published to LBRY, thus allowing greater oversight into the overall sync process. + +Just a few days ago we crossed one big milestone: 100,000 videos were successfully published to LBRY through the YT sync program and more than 1,000 channels are now streaming on our network. Big numbers come with great responsibilities - our current focus is on making the process even faster and more distributed in order to clear out the current queue and then keep every single channel up to date with the very latest videos published on YouTube. + +### LBRY.tech Update {#lbry-tech} +Underneath the hood, lbry.tech received a major overhaul in the past month which should allow for a smoother development process going forward - you can track all the progress on our [GitHub repository](https://github.com/lbryio/lbry.tech). Along with the Protocol and Blockchain teams, we’ve finalized on an API format for both projects and implemented it on lbry.tech, check out the [preview here](https://lbry.tech/api). The next steps are to overhaul the Tour portion which walks a user through a few different interactions with the LBRY protocol and finish filling in the Build/Contribute areas. + +![lbrytech publish](https://spee.ch/b19b42a753612cef1079c6083686df7ca2550e66/lbry-tech-ecosystem-early-preview.jpeg) + +# Want to develop on the LBRY protocol? +All of our code is open source and available on [GitHub](https://github.com/lbryio). Are you a developer and want to find out more? Check out our [general contributing guide](https://lbry.io/faq/contributing) and our LBRY App specific contributing [document](https://github.com/lbryio/lbry-app/blob/master/CONTRIBUTING.md). Please be patient with us while we improve our technical documentation. In the next few weeks we’ll be releasing [lbry.tech](#lbry-tech), a technical reference / guide website which will be developer and contributor focused to drive more apps and services on top of LBRY. + +[Back to **Development Updates**](#dev-updates) + +Thanks for supporting LBRY - stay tuned for more news and updates! And if you haven’t downloaded the [LBRY app](https://lbry.io/get?auto=1) yet, what are you waiting for? diff --git a/content/news/179-is-everything-better-on-the-blockchain.md b/content/news/179-is-everything-better-on-the-blockchain.md new file mode 100644 index 00000000..6ca01f9e --- /dev/null +++ b/content/news/179-is-everything-better-on-the-blockchain.md @@ -0,0 +1,165 @@ +--- +author: samuel-lbryian +title: 'Is Everything Better On The Blockchain?' +date: '2018-08-20 08:08:08' +cover: 'Growing-Blockchain.png' +category: blog +--- +It’s 2018 and everyone’s scrambling to be the first to put something "on the blockchain". + +A few strange examples include the following: + +All Sports ($SOC) promises to bring sports to the blockchain. + +Game.com ($GTC) promises to bring gaming to the blockchain. + +PotCoin ($POT) promises to bring- well, pot, to the blockchain. + +The pertinent question is: do we really need a blockchain to watch sports, game out, or toke up? + +Probably not. + +That being said, when I first heard of Bitcoin I was convinced that we didn’t need it. I was spending my dollars and they seemed to be working just fine as a form of money. + +Two years after hearing about Bitcoin for the first time I heard BitPay CEO, Stephen Pair, thoroughly explain the concept of Bitcoin and blockchain. He stated the major advantages: deflationary, decentralized, and faster. I was pretty convinced that we would need Bitcoin as a society and I bought some BTC at around $4K (though buying it at $400 would’ve been a lot nicer). + +How do I know I’m not making the same mistake this time? What if PotCoin is the next Bitcoin? What if I’m missing out on... **_THE FUTURE?!?_** + +Perhaps the founders of these obscure applications of blockchain technology are derivatives of the visionary Satoshi Nakamoto. Maybe we’ll look up to them one day for their foresight. Maybe stoners will look back and wish they had invested their money in PotCoin instead of just spending it on pot for their next joint. Maybe, just maybe, an investment in PotCoin today will yield 420X returns in the next few years. + +A more likely scenario: the creators of these cryptocurrencies have no intentions of actually building anything useful. Instead, they are hyping their narrow application of "blockchain" while the word is buzzing in order to garner disproportionate funding from speculators in the hopes of winning big in a pump and dump. + +Let’s examine the websites of these three coins listed above and see if there is a real problem they’re trying to solve. + +Note that these coins were picked randomly and that there are MANY more cryptocurrencies offering little to no value that this article won’t examine. + +There are a number of telling signs from the home page of each coin’s respective website that these projects are attempting to cash in on the hype of blockchain. I’ll cover 3 red flags for each site and analyze the "value" of their project. + +## Are sports better on the blockchain? + +![All Sports Coin](https://staging.spee.ch/d389f79f88b7008508f71f4964e9b5f858ad91c0/soc-coin.png) + +Red flags: + +1. Half the page is covered by endorsements from "Super Football Stars" + +2. The sentence starts with "With the increasing popularity of blockchain technology" and ends with “which is a sun-rising industry all over the world.” + +3. No problems or specific solutions related to the sports industry are mentioned + +Do sports belong on the blockchain? + +This is a question that All Sports Coin seems to take for granted. Because blockchain is "popular" (it’s not popular actually, just saying “blockchain” is what’s popular) and a couple of “Super Football Stars” endorse SOC, it **_MUST_** be legit. + +Now, to their credit, at least they have a "Technical white paper draft". This does lay out some application ideas such as prediction markets and IP support for the sporting industry. + +That being said, the site doesn’t leave you with the impression that there is a problem in the sports industry that is lacking a blockchain solution specifically. + +It seems that the white paper fabricates some solutions to problems that don’t actually exist. + +If there was one solution they might’ve been offering it would be the a decentralized sports prediction market. The thing is, there’s already a blockchain project, [Augur](https://www.augur.net), tackling the core issue of betting/prediction markets as a whole. At this point in time it seems far more likely to take off than SOC. Unsurprisingly, the site does not mention any advantages it has over Augur. + +The creators of SOC seem to take for granted that the sports industry needs its own blockchain, and that having a couple of famous soccer player faces on the front page of the website will surely be a way to draw speculators to invest in their token. This is the sport of pump and dump, and you have to be a bad sport to play it. + +## Is gaming better on the blockchain? + +![Game.com](https://staging.spee.ch/9fdb30146b3a78c6dffdb78edc95f16c29b38743/game-coin.png) + +Red flags: + +1. The header "The future of gaming" and caption “A new world of gaming” scream ambiguity and lack of a specific solution + +2. The video tells you nothing about what Game.com is actually doing + +3. Game.com flashes the fact that it is "Game.com" above all else + +The biggest thing Game.com has going for it seems to be that it’s Game **_.com_** + +The video attempts to combine cringe-worthy stock music and nebulous imagery to create a buy-in for Game.com’s overwhelmingly vague mission of "creating a global gaming and entertainment platform for blockchain digital currencies." + +If you browse through the site more you’ll end up finding a couple of games: ‘Battle Pets’ and ‘love.pet’. I was actually able to find a version of ‘Battle Pets’ called AnimalTower in the iOS store. Thing is, it has absolutely no relation to blockchain from what I can tell. + +![Animal Wars](https://staging.spee.ch/c22fae94e884eaf37a5b958d71d7e7e762e24c14/animal-wars.png) + +When you click on the "Start Game" on the link from Game.com’s site you get an error message in Chinese: ![Chinese Error Message](https://staging.spee.ch/4ccaacf663871c29e9266a912795cded5a6f480b/chinese-error.png) + +I have to give them credit for having a subtle backdrop of animals from the game in the background, but would have liked to see the game open and see a wallet of some sort so that I understand how cryptocurrency relates to the game. + +![Animal Tower](https://staging.spee.ch/665668ecf71fa8e676df7e17e6956550ce4a3cc0/animal-tower.png) + +Me losing a game of Animal Tower (not on the blockchain). + +I think me going to download the game in the iOS store and having a successful experience (or unsuccessful considering I was pretty bad at it) demonstrates that most games probably won’t have much of a reason to be on the blockchain unless there’s value in having a decentralized ledger for the gaming experience. + +## Is marijuana better on the blockchain? + +![PotCoin Page](https://spee.ch/d08289202108536390d5731c720d883a2c33f265/potcoin-page.png) + +Red flags: + +1. The section under PotCoin redundantly says "PotCoin is.." 4 times + +2. One sentence claims that it is the solution to the global marijuana industry but fails to state why that is the case. + +3. The header "Banking for the cannabis industry" is a misnomer. Cryptocurrencies are antithetical to “banking” and banks aren’t going to convert your PotCoin to dollars. + +Like Game.com’s domain, the biggest thing Potcoin seems to have going for it is the name. + +This can be seen by the repetitive use of ‘PotCoin’ in the first paragraph. The site seems to be attempting to instill into your psychology the fact that this *will* be the coin uses for all future pot purchases because after all, it’s PotCoin- how could it not be the pot-purchasing coin of the future? + +Claiming to be the solution to an emerging industry is a **bold** claim. But the website fails to back it up. + +Cryptocurrencies probably do have a place in the world of substances- the silk road was a haven for buying and selling pot (and less benign substances) and cryptos like Bitcoin and Monero thrive on the dark web. + +PotCoin fails to mention why it’s the solution to the world of legal marijuana and not Bitcoin (similar to All Sports Coin failing to mention any advantages over Augur). All of the advantages that PotCoin offers over U.S. dollars are also offered by the more ubiquitous and secure Bitcoin. If I had to guess, more pot has probably been purchased with Bitcoin and other privacy coins like Monero or Dash than PotCoin. + +Even High Times doesn’t respect PotCoin enough to let in in on their IPO while they DO accept Bitcoin and Ethereum. Tweeting at High Times that they need to accept PotCoin but not providing any reasons why it’s better than Bitcoin or Ethereum is pretty unconvincing… + +![PotCoin Tweet](https://staging.spee.ch/bc12ceb5eaa47e1ae348a711b29a3f4ca0a37f53/potion-hightimes.png) + +Maybe High Times doesn’t want to associate itself with Dennis Rodman, who was sponsored by PotCoin [on his trip to Singapore(note the PotCoin shirt) for the North Korea summit this year](https://www.youtube.com/watch?v=TI5KULypIdw) and a trip to North Korea last year. + +PotCoin's website also claims to be a banking solution for the cannabis industry (not true) despite also claiming to be transferable without banks (true). + +"PotCoin is a… ...banking solution for the $100 billion global legal marijuana industry." + +"PotCoins are transferred directly from person to person via the net, without going through a bank or clearing house." + +Cryptocurrencies are not banking solutions, but banking alternatives. If you want to accept a cryptocurrency at your dispensary and have the funds flow into your bank account, you would need to use a payment service like BitPay, which unsurprisingly only accepts Bitcoin and it’s rivalrous brother Bitcoin Cash, not PotCoin! + +What’s worth noting is that there is another pot-related coin that I think might just blow PotCoin out of the bong water, and it’s called [Tokes.](https://tokesplatform.org) + +What I love about their site is that they actually explain the industry specific problems that cryptocurrency can help solve, rather than repeatedly spewing blockchain jargon. + +![Tokes Blockchain](https://spee.ch/83262c3b22ed9b5dbd77a6f201729a77a1c418a7/tokes.png) + +Rather than just creating a coin to pump and dump, Tokes is building a solution for various aspects of the cannabis industry- like supply chain and compliance from cultivation to out-of-the-door packaging. It actually asks the question "Why does cannabis need cryptocurrency?" instead of taking the answer for granted. + +Platforms like Tokes and Augur are more obscure than Bitcoin, but show promise in their solution by being specific in how blockchain technology can solve real problems outside of the inflation/Federal Reserve printing money problem. + +Platforms like PotCoin, All Sports Coin, and Game.com seem to merely play off the hype that has been generated by Bitcoin and other cryptocurrencies, in order to boost the price of their coin rather than solving problems. + +So, is everything really better on the blockchain? + +The answer that time will tell what things are better on it and what things can be left alone. + +After researching these sites, my conclusion is that there will be a multitude of applications for the blockchain, maybe many more than we can imagine at the moment. + +The issue is that so many companies are out there creating hype for their " on the blockchain" when might be better off the blockchain. + +If you’re looking to speculate on projects, don’t fall for projects that aren’t explaining the problems that they’re solving. Look for projects like Tokes that are willing to ask and answer the question "Why does industry need blockchain/cryptocurrency?" These companies are far more likely to actually create something of value and be a good coin to HODL for the long run. + +## Outro: Is content really better on the blockchain? + +We’re so anti-hype at LBRY that it’s almost a fault. We don’t even use the word "blockchain" or “cryptocurrency” on our homepage, [lbry.io](https://lbry.io). + +This is because we see the value of LBRY transcending the hype around blockchain. Our mantra isn’t "HODL" (Hold), but “BUIDL” (Build). + +The problems that we’re solving are explained in this [essay](https://lbry.io/what). + +"LBRY is the first digital marketplace to be controlled by the market’s participants rather than a corporation or other 3rd-party. It is the most open, fair, and efficient marketplace for digital goods ever created, with an incentive design encouraging it to become the most complete." + +We see our blockchain as solving the problem of phenomena like the infamous "adpocalypse" of YouTube, where big corporations decide who gets paid and who doesn’t, regardless of whose content is truly in demand and worth monetization. + +We think that the best way for creators to make money is by being by paid by the consumers of their content directly, without a third party- which *does* require a blockchain. + diff --git a/content/news/19-free-lbry-credits-come-and-get-em.md b/content/news/19-free-lbry-credits-come-and-get-em.md index 18c0107a..d8ac50f0 100644 --- a/content/news/19-free-lbry-credits-come-and-get-em.md +++ b/content/news/19-free-lbry-credits-come-and-get-em.md @@ -7,13 +7,13 @@ date: '2015-11-24 19:43:59' We are fast approaching the deadline to earn free LBRY credits (LBC) by being an alpha tester of the LBRY protocol. That’s right – we’re giving away 1,000 LBC to anyone who just **tries** to install LBRY, then completes our survey. [Get it here](https://lbry.io/get) and be the first on your block with LBC. A warm shoutout to Amanda Johnson, who reminded her viewers of this opportunity in the most recent regular episode of [The Daily Decrypt](https://www.youtube.com/channel/UCqNCLd2r19wpWWQE6yDLOOQ/featured). -

The Daily Decrypt plugs LBRY

+

The Daily Decrypt plugs LBRY

Here’s what she had to say: > "LBRY is an alpha stage protocol right now, which would make content viewable on URIs [Uniform Resource Identifier] which are not HTTP, but rather LBRY-specific URIs. Basically, combining the functionality of BitTorrent with the incentives of Bitcoin and the file storage capabilities of STRJ. They’re looking for people to try to download and use their alpha software, which has to be done from the command line. If you’re comfortable with that and you’re willing just to download it and tell them how it felt for you, they’ll give you 1,000 LBRY tokens. You just fill out a survey once you’ve completed the download process, send it back to them, and you get the bits." -You may remember Amanda from her guest blog post here last month – [LBRY Gets Content Creators Out of Precarious Position](http://blog.lbry.io/lbry-gets-content-creators-out-of-precarious-position-daily-decrypts-amanda-b-johnson/). +You may remember Amanda from her guest blog post here last month – [LBRY Gets Content Creators Out of Precarious Position](https://blog.lbry.io/lbry-gets-content-creators-out-of-precarious-position-daily-decrypts-amanda-b-johnson/). -LBRY also owes a thank you to Joël Valenzuela of Liberty Upward, who wrote an article about us last week – [LBRY Leads the Decentralized Information Revolution](http://libertyupward.com/lbry-leads-the-decentralized-information-revolution/). We’re excited to watch people like Joël get excited about LBRY. Free market and blockchain enthusiasts quickly grasp the powerful but simple evolution in content distribution LBRY represents. Joël writes: +LBRY also owes a thank you to Joël Valenzuela of Liberty Upward, who wrote an article about us last week – [LBRY Leads the Decentralized Information Revolution](https://libertyupward.com/lbry-leads-the-decentralized-information-revolution/). We’re excited to watch people like Joël get excited about LBRY. Free market and blockchain enthusiasts quickly grasp the powerful but simple evolution in content distribution LBRY represents. Joël writes: > "With LBRY, no one can stop you from publishing your content, or getting compensated. Power is exclusively in the individual’s hands. diff --git a/content/news/20-open-a-valve-to-gush-classic-movies.md b/content/news/20-open-a-valve-to-gush-classic-movies.md index d5c4ad74..a49d30c3 100644 --- a/content/news/20-open-a-valve-to-gush-classic-movies.md +++ b/content/news/20-open-a-valve-to-gush-classic-movies.md @@ -6,9 +6,9 @@ date: '2015-12-02 19:04:36' Hundreds of classic films remain locked away in musty movie studio vaults, just waiting for somebody to make them available to the public. -

The Wild Party, featuring Clara Bow

+

The Wild Party, featuring Clara Bow

-Movie history experts estimate some 700 films from the 1930s and 40s are sequestered in the vaults of Universal Pictures alone. As the LA Times points out in a recent article, [a lot of people would love to have access to these classics](http://www.latimes.com/business/hiltzik/la-fi-hiltzik-20151025-column.html): +Movie history experts estimate some 700 films from the 1930s and 40s are sequestered in the vaults of Universal Pictures alone. As the LA Times points out in a recent article, [a lot of people would love to have access to these classics](https://www.latimes.com/business/hiltzik/la-fi-hiltzik-20151025-column.html): >“Will McKinley, a New York film writer, is dying to get his hands on a copy of ‘Alias Nick Beal,’ a 1949 film noir starring Ray Milland as a satanic gangster. For classic film blogger Nora Fiore, the Grail might be ‘The Wild Party’ (1929), the first talkie to star 1920's ‘It’ girl Clara Bow, directed by the pioneering female director Dorothy Arzner. Film critic Leonard Maltin says he'd like to score a viewing of ‘Hotel Haywire,’ a 1937 screwball comedy written by the great comic director Preston Sturges.” diff --git a/content/news/23-bravenewcoin.md b/content/news/23-bravenewcoin.md index 98bf6f47..94030075 100644 --- a/content/news/23-bravenewcoin.md +++ b/content/news/23-bravenewcoin.md @@ -6,11 +6,11 @@ date: '2015-12-17 21:55:57' If you haven't heard, LBRY has a little competition – Alexandria, "The People's Library". Now don't get us wrong; LBRY welcomes competition! In fact, we're enthusiastic supporters of anyone who wants to put more power in the hands of individuals by building more effective markets for information. Heck, we even engaged in some innocent crypto-flirting with Alexandria this week: -

LBRY & Alexandria crypto-flirt on Twitter

+

LBRY & Alexandria crypto-flirt on Twitter

Perhaps our biggest critique of Alexandria is their waste of money in buying all those vowels ;). -But to the point. [BraveNewCoin published the first side-by-side comparison of LBRY and Alexandria this week](http://bravenewcoin.com/news/alexandria-vs-lbry-which-will-be-the-file-sharing-application-of-the-next-generation/). The article is thorough and fair, highlighting what we believe to be one of LBRY's strongest asset: +But to the point. [BraveNewCoin published the first side-by-side comparison of LBRY and Alexandria this week](https://bravenewcoin.com/news/alexandria-vs-lbry-which-will-be-the-file-sharing-application-of-the-next-generation/). The article is thorough and fair, highlighting what we believe to be one of LBRY's strongest asset: >"Perhaps the most interesting difference is the naming system, which works a lot like the internet's Domain Naming System (DNS). diff --git a/content/news/25-huemer-joins-lbry-ethical-advisor.md b/content/news/25-huemer-joins-lbry-ethical-advisor.md index e2b15e0f..d1552bf2 100644 --- a/content/news/25-huemer-joins-lbry-ethical-advisor.md +++ b/content/news/25-huemer-joins-lbry-ethical-advisor.md @@ -12,7 +12,7 @@ Songwriter Steve Taylor painted a grim picture of our modern day ethical landsca We might have our moments of cynicism, but we don’t buy into the ethics-free business model prevalent in the world today. That’s why we are excited to announce the addition of Professor Michael Huemer as Ethical Advisor to LBRY Inc. -

Michael Huemer joins LBRY as Ethical Advisor

+

Michael Huemer joins LBRY as Ethical Advisor

Huemer is a rising star in the world of ethical philosophy, with several books to his name and a [hit TED talk](https://www.youtube.com/watch?v=4JYL5VUe5NQ). @@ -32,4 +32,4 @@ With decentralization at its core, LBRY is designed to be a “trustless system, “Professor Huemer studies a subject few will touch nowadays: ethics. He's known for starting from premises everyone can agree on and be arriving at profound conclusions – simply by following a logical train of thought,” Vine said. “If LBRY succeeds, the executive team will face decisions that will affect multitudes of people. I'm glad to have someone on board who can serve as an independent voice if we lose sight of our mission, or start rationalizing short-sighted decisions. Many people will question what use a company has for an Ethical Advisor; the better questions is why every company doesn't have one.” -*Want to join Professor Huemer and the rest of the LBRY team? [Become an Alpha Tester and receive 1,000 LBRY credits (LBC)](https://lbry.io/get).* \ No newline at end of file +*Want to join Professor Huemer and the rest of the LBRY team? [Become an Alpha Tester and receive 1,000 LBRY credits (LBC)](https://lbry.io/get).* diff --git a/content/news/27-lbry-adds-chief-growth-officer-josh-finer.md b/content/news/27-lbry-adds-chief-growth-officer-josh-finer.md index a1b25d20..7894e8a5 100644 --- a/content/news/27-lbry-adds-chief-growth-officer-josh-finer.md +++ b/content/news/27-lbry-adds-chief-growth-officer-josh-finer.md @@ -12,7 +12,7 @@ We’re excited to announce that Josh Finer will be “that guy” on the LBRY t Finer’s official title is Chief Growth Officer, but we just think of him as Mr. Problem Solver. -

LBRY's Chief Growth Officer, Josh Finer

+

LBRY's Chief Growth Officer, Josh Finer

Josh is as comfortable in the suit-and-tie world of business and finance as he is in the nerdy world of computers and coding. As he put it, “Walking the line between technical and business issues is my pastime.” @@ -37,4 +37,4 @@ Mike Vine, LBRY’s Technology Evangelist, said that he is thrilled to have a pr > “We’ve only just started working together, and I already see what Josh brings to the project. I am anxious to tell the world about using a blockchain to deliver content into every living room, and Josh is going to make sure that message reaches key audiences. We’re looking for developers, artists, and investors to come together to make this work. We’re already moving forward, but Josh will turn our steps into leaps.” -***

[Become a LBRY Alpha Tester and earn 1,000 LBRY credits (LBC)](https://lbry.io/get).

*** \ No newline at end of file +***

[Become a LBRY Alpha Tester and earn 1,000 LBRY credits (LBC)](https://lbry.io/get).

*** diff --git a/content/news/28-the-dmcas-chilling-effect-on-security-research-and-innovation.md b/content/news/28-the-dmcas-chilling-effect-on-security-research-and-innovation.md index 2f5bc9ae..9aaff489 100644 --- a/content/news/28-the-dmcas-chilling-effect-on-security-research-and-innovation.md +++ b/content/news/28-the-dmcas-chilling-effect-on-security-research-and-innovation.md @@ -4,9 +4,9 @@ title: The DMCA's Chilling Effect on Security Research and Innovation date: '2016-01-14 16:31:51' --- -You walk into a Barnes and Noble, pick up a copy of *[Look Me in the Eye](http://www.amazon.com/Look-Me-Eye-Life-Aspergers/dp/0307396185)*, hand the cashier money, and leave the store. The book now belongs to you, right? Of course, it does. You are free to write notes in the margins, sell it second-hand to a friend, or even rip it up if you felt so inclined. What you can’t do is copy portions of it and claim them as your own work; you own your copy of the book, but not the copyright. +You walk into a Barnes and Noble, pick up a copy of *[Look Me in the Eye](https://www.amazon.com/Look-Me-Eye-Life-Aspergers/dp/0307396185)*, hand the cashier money, and leave the store. The book now belongs to you, right? Of course, it does. You are free to write notes in the margins, sell it second-hand to a friend, or even rip it up if you felt so inclined. What you can’t do is copy portions of it and claim them as your own work; you own your copy of the book, but not the copyright. -

The Battle of Copyright

+

The Battle of Copyright

This is pretty straightforward and doesn’t violate most people’s understanding of copyright and ownership. But let's say you skipped the Barnes and Noble and instead went to Walmart to buy a Sony PS3. Is it any different? Actually it is. @@ -14,9 +14,9 @@ When the PS3 was released, many tech enthusiasts were eager to buy such a powerf Copyright has gone far beyond its original intent and beyond how most people understand it to work. Instead of being used to prevent copying, it is now also used to prevent modification – even if there is no commercial angle to the modification and the only purpose is better satisfying the desires of the owner. Maybe taking notes in the margin of your favorite book isn’t so clearly legal after all; the fact that such an argument could be made demonstrates the ridiculousness of the DMCA and how it hurts customers. -Auto manufacturers have exploited the you-own-what-you-buy-except-for-when-we-don’t-like-how-you-use-it DMCA too. Want to reprogram your car’s engine control unit? You might be violating the DMCA. Really, any work done on the electronics in a car risks violating the DMCA. This exposed tinkerers and independent shops alike to a tremendous risk, leaving official dealerships as the only safe route for these repairs. But fret not, all of that changed this past fall. In a first, [the government has issued an exception to the DMCA](http://stfi.re/dbgdwo) to explicitly allow tinkering with automotive electronics and software. +Auto manufacturers have exploited the you-own-what-you-buy-except-for-when-we-don’t-like-how-you-use-it DMCA too. Want to reprogram your car’s engine control unit? You might be violating the DMCA. Really, any work done on the electronics in a car risks violating the DMCA. This exposed tinkerers and independent shops alike to a tremendous risk, leaving official dealerships as the only safe route for these repairs. But fret not, all of that changed this past fall. In a first, [the government has issued an exception to the DMCA](https://stfi.re/dbgdwo) to explicitly allow tinkering with automotive electronics and software. -So what pushed the government to do this? In large part, it was the recent Volkswagen scandal. The Electronic Frontier Foundation (EFF) [argued that the DMCA had prevented independent shops and tinkerers from testing and identifying VW’s deception](http://stfi.re/beoyap) for years – and the government listened. That said, it’s a real shame that it takes a very public deception being uncovered to change the law. And it raises the question – how much deception, negligence, and incompetence is still being covered up in all of the areas without a DMCA exemption? Don’t expect an answer, because as the EFF has pointed out, the DMCA has a chilling effect on security research. +So what pushed the government to do this? In large part, it was the recent Volkswagen scandal. The Electronic Frontier Foundation (EFF) [argued that the DMCA had prevented independent shops and tinkerers from testing and identifying VW’s deception](https://stfi.re/beoyap) for years – and the government listened. That said, it’s a real shame that it takes a very public deception being uncovered to change the law. And it raises the question – how much deception, negligence, and incompetence is still being covered up in all of the areas without a DMCA exemption? Don’t expect an answer, because as the EFF has pointed out, the DMCA has a chilling effect on security research. Researchers of both the academic and DIY types steer clear of looking for such problems, because by finding them they may violate the DMCA and come under legal pressure. That means the only major efforts to root out security vulnerabilities and misrepresentations are under the table, and the hackers doing such work don’t tend to have the good of the public in mind. diff --git a/content/news/33-zarghamjoins.md b/content/news/33-zarghamjoins.md index 92e82f31..99a9054a 100644 --- a/content/news/33-zarghamjoins.md +++ b/content/news/33-zarghamjoins.md @@ -12,11 +12,11 @@ For those new to our project, LBRY harnesses the same blockchain technology unde Things are moving at breakneck speed... -**In February, we released an alpha version of LBRY's Graphical Interface on OS X El Capitan** and [unveiled our software publicly for the first time with a live demo](https://www.youtube.com/watch?v=nu-yk5NYy1o) at [Liberty Forum 2016](http://nhlibertyforum.com) (sorry for A/V quality - we're saving money by doing all recording on a stack of spare VHS tapes from 1982). +**In February, we released an alpha version of LBRY's Graphical Interface on OS X El Capitan** and [unveiled our software publicly for the first time with a live demo](https://www.youtube.com/watch?v=nu-yk5NYy1o) at [Liberty Forum 2016](https://nhlibertyforum.com) (sorry for A/V quality - we're saving money by doing all recording on a stack of spare VHS tapes from 1982). **Today, we are excited to announce the addition of data scientist Dr. Michael Zargham to the LBRY team.** Zargham is going to open up a lot of avenues for us through his intense intellect and professional associations. He’s the guy that can take complex datasets and mathematical equations and put them to practical use. He earned a Ph.D. in electrical engineering from the University of Pennsylvania and has over a decade of experience building problem-solving tools for businesses. Zargham has [more than a dozen academic papers to his credit](https://www.linkedin.com/in/mczargham), and his work has been cited extensively. -**The crypto community will have an opportunity to meet the LBRY team this weekend at the [2016 MIT Bitcoin Expo](http://mitbitcoinexpo.org).** This will be our first chance to show off the LBRY platform to the community that literally built our foundation. +**The crypto community will have an opportunity to meet the LBRY team this weekend at the [2016 MIT Bitcoin Expo](https://mitbitcoinexpo.org).** This will be our first chance to show off the LBRY platform to the community that literally built our foundation. We’re excited about connecting with our roots, but make no mistake: our mission is to grow a platform that will put cryptocurrency in every living room – whether or not users care that it’s there! diff --git a/content/news/35-why-doesnt-lbry-just-use-bitcoin.md b/content/news/35-why-doesnt-lbry-just-use-bitcoin.md index f155b91c..280708b8 100644 --- a/content/news/35-why-doesnt-lbry-just-use-bitcoin.md +++ b/content/news/35-why-doesnt-lbry-just-use-bitcoin.md @@ -32,7 +32,7 @@ If LBRY starts growing exponentially, we don’t want to worry about contributin One of the main draws of Bitcoin has always been its relative decentralization of control and independence from existing legal and financial systems. Similarly, using LBC as an “appcoin” gives LBRY some healthy autonomy from Bitcoin while allowing for the technical innovations explained above. -An appcoin, as described by LBRY’s Mike Vine on [Decentral Vancouver’s “#Blocktalk”](http://blog.lbry.io/lbry-app-sneak-peak-big-questions-answered-lbry-on-blocktalk-last-night/) and in [CoinTelegraph](http://cointelegraph.com/news/the-appcoin-revolution-interview-with-mike-vine-of-lbry), is a cryptocurrency that is designed specifically to power an application, with only that application’s precise functions in mind. The purpose of this appcoin is not to compete for a better form of money than Bitcoin, but to function as a special purpose tool in ways Bitcoin cannot. Creating a special tool in the form of a new coin allows us to start fresh, customizing features like the initial allocation and blockchain rules. +An appcoin, as described by LBRY’s Mike Vine on [Decentral Vancouver’s “#Blocktalk”](http://blog.lbry.io/lbry-desktop-sneak-peak-big-questions-answered-lbry-on-blocktalk-last-night/) and in [CoinTelegraph](http://cointelegraph.com/news/the-appcoin-revolution-interview-with-mike-vine-of-lbry), is a cryptocurrency that is designed specifically to power an application, with only that application’s precise functions in mind. The purpose of this appcoin is not to compete for a better form of money than Bitcoin, but to function as a special purpose tool in ways Bitcoin cannot. Creating a special tool in the form of a new coin allows us to start fresh, customizing features like the initial allocation and blockchain rules. In the early days of our protocol, LBRY Inc. will be making a concerted effort to deploy LBC in a non-neutral way. We will be incentivizing early adopters, amazing content publishers, and even nonprofits which share our vision of a free and open internet. We will be retaining a portion to finance the continued development of the ecosystem. LBRY Credits will work on behalf of development of the LBRY content distribution network, not the other way around. diff --git a/content/news/36-built-for-artists-by-autists-lbry-takes-autism-personally.md b/content/news/36-built-for-artists-by-autists-lbry-takes-autism-personally.md index d862a758..98647004 100644 --- a/content/news/36-built-for-artists-by-autists-lbry-takes-autism-personally.md +++ b/content/news/36-built-for-artists-by-autists-lbry-takes-autism-personally.md @@ -6,10 +6,10 @@ date: '2016-03-21 20:06:18' Understanding autism is personal for us. -![Built for Artists by Autists: LBRY Takes Autism Personally](http://i.imgur.com/gDip22e.jpg) +![Built for Artists by Autists: LBRY Takes Autism Personally](https://i.imgur.com/gDip22e.jpg) -A recent New York Times [op-ed](http://well.blogs.nytimes.com/2016/03/18/an-experimental-autism-treatment-cost-me-my-marriage/) by John Elder Robison tells the story of his life before and after taking part in a groundbreaking experiment into using an innovative electromagnetic therapy to perhaps remediate a core disability of autism. Robison, who is on the autism spectrum had a successful career and family life, but nevertheless faced personal challenges and decided to try TMS, or transcranial magnetic stimulation, to gain greater insight into the emotional cues and nonverbal communications of other people. Researchers hoped to gain better insight into how TMS might help address these challenges. Robison hopes these insights will one day form the basis of therapies, and he experienced great rewards - even from this early stage experiment. The treatment granted him the ability to read better and feel emotions, but it was quite a ride. His marriage fell apart, and many of his personal relationships became strained for years. Conversely, with new eyes, he formed many new relationships, he re-married, and many others relationships grew stronger. +A recent New York Times [op-ed](https://well.blogs.nytimes.com/2016/03/18/an-experimental-autism-treatment-cost-me-my-marriage/) by John Elder Robison tells the story of his life before and after taking part in a groundbreaking experiment into using an innovative electromagnetic therapy to perhaps remediate a core disability of autism. Robison, who is on the autism spectrum had a successful career and family life, but nevertheless faced personal challenges and decided to try TMS, or transcranial magnetic stimulation, to gain greater insight into the emotional cues and nonverbal communications of other people. Researchers hoped to gain better insight into how TMS might help address these challenges. Robison hopes these insights will one day form the basis of therapies, and he experienced great rewards - even from this early stage experiment. The treatment granted him the ability to read better and feel emotions, but it was quite a ride. His marriage fell apart, and many of his personal relationships became strained for years. Conversely, with new eyes, he formed many new relationships, he re-married, and many others relationships grew stronger. -John’s son, Jack Robison, is LBRY’s own core developer. Jack, also on the autism spectrum (and who once [faced 60 years in prison](http://blog.lbry.io/jack-robison-escaped-60-years-in-prison-now-hes-revolutionizing-the-internet/) for misunderstood high school chemistry experiments), has been a steadfast advocate for autism rights. To say autism awareness and understanding is an issue near and dear to our hearts is an understatement. +John’s son, Jack Robison, is LBRY’s own core developer. Jack, also on the autism spectrum (and who once [faced 60 years in prison](https://blog.lbry.io/jack-robison-escaped-60-years-in-prison-now-hes-revolutionizing-the-internet/) for misunderstood high school chemistry experiments), has been a steadfast advocate for autism rights. To say autism awareness and understanding is an issue near and dear to our hearts is an understatement. -What’s more, we plan to put our credits where our mouth is and grant a significant sum of LBRY credits to [William and Mary’s neurodiversity program](http://www.wm.edu/sites/neurodiversity/) to further research, education, and outreach into autism. +What’s more, we plan to put our credits where our mouth is and grant a significant sum of LBRY credits to [William and Mary’s neurodiversity program](https://www.wm.edu/sites/neurodiversity/) to further research, education, and outreach into autism. diff --git a/content/news/51-launch-a-disaster.md b/content/news/51-launch-a-disaster.md index f6f00b12..fbaa0c42 100644 --- a/content/news/51-launch-a-disaster.md +++ b/content/news/51-launch-a-disaster.md @@ -1,4 +1,4 @@ ---- +--- author: samuel-lbryian title: 'LBRY Launch A Disaster' date: '2016-07-20 00:06:18' @@ -17,7 +17,7 @@ A groundbreaking content distribution platform is useless without great content. The film stars David Cross (*Arrested Development, Mr. Show*), Julia Stiles (*The Bourne Ultimatum, Dexter*), and was produced by some of Hollywood’s brightest rising talent. The setting for this dark comedy is a regular Sunday brunch attended by four couples. The gathering is filled with the tension, and awkwardness audiences have grown to love in Cross’ performances. The strained brunch goes even further off the rails when a deadly chemical attack occurs in the nearby city, apparently dooming the attendees to an unpleasant death. -*It’s a Disaster* is [Certified Fresh on Rotten Tomatoes](https://www.rottentomatoes.com/m/its_a_disaster/). It won [3 film festival awards](http://www.imdb.com/title/tt1995341/awards?ref_=tt_awd) when it was released, including Best Feature at both the Edmonton International and the New Orleans Film Festivals. Jim Emerson reviewed it for [RogerEbert.com](http://www.rogerebert.com/reviews/its-a-disaster-2013), giving it a solid recommendation: +*It’s a Disaster* is [Certified Fresh on Rotten Tomatoes](https://www.rottentomatoes.com/m/its_a_disaster/). It won [3 film festival awards](https://www.imdb.com/title/tt1995341/awards?ref_=tt_awd) when it was released, including Best Feature at both the Edmonton International and the New Orleans Film Festivals. Jim Emerson reviewed it for [RogerEbert.com](https://www.rogerebert.com/reviews/its-a-disaster-2013), giving it a solid recommendation: >“The movie’s funniest touches are quiet flashes of character, expertly timed and nimbly played by a deft ensemble. *It’s a Disaster* is consistently funny, but you wince more often than you laugh out loud. It’s like a Christopher Guest improvisational farce with the volume turned down to 5.” @@ -29,7 +29,7 @@ Reilly said he hoped exposing users to Oscilloscope Labs would “lead viewers d >”*It’s a Disaster* is the quintessential indie comedy with something for everyone. We couldn’t have been luckier – this film is the perfect way to celebrate the launch of LBRY.” -If you are a LBRY beta user and haven’t watched *It’s a Disaster* yet, [check it out](lbry://itsadisaster). We think you’ll enjoy the film. And the more people who watch and love *It’s a Disaster*, the more great movies LBRY will get in the future. +If you are a LBRY beta user and haven’t watched *It’s a Disaster* yet, [check it out](https://open.lbry.io/itsadisaster). We think you’ll enjoy the film. And the more people who watch and love *It’s a Disaster*, the more great movies LBRY will get in the future. diff --git a/content/news/65-animal-robot-on-lbry.md b/content/news/65-animal-robot-on-lbry.md index 9e0d3d3a..737a83ad 100644 --- a/content/news/65-animal-robot-on-lbry.md +++ b/content/news/65-animal-robot-on-lbry.md @@ -22,10 +22,10 @@ lbry://rickandmortyrapgod AnimalRobot spins all night, every night on LBRY: -- *[The Blow Up: Blackalicious vs. Mr. Fox](lbry://theblowup)* – Wes Anderson + Blackalicious = Profit??? -- *[Hypnotize: Notorious B.I.G. vs. Earl Sinclair](lbry://hypnotize)* – The dad joke of a dinosaur channels his best Biggie impression. -- *[Dopeman: Redman vs. Homer Simpson](lbry://dopeman)* – Homer lends Redman a hand in selling his latest album to remixed results. -- *[Rap God: Eminem vs. Rick and Morty](lbry://rickandmortyrapgod)* – Rap God pt. 2 -- *[Straight Outta Compton: A Los Santos Story](lbry://gtasoc)* – Franklin and friends take to Los Santos with their own take on NWA’s legendary hit, Straight Outta Compton +- *[The Blow Up: Blackalicious vs. Mr. Fox](https://open.lbry.io/theblowup)* – Wes Anderson + Blackalicious = Profit??? +- *[Hypnotize: Notorious B.I.G. vs. Earl Sinclair](https://open.lbry.io/hypnotize)* – The dad joke of a dinosaur channels his best Biggie impression. +- *[Dopeman: Redman vs. Homer Simpson](https://open.lbry.io/dopeman)* – Homer lends Redman a hand in selling his latest album to remixed results. +- *[Rap God: Eminem vs. Rick and Morty](https://open.lbry.io/rickandmortyrapgod)* – Rap God pt. 2 +- *[Straight Outta Compton: A Los Santos Story](https://open.lbry.io/gtasoc)* – Franklin and friends take to Los Santos with their own take on NWA’s legendary hit, Straight Outta Compton **Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Just can’t wait? If you’re a creator, skip our waiting list line for a chance to earn $1,000 in LBRY Credits at the same time. [Learn more here](https://lbry.io/publish). diff --git a/content/news/66-brookes-eggleston-character-design-forge.md b/content/news/66-brookes-eggleston-character-design-forge.md index d49199ba..53a9770c 100644 --- a/content/news/66-brookes-eggleston-character-design-forge.md +++ b/content/news/66-brookes-eggleston-character-design-forge.md @@ -1,4 +1,4 @@ ---- +--- author: reilly-smith title: 'Storytelling Art 101' date: '2016-09-15 00:06:00' @@ -12,14 +12,14 @@ We’re starting that trend this week with [Character Design Forge, AKA Brookes >”Characters aren’t just meant to sell people on an idea. Characters are vessels for feelings and personalities that can connect with your audience. With a well-designed character, our brains don’t really understand them to be artificial.” **- Brookes Eggleston** -The best place to start is *[5 Tips for Better Drawing](lbry://drawbetter)*. +The best place to start is *[5 Tips for Better Drawing](https://open.lbry.io/drawbetter)*. Once you’ve covered the basics, check out the rest of the courses: -- *[How to Be a Character Designer](lbry://characterdesigner)* – Learn the qualities that empower character designers in their work. -- *[Drawing a Comic Page from Start to Finish](lbry://drawingcomics)* – Walk through the steps taken to create a comic page in Photoshop. -- *[Play to Your Strengths](lbry://playtoyourstrengths)* – Time is finite, make sure you're really working on what you'd like to be. We're talking about the ways to play to your strengths and how to balance the internet treadmill with bigger projects. -- *[5 Ways to Make a Character More Likable](lbry://likeablecharacters)* – The appeal of a character is essential in getting an audience to like them. Use these tips to keep them in your corner. +- *[How to Be a Character Designer](https://open.lbry.io/characterdesigner)* – Learn the qualities that empower character designers in their work. +- *[Drawing a Comic Page from Start to Finish](https://open.lbry.io/drawingcomics)* – Walk through the steps taken to create a comic page in Photoshop. +- *[Play to Your Strengths](https://open.lbry.io/playtoyourstrengths)* – Time is finite, make sure you're really working on what you'd like to be. We're talking about the ways to play to your strengths and how to balance the internet treadmill with bigger projects. +- *[5 Ways to Make a Character More Likable](https://open.lbry.io/likeablecharacters)* – The appeal of a character is essential in getting an audience to like them. Use these tips to keep them in your corner. **Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Just can’t wait? If you’re a creator, skip our waiting list line for a chance to earn $1,000 in LBRY Credits at the same time. [Learn more here](https://lbry.io/publish). diff --git a/content/news/68-skate-yrself-clean.md b/content/news/68-skate-yrself-clean.md index 0d61f774..3af62c43 100644 --- a/content/news/68-skate-yrself-clean.md +++ b/content/news/68-skate-yrself-clean.md @@ -1,4 +1,4 @@ ---- +--- author: reilly-smith title: 'Skate, Relax, Enjoy' date: '2016-09-22 00:06:00' @@ -16,6 +16,6 @@ Part biography, part LCD Soundsystem music video, *SKATE* captures the American Janna’s six-minute short won the Grand Jury Prize at [Florida Film Festival for Best Short Doc](http://articles.orlandosentinel.com/2013-05-01/entertainment/os-florida-film-festival-winners-20130501_1_florida-film-festival-award-winners-audience-award), as well as being selected by [Nashville Film Festival](https://nashvillefilmfestival.org/news/full-short-film-lineup/) and [DOC NYC](http://www.docnyc.net/film/obsessions/). -It can be seen at **[lbry://skateyrselfclean](lbry://skateyrselfclean)** now. +It can be seen at **[lbry://skateyrselfclean](https://open.lbry.io/skateyrselfclean)** now. **Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Or if you’re a creator, skip our wait list for a chance to earn $1,000 in LBRY Credits. [Learn more here](https://lbry.io/publish). diff --git a/content/news/70-fleischer-superman.md b/content/news/70-fleischer-superman.md index 9da69c59..ca8a4754 100644 --- a/content/news/70-fleischer-superman.md +++ b/content/news/70-fleischer-superman.md @@ -1,4 +1,4 @@ ---- +--- author: reilly-smith title: 'It’s a Bird, It’s a Plane, It’s Pub Domain Superman!' date: '2016-09-29 00:06:00' @@ -16,14 +16,14 @@ We’re kicking off a trend of LBRY being the number one place to find top publi These are true gems in the hall of film history. Check out the first nine episodes today: -- [*Superman* (a.k.a. *The Mad Scientist*)](lbry://superman1940-e1) -- [*The Mechanical Monsters*](lbry://superman1940-e2) -- [*Billion Dollar Limited*](lbry://superman1940-e3) -- [*The Arctic Giant*](lbry://superman1940-e4) -- [*The Bulleteers*](lbry://superman1940-e5) -- [*The Magnetic Telescope*](lbry://superman1940-e6) -- [*Electric Earthquake*](lbry://superman1940-e7) -- [*Volcano*](lbry://superman1940-e8) -- [*Terror on the Midway*](lbry://superman1940-e9) +- [*Superman* (a.k.a. *The Mad Scientist*)](https://open.lbry.io/superman1940-e1) +- [*The Mechanical Monsters*](https://open.lbry.io/superman1940-e2) +- [*Billion Dollar Limited*](https://open.lbry.io/superman1940-e3) +- [*The Arctic Giant*](https://open.lbry.io/superman1940-e4) +- [*The Bulleteers*](https://open.lbry.io/superman1940-e5) +- [*The Magnetic Telescope*](https://open.lbry.io/superman1940-e6) +- [*Electric Earthquake*](https://open.lbry.io/superman1940-e7) +- [*Volcano*](https://open.lbry.io/superman1940-e8) +- [*Terror on the Midway*](https://open.lbry.io/superman1940-e9) -**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Know some great public domain content to share? Have high-quality scans? Email reilly@lbry.io for a trip to the front of the line. +**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Know some great public domain content to share? Have high-quality scans? Email [reilly@lbry.io](mailto:reilly@lbry.io) for a trip to the front of the line. diff --git a/content/news/71-credit-policy-third-quarter-report.md b/content/news/71-credit-policy-third-quarter-report.md index ce6c301b..f68f404b 100644 --- a/content/news/71-credit-policy-third-quarter-report.md +++ b/content/news/71-credit-policy-third-quarter-report.md @@ -1,4 +1,4 @@ ---- +--- author: samuel-lbryian title: 'Credit Policy and 3rd Quarter Credit Report' date: '2016-10-05 00:06:18' @@ -28,4 +28,4 @@ The [Third Quarter 2016 Report](https://lbry.io/faq/quarterly-report-3q-2016) is - No institutional credits were moved or spent. - No anticipated sales from operational reserve until at least the Second Quarter 2017. -**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Know some great public domain content to share? Have high-quality scans? Email reilly@lbry.io for a trip to the front of the line. +**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Know some great public domain content to share? Have high-quality scans? Email [reilly@lbry.io](mailto:reilly@lbry.io) for a trip to the front of the line. diff --git a/content/news/72-sporthocking.md b/content/news/72-sporthocking.md index 4746f24b..1b800d3a 100644 --- a/content/news/72-sporthocking.md +++ b/content/news/72-sporthocking.md @@ -1,4 +1,4 @@ ---- +--- author: reilly-smith title: 'Will the Real Slim Shady Please Sit Down' date: '2016-10-06 00:06:00' @@ -12,17 +12,17 @@ Hocker is the German word for stool. So Sporthocker literally means SportStool. ![Sporthocking](/img/news/sporthock-inline1.png) -In his first video, [TJ in Berlin](lbry://sporthock-berlin), Trenton “TJ” Rawdon shows us various tricks and moves taken from disciplines such as juggling, breakdancing, board sports and acrobatics. +In his first video, [TJ in Berlin](https://open.lbry.io/sporthock-berlin), Trenton “TJ” Rawdon shows us various tricks and moves taken from disciplines such as juggling, breakdancing, board sports and acrobatics. ![More sporthocking](/img/news/sporthock-inline2.png) -Inspired by guys freestyling tricks with homemade stools in the German city of Kiel, the Landschutz brothers designed the first Sporthocker prototype in 2006. Today, they own a Sporthocker brand, and product design company called [Salzig in Berlin, Germany](http://www.sporthocker.com/en/). They’ve travelled two European tours and hold annual events for the budding sport. +Inspired by guys freestyling tricks with homemade stools in the German city of Kiel, the Landschutz brothers designed the first Sporthocker prototype in 2006. Today, they own a Sporthocker brand, and product design company called [Salzig in Berlin, Germany](https://www.sporthocker.com/en/). They’ve travelled two European tours and hold annual events for the budding sport. This is a sport worth checking out. See the tour of Europe today: -- [*Hock Tour Madrid*](lbry://sporthock-madrid) -- [*Hock Tour Rome*](lbry://sporthock-rome) -- [*Hock Tour Paris*](lbry://sporthock-paris) -- [*Hock Tour Rotterdam*](lbry://sporthock-rotterdam) +- [*Hock Tour Madrid*](https://open.lbry.io/sporthock-madrid) +- [*Hock Tour Rome*](https://open.lbry.io/sporthock-rome) +- [*Hock Tour Paris*](https://open.lbry.io/sporthock-paris) +- [*Hock Tour Rotterdam*](https://open.lbry.io/sporthock-rotterdam) -**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Want to share your niche with the world? Email reilly@lbry.io for a trip to the front of the line. +**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Want to share your niche with the world? Email [reilly@lbry.io](mailto:reilly@lbry.io) for a trip to the front of the line. diff --git a/content/news/74-lbry-trailblazers.md b/content/news/74-lbry-trailblazers.md index e1403d4c..9fb16508 100644 --- a/content/news/74-lbry-trailblazers.md +++ b/content/news/74-lbry-trailblazers.md @@ -1,4 +1,4 @@ ---- +--- author: reilly-smith title: 'Shoutout to All You trailblazers' date: '2016-10-13 00:06:00' @@ -19,60 +19,60 @@ We salute them. And then we enjoy them. **Oscilloscope Pictures presents** -[*It’s a Disaster*, directed by Todd Berger. RT: 90mins](lbry://itsadisaster) +[*It’s a Disaster*, directed by Todd Berger. RT: 90mins](https://open.lbry.io/itsadisaster) **Are We There Yet Productions presents** -[*Skate Yrself Clean*, directed by Janna Jude. RT: 7mins](lbry://skateyrselfclean) +[*Skate Yrself Clean*, directed by Janna Jude. RT: 7mins](https://open.lbry.io/skateyrselfclean) **Emergent Order presents** -[*Fight of the Century: Keynes vs. Hayek*, directed by John Papola. RT: 10mins](lbry://keynesvhayek) +[*Fight of the Century: Keynes vs. Hayek*, directed by John Papola. RT: 10mins](https://open.lbry.io/keynesvhayek) **AnimalRobot** -- [The Blow Up: Blackalicious vs. Mr. Fox](lbry://theblowup) -- [Hypnotize: Notorious B.I.G. vs. Earl Sinclair](lbry://hypnotize) -- [Dopeman: Redman vs. Homer Simpson](lbry://dopeman) -- [Rap God: Eminem vs. Rick and Morty](lbry://rickandmortyrapgod) -- [Straight Outta Compton: A Los Santos Story](lbry://gtasoc) +- [The Blow Up: Blackalicious vs. Mr. Fox](https://open.lbry.io/theblowup) +- [Hypnotize: Notorious B.I.G. vs. Earl Sinclair](https://open.lbry.io/hypnotize) +- [Dopeman: Redman vs. Homer Simpson](https://open.lbry.io/dopeman) +- [Rap God: Eminem vs. Rick and Morty](https://open.lbry.io/rickandmortyrapgod) +- [Straight Outta Compton: A Los Santos Story](https://open.lbry.io/gtasoc) **Brookes Eggleston’s Character Design Forge** -- [How To Be A Character Designer](lbry://characterdesigner) -- [Drawing a Comic Page from Start to Finish](lbry://drawingcomics) -- [Play to Your Strengths](lbry://playtoyourstrengths) -- [5 Ways to Make a Character More Likable](lbry://likeablecharacters) +- [How To Be A Character Designer](https://open.lbry.io/characterdesigner) +- [Drawing a Comic Page from Start to Finish](https://open.lbry.io/drawingcomics) +- [Play to Your Strengths](https://open.lbry.io/playtoyourstrengths) +- [5 Ways to Make a Character More Likable](https://open.lbry.io/likeablecharacters) **Charney Comedy** -- [Lenders’ Den (Shark Tank)](lbry://LendersDen) -- [Moneyweasel (Moneyball)](lbry://moneyweasel) -- [Loose Cannon (Resevoir Dogs)](lbry://LooseCannon) +- [Lenders’ Den (Shark Tank)](https://open.lbry.io/LendersDen) +- [Moneyweasel (Moneyball)](https://open.lbry.io/moneyweasel) +- [Loose Cannon (Resevoir Dogs)](https://open.lbry.io/LooseCannon) **HeckBender** -- [How to Help Non-Sports Fans Enjoy Sports](lbry://smallpenis) -- [Colorado Bridge Incident](lbry://coloradobridge) -- [Eminem - Rap God (Unofficial Pug Edition)](lbry://pugrapgod) -- [True Christmas](lbry://truechristmas) +- [How to Help Non-Sports Fans Enjoy Sports](https://open.lbry.io/smallpenis) +- [Colorado Bridge Incident](https://open.lbry.io/coloradobridge) +- [Eminem - Rap God (Unofficial Pug Edition)](https://open.lbry.io/pugrapgod) +- [True Christmas](https://open.lbry.io/truechristmas) **MillionDollarExtreme** -- [Sam Hyde’s 2070 Paradigm Shift](lbry://samhyde2070) -- [College Cunts](lbry://collegecunts) -- [Williamsburg Street Fashion](lbry://WilliamsburgFashion1) +- [Sam Hyde’s 2070 Paradigm Shift](https://open.lbry.io/samhyde2070) +- [College Cunts](https://open.lbry.io/collegecunts) +- [Williamsburg Street Fashion](https://open.lbry.io/WilliamsburgFashion1) **Superman, the 1940s serial** -- [*Superman* (a.k.a. *The Mad Scientist*)](lbry://superman1940-e1) -- [*The Mechanical Monsters*](lbry://superman1940-e2) -- [*Billion Dollar Limited*](lbry://superman1940-e3) -- [*The Arctic Giant*](lbry://superman1940-e4) -- [*The Bulleteers*](lbry://superman1940-e5) -- [*The Magnetic Telescope*](lbry://superman1940-e6) -- [*Electric Earthquake*](lbry://superman1940-e7) -- [*Volcano*](lbry://superman1940-e8) -- [*Terror on the Midway*](lbry://superman1940-e9) +- [*Superman* (a.k.a. *The Mad Scientist*)](https://open.lbry.io/superman1940-e1) +- [*The Mechanical Monsters*](https://open.lbry.io/superman1940-e2) +- [*Billion Dollar Limited*](https://open.lbry.io/superman1940-e3) +- [*The Arctic Giant*](https://open.lbry.io/superman1940-e4) +- [*The Bulleteers*](https://open.lbry.io/superman1940-e5) +- [*The Magnetic Telescope*](https://open.lbry.io/superman1940-e6) +- [*Electric Earthquake*](https://open.lbry.io/superman1940-e7) +- [*Volcano*](https://open.lbry.io/superman1940-e8) +- [*Terror on the Midway*](https://open.lbry.io/superman1940-e9) **TJ’s Sporthocking Playlist** -- [*Hock Tour Madrid*](lbry://sporthock-madrid) -- [*Hock Tour Rome*](lbry://sporthock-rome) -- [*Hock Tour Paris*](lbry://sporthock-paris) -- [*Hock Tour Rotterdam*](lbry://sporthock-rotterdam) +- [*Hock Tour Madrid*](https://open.lbry.io/sporthock-madrid) +- [*Hock Tour Rome*](https://open.lbry.io/sporthock-rome) +- [*Hock Tour Paris*](https://open.lbry.io/sporthock-paris) +- [*Hock Tour Rotterdam*](https://open.lbry.io/sporthock-rotterdam) -**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Want to share your niche with the world? Email reilly@lbry.io for a trip to the front of the line. +**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Want to share your niche with the world? Email [reilly@lbry.io](mailto:reilly@lbry.io) for a trip to the front of the line. diff --git a/content/news/76-night-living-dead.md b/content/news/76-night-living-dead.md index 2ed6cb44..50ba1dc4 100644 --- a/content/news/76-night-living-dead.md +++ b/content/news/76-night-living-dead.md @@ -1,4 +1,4 @@ ---- +--- author: reilly-smith title: 'Braaains!' date: '2016-10-20 00:06:00' @@ -10,10 +10,10 @@ It’s also the most downloaded public domain title of all-time. Except for one Fear not: we’re making it easier than ever to fear. -You can [stream the original](lbry://nightofthelivingdead) today at: lbry://nightofthelivingdead. +You can [stream the original](https://open.lbry.io/nightofthelivingdead) today at: lbry://nightofthelivingdead. ![George A. Romero’s Night of the Living Dead](/img/news/notld-inline.jpg) If you’re looking for a real treat this Halloween season, the [MoMA in New York announced the premiere](http://www.ew.com/article/2016/10/19/night-living-dead-4k-restoration-world-premiere-moma) of a new 4K restoration of this influential masterpiece. -**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Know some great public domain content to share? Have high-quality scans? Email reilly@lbry.io for a trip to the front of the line. +**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Know some great public domain content to share? Have high-quality scans? Email [reilly@lbry.io](mailto:reilly@lbry.io) for a trip to the front of the line. diff --git a/content/news/77-lbry-windows.md b/content/news/77-lbry-windows.md index 8efb2bff..35b2e66b 100644 --- a/content/news/77-lbry-windows.md +++ b/content/news/77-lbry-windows.md @@ -1,4 +1,4 @@ ---- +--- author: jeremy-kauffman title: 'LBRY now on Windows. Bringing Blockchain Video to the Masses.' date: '2016-10-25 00:06:18' @@ -28,4 +28,4 @@ LBRY is a content-sharing and publishing platform that is decentralized and cont LBRY is an open-source protocol, as opposed to a centralized service, so there is no entity to take a “cut” of transactions or change the terms in an attempt to monetize the product. It’s like a new extension of the internet for delivering all media – films, ebooks, songs, and apps – from creators directly to consumers with radical efficiency. -**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Know some great public domain content to share? Have high-quality scans? Email reilly@lbry.io for a trip to the front of the line. +**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Know some great public domain content to share? Have high-quality scans? Email [reilly@lbry.io](mailto:reilly@lbry.io)for a trip to the front of the line. diff --git a/content/news/78-bellflower-movie.md b/content/news/78-bellflower-movie.md index 221c60df..92a807a8 100644 --- a/content/news/78-bellflower-movie.md +++ b/content/news/78-bellflower-movie.md @@ -6,7 +6,7 @@ cover: 'bellflower-banner.jpg' --- When we say LBRY challenges the Netflix and iTunes of the world, we don’t mean it lightly. -That journey began when [Oscilloscope Laboratories](http://www.oscilloscope.net/) took the LBRY leap of faith with its darkly comic [*It’s A Disaster*](lbry://itsadisaster), starring David Cross and Julia Stiles. You’ve been streaming it almost daily since our July 4th launch. +That journey began when [Oscilloscope Laboratories](http://www.oscilloscope.net/) took the LBRY leap of faith with its darkly comic [*It’s A Disaster*](https://open.lbry.io/itsadisaster), starring David Cross and Julia Stiles. You’ve been streaming it almost daily since our July 4th launch. Today, Oscilloscope builds on that foundation. @@ -26,4 +26,4 @@ Each month through 2016, a new Oscilloscope film will land on LBRY. Next month, Oscilloscope has released academy award nominated films such as [*Embrace of the Serpent*](https://www.rottentomatoes.com/m/embrace_of_the_serpent/) and Banksy’s documentary [*Exit Through the Gift Shop*](https://www.rottentomatoes.com/m/exit_through_the_gift_shop/), off-beat indies like James Franco’s [*Howl*](https://www.rottentomatoes.com/m/1211483-howl) and new cult favorites like [*Girl Asleep*](https://www.rottentomatoes.com/m/girl_asleep_2016) and [*The Fits*](https://www.rottentomatoes.com/m/the_fits_2016). -**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Do you make great movies? Email reilly@lbry.io for some red carpet treatment, LBRY style. +**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Do you make great movies? Email [reilly@lbry.io](mailto:reilly@lbry.io) for some red carpet treatment, LBRY style. diff --git a/content/news/79-singletree.md b/content/news/79-singletree.md index 52b8f63c..cfcce2e1 100644 --- a/content/news/79-singletree.md +++ b/content/news/79-singletree.md @@ -10,7 +10,7 @@ It's a classic indie comedy double feature that’s actually funny. If you haven ![S&M Lawn Care](/img/news/singletree-inline1.jpg) -[*S&M Lawn Care*](lbry://smlawncare) has a ridiculous yet effective premise: two local lawnmowers have their neighborhood business displaced by a chiseled-jaw competitor and his band of hot lawn-mowing babes. +[*S&M Lawn Care*](https://open.lbry.io/smlawncare) has a ridiculous yet effective premise: two local lawnmowers have their neighborhood business displaced by a chiseled-jaw competitor and his band of hot lawn-mowing babes. It won the Special Jury Prize in 2010 at the legendary Friar’s Club in New York. On the heels of success, director Mark Potts immediately went into production on his next film... @@ -20,10 +20,10 @@ It won the Special Jury Prize in 2010 at the legendary Friar’s Club in New Yor > -Don Simpson, [Smells Like Screen Spirit](http://smellslikescreenspirit.com/2012/04/cinema-six-review/) -[*Cinema Six*](lbry://cinemasix) is a rare film that foreshadowed many career explosions since its 2011 opening: [*It’s a Disaster*](lbry://itsadisaster) producer-actor [Kevin Brennan](http://www.imdb.com/name/nm1059821/), former SNL and *Trainwreck* star [Bill Hader](http://newsok.com/article/5388514), multi-Sundance selected character actors [John Merriman](http://www.imdb.com/name/nm1332470/) and [Chris Doubek](http://www.imdb.com/name/nm1953775/), and many more behind the camera. +[*Cinema Six*](https://open.lbry.io/cinemasix) is a rare film that foreshadowed many career explosions since its 2011 opening: [*It’s a Disaster*](https://open.lbry.io/itsadisaster) producer-actor [Kevin Brennan](http://www.imdb.com/name/nm1059821/), former SNL and *Trainwreck* star [Bill Hader](http://newsok.com/article/5388514), multi-Sundance selected character actors [John Merriman](http://www.imdb.com/name/nm1332470/) and [Chris Doubek](http://www.imdb.com/name/nm1953775/), and many more behind the camera. **About Singletree Productions** Singletree was the original creative outlet of writer-director Mark Potts. He also writes for another [LBRY Creator’s Club member HeckBender](https://lbry.io/news/heckbender-charney-on-lbry), wins [Pulitzer Prizes at the LA Times](http://www.latimes.com/local/california/la-me-2016-pultizer-20160418-snap-htmlstory.html), and loves pug son, [Gizmo](http://uproxx.com/filmdrunk/behind-the-viral-image-pug-man-tells-the-story-behind-his-high-level-dog-troll/). -**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Do you make great movies? Email reilly@lbry.io for some red carpet treatment, LBRY style. +**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Do you make great movies? Email [reilly@lbry.io](mailto:reilly@lbry.io) for some red carpet treatment, LBRY style. diff --git a/content/news/80-tpbafk.md b/content/news/80-tpbafk.md index 2506560c..ee978651 100644 --- a/content/news/80-tpbafk.md +++ b/content/news/80-tpbafk.md @@ -1,4 +1,4 @@ ---- +--- author: reilly-smith title: 'Political Action via Creation' date: '2016-11-10 00:06:00' @@ -12,7 +12,7 @@ This week’s films elevate their subject matter while bringing a new perspectiv ![TPB AFK: The Pirate Bay Away from Keyboard](/img/news/tpb-inline.jpg) -[*TPB AFK: The Pirate Bay Away from Keyboard*](lbry://tpbafk) chronicles the recent history of polarizing torrent site The Pirate Bay, along with its creators. If you know LBRY, you certainly know of the legendary Bay. Without the conversations, controversy and technology sparked by Peter Sunde and TPB, the LBRY network may never have come to pass. +[*TPB AFK: The Pirate Bay Away from Keyboard*](https://open.lbry.io/tpbafk) chronicles the recent history of polarizing torrent site The Pirate Bay, along with its creators. If you know LBRY, you certainly know of the legendary Bay. Without the conversations, controversy and technology sparked by Peter Sunde and TPB, the LBRY network may never have come to pass. It was once censored on YouTube by Hollywood studios by way of bogus takedown claims. On LBRY, that isn't possible. @@ -20,6 +20,6 @@ It was once censored on YouTube by Hollywood studios by way of bogus takedown cl This is arguably one of the most important documentaries in recent memory (and a personal favorite of mine). The late Aaron Swartz, co-founder of Reddit, took his life in the wake of impending legal doom. Director Brian Knappenberger went to extraordinary lengths to tell Aaron’s story. -[*The Internet’s Own Boy*](lbry://theinternetsownboy) premiered at Sundance in 2014, SXSW, and was shortlisted for the Academy Awards for Best Documentary Film. +[*The Internet’s Own Boy*](https://open.lbry.io/theinternetsownboy) premiered at Sundance in 2014, SXSW, and was shortlisted for the Academy Awards for Best Documentary Film. -**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Do you make great movies? Email reilly@lbry.io to get in on the action. +**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Do you make great movies? Email [reilly@lbry.io](mailto:reilly@lbry.io) to get in on the action. diff --git a/content/news/82-coherence.md b/content/news/82-coherence.md index b39d9c9a..57c9c2ad 100644 --- a/content/news/82-coherence.md +++ b/content/news/82-coherence.md @@ -1,10 +1,10 @@ ---- +--- author: reilly-smith title: 'Ceci n’est pas une pipe' date: '2016-11-17 00:06:00' cover: 'coherence-banner.jpg' --- -If you thought your Thanksgiving dinner party was about to get weird… you ain’t seen nothing yet. This week we bring you: [*Coherence*](lbry://coherence). +If you thought your Thanksgiving dinner party was about to get weird… you ain’t seen nothing yet. This week we bring you: [*Coherence*](https://open.lbry.io/coherence). An ensemble piece. Science fiction. Horror. *Coherence* is a stroke of independent film genius that only graces us once in a blue moon. Or in this case, a blue comet. @@ -17,17 +17,17 @@ An ensemble piece. Science fiction. Horror. *Coherence* is a stroke of independe On top of that, we're putting all Oscilloscope titles (and then some) on sale from tomorrow, November 17th. It's the first annual **Too Soon? Black Friday on LBRY**. **Oscilloscope Labs LBRY Black Friday Special** -- [*Coherence*](lbry://coherence) ($2 | 130 LBC) -- [*It’s A Disaster*](lbry://itsadisaster) ($0.50 | 35 LBC) -- [*Bellflower*](lbry://bellfower) ($0.50 | 35 LBC) +- [*Coherence*](https://open.lbry.io/coherence) ($2 | 130 LBC) +- [*It’s A Disaster*](https://open.lbry.io/itsadisaster) ($0.50 | 35 LBC) +- [*Bellflower*](https://open.lbry.io/bellfower) ($0.50 | 35 LBC) **ICYMI** -- [*The Internet’s Own Boy*](lbry://theinternetsownboy) (FREE!) -- [*TPB AFK: The Pirate Bay Away from Keyboard*](lbry://tpbafk) (FREE!) -- [*S&M Lawn Care*](lbry://smlawncare) ($1) -- [*Cinema Six*](lbry://cinemasix) ($2) +- [*The Internet’s Own Boy*](https://open.lbry.io/theinternetsownboy) (FREE!) +- [*TPB AFK: The Pirate Bay Away from Keyboard*](https://open.lbry.io/tpbafk) (FREE!) +- [*S&M Lawn Care*](https://open.lbry.io/smlawncare) ($1) +- [*Cinema Six*](https://open.lbry.io/cinemasix) ($2) **About Oscilloscope Laboratories** Oscilloscope has released academy award nominated films such as [*Embrace of the Serpent*](https://www.rottentomatoes.com/m/embrace_of_the_serpent/) and Banksy’s documentary [*Exit Through the Gift Shop*](https://www.rottentomatoes.com/m/exit_through_the_gift_shop/), off-beat indies like James Franco’s [*Howl*](https://www.rottentomatoes.com/m/1211483-howl) and new cult favorites like [*Girl Asleep*](https://www.rottentomatoes.com/m/girl_asleep_2016) and [*The Fits*](https://www.rottentomatoes.com/m/the_fits_2016) -**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Do you make great movies or have a lively YouTube channel? Email reilly@lbry.io for some red carpet treatment, LBRY style. +**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Do you make great movies or have a lively YouTube channel? Email [reilly@lbry.io](mailto:reilly@lbry.io) for some red carpet treatment, LBRY style. diff --git a/content/news/83-listenup.md b/content/news/83-listenup.md index 7497e7c4..e8d016bf 100644 --- a/content/news/83-listenup.md +++ b/content/news/83-listenup.md @@ -1,4 +1,4 @@ ---- +--- author: reilly-smith title: 'Listen Up' date: '2016-12-01 00:06:00' @@ -20,4 +20,4 @@ Jay, Leah and Earl give a rare insight into the world of rising standup comics. Katie and Will keep the candid going and add a dash of deep analysis: from why Game of Thrones is a bad show, the history of James Bond, to the structure of storytelling… if you watch it, they beg to differ. ->**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Have a podcast you love? Email reilly@lbry.io, and we’ll get it on LBRY. +>**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Have a podcast you love? Email [reilly@lbry.io](mailto:reilly@lbry.io), and we’ll get it on LBRY. diff --git a/content/news/84-jinglebells.md b/content/news/84-jinglebells.md index e9dc1ae6..6a7e9a7e 100644 --- a/content/news/84-jinglebells.md +++ b/content/news/84-jinglebells.md @@ -6,7 +6,7 @@ cover: 'jingle-banner.PNG' --- Another month, another holiday. And not just any holiday… this one has theme songs. -So, to expand your Christmas music palette, we bring you: [*Jingle Bell Rocks*](lbry://jinglebellrocks). +So, to expand your Christmas music palette, we bring you: [*Jingle Bell Rocks*](https://open.lbry.io/jinglebellrocks). The seedy underbelly of the Christmas music world is your ticket to freedom from dreadful, ear-splitting Christmas tropes of old. You don’t have to suffer! There is a better song out there! @@ -24,16 +24,16 @@ The seedy underbelly of the Christmas music world is your ticket to freedom from > -[William Brownridge,Toronto Film Scene](http://thetfs.ca/2013/12/05/review-jingle-bell-rocks/) **Check out the rest of the LBRY film playlist** -- [*The Internet’s Own Boy*](lbry://theinternetsownboy) -- [*Coherence*](lbry://coherence) -- [*It’s A Disaster*](lbry://itsadisaster) -- [*Bellflower*](lbry://bellfower) -- [*TPB AFK: The Pirate Bay Away from Keyboard*](lbry://tpbafk) -- [*S&M Lawn Care*](lbry://smlawncare) -- [*Cinema Six*](lbry://cinemasix) -- [*Jingle Bell Rocks*](lbry://jinglebellrocks) +- [*The Internet’s Own Boy*](https://open.lbry.io/theinternetsownboy) +- [*Coherence*](https://open.lbry.io/coherence) +- [*It’s A Disaster*](https://open.lbry.io/itsadisaster) +- [*Bellflower*](https://open.lbry.io/bellfower) +- [*TPB AFK: The Pirate Bay Away from Keyboard*](https://open.lbry.io/tpbafk) +- [*S&M Lawn Care*](https://open.lbry.io/smlawncare) +- [*Cinema Six*](https://open.lbry.io/cinemasix) +- [*Jingle Bell Rocks*](https://open.lbry.io/jinglebellrocks) **About Oscilloscope Laboratories** Oscilloscope has released academy award nominated films such as [*After Tiller*](https://www.rottentomatoes.com/m/after_tiller_2013/) and Banksy’s documentary [*Exit Through the Gift Shop*](https://www.rottentomatoes.com/m/exit_through_the_gift_shop/), off-beat indies like James Franco’s [*Howl*](https://www.rottentomatoes.com/m/1211483-howl) and new cult favorites like [*Girl Asleep*](https://www.rottentomatoes.com/m/girl_asleep_2016) and [*The Fits*](https://www.rottentomatoes.com/m/the_fits_2016) -**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Do you make great movies? Email reilly@lbry.io for some red carpet treatment, LBRY style. +**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Do you make great movies? Email [reilly@lbry.io](mailto:reilly@lbry.io) for some red carpet treatment, LBRY style. diff --git a/content/news/85-freedomfm.md b/content/news/85-freedomfm.md index d8b659ab..a9ad667d 100644 --- a/content/news/85-freedomfm.md +++ b/content/news/85-freedomfm.md @@ -1,4 +1,4 @@ ---- +--- author: reilly-smith title: 'Freedom.fm' date: '2016-12-15 00:15:00' @@ -21,13 +21,13 @@ Stefan Molyneux’s Freedomain Radio has grown from a part-time podcast to a ful Known for his [bold predictions](https://www.youtube.com/watch?v=LfascZSTU4o) of the 2008 financial crisis coming true and subsequent market corrections – CNBC even [admitted it after the fact!](https://www.youtube.com/watch?v=5sie11QXI_Q) – Peter Schiff is a successful financial advisor and radio host outside the traditional Wall Street beltway. Author of several books, including my personal favorite, “The Little Book of Bull Moves in Bear Markets”, his financial opines will seldom tow the party line. **After the brain food, cozy up with the LBRY film playlist during the holidays!** -- [*Jingle Bell Rocks!*](lbry://jinglebellrocks) -- [*The Internet’s Own Boy*](lbry://theinternetsownboy) -- [*Coherence*](lbry://coherence) -- [*It’s A Disaster*](lbry://itsadisaster) -- [*Bellflower*](lbry://bellfower) -- [*TPB AFK: The Pirate Bay Away from Keyboard*](lbry://tpbafk) -- [*S&M Lawn Care*](lbry://smlawncare) -- [*Cinema Six*](lbry://cinemasix) +- [*Jingle Bell Rocks!*](https://open.lbry.io/jinglebellrocks) +- [*The Internet’s Own Boy*](https://open.lbry.io/theinternetsownboy) +- [*Coherence*](https://open.lbry.io/coherence) +- [*It’s A Disaster*](https://open.lbry.io/itsadisaster) +- [*Bellflower*](https://open.lbry.io/bellfower) +- [*TPB AFK: The Pirate Bay Away from Keyboard*](https://open.lbry.io/tpbafk) +- [*S&M Lawn Care*](https://open.lbry.io/smlawncare) +- [*Cinema Six*](https://open.lbry.io/cinemasix) -**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Have a podcast to publish? Email reilly@lbry.io to get your show in the queue. +**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Have a podcast to publish? Email [reilly@lbry.io](mailto:reilly@lbry.io) to get your show in the queue. diff --git a/content/news/86-rare-exports.md b/content/news/86-rare-exports.md index b0d3801a..5d5ed709 100644 --- a/content/news/86-rare-exports.md +++ b/content/news/86-rare-exports.md @@ -7,7 +7,7 @@ cover: 'rare-banner.jpg' Since it’s still December, we decided another Christmas flick was in order. -Oscilloscope presents their second ode to St. Nick this month on LBRY: [*Rare Exports: A Christmas Tale*](lbry://rareexports). +Oscilloscope presents their second ode to St. Nick this month on LBRY: [*Rare Exports: A Christmas Tale*](https://open.lbry.io/rareexports). From Finnish director Jalmari Helander comes the twisted story of the biggest Christmas secret of all. The answer has nothing to do with Jesus. @@ -15,21 +15,21 @@ From Finnish director Jalmari Helander comes the twisted story of the biggest Ch >"Rare Exports: A Christmas Tale" is a rather brilliant lump of coal for your stocking hung by the fireside with care. How else to explain an R-rated Santa Claus origin story crossed with "The Thing" (1982)? -> [Roger Ebert](http://www.rogerebert.com/reviews/rare-exports-a-christmas-tale-2010) +> [Roger Ebert](https://www.rogerebert.com/reviews/rare-exports-a-christmas-tale-2010) Merry Christmas to everyone who tried out LBRY in 2016. It’s been a pleasure. **Check out the rest of the LBRY film playlist** -- [*Jingle Bell Rocks!*](lbry://jinglebellrocks) -- [*Coherence*](lbry://coherence) -- [*It’s A Disaster*](lbry://itsadisaster) -- [*Bellflower*](lbry://bellfower) -- [*The Internet’s Own Boy*](lbry://theinternetsownboy) -- [*TPB AFK: The Pirate Bay Away from Keyboard*](lbry://tpbafk) -- [*S&M Lawn Care*](lbry://smlawncare) -- [*Cinema Six*](lbry://cinemasix) +- [*Jingle Bell Rocks!*](https://open.lbry.io/jinglebellrocks) +- [*Coherence*](https://open.lbry.io/coherence) +- [*It’s A Disaster*](https://open.lbry.io/itsadisaster) +- [*Bellflower*](https://open.lbry.io/bellfower) +- [*The Internet’s Own Boy*](https://open.lbry.io/theinternetsownboy) +- [*TPB AFK: The Pirate Bay Away from Keyboard*](https://open.lbry.io/tpbafk) +- [*S&M Lawn Care*](https://open.lbry.io/smlawncare) +- [*Cinema Six*](https://open.lbry.io/cinemasix) **About Oscilloscope Laboratories** Oscilloscope has released academy award nominated films such as [*After Tiller*](https://www.rottentomatoes.com/m/after_tiller_2013/) and Banksy’s documentary [*Exit Through the Gift Shop*](https://www.rottentomatoes.com/m/exit_through_the_gift_shop/), off-beat indies like James Franco’s [*Howl*](https://www.rottentomatoes.com/m/1211483-howl) and new cult favorites like [*Girl Asleep*](https://www.rottentomatoes.com/m/girl_asleep_2016) and [*The Fits*](https://www.rottentomatoes.com/m/the_fits_2016) -**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Do you make great movies? Email reilly@lbry.io for some red carpet treatment, LBRY style. +**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Do you make great movies? Email [reilly@lbry.io](mailto:reilly@lbry.io) for some red carpet treatment, LBRY style. diff --git a/content/news/87-positive-returns.md b/content/news/87-positive-returns.md index c44d94d5..dbbae5cd 100644 --- a/content/news/87-positive-returns.md +++ b/content/news/87-positive-returns.md @@ -1,4 +1,4 @@ ---- +--- author: reilly-smith title: 'Stay Positive' date: '2016-12-29 00:03:00' @@ -35,4 +35,4 @@ Here’s to LBRY in 2017 and a happy new year! - [*S&M Lawn Care*](lbry://smlawncare) - [*Cinema Six*](lbry://cinemasix) -**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Do you write novels? Email reilly@lbry.io, and we'll help you make publishing outside of Amazon a reality. +**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Do you write novels? Email [reilly@lbry.io](mailto:reilly@lbry.io), and we'll help you make publishing outside of Amazon a reality. diff --git a/content/news/88-cryptoverse.md b/content/news/88-cryptoverse.md index e0966945..0dc946fe 100644 --- a/content/news/88-cryptoverse.md +++ b/content/news/88-cryptoverse.md @@ -21,14 +21,14 @@ Both Chris and Trevon were LBRY early adopters. It’s still pretty early; you o Sync your YouTube channel today with this handy tool: https://lbry.io/youtube **Check out the LBRY film playlist** -- [*Coherence*](lbry://coherence) -- [*It’s A Disaster*](lbry://itsadisaster) -- [*Bellflower*](lbry://bellfower) -- [*Jingle Bell Rocks!*](lbry://jinglebellrocks) -- [*Rare Exports: A Christmas Tale*](lbry://rareexports) -- [*The Internet’s Own Boy*](lbry://theinternetsownboy) -- [*TPB AFK: The Pirate Bay Away from Keyboard*](lbry://tpbafk) -- [*S&M Lawn Care*](lbry://smlawncare) -- [*Cinema Six*](lbry://cinemasix) +- [*Coherence*](https://open.lbry.io/coherence) +- [*It’s A Disaster*](https://open.lbry.io/itsadisaster) +- [*Bellflower*](https://open.lbry.io/bellfower) +- [*Jingle Bell Rocks!*](https://open.lbry.io/jinglebellrocks) +- [*Rare Exports: A Christmas Tale*](https://open.lbry.io/rareexports) +- [*The Internet’s Own Boy*](https://open.lbry.io/theinternetsownboy) +- [*TPB AFK: The Pirate Bay Away from Keyboard*](https://open.lbry.io/tpbafk) +- [*S&M Lawn Care*](https://open.lbry.io/smlawncare) +- [*Cinema Six*](https://open.lbry.io/cinemasix) -**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Email reilly@lbry.io to join. +**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Email [reilly@lbry.io](mailto:reilly@lbry.io) to join. diff --git a/content/news/89-steal-this.md b/content/news/89-steal-this.md index 216065ab..af5f9454 100644 --- a/content/news/89-steal-this.md +++ b/content/news/89-steal-this.md @@ -14,4 +14,4 @@ STEAL THIS SHOW is presented by [TorrentFreak](https://torrentfreak.com/). You can help [support the show on Patreon](https://www.patreon.com/stealthisshow) and start listening on LBRY today. -**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Have a great podcast looking for more listeners? Email reilly@lbry.io to join. +**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Have a great podcast looking for more listeners? Email [reilly@lbry.io](mailto:reilly@lbry.io) to join. diff --git a/content/news/90-sundance2017.md b/content/news/90-sundance2017.md index 9a99f44d..abe8ff4d 100644 --- a/content/news/90-sundance2017.md +++ b/content/news/90-sundance2017.md @@ -1,4 +1,4 @@ ---- +--- author: reilly-smith title: 'Movie Soup for the Soul' date: '2017-01-19 00:16:00' @@ -24,4 +24,4 @@ This little six-minute gem directed by Kat Candler went on to play 40 film festi Without *Hellion* and our [November film *Cinema Six*](https://lbry.io/news/singletree), I would not be here today with LBRY. So here’s to keeping the dream of great indie film alive. -**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Or if you’re a creator, skip the wait and email reilly@lbry.io. +**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Or if you’re a creator, skip the wait and email [reilly@lbry.io](mailto:reilly@lbry.io). diff --git a/content/news/92-mike-hill.md b/content/news/92-mike-hill.md index 489cfa68..6d400479 100644 --- a/content/news/92-mike-hill.md +++ b/content/news/92-mike-hill.md @@ -1,4 +1,4 @@ ---- +--- author: reilly-smith title: 'Masterclass Blast' date: '2017-01-26 00:17:00' @@ -18,4 +18,4 @@ Mike’s lectures so far range from the lost art of Hollywood blockbusters to th The Forgotten Art of Blockbuster Cinema is one of my absolute favorite lectures. I hope you enjoy it at lbry://mikehill-blockbuster. Search “Mike Hill” for the rest of the playlist. -**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Have amazing lectures and educational content to share with the world? Email reilly@lbry.io for true enlightenment. +**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Have amazing lectures and educational content to share with the world? Email https://open.lbry.io/ for true enlightenment. diff --git a/content/news/93-1791l.md b/content/news/93-1791l.md index 6e3acd42..08ca741e 100644 --- a/content/news/93-1791l.md +++ b/content/news/93-1791l.md @@ -1,4 +1,4 @@ ---- +--- author: reilly-smith title: 'Watching the Watchers of the Watchers' date: '2017-02-02 00:16:00' @@ -20,4 +20,4 @@ How did CNN and Fox cover the above? Who, watches the watchers? More watchers of Get the story about the story with concise analysis from 1791L. Right here on LBRY. -**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Have a news channel you want to syndicate? Email reilly@lbry.io to give your take on the situation. +**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Have a news channel you want to syndicate? Email [reilly@lbry.io](mailto:reilly@lbry.io) to give your take on the situation. diff --git a/content/news/94-crypt0.md b/content/news/94-crypt0.md index 6dcc7c98..2a3c98f2 100644 --- a/content/news/94-crypt0.md +++ b/content/news/94-crypt0.md @@ -14,4 +14,4 @@ Crypt0 joins Chris Coney’s Cryptoverse and Trevon James as the third show with You can also find his work at www.crypt0snews.com. -**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Sync your crypto vlog channel today with this handy tool at https://lbry.io/youtube or email reilly@lbry.io. +**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Sync your crypto vlog channel today with this handy tool at https://lbry.io/youtube or email [reilly@lbry.io](mailto:reilly@lbry.io). diff --git a/content/news/95-gutenberg.md b/content/news/95-gutenberg.md index 74f154b1..056719f5 100644 --- a/content/news/95-gutenberg.md +++ b/content/news/95-gutenberg.md @@ -24,4 +24,4 @@ Hundreds of historical archives are now available on LBRY, including: The project was started by the late Michael Hart in 1971 with the digitization of the US Declaration of Independence. Make a donation [to Project Gutenberg here](https://www.gutenberg.org/wiki/Gutenberg:Project_Gutenberg_Needs_Your_Donation), and enjoy thousands of books now available on LBRY. -**Not on LBRY yet?** [Download here](https://lbry.io/get). Have a big library bounty to claim? Email reilly@lbry.io and take all the glory. +**Not on LBRY yet?** [Download here](https://lbry.io/get). Have a big library bounty to claim? Email [reilly@lbry.io](mailto:reilly@lbry.io) and take all the glory. diff --git a/content/news/96-timcast.md b/content/news/96-timcast.md index 1db7fa2d..130bfcdd 100644 --- a/content/news/96-timcast.md +++ b/content/news/96-timcast.md @@ -18,4 +18,4 @@ Start following his coverage of Sweden’s crime issues at lbry://timcast-sweden Tim Pool’s work has been syndicated by NBC, Al Jazeera, VICE, Fusion TV, RT and Time. -**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Have news you want to share uncensored on LBRY? Email reilly@lbry.io to get to the bottom of the story. +**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Have news you want to share uncensored on LBRY? Email [reilly@lbry.io](mailto:reilly@lbry.io) to get to the bottom of the story. diff --git a/content/news/97-ohgeegeo.md b/content/news/97-ohgeegeo.md index d7876250..6591a571 100644 --- a/content/news/97-ohgeegeo.md +++ b/content/news/97-ohgeegeo.md @@ -27,4 +27,4 @@ Games so hot, his rig overheats because it can’t run them. I (Reilly) will also be doing a new stream with OhGeeGeo as he learns to master Rocket League starting mid-March! -**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Monetize your stream archives by syncing your channel with https://lbry.io/youtube or email reilly@lbry.io to learn more. +**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Monetize your stream archives by syncing your channel with https://lbry.io/youtube or email [reilly@lbry.io](mailto:reilly@lbry.io) to learn more. diff --git a/content/news/99-censored-gaming.md b/content/news/99-censored-gaming.md index 30b079b4..9c21eebb 100644 --- a/content/news/99-censored-gaming.md +++ b/content/news/99-censored-gaming.md @@ -1,4 +1,4 @@ ---- +--- author: reilly-smith title: 'Play No Evil' date: '2017-03-09 00:19:00' @@ -22,4 +22,4 @@ Free your voice while being delightfully entertaining. Censored Gaming and LBRY Support Censored Gaming on Patreon right here: https://www.patreon.com/CensoredGaming -**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Feeling censored and want to stretch your expression a bit more? Email reilly@lbry.io for a ride on the wild side of publishing. +**Not on LBRY yet?** [Get an invite here](https://lbry.io/get). Feeling censored and want to stretch your expression a bit more? Email [reilly@lbry.io](mailto:reilly@lbry.io) for a ride on the wild side of publishing. diff --git a/controller/Actions.class.php b/controller/Actions.class.php index 44959bd6..65905abd 100644 --- a/controller/Actions.class.php +++ b/controller/Actions.class.php @@ -2,4 +2,4 @@ class Actions { -} \ No newline at end of file +} diff --git a/controller/Controller.class.php b/controller/Controller.class.php index 51a95cc9..94c9745d 100644 --- a/controller/Controller.class.php +++ b/controller/Controller.class.php @@ -2,237 +2,186 @@ class Controller { - const CACHE_CLEAR_PATH = '/clear-cache'; + const CACHE_CLEAR_PATH = '/clear-cache'; - protected static $queuedFunctions = []; + protected static $queuedFunctions = []; - public static function dispatch($uri) - { - try + public static function dispatch($uri) { - if (IS_PRODUCTION && function_exists('newrelic_name_transaction')) - { - newrelic_name_transaction(Request::getMethod() . ' ' . strtolower($uri)); - } + try { + if (IS_PRODUCTION && function_exists('newrelic_name_transaction')) { + newrelic_name_transaction(Request::getMethod() . ' ' . strtolower($uri)); + } - $viewAndParams = static::execute(Request::getMethod(), $uri); - $viewTemplate = $viewAndParams[0]; - $viewParameters = $viewAndParams[1] ?? []; - if (!IS_PRODUCTION && isset($viewAndParams[2])) - { - throw new Exception('use response::setheader instead of returning headers'); - } + $viewAndParams = static::execute(Request::getMethod(), $uri); + $viewTemplate = $viewAndParams[0]; + $viewParameters = $viewAndParams[1] ?? []; + if (!IS_PRODUCTION && isset($viewAndParams[2])) { + throw new Exception('use response::setheader instead of returning headers'); + } - if (!$viewTemplate) - { - if ($viewTemplate !== null) - { - throw new LogicException('All execute methods must return a template or NULL.'); + if (!$viewTemplate) { + if ($viewTemplate !== null) { + throw new LogicException('All execute methods must return a template or NULL.'); + } + } else { + $layout = !(isset($viewParameters['_no_layout']) && $viewParameters['_no_layout']); + unset($viewParameters['_no_layout']); + + $layoutParams = $viewParameters[View::LAYOUT_PARAMS] ?? []; + unset($viewParameters[View::LAYOUT_PARAMS]); + + $content = View::render($viewTemplate, $viewParameters + ['fullPage' => true]); + + Response::setContent($layout ? View::render('layout/basic', ['content' => $content] + $layoutParams) : $content); + } + + Response::setDefaultSecurityHeaders(); + if (Request::isGzipAccepted()) { + Response::gzipContentIfNotDisabled(); + } + + Response::send(); + } catch (StopException $e) { } - } - else - { - $layout = !(isset($viewParameters['_no_layout']) && $viewParameters['_no_layout']); - unset($viewParameters['_no_layout']); - - $layoutParams = $viewParameters[View::LAYOUT_PARAMS] ?? []; - unset($viewParameters[View::LAYOUT_PARAMS]); - - $content = View::render($viewTemplate, $viewParameters + ['fullPage' => true]); - - Response::setContent($layout ? View::render('layout/basic', ['content' => $content] + $layoutParams) : $content); - } - - Response::setDefaultSecurityHeaders(); - if (Request::isGzipAccepted()) - { - Response::gzipContentIfNotDisabled(); - } - - Response::send(); } - catch (StopException $e) + + public static function execute($method, $uri) { - + $router = static::getRouterWithRoutes(); + static::performSubdomainRedirects(); + try { + $dispatcher = new Routing\Dispatcher($router->getData()); + return $dispatcher->dispatch($method, $uri); + } catch (\Routing\HttpRouteNotFoundException $e) { + return NavActions::execute404(); + } catch (\Routing\HttpMethodNotAllowedException $e) { + Response::setStatus(405); + Response::setHeader('Allow', implode(', ', $e->getAllowedMethods())); + return ['page/405']; + } } - } - public static function execute($method, $uri) - { - $router = static::getRouterWithRoutes(); - static::performSubdomainRedirects(); - try + protected static function performSubdomainRedirects() { - $dispatcher = new Routing\Dispatcher($router->getData()); - return $dispatcher->dispatch($method, $uri); - } - catch (\Routing\HttpRouteNotFoundException $e) - { - return NavActions::execute404(); - } - catch (\Routing\HttpMethodNotAllowedException $e) - { - Response::setStatus(405); - Response::setHeader('Allow', implode(', ', $e->getAllowedMethods())); - return ['page/405']; - } - } + $subDomain = Request::getSubDomain(); - protected static function performSubdomainRedirects() - { - $subDomain = Request::getSubDomain(); - - switch($subDomain) { + switch ($subDomain) { case 'chat': case 'slack': return static::redirect('https://discord.gg/Z3bERWA'); } - } - - protected static function getRouterWithRoutes(): \Routing\RouteCollector - { - $router = new Routing\RouteCollector(); - - $router->get(['/', 'home'], 'ContentActions::executeHome'); - - $router->get(['/get', 'get'], 'DownloadActions::executeGet'); - $router->get(['/getrubin', 'getrubin'], 'DownloadActions::executeGet'); - foreach(array_keys(OS::getAll()) as $os) - { - $router->get(['/' . $os, 'get-' . $os], 'DownloadActions::executeGet'); } - $router->get('/roadmap', 'ContentActions::executeRoadmap'); - $router->post('/quickstart/auth', 'DeveloperActions::executeQuickstartAuth'); - $router->get('/quickstart/{step}?', 'DeveloperActions::executeQuickstart'); - $router->get('/quickstart/github/callback', 'DeveloperActions::executeQuickstartGithubCallback'); + protected static function getRouterWithRoutes(): \Routing\RouteCollector + { + $router = new Routing\RouteCollector(); - $router->get(['/press-kit.zip', 'press-kit'], 'ContentActions::executePressKit'); + $router->get(['/', 'home'], 'ContentActions::executeHome'); - $router->post('/postcommit', 'OpsActions::executePostCommit'); - $router->post('/log-upload', 'OpsActions::executeLogUpload'); - $router->get(static::CACHE_CLEAR_PATH, 'OpsActions::executeClearCache'); + $router->get(['/get', 'get'], 'DownloadActions::executeGet'); + $router->get(['/getrubin', 'getrubin'], 'DownloadActions::executeGet'); + foreach (array_keys(OS::getAll()) as $os) { + $router->get(['/' . $os, 'get-' . $os], 'DownloadActions::executeGet'); + } + $router->get('/roadmap', 'ContentActions::executeRoadmap'); - $router->any('/list/subscribe', 'MailActions::executeSubscribe'); - $router->any('/list/subscribed', 'MailActions::executeSubscribed'); - $router->get('/list/unsubscribe/{email}', 'MailActions::executeUnsubscribe'); + $router->post('/quickstart/auth', 'DeveloperActions::executeQuickstartAuth'); + $router->get('/quickstart/{step}?', 'DeveloperActions::executeQuickstart'); + $router->get('/quickstart/github/callback', 'DeveloperActions::executeQuickstartGithubCallback'); - $router->any('/dmca', 'ReportActions::executeDmca'); + $router->get(['/press-kit.zip', 'press-kit'], 'ContentActions::executePressKit'); + + $router->post('/postcommit', 'OpsActions::executePostCommit'); + $router->post('/log-upload', 'OpsActions::executeLogUpload'); + $router->get(static::CACHE_CLEAR_PATH, 'OpsActions::executeClearCache'); + + $router->any('/list/subscribe', 'MailActions::executeSubscribe'); + $router->any('/list/subscribed', 'MailActions::executeSubscribed'); + $router->get('/list/unsubscribe/{email}', 'MailActions::executeUnsubscribe'); + $router->any('/list/edit/{token}','MailActions::editEmailSettings'); + + $router->any('/dmca', 'ReportActions::executeDmca'); $router->any('/youtube/sub', 'AcquisitionActions::executeYouTubeSub'); $router->post('/youtube/edit', 'AcquisitionActions::executeYoutubeEdit'); $router->post('/youtube/token', 'AcquisitionActions::executeYoutubeToken'); $router->any('/youtube/status/{token}', 'AcquisitionActions::executeYoutubeStatus'); - $router->any('/youtube', 'AcquisitionActions::executeYouTube'); $router->any('/youtube/status', 'AcquisitionActions::executeRedirectYoutube'); + $router->any('/youtube', 'AcquisitionActions::executeYouTube'); + $router->get('/youtube/{version}', 'AcquisitionActions::executeYouTube'); - $router->get('/verify/{token}', 'AcquisitionActions::executeVerify'); + $router->get('/verify/{token}', 'AcquisitionActions::executeVerify'); - $router->get('/news/category/{category}', 'ContentActions::executePostCategoryFilter'); + $router->get('/news/category/{category}', 'ContentActions::executePostCategoryFilter'); - $router->post('/set-culture', 'i18nActions::setCulture'); + $router->post('/set-culture', 'i18nActions::setCulture'); - $permanentRedirects = [ - '/lbry-osx-latest.dmg' => '/get', - '/lbry-linux-latest.deb' => '/get', - '/dl/lbry_setup.sh' => '/get', - '/art' => '/what', - '/why' => '/learn', - '/feedback' => '/learn', - '/joinus' => '/join-us', - '/faq/when-referral-payouts' => '/faq/referrals', - '/faq/why-care-about-lbry' => '/get', - '/news/meet-the-lbry-founders' => '/team', - '/faq/no-auction-options' => '/faq/naming', - '/join-list' => '/list/subscribe', - '/publish' => '/faq/how-to-publish', - '/faq/quarterly-report-july-2016' => '/credit-reports/2016-Q2', - '/faq/quarterly-report-3q-2016' => '/credit-reports/2016-Q3', - '/faq/Q4-credit-report' => '/credit-reports/2016-Q4', - '/faq/Q1-17-CreditReport' => '/credit-reports/2017-Q1', - '/faq/how-to-report-bugs' => '/faq/support', - '/faq/make-money' => '/faq/earn-income', - ]; + $permanentRedirectsPath = ROOT_DIR . '/data/redirect/permanent.yaml'; + $tempRedirectsPath = ROOT_DIR . '/data/redirect/temporary.yaml'; - $tempRedirects = [ - '/apple-touch-icon.png' => '/img/fav/apple-touch-icon.png', - '/LBRY-deck.pdf' => 'https://www.dropbox.com/s/0xj4vgucsbi8rtv/lbry-deck.pdf?dl=1', - '/deck.pdf' => 'https://www.dropbox.com/s/0xj4vgucsbi8rtv/lbry-deck.pdf?dl=1', - '/pln.pdf' => 'https://www.dropbox.com/s/uevjrwnyr672clj/lbry-pln.pdf?dl=1', - '/plan.pdf' => 'https://www.dropbox.com/s/uevjrwnyr672clj/lbry-pln.pdf?dl=1', - '/api' => 'https://lbryio.github.io/lbry', - '/api-help' => 'https://lbryio.github.io/lbry', - '/security' => '/faq/security', - '/live' => 'https://www.youtube.com/watch?v=WM60vLOCRps', //'https://www.youtube.com/channel/UCXAcp3dJuPqeUekOacsuyaQ', - ]; + $permanentRedirects = SpyC::YAMLLoadString(file_get_contents($permanentRedirectsPath)); + $tempRedirects = SpyC::YAMLLoadString(file_get_contents($tempRedirectsPath)); + foreach ([307 => $tempRedirects, 301 => $permanentRedirects] as $code => $redirects) { + foreach ($redirects as $src => $target) { + $router->any($src, function () use ($target, $code) { + return static::redirect($target, $code); + }); + } + } - foreach ([307 => $tempRedirects, 301 => $permanentRedirects] as $code => $redirects) - { - foreach ($redirects as $src => $target) - { - $router->any($src, function () use ($target, $code) { return static::redirect($target, $code); }); - } - } + $router->any('/get/lbry.pre.{ext:c}', 'DownloadActions::executeGetAppPrereleaseRedirect'); + $router->any('/get/lbry.{ext:c}', 'DownloadActions::executeGetAppRedirect'); + $router->any('/get/lbrynet.{os:c}.zip', 'DownloadActions::executeGetDaemonRedirect'); - $router->any('/get/lbry.pre.{ext:c}', 'DownloadActions::executeGetAppPrereleaseRedirect'); - $router->any('/get/lbry.{ext:c}', 'DownloadActions::executeGetAppRedirect'); - $router->any('/get/lbrynet.{os:c}.zip', 'DownloadActions::executeGetDaemonRedirect'); - - $router->get([ContentActions::URL_NEWS . '/{slug:c}?', 'news'], 'ContentActions::executeNews'); - $router->get([ContentActions::URL_FAQ . '/{slug:c}?', 'faq'], 'ContentActions::executeFaq'); - $router->get([ContentActions::URL_BOUNTY . '/{slug:c}?', 'bounty'], 'ContentActions::executeBounty'); - $router->get([ContentActions::URL_PRESS . '/{slug:c}', 'press'], 'ContentActions::executePress'); + $router->get([ContentActions::URL_NEWS . '/{slug:c}?', 'news'], 'ContentActions::executeNews'); + $router->get([ContentActions::URL_FAQ . '/{slug:c}?', 'faq'], 'ContentActions::executeFaq'); + $router->get([ContentActions::URL_BOUNTY . '/{slug:c}?', 'bounty'], 'ContentActions::executeBounty'); + $router->get([ContentActions::URL_PRESS . '/{slug:c}', 'press'], 'ContentActions::executePress'); // $router->get([ContentActions::URL_CREDIT_REPORTS . '/{slug:c}?', 'faq'], 'ContentActions::executeFaq'); - $router->get(ContentActions::URL_CREDIT_REPORTS, 'ContentActions::executeCreditReports'); - $router->get([ContentActions::URL_CREDIT_REPORTS . '/{year:c}-q{quarter:c}', ContentActions::URL_CREDIT_REPORTS . '/{year:c}-Q{quarter:c}'], 'ContentActions::executeCreditReport'); + $router->get(ContentActions::URL_CREDIT_REPORTS, 'ContentActions::executeCreditReports'); + $router->get([ContentActions::URL_CREDIT_REPORTS . '/{year:c}-q{quarter:c}', ContentActions::URL_CREDIT_REPORTS . '/{year:c}-Q{quarter:c}'], 'ContentActions::executeCreditReport'); - $router->get('/{slug}', function (string $slug) - { - if (View::exists('page/' . $slug)) - { - Response::enableHttpCache(); - return ['page/' . $slug, []]; - } - else - { - return NavActions::execute404(); - } - }); + $router->get('/{slug}', function (string $slug) { + if (View::exists('page/' . $slug)) { + Response::enableHttpCache(); + return ['page/' . $slug, []]; + } else { + return NavActions::execute404(); + } + }); - return $router; - } - - public static function redirect($url, $statusCode = 302) - { - if (!$url) - { - throw new InvalidArgumentException('Cannot redirect to an empty URL.'); + return $router; } - $url = str_replace('&', '&', $url); - - Response::setStatus($statusCode); - - if ($statusCode == 201 || ($statusCode >= 300 && $statusCode < 400)) + public static function redirect($url, $statusCode = 302) { - Response::setHeader(Response::HEADER_LOCATION, $url); + if (!$url) { + throw new InvalidArgumentException('Cannot redirect to an empty URL.'); + } + + $url = str_replace('&', '&', $url); + + Response::setStatus($statusCode); + + if ($statusCode == 201 || ($statusCode >= 300 && $statusCode < 400)) { + Response::setHeader(Response::HEADER_LOCATION, $url); + } + + return ['internal/redirect', ['url' => $url]]; } - return ['internal/redirect', ['url' => $url]]; - } - - public static function queueToRunAfterResponse(callable $fn) - { - static::$queuedFunctions[] = $fn; - } - - public static function shutdown() - { - while ($fn = array_shift(static::$queuedFunctions)) + public static function queueToRunAfterResponse(callable $fn) { - call_user_func($fn); + static::$queuedFunctions[] = $fn; + } + + public static function shutdown() + { + while ($fn = array_shift(static::$queuedFunctions)) { + call_user_func($fn); + } } - } } diff --git a/controller/Request.class.php b/controller/Request.class.php index 5ef4804d..09dd5351 100644 --- a/controller/Request.class.php +++ b/controller/Request.class.php @@ -2,161 +2,158 @@ class Request { - const GET = 'GET'; - const POST = 'POST'; - const HEAD = 'HEAD'; - const OPTIONS = 'OPTIONS'; + const GET = 'GET'; + const POST = 'POST'; + const HEAD = 'HEAD'; + const OPTIONS = 'OPTIONS'; - protected static $method; + protected static $method; - public static function getParam(string $key, $default = null) - { - return $_POST[$key] ?? $_GET[$key] ?? $default; - } - - public static function getPostParam(string $key, $default = null) - { - return $_POST[$key] ?? $default; - } - - public static function getMethod(): string - { - if (!static::$method) + public static function getParam(string $key, $default = null) { - $method = static::getHeader('REQUEST_METHOD') ? strtoupper(static::getHeader('REQUEST_METHOD')) : null; - - static::$method = in_array($method, [static::GET, static::POST, static::HEAD, static::OPTIONS]) ? $method : static::GET; + return $_POST[$key] ?? $_GET[$key] ?? $default; } - return static::$method; - } - protected static function getHeader(string $name, $default = null) - { - return $_SERVER[strtoupper($name)] ?? $default; - } + public static function getPostParam(string $key, $default = null) + { + return $_POST[$key] ?? $default; + } - public static function getHttpHeader(string $name, $default = null) - { - $header = 'HTTP_' . strtoupper(strtr($name, '-', '_')); - return isset($_SERVER[$header]) ? static::stripSlashes($_SERVER[$header]) : $default; - } + public static function getMethod(): string + { + if (!static::$method) { + $method = static::getHeader('REQUEST_METHOD') ? strtoupper(static::getHeader('REQUEST_METHOD')) : null; - protected static function stripSlashes($value) - { - return is_array($value) ? array_map(get_called_class() . '::stripSlashes', $value) : stripslashes($value); - } + static::$method = in_array($method, [static::GET, static::POST, static::HEAD, static::OPTIONS]) ? $method : static::GET; + } + return static::$method; + } + + protected static function getHeader(string $name, $default = null) + { + return $_SERVER[strtoupper($name)] ?? $default; + } + + public static function getHttpHeader(string $name, $default = null) + { + $header = 'HTTP_' . strtoupper(strtr($name, '-', '_')); + return isset($_SERVER[$header]) ? static::stripSlashes($_SERVER[$header]) : $default; + } + + protected static function stripSlashes($value) + { + return is_array($value) ? array_map(get_called_class() . '::stripSlashes', $value) : stripslashes($value); + } - public static function isGet(): bool - { - return static::getMethod() == static::GET; - } + public static function isGet(): bool + { + return static::getMethod() == static::GET; + } - public static function isPost(): bool - { - return static::getMethod() == static::POST; - } + public static function isPost(): bool + { + return static::getMethod() == static::POST; + } - public static function isCacheableMethod(): bool - { - return in_array(static::getMethod(), [static::GET, static::HEAD]); - } + public static function isCacheableMethod(): bool + { + return in_array(static::getMethod(), [static::GET, static::HEAD]); + } - public static function getOriginalIp(): string - { - return static::getHttpHeader('X-Real-Ip') ?? + public static function getOriginalIp(): string + { + return static::getHttpHeader('X-Real-Ip') ?? (static::getHttpHeader('X-Forwarded-For') ? trim(explode(',', static::getHttpHeader('X-Forwarded-For'))[0]) : static::getHeader('REMOTE_ADDR', '')); - } + } - public static function getUserAgent(): string - { - return static::getHttpHeader('User-Agent') ?? ''; - } - - public static function getRoutingUri() - { - $host = preg_replace('/^www\./', '', static::getHost()); - switch($host) + public static function getUserAgent(): string { + return static::getHttpHeader('User-Agent') ?? ''; + } + + public static function getRoutingUri() + { + $host = preg_replace('/^www\./', '', static::getHost()); + switch ($host) { case 'betteryoutube.com': case 'lbrycreators.com': return '/youtube'; } - return static::getRelativeUri(); - } - - public static function getHost(): string - { - // apparently trailing period is legal: http://www.dns-sd.org/TrailingDotsInDomainNames.html - return static::getHttpHeader('Host') ? rtrim(static::getHttpHeader('Host'), '.') : ''; - } - - public static function getSubDomain(): string - { - $host = static::getHost(); - $domainParts = explode('.', $host); - $domainPartCount = count($domainParts); - - if (count($domainParts) < 1) - { - return ''; + return static::getRelativeUri(); } - $isLocalhost = $domainParts[$domainPartCount - 1] === 'localhost'; - - if (count($domainParts) < ($isLocalhost ? 2 : 3)) + public static function getHost(): string { - return ''; + // apparently trailing period is legal: http://www.dns-sd.org/TrailingDotsInDomainNames.html + return static::getHttpHeader('Host') ? rtrim(static::getHttpHeader('Host'), '.') : ''; } - return $isLocalhost ? + public static function getSubDomain(): string + { + $host = static::getHost(); + $domainParts = explode('.', $host); + $domainPartCount = count($domainParts); + + if (count($domainParts) < 1) { + return ''; + } + + $isLocalhost = $domainParts[$domainPartCount - 1] === 'localhost'; + + if (count($domainParts) < ($isLocalhost ? 2 : 3)) { + return ''; + } + + return $isLocalhost ? $domainParts[$domainPartCount - 2] : $domainParts[$domainPartCount - 3]; - } + } - public static function getHostAndProto(): string - { - return (static::isSSL() ? 'https' : 'http') . '://' . static::getHost(); - } + public static function getHostAndProto(): string + { + return (static::isSSL() ? 'https' : 'http') . '://' . static::getHost(); + } - public static function isSSL(): bool - { - return static::getHeader('HTTPS') || strtolower(static::getHttpHeader('X_FORWARDED_PROTO')) == 'https'; - } + public static function isSSL(): bool + { + return static::getHeader('HTTPS') || strtolower(static::getHttpHeader('X_FORWARDED_PROTO')) == 'https'; + } - public static function getServerName(): string - { - return static::getHeader('SERVER_NAME'); - } + public static function getServerName(): string + { + return static::getHeader('SERVER_NAME'); + } - public static function getReferrer(string $fallback = '/') - { - return Request::getHttpHeader('Referer', $fallback); - } + public static function getReferrer(string $fallback = '/') + { + return Request::getHttpHeader('Referer', $fallback); + } - public static function getRelativeUri(): string - { - return static::getHeader('REQUEST_URI') ? parse_url(static::getHeader('REQUEST_URI'), PHP_URL_PATH) : ''; - } + public static function getRelativeUri(): string + { + return static::getHeader('REQUEST_URI') ? parse_url(static::getHeader('REQUEST_URI'), PHP_URL_PATH) : ''; + } - public static function isGzipAccepted(): bool - { - return static::getHttpHeader('Accept-Encoding') && strpos(strtolower(static::getHttpHeader('Accept-Encoding')), 'gzip') !== false; - } + public static function isGzipAccepted(): bool + { + return static::getHttpHeader('Accept-Encoding') && strpos(strtolower(static::getHttpHeader('Accept-Encoding')), 'gzip') !== false; + } - public static function isRobot() - { - $bots = [ + public static function isRobot() + { + $bots = [ 'bot', 'spider', 'crawler', 'siteexplorer', 'yahoo', 'slurp', 'dataaccessd', 'facebook', 'twitter', 'coccoc', 'calendar', 'curl', 'wget', 'panopta', 'blogtrottr', 'zapier', 'newrelic', 'luasocket', 'okhttp', 'python' ]; - return preg_match('/(' . join('|', $bots) . ')/i', static::getUserAgent()); - } - //Method that encode html tags to special character - public static function encodeStringFromUser($string){ - return htmlspecialchars($string, ENT_QUOTES, 'UTF-8'); - } + return preg_match('/(' . join('|', $bots) . ')/i', static::getUserAgent()); + } + //Method that encode html tags to special character + public static function encodeStringFromUser($string) + { + return htmlspecialchars($string, ENT_QUOTES, 'UTF-8'); + } } diff --git a/controller/Session.class.php b/controller/Session.class.php index a0885bd6..7ff0a3bf 100644 --- a/controller/Session.class.php +++ b/controller/Session.class.php @@ -1,8 +1,8 @@ $val) + public static function get($key, $default = null, $ns = self::NAMESPACE_DEFAULT) { - static::set($key, true, static::NAMESPACE_FLASH_REMOVE); + return $_SESSION[$ns][$key] ?? $default; } - Controller::queueToRunAfterResponse([__CLASS__, 'cleanupFlashes']); - } - - public static function cleanupFlashes() - { - foreach(array_keys(static::getNamespace(static::NAMESPACE_FLASH_REMOVE)) as $flashName) + public static function set($key, $value, $ns = self::NAMESPACE_DEFAULT) { - static::unsetKey($flashName, static::NAMESPACE_FLASH); - static::unsetKey($flashName, static::NAMESPACE_FLASH_REMOVE); + $_SESSION[$ns][$key] = $value; } - } - public static function getFlash($name, $default = null) - { - return static::get($name, $default, static::NAMESPACE_FLASH); - } + public static function unsetKey($key, $ns = self::NAMESPACE_DEFAULT) + { + unset($_SESSION[$ns][$key]); + } - public static function setFlash($name, $value) - { - static::set($name, $value, static::NAMESPACE_FLASH); - static::unsetKey($name, static::NAMESPACE_FLASH_REMOVE); - } + protected static function getNamespace($ns) + { + return $_SESSION[$ns] ?? []; + } - public function persistFlashes() - { - static::unsetNamespace(static::NAMESPACE_FLASH_REMOVE); - } + protected static function setNamespace($ns, $value) + { + $_SESSION[$ns] = $value; + } + + protected static function unsetNamespace($ns) + { + unset($_SESSION[$ns]); + } + + protected static function initFlashes() + { + foreach (static::getNamespace(static::NAMESPACE_FLASH) as $key => $val) { + static::set($key, true, static::NAMESPACE_FLASH_REMOVE); + } + + Controller::queueToRunAfterResponse([__CLASS__, 'cleanupFlashes']); + } + + public static function cleanupFlashes() + { + foreach (array_keys(static::getNamespace(static::NAMESPACE_FLASH_REMOVE)) as $flashName) { + static::unsetKey($flashName, static::NAMESPACE_FLASH); + static::unsetKey($flashName, static::NAMESPACE_FLASH_REMOVE); + } + } + + public static function getFlash($name, $default = null) + { + return static::get($name, $default, static::NAMESPACE_FLASH); + } + + public static function setFlash($name, $value) + { + static::set($name, $value, static::NAMESPACE_FLASH); + static::unsetKey($name, static::NAMESPACE_FLASH_REMOVE); + } + + public function persistFlashes() + { + static::unsetNamespace(static::NAMESPACE_FLASH_REMOVE); + } } diff --git a/controller/action/AcquisitionActions.class.php b/controller/action/AcquisitionActions.class.php index e5e8cd79..8e597ec2 100644 --- a/controller/action/AcquisitionActions.class.php +++ b/controller/action/AcquisitionActions.class.php @@ -2,138 +2,147 @@ class AcquisitionActions extends Actions { - public static function executeThanks() - { - return ['acquisition/thanks']; - } - - public static function executeYouTubeSub() - { - if (!Request::isPost()) { - return Controller::redirect('/youtube'); + public static function executeThanks() + { + return ['acquisition/thanks']; } - $email = Request::getPostParam('email'); + public static function executeYouTubeSub() + { + if (!Request::isPost()) { + return Controller::redirect('/youtube'); + } - if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { - Session::setFlash('error', 'Please enter a valid email.'); - return Controller::redirect('/youtube'); + $email = Request::getPostParam('email'); + + if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { + Session::setFlash('error', 'Please enter a valid email.'); + return Controller::redirect('/youtube'); + } + + Salesforce::createContact($email, SalesForce::DEFAULT_LIST_ID, 'YouTube Campaign'); + Mailgun::sendYouTubeWarmLead(['email' => $email]); + + Session::setFlash('success', 'Thanks! We\'ll be in touch. The good kind of touch.'); + + return Controller::redirect(Request::getReferrer(), 303); } + + public static function executeYouTube(string $version = '') + { + if (isset($_GET['error_message'])) { + $error_message = Request::encodeStringFromUser($_GET['error_message']); + } - Salesforce::createContact($email, SalesForce::DEFAULT_LIST_ID, 'YouTube Campaign'); - Mailgun::sendYouTubeWarmLead(['email' => $email]); + $baseTemplate = 'acquisition/youtube'; + $versionedTemplate = $baseTemplate . '-' . $version; + $template = $version && View::exists($versionedTemplate) ? $versionedTemplate : $baseTemplate; - Session::setFlash('success', 'Thanks! We\'ll be in touch. The good kind of touch.'); + if ($version && View::exists($versionedTemplate)) { + Session::set(SESSION::KEY_YOUTUBE_TEMPLATE, $template); + } - return Controller::redirect(Request::getReferrer(), 303); - } + $template = Session::get(SESSION::KEY_YOUTUBE_TEMPLATE) ?? $template; + if (!View::exists($template)) { + Session::unsetKey(SESSION::KEY_YOUTUBE_TEMPLATE); + $template = $baseTemplate; + } - public static function executeYouTube() - { - if(isset($_GET['error_message'])){ - $error_message = Request::encodeStringFromUser($_GET['error_message']); - } - - return ['acquisition/youtube', [ + return [$template, [ 'reward' => LBRY::youtubeReward(), 'error_message' => $error_message ?? '' ]]; - } - - public static function executeVerify(string $token) - { - return ['acquisition/verify', ['token' => $token]]; - } - - public static function executeYoutubeToken() - { - return ['acquisition/youtube_token', ['_no_layout' => true]]; - } - - public static function executeYoutubeStatus(string $token) - { - if(isset($_GET['error_message'])){ - $error_message = Request::encodeStringFromUser($_GET['error_message']); } - $data = LBRY::statusYoutube($token); - if ($data['success'] == false){ - Controller::redirect('/youtube?error=true&error_message=' . $data['error']); + public static function executeVerify(string $token) + { + return ['acquisition/verify', ['token' => $token]]; } - return ['acquisition/youtube_status', [ + + public static function executeYoutubeToken() + { + return ['acquisition/youtube_token', ['_no_layout' => true]]; + } + + public static function executeYoutubeStatus(string $token) + { + if (isset($_GET['error_message'])) { + $error_message = Request::encodeStringFromUser($_GET['error_message']); + } + + $data = LBRY::statusYoutube($token); + if ($data['success'] == false) { + Controller::redirect('/youtube?error=true&error_message=' . $data['error']); + } + return ['acquisition/youtube_status', [ 'token' => $token, 'status_token' => $data, 'error_message' => $error_message ?? '' ]]; - } - - public static function actionYoutubeToken(string $desired_lbry_channel_name) - { - - $desired_lbry_channel_name_is_valid = static::lbry_channel_verification($desired_lbry_channel_name); - - if ($desired_lbry_channel_name_is_valid) { - $token = LBRY::connectYoutube($desired_lbry_channel_name); - if ($token['success'] == false) { - Controller::redirect('/youtube?error=true&error_message=' . $token['error']); - } - else { - Controller::redirect($token['data']); - } - } - } - public static function actionYoutubeEdit($status_token, $channel_name, $email, $sync_consent) - { - $current_value = LBRY::statusYoutube($status_token); - if($current_value['data']['email'] == $email) + + public static function actionYoutubeToken(string $desired_lbry_channel_name) { - $status = LBRY::editYoutube($status_token, $channel_name, null, $sync_consent); + $desired_lbry_channel_name_is_valid = static::lbry_channel_verification($desired_lbry_channel_name); + + if ($desired_lbry_channel_name_is_valid) { + $token = LBRY::connectYoutube($desired_lbry_channel_name); + if ($token['success'] == false) { + Controller::redirect('/youtube?error=true&error_message=' . $token['error']); + } else { + Controller::redirect($token['data']); + } + } } - else + public static function actionYoutubeEdit($status_token, $channel_name, $email, $sync_consent) { - $status = LBRY::editYoutube($status_token, $channel_name, $email, $sync_consent); + $current_value = LBRY::statusYoutube($status_token); + if ($current_value['data']['email'] == $email) { + $status = LBRY::editYoutube($status_token, $channel_name, null, $sync_consent); + } else { + $status = LBRY::editYoutube($status_token, $channel_name, $email, $sync_consent); + } + + if ($status['success'] == false) { + Controller::redirect("/youtube/status/". $status_token . "?error=true&error_message=" . $status['error']); + } else { + Controller::redirect("/youtube/status/" . $status_token); + } + } + public static function executeYoutubeEdit() + { + return ['acquisition/youtube_edit']; } - if($status['success'] == false){ - Controller::redirect("/youtube/status/". $status_token . "?error=true&error_message=" . $status['error']); + public static function executeRedirectYoutube() + { + return ['acquisition/youtube_status_redirect']; } - else{ - Controller::redirect("/youtube/status/" . $status_token); - } - } - public static function executeYoutubeEdit(){ - return ['acquisition/youtube_edit']; - } - public static function executeRedirectYoutube(){ - return ['acquisition/youtube_status_redirect']; - } - - protected static function email_verification($email) - { - if (preg_match('/\S+@\S+\.\S+/', $email)) { - return true; - } else { - return false; + protected static function email_verification($email) + { + if (preg_match('/\S+@\S+\.\S+/', $email)) { + return true; + } else { + return false; + } } - } - protected static function youtube_channel_verification($youtube_channel_id) - { - if (preg_match('/^UC[A-Za-z0-9_-]{22}$/', $youtube_channel_id)) { - return true; - } else { - return false; + protected static function youtube_channel_verification($youtube_channel_id) + { + if (preg_match('/^UC[A-Za-z0-9_-]{22}$/', $youtube_channel_id)) { + return true; + } else { + return false; + } } - } - protected static function lbry_channel_verification($lbry_channel) - { - if (preg_match('/[1-z]+/', $lbry_channel)) { - return true; - } else { - return false; + protected static function lbry_channel_verification($lbry_channel) + { + if (preg_match('/[1-z]+/', $lbry_channel)) { + return true; + } else { + return false; + } } - } } diff --git a/controller/action/ContentActions.class.php b/controller/action/ContentActions.class.php index c06d9406..547931ae 100644 --- a/controller/action/ContentActions.class.php +++ b/controller/action/ContentActions.class.php @@ -2,7 +2,7 @@ class ContentActions extends Actions { - const + const SLUG_RSS = 'rss.xml', SLUG_NEWS = 'news', SLUG_FAQ = 'faq', @@ -26,263 +26,242 @@ class ContentActions extends Actions VIEW_FOLDER_CREDIT_REPORTS = self::CONTENT_DIR . '/' . self::SLUG_CREDIT_REPORTS, VIEW_FOLDER_JOBS = self::CONTENT_DIR . '/' . self::SLUG_JOBS; - public static function executeHome(): array - { - Response::enableHttpCache(); - return ['page/home']; - } - - public static function executeNews(string $slug = null): array - { - Response::enableHttpCache(); - - if (!$slug || $slug == static::SLUG_RSS) + public static function executeHome(): array { - $posts = array_filter( - Post::find(static::VIEW_FOLDER_NEWS, Post::SORT_DATE_DESC), - function(Post $post) { - return !$post->getDate() || $post->getDate()->format('U') <= date('U'); - }); + Response::enableHttpCache(); + return ['page/home']; + } - if ($slug == static::SLUG_RSS) - { - Response::setHeader(Response::HEADER_CONTENT_TYPE, 'text/xml; charset=utf-8'); - return ['content/rss', [ + public static function executeNews(string $slug = null): array + { + Response::enableHttpCache(); + + if (!$slug || $slug == static::SLUG_RSS) { + $posts = array_filter( + Post::find(static::VIEW_FOLDER_NEWS, Post::SORT_DATE_DESC), + function (Post $post) { + return !$post->getDate() || $post->getDate()->format('U') <= date('U'); + } + ); + + if ($slug == static::SLUG_RSS) { + Response::setHeader(Response::HEADER_CONTENT_TYPE, 'text/xml; charset=utf-8'); + return ['content/rss', [ 'posts' => array_slice($posts, 0, 10), '_no_layout' => true ]]; - } + } - return ['content/news', [ + return ['content/news', [ 'posts' => $posts, View::LAYOUT_PARAMS => [ 'showRssLink' => true ] ]]; - } + } - try - { - $post = Post::load(static::SLUG_NEWS . '/' . ltrim($slug, '/')); - } - catch (PostNotFoundException $e) - { - return NavActions::execute404(); - } + try { + $post = Post::load(static::SLUG_NEWS . '/' . ltrim($slug, '/')); + } catch (PostNotFoundException $e) { + return NavActions::execute404(); + } - return ['content/news-post', [ + return ['content/news-post', [ 'post' => $post, View::LAYOUT_PARAMS => [ 'showRssLink' => true ] ]]; - } + } - public static function executeFaq(string $slug = null): array - { - Response::enableHttpCache(); - - if (!$slug) + public static function executeFaq(string $slug = null): array { - $allPosts = Post::find(static::VIEW_FOLDER_FAQ, Post::SORT_ORD_ASC); + Response::enableHttpCache(); - $allCategories = [ + if (!$slug) { + $allPosts = Post::find(static::VIEW_FOLDER_FAQ, Post::SORT_ORD_ASC); + + $allCategories = [ 'LBRY 101' => 'Intro to LBRY', 'getstarted' => 'Getting Started', 'setup' => 'Installing and Running LBRY', 'publisher' => 'Publishers and Creators', 'troubleshooting' => 'Help and Troubleshooting', 'wallet' => 'Wallet and Transactions', + 'tipbots' => 'LBRY Tipbots', 'mining' => 'Mining LBC', 'developer' => 'Developers', 'differences' => 'What Makes LBRY Different?', 'other' => 'Other Questions', ] + Post::collectMetadata($allPosts, 'category'); - $selectedCategory = Request::getParam('category'); - $filters = array_filter([ + $selectedCategory = Request::getParam('category'); + $filters = array_filter([ 'category' => $selectedCategory && isset($allCategories[$selectedCategory]) ? $selectedCategory : null, ]); - $posts = $filters ? Post::filter($allPosts, $filters) : $allPosts; + $posts = $filters ? Post::filter($allPosts, $filters) : $allPosts; - $groups = array_fill_keys(array_keys($allCategories), []); + $groups = array_fill_keys(array_keys($allCategories), []); - foreach ($posts as $post) - { - $groups[$post->getCategory()][] = $post; - } + foreach ($posts as $post) { + $groups[$post->getCategory()][] = $post; + } - return ['content/faq', [ + return ['content/faq', [ 'categories' => $allCategories, 'selectedCategory' => $selectedCategory, 'postGroups' => $groups ]]; + } + + try { + $post = Post::load(static::SLUG_FAQ . '/' . ltrim($slug, '/')); + } catch (PostNotFoundException $e) { + return Controller::redirect('/' . static::SLUG_FAQ); + } + return ['content/faq-post', ['post' => $post]]; } - try + + public static function executeCreditReports(string $year = null, string $month = null): array { - $post = Post::load(static::SLUG_FAQ . '/' . ltrim($slug, '/')); - } - catch (PostNotFoundException $e) - { - return Controller::redirect('/' . static::SLUG_FAQ); - } - return ['content/faq-post', ['post' => $post]]; - } + Response::enableHttpCache(); + $posts = Post::find(static::VIEW_FOLDER_CREDIT_REPORTS); - public static function executeCreditReports(string $year = null, string $month = null): array - { - Response::enableHttpCache(); - - $posts = Post::find(static::VIEW_FOLDER_CREDIT_REPORTS); - - return ['content/credit-reports', [ + return ['content/credit-reports', [ 'posts' => $posts ]]; - } - - public static function executeCreditReport(string $year = null, string $quarter = null): array - { - - Response::enableHttpCache(); - - try - { - $post = Post::load(static::SLUG_CREDIT_REPORTS . '/' . $year . '-Q' . $quarter); } - catch (PostNotFoundException $e) + + public static function executeCreditReport(string $year = null, string $quarter = null): array { - return Controller::redirect('/' . static::SLUG_CREDIT_REPORTS); - } - $metadata = $post->getMetadata(); - return ['content/credit-report', [ + Response::enableHttpCache(); + + try { + $post = Post::load(static::SLUG_CREDIT_REPORTS . '/' . $year . '-Q' . $quarter); + } catch (PostNotFoundException $e) { + return Controller::redirect('/' . static::SLUG_CREDIT_REPORTS); + } + $metadata = $post->getMetadata(); + return ['content/credit-report', [ 'post' => $post, 'sheetUrl' => $metadata['sheet'] ]]; - } - - public static function executePress(string $slug = null): array - { - Response::enableHttpCache(); - try - { - $post = Post::load(static::SLUG_PRESS . '/' . ltrim($slug, '/')); } - catch (PostNotFoundException $e) + + public static function executePress(string $slug = null): array { - return NavActions::execute404(); + Response::enableHttpCache(); + try { + $post = Post::load(static::SLUG_PRESS . '/' . ltrim($slug, '/')); + } catch (PostNotFoundException $e) { + return NavActions::execute404(); + } + return ['content/press-post', ['post' => $post]]; } - return ['content/press-post', ['post' => $post]]; - } - protected static function convertBountyAmount($amount) - { - return is_numeric($amount) ? round($amount / LBRY::getLBCtoUSDRate(), -1) : $amount; - } - - public static function executeBounty(string $slug = null): array - { - Response::enableHttpCache(); - - - - if ($slug) + protected static function convertBountyAmount($amount) { - list($metadata, $postHtml) = View::parseMarkdown(ContentActions::VIEW_FOLDER_BOUNTY . '/' . $slug . '.md'); + return is_numeric($amount) ? round($amount / LBRY::getLBCtoUSDRate(), -1) : $amount; + } - $metadata['lbc_award'] = static::convertBountyAmount($metadata['award']); + public static function executeBounty(string $slug = null): array + { + Response::enableHttpCache(); - if (!$postHtml) - { - return NavActions::execute404(); - } - return ['bounty/show', [ + + if ($slug) { + list($metadata, $postHtml) = View::parseMarkdown(ContentActions::VIEW_FOLDER_BOUNTY . '/' . $slug . '.md'); + + $metadata['lbc_award'] = static::convertBountyAmount($metadata['award']); + + if (!$postHtml) { + return NavActions::execute404(); + } + + return ['bounty/show', [ 'postHtml' => $postHtml, 'metadata' => $metadata ]]; - } + } - $allBounties = Post::find(static::CONTENT_DIR . '/bounty'); + $allBounties = Post::find(static::CONTENT_DIR . '/bounty'); - $allCategories = ['' => ''] + Post::collectMetadata($allBounties, 'category'); - $allStatuses = ['' => ''] + array_merge(Post::collectMetadata($allBounties, 'status'), ['complete' => 'unavailable']); + $allCategories = ['' => ''] + Post::collectMetadata($allBounties, 'category'); + $allStatuses = ['' => ''] + array_merge(Post::collectMetadata($allBounties, 'status'), ['complete' => 'unavailable']); - $selectedStatus = Request::getParam('status', 'available'); - $selectedCategory = Request::getParam('category'); + $selectedStatus = Request::getParam('status', 'available'); + $selectedCategory = Request::getParam('category'); - $filters = array_filter([ + $filters = array_filter([ 'category' => $selectedCategory && isset($allCategories[$selectedCategory]) ? $selectedCategory : null, 'status' => $selectedStatus && isset($allStatuses[$selectedStatus]) ? $selectedStatus : null ]); - $bounties = $filters ? Post::filter($allBounties, $filters) : $allBounties; + $bounties = $filters ? Post::filter($allBounties, $filters) : $allBounties; - uasort($bounties, function($postA, $postB) { - $metadataA = $postA->getMetadata(); - $metadataB = $postB->getMetadata(); - $awardA = strpos('-', $metadataA['award']) !== false ? rtrim(explode('-', $metadataA['award'])[0], '+') : $metadataA['award']; - $awardB = strpos('-', $metadataB['award']) !== false ? rtrim(explode('-', $metadataB['award'])[0], '+') : $metadataB['award']; - if ($awardA != $awardB) - { - return $awardA > $awardB ? -1 : 1; - } - return $metadataA['title'] < $metadataB['title'] ? -1 : 1; - }); + uasort($bounties, function ($postA, $postB) { + $metadataA = $postA->getMetadata(); + $metadataB = $postB->getMetadata(); + $awardA = strpos('-', $metadataA['award']) !== false ? rtrim(explode('-', $metadataA['award'])[0], '+') : $metadataA['award']; + $awardB = strpos('-', $metadataB['award']) !== false ? rtrim(explode('-', $metadataB['award'])[0], '+') : $metadataB['award']; + if ($awardA != $awardB) { + return $awardA > $awardB ? -1 : 1; + } + return $metadataA['title'] < $metadataB['title'] ? -1 : 1; + }); - foreach($bounties as $bounty) { - $metadata = $bounty->getMetadata(); - $bounty->setMetadataItem('lbc_award', static::convertBountyAmount($metadata['award'])); - } + foreach ($bounties as $bounty) { + $metadata = $bounty->getMetadata(); + $bounty->setMetadataItem('lbc_award', static::convertBountyAmount($metadata['award'])); + } - return ['bounty/list', [ + return ['bounty/list', [ 'bounties' => $bounties, 'categories' => $allCategories, 'statuses' => $allStatuses, 'selectedCategory' => $selectedCategory, 'selectedStatus' => $selectedStatus ]]; - } - - public static function executeRoadmap() - { - $cache = !Request::getParam('nocache'); - $githubItems = Github::listRoadmapChangesets($cache); - $projectMaxVersions = []; - foreach($githubItems as $group => $items) - { - if ($items) - { - $firstItem = reset($items); - $project = $firstItem['project']; - if (!isset($projectMaxVersions[$project]) || $firstItem['sort_key'] > $projectMaxVersions[$project]) - { - $projectMaxVersions[$project] = $firstItem['sort_key']; - } - } } - $items = array_merge(Asana::listRoadmapTasks($cache), $githubItems); - return ['content/roadmap', [ + public static function executeRoadmap() + { + $cache = !Request::getParam('nocache'); + $githubItems = Github::listRoadmapChangesets($cache); + $projectMaxVersions = []; + foreach ($githubItems as $group => $items) { + if ($items) { + $firstItem = reset($items); + $project = $firstItem['project']; + if (!isset($projectMaxVersions[$project]) || $firstItem['sort_key'] > $projectMaxVersions[$project]) { + $projectMaxVersions[$project] = $firstItem['sort_key']; + } + } + } + + $items = array_merge(Asana::listRoadmapTasks($cache), $githubItems); + return ['content/roadmap', [ 'projectMaxVersions' => $projectMaxVersions, 'items' => $items ]]; - } + } - public static function executePressKit(): array - { - $zipFileName = 'lbry-press-kit-' . date('Y-m-d') . '.zip'; - $zipPath = tempnam('/tmp', $zipFileName); + public static function executePressKit(): array + { + $zipFileName = 'lbry-press-kit-' . date('Y-m-d') . '.zip'; + $zipPath = tempnam('/tmp', $zipFileName); - $zip = new ZipArchive(); - $zip->open($zipPath, ZipArchive::OVERWRITE); + $zip = new ZipArchive(); + $zip->open($zipPath, ZipArchive::OVERWRITE); // $pageHtml = View::render('page/press-kit', ['showHeader' => false]); // $html = << -// + // + // // // LBRY Press Kit // @@ -291,28 +270,26 @@ class ContentActions extends Actions // // $pageHtml // -// -//EOD; + // + //EOD; // // $zip->addFromString('press.html', $html); - foreach (glob(ROOT_DIR . '/web/img/press/*') as $productImgPath) - { - $imgPathTokens = explode('/', $productImgPath); - $imgName = $imgPathTokens[count($imgPathTokens) - 1]; - $zip->addFile($productImgPath, '/logo_and_product/' . $imgName); - } + foreach (glob(ROOT_DIR . '/web/img/press/*') as $productImgPath) { + $imgPathTokens = explode('/', $productImgPath); + $imgName = $imgPathTokens[count($imgPathTokens) - 1]; + $zip->addFile($productImgPath, 'logo_and_product/' . $imgName); + } - foreach (glob(ContentActions::CONTENT_DIR . '/bio/*.md') as $bioPath) - { - list($metadata, $bioHtml) = View::parseMarkdown($bioPath); - $zip->addFile($bioPath, '/team_bios/' . $metadata['name'] . ' - ' . $metadata['role'] . '.txt'); - } + foreach (glob(ContentActions::CONTENT_DIR . '/bio/*.md') as $bioPath) { + list($metadata, $bioHtml) = View::parseMarkdown($bioPath); + $zip->addFile($bioPath, 'team_bios/' . $metadata['name'] . ' - ' . $metadata['role'] . '.txt'); + } - /* - * team bio images are no longer included in press kit now that they've moved to spee.ch - * this should be fixed if we care about the press-kit page - */ + /* + * team bio images are no longer included in press kit now that they've moved to spee.ch + * this should be fixed if we care about the press-kit page + */ // foreach (array_filter(glob(ROOT_DIR . '/web/img/team/*.jpg'), function ($path) // { // return strpos($path, 'spooner') === false; @@ -324,35 +301,34 @@ class ContentActions extends Actions // } - $zip->close(); + $zip->close(); - Response::enableHttpCache(); - Response::setDownloadHttpHeaders($zipFileName, 'application/zip', filesize($zipPath)); + Response::enableHttpCache(); + Response::setDownloadHttpHeaders($zipFileName, 'application/zip', filesize($zipPath)); - return ['internal/zip', [ + return ['internal/zip', [ '_no_layout' => true, 'zipPath' => $zipPath ]]; - } + } - public static function prepareBioPartial(array $vars): array - { - $person = $vars['person']; - $path = 'bio/' . $person . '.md'; - list($metadata, $bioHtml) = View::parseMarkdown($path); - $imgSrc = 'https://spee.ch/@lbryteam:6/' . $person . '.jpg'; - return $vars + $metadata + [ + public static function prepareBioPartial(array $vars): array + { + $person = $vars['person']; + $path = 'bio/' . $person . '.md'; + list($metadata, $bioHtml) = View::parseMarkdown($path); + $imgSrc = 'https://spee.ch/@lbryteam:6/' . $person . '.jpg'; + return $vars + $metadata + [ 'imgSrc' => $imgSrc, 'bioHtml' => $bioHtml, 'orientation' => 'vertical' ]; + } - } - - public static function preparePostAuthorPartial(array $vars): array - { - $post = $vars['post']; - return [ + public static function preparePostAuthorPartial(array $vars): array + { + $post = $vars['post']; + return [ 'authorName' => $post->getAuthorName(), 'photoImgSrc' => $post->getAuthorPhoto(), 'authorBioHtml' => $post->getAuthorBioHtml(), @@ -360,52 +336,55 @@ class ContentActions extends Actions 'authorTwitter' => $post->getAuthorTwitterID(), 'authorEmail' => $post->getAuthorPostEmail() ]; - } + } - public static function preparePostListPartial(array $vars): array - { - $count = $vars['count'] ?? 3; - return [ + public static function preparePostListPartial(array $vars): array + { + $count = $vars['count'] ?? 3; + return [ 'posts' => array_slice(Post::find(static::VIEW_FOLDER_NEWS, Post::SORT_DATE_DESC), 0, $count) ]; - } - public static function executePostCategoryFilter(string $category) - { - Response::enableHttpCache(); + } + public static function executePostCategoryFilter(string $category) + { + Response::enableHttpCache(); - $filter_post = []; + $filter_post = []; - $posts = array_filter( + $posts = array_filter( Post::find(static::VIEW_FOLDER_NEWS, Post::SORT_DATE_DESC), - function(Post $post) use ($category) { - return (($post->getCategory() === $category) && (!$post->getDate() || $post->getDate()->format('U') <= date('U'))); - }); + function (Post $post) use ($category) { + return (($post->getCategory() === $category) && (!$post->getDate() || $post->getDate()->format('U') <= date('U'))); + } + ); - return ['content/news', [ + return ['content/news', [ 'posts' => $posts, View::LAYOUT_PARAMS => [ 'showRssLink' => true ] ]]; - } + } - public static function prepareJobsPartial(array $vars) - { - $jobs = + public static function prepareJobsPartial(array $vars) + { + $jobs = array_filter( array_map('View::parseMarkdown', glob(static::VIEW_FOLDER_JOBS . '/*')), - function($job) { return $job[0]['status'] !== 'closed'; } + function ($job) { + return $job[0]['status'] !== 'closed'; + } ); - usort($jobs, function($jobA, $jobB){ - if ($jobA[0]['status'] === 'active' xor $jobB[0]['status'] === 'active') { - return $jobA[0]['status'] === 'active' ? -1 : 1; - } - return $jobA[0]['order'] <=> $jobB[0]['order']; - }); + usort($jobs, function ($jobA, $jobB) { + if ($jobA[0]['status'] === 'active' xor $jobB[0]['status'] === 'active') { + return $jobA[0]['status'] === 'active' ? -1 : 1; + } + return $jobA[0]['order'] <=> $jobB[0]['order']; + }); - return $vars + ['jobs' => $jobs]; - } + return $vars + ['jobs' => $jobs]; + } } diff --git a/controller/action/DeveloperActions.class.php b/controller/action/DeveloperActions.class.php index 547c688b..69ab7e8f 100644 --- a/controller/action/DeveloperActions.class.php +++ b/controller/action/DeveloperActions.class.php @@ -2,115 +2,108 @@ class DeveloperActions extends Actions { - const DEVELOPER_REWARD = 10, + const DEVELOPER_REWARD = 10, API_DOC_URL = 'https://lbryio.github.io/lbry/'; - public static function executeQuickstart(string $step = null) - { - $stepLabels = [ + public static function executeQuickstart(string $step = null) + { + $stepLabels = [ '' => 'Home', 'install' => 'Installation', 'api' => 'The API', 'credits' => 'Credits' ]; - $allSteps = array_keys($stepLabels); - $currentStep = $step ?: $allSteps[0]; + $allSteps = array_keys($stepLabels); + $currentStep = $step ?: $allSteps[0]; - $viewParams = [ + $viewParams = [ 'currentStep' => $currentStep, 'stepLabels' => $stepLabels ]; - if ($currentStep !== 'all') - { - if (!isset($stepLabels[$currentStep])) - { - Controller::redirect('/quickstart'); - } + if ($currentStep !== 'all') { + if (!isset($stepLabels[$currentStep])) { + Controller::redirect('/quickstart'); + } - $stepNum = array_flip($allSteps)[$currentStep]; + $stepNum = array_flip($allSteps)[$currentStep]; - $viewParams += [ + $viewParams += [ 'stepNum' => $stepNum, 'prevStep' => $stepNum === 0 ? null : $allSteps[$stepNum - 1], 'nextStep' => $stepNum + 1 >= count($allSteps) ? null : $allSteps[$stepNum + 1], ]; + } + + return ['developer/quickstart', $viewParams]; } - return ['developer/quickstart', $viewParams]; - } - - public static function prepareQuickstartHomePartial(array $vars) - { - return $vars + [ + public static function prepareQuickstartHomePartial(array $vars) + { + return $vars + [ 'usdValue' => static::DEVELOPER_REWARD * LBRY::getLBCtoUSDRate() ]; - } + } - public static function prepareQuickstartInstallPartial(array $vars) - { - return $vars + ['versions' => [ + public static function prepareQuickstartInstallPartial(array $vars) + { + return $vars + ['versions' => [ Os::OS_LINUX => Github::getDaemonReleaseProperty(OS::OS_LINUX, 'tag_name'), Os::OS_OSX => Github::getDaemonReleaseProperty(OS::OS_OSX, 'tag_name'), Os::OS_WINDOWS => Github::getDaemonReleaseProperty(OS::OS_WINDOWS, 'tag_name'), ]]; - } + } - public static function prepareFormNewDeveloperRewardPartial(array $vars) - { - return $vars + [ + public static function prepareFormNewDeveloperRewardPartial(array $vars) + { + return $vars + [ 'defaultWalletAddress' => Session::get(Session::KEY_DEVELOPER_CREDITS_WALLET_ADDRESS), 'error' => Session::get(Session::KEY_DEVELOPER_LAST_FORM) == "new_developer" ? Session::getFlash(Session::KEY_DEVELOPER_CREDITS_ERROR) : '', 'apiUrl' => LBRY::getApiUrl('/reward/new?reward_type=new_developer') ]; - } + } - public static function prepareFormCreditsPublishPartial(array $vars) - { - return $vars + [ + public static function prepareFormCreditsPublishPartial(array $vars) + { + return $vars + [ 'defaultWalletAddress' => Session::get(Session::KEY_DEVELOPER_CREDITS_WALLET_ADDRESS), 'error' => Session::get(Session::KEY_DEVELOPER_LAST_FORM) == "new_publish" ? Session::getFlash(Session::KEY_DEVELOPER_CREDITS_ERROR) : '', 'apiUrl' => LBRY::getApiUrl('/reward/new?reward_type=first_publish') ]; - } - - public static function executeQuickstartAuth() - { - Session::set(Session::KEY_DEVELOPER_CREDITS_WALLET_ADDRESS, trim(Request::getPostParam('wallet_address'))); - Session::set(Session::KEY_DEVELOPER_LAST_FORM, Request::getPostParam('formName')); - - if (Request::getPostParam('returnUrl')) - { - Session::set(Session::KEY_DEVELOPER_RETURN_URL_SUCCESS, Request::getPostParam('returnUrl')); } - if (!Config::get(Config::GITHUB_DEVELOPER_CREDITS_CLIENT_ID)) + public static function executeQuickstartAuth() { - throw new Exception('no github client id'); - } + Session::set(Session::KEY_DEVELOPER_CREDITS_WALLET_ADDRESS, trim(Request::getPostParam('wallet_address'))); + Session::set(Session::KEY_DEVELOPER_LAST_FORM, Request::getPostParam('formName')); - $gitHubParams = [ + if (Request::getPostParam('returnUrl')) { + Session::set(Session::KEY_DEVELOPER_RETURN_URL_SUCCESS, Request::getPostParam('returnUrl')); + } + + if (!Config::get(Config::GITHUB_DEVELOPER_CREDITS_CLIENT_ID)) { + throw new Exception('no github client id'); + } + + $gitHubParams = [ 'client_id' => Config::get(Config::GITHUB_DEVELOPER_CREDITS_CLIENT_ID), 'redirect_uri' => Request::getHostAndProto() . '/quickstart/github/callback', 'scope' => 'user:email', 'allow_signup' => false ]; - return Controller::redirect('https://github.com/login/oauth/authorize?' . http_build_query($gitHubParams)); - } - - public static function executeQuickstartGithubCallback() - { - $code = Request::getParam('code'); - - if (!$code) - { - Session::setFlash(Session::KEY_DEVELOPER_CREDITS_ERROR, 'This does not appear to be a valid response from GitHub.'); + return Controller::redirect('https://github.com/login/oauth/authorize?' . http_build_query($gitHubParams)); } - else + + public static function executeQuickstartGithubCallback() { - $authResponseData = Curl::post('https://github.com/login/oauth/access_token', [ + $code = Request::getParam('code'); + + if (!$code) { + Session::setFlash(Session::KEY_DEVELOPER_CREDITS_ERROR, 'This does not appear to be a valid response from GitHub.'); + } else { + $authResponseData = Curl::post('https://github.com/login/oauth/access_token', [ 'code' => $code, 'client_id' => Config::get(Config::GITHUB_DEVELOPER_CREDITS_CLIENT_ID), 'client_secret' => Config::get(Config::GITHUB_DEVELOPER_CREDITS_CLIENT_SECRET) @@ -119,20 +112,15 @@ class DeveloperActions extends Actions 'json_response' => true ]); - if (!$authResponseData || !isset($authResponseData['access_token'])) - { - Session::setFlash(Session::KEY_DEVELOPER_CREDITS_ERROR, 'Request to GitHub failed.'); - } - elseif (isset($authResponseData['error_description'])) - { - Session::setFlash(Session::KEY_DEVELOPER_CREDITS_ERROR, 'GitHub replied: ' . $authResponseData['error_description']); - } - else - { - Session::set(Session::KEY_GITHUB_ACCESS_TOKEN, $authResponseData['access_token']); - } - } + if (!$authResponseData || !isset($authResponseData['access_token'])) { + Session::setFlash(Session::KEY_DEVELOPER_CREDITS_ERROR, 'Request to GitHub failed.'); + } elseif (isset($authResponseData['error_description'])) { + Session::setFlash(Session::KEY_DEVELOPER_CREDITS_ERROR, 'GitHub replied: ' . $authResponseData['error_description']); + } else { + Session::set(Session::KEY_GITHUB_ACCESS_TOKEN, $authResponseData['access_token']); + } + } - return Controller::redirect(Session::get(Session::KEY_DEVELOPER_RETURN_URL_SUCCESS, '/quickstart/credits')); - } + return Controller::redirect(Session::get(Session::KEY_DEVELOPER_RETURN_URL_SUCCESS, '/quickstart/credits')); + } } diff --git a/controller/action/DownloadActions.class.php b/controller/action/DownloadActions.class.php index 6e8f9fb4..d4e680d0 100644 --- a/controller/action/DownloadActions.class.php +++ b/controller/action/DownloadActions.class.php @@ -2,115 +2,124 @@ class DownloadActions extends Actions { - public static function executeGetAppRedirect(string $ext) - { - return Controller::redirect(GitHub::getAppDownloadUrl(OS::getOsForExtension($ext)) ?: '/get', 302); - } - - public static function executeGetAppPrereleaseRedirect(string $ext) - { - return Controller::redirect(GitHub::getAppPrereleaseDownloadUrl(OS::getOsForExtension($ext)) ?: '/get', 302); - } - - - public static function executeGetDaemonRedirect(string $os) - { - $uri = null; - $oses = Os::getAll(); - - if (isset($oses[$os])) + public static function executeGetAppRedirect(string $ext) { - $uri = GitHub::getDaemonDownloadUrl($os); + return Controller::redirect(GitHub::getAppDownloadUrl(OS::getOsForExtension($ext)) ?: '/get', 302); } - return Controller::redirect($uri ?: '/quickstart', 302); - } - - public static function executeGet() - { - $osChoices = OS::getAll(); - $os = static::guessOs(); - - if ($os && isset($osChoices[$os])) + public static function executeGetAppPrereleaseRedirect(string $ext) { - list($uri, $osTitle, $osIcon, $buttonLabel, $analyticsLabel) = $osChoices[$os]; - $release = GitHub::getAppRelease(); - $asset = GitHub::getAppAsset($os); - return ['download/get', [ - 'analyticsLabel' => $analyticsLabel, - 'buttonLabel' => $buttonLabel, - 'downloadUrl' => $asset ? $asset['browser_download_url'] : null, - 'isAuto' => Request::getParam('auto'), - 'os' => $os, - 'osTitle' => $osTitle, - 'osIcon' => $osIcon, - 'releaseTimestamp' => $release ? strtotime($release['created_at']) : null, - 'size' => $asset ? $asset['size'] / ( 1024 * 1024 ) : 0, //bytes -> MB - 'version' => $release ? $release['name'] : null, - ]]; + return Controller::redirect(GitHub::getAppPrereleaseDownloadUrl(OS::getOsForExtension($ext)) ?: '/get', 302); } - return ['download/get-no-os']; - } - public static function prepareListPartial(array $vars) - { - return $vars + ['osChoices' => isset($vars['excludeOs']) ? + public static function executeGetDaemonRedirect(string $os) + { + $uri = null; + $oses = Os::getAll(); + + if (isset($oses[$os])) { + $uri = GitHub::getDaemonDownloadUrl($os); + } + + return Controller::redirect($uri ?: '/quickstart', 302); + } + + public static function executeGet() + { + $osChoices = OS::getAll(); + + $os = static::guessOS(); + + if (isset($os) && isset($osChoices[$os])) { + list($uri, $osTitle, $osIcon, $buttonLabel, $analyticsLabel) = $osChoices[$os]; + $asset = Github::getAppAsset($os); + $param = ['osTitle' => $osTitle, 'osIcon' => $osIcon, 'os' => $os, 'downloadUrl' => $asset ? $asset['browser_download_url'] : null]; + return ['download/get', $param]; + } else { + return ['download/get-no-os']; + } + } + + public static function prepareListPartial(array $vars) + { + return $vars + ['osChoices' => isset($vars['excludeOs']) ? array_diff_key(OS::getAll(), [$vars['excludeOs'] => null]) : OS::getAll() ]; - } - - protected static function guessOs() - { - //if exact OS is requested, use that - $uri = Request::getRelativeUri(); - foreach (OS::getAll() as $os => $osChoice) - { - if ($osChoice[0] == $uri) - { - return $os; - } } - if (Request::isRobot()) + protected static function guessOs() { - return null; - } - - //otherwise guess from UA - $ua = Request::getUserAgent(); - if (stripos($ua, 'OS X') !== false) - { - return strpos($ua, 'iPhone') !== false || stripos($ua, 'iPad') !== false ? OS::OS_IOS : OS::OS_OSX; - } - if (stripos($ua, 'Linux') !== false || strpos($ua, 'X11') !== false) - { - return strpos($ua, 'Android') !== false ? OS::OS_ANDROID : OS::OS_LINUX; - } - if (stripos($ua, 'Windows') !== false) - { - return OS::OS_WINDOWS; - } - } - - protected static function getEmailParam() - { - $email = Request::getParam('e'); - - if (!$email) - { - $encoded = Request::getParam('ec'); - if ($encoded) - { - $email = Smaz::decode(Encoding::base64DecodeUrlsafe($encoded), Smaz::CODEBOOK_EMAIL); - if (!filter_var($email, FILTER_VALIDATE_EMAIL)) - { - $email = null; + //if exact OS is requested, use that + $uri = Request::getRelativeUri(); + foreach (OS::getAll() as $os => $osChoice) { + if ($osChoice[0] == $uri) { + return $os; + } + } + + if (Request::isRobot()) { + return null; + } + + //otherwise guess from UA + $ua = Request::getUserAgent(); + if (stripos($ua, 'OS X') !== false) { + return strpos($ua, 'iPhone') !== false || stripos($ua, 'iPad') !== false ? OS::OS_IOS : OS::OS_OSX; + } + if (stripos($ua, 'Linux') !== false || strpos($ua, 'X11') !== false) { + return strpos($ua, 'Android') !== false ? OS::OS_ANDROID : OS::OS_LINUX; + } + if (stripos($ua, 'Windows') !== false) { + return OS::OS_WINDOWS; } - } } - return $email; - } + protected static function getEmailParam() + { + $email = Request::getParam('e'); + + if (!$email) { + $encoded = Request::getParam('ec'); + if ($encoded) { + $email = Smaz::decode(Encoding::base64DecodeUrlsafe($encoded), Smaz::CODEBOOK_EMAIL); + if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { + $email = null; + } + } + } + + return $email; + } + + public static function prepareDownloadButtonPartial(array $vars) + { + $osChoices = OS::getAll(); + + $os = static::guessOS(); + + if ($os && isset($osChoices[$os])) { + list($uri, $osTitle, $osIcon, $buttonLabel, $analyticsLabel) = $osChoices[$os]; + $release = Github::getAppRelease(); + $asset = Github::getAppAsset($os); + + $vars += [ + 'analyticsLabel' => $analyticsLabel, + 'buttonLabel' => $buttonLabel, + 'downloadUrl' => $asset ? $asset['browser_download_url'] : null, + 'meta' => true, + 'os' => $os, + 'osTitle' => $osTitle, + 'osIcon' => $osIcon, + 'releaseTimestamp' => $release ? strtotime($release['created_at']) : null, + 'size' => $asset ? $asset['size'] / (1024 * 1024) : 0, //bytes -> MB + 'sourceLink' => false, + 'version' => $release ? $release['name'] : null, + 'isAuto' => Request::getParam('auto'), + ]; + } + + return $vars; + } } diff --git a/controller/action/MailActions.class.php b/controller/action/MailActions.class.php index a2cbc9b9..b97fe21e 100644 --- a/controller/action/MailActions.class.php +++ b/controller/action/MailActions.class.php @@ -2,64 +2,109 @@ class MailActions extends Actions { - public static function executeSubscribe() - { - if (!Request::isPost()) + public static function executeSubscribe() { - return ['mail/subscribe']; + if (!Request::isPost()) { + return ['mail/subscribe']; + } + + $nextUrl = Request::getPostParam('returnUrl', '/'); + if (!$nextUrl || $nextUrl[0] != '/' || !filter_var($nextUrl, FILTER_VALIDATE_URL)) { + $nextUrl = '/'; + } + + $email = Request::getPostParam('email'); + if (!$email || !filter_var($email, FILTER_VALIDATE_EMAIL)) { + Session::set( + Session::KEY_LIST_SUB_ERROR, + $email ? __('Please provide a valid email address.') : __('Please provide an email address.') + ); + + return Controller::redirect(Request::getRelativeUri()); + } + + $tag = Request::getPostParam('tag'); + + $response = LBRY::subscribe($email, $tag); + if ($response['error']) { + return ['mail/subscribe', ['error' => $response['error']]]; + } + + return ['mail/subscribe', ['subscribeSuccess' => true, 'nextUrl' => $nextUrl]]; } - $nextUrl = Request::getPostParam('returnUrl', '/'); - if (!$nextUrl || $nextUrl[0] != '/' || !filter_var($nextUrl, FILTER_VALIDATE_URL)) + public static function executeSubscribed() { - $nextUrl = '/'; + return ['mail/subscribe', ['confirmSuccess' => true, 'learnFooter' => true]]; } - $email = Request::getPostParam('email'); - if (!$email || !filter_var($email, FILTER_VALIDATE_EMAIL)) - { - Session::set(Session::KEY_LIST_SUB_ERROR, - $email ? __('Please provide a valid email address.') : __('Please provide an email address.')); - return Controller::redirect(Request::getRelativeUri()); + public static function prepareSubscribeFormPartial(array $vars) + { + $vars += ['btnClass' => 'btn-primary', 'returnUrl' => Request::getRelativeUri()]; + + $vars['error'] = Session::get(Session::KEY_LIST_SUB_ERROR); + Session::unsetKey(Session::KEY_LIST_SUB_ERROR); + + return $vars; } - $tag = Request::getPostParam('tag'); - - $response = LBRY::subscribe($email, $tag); - if ($response['error']) + public static function executeUnsubscribe(string $email) { - return ['mail/subscribe', ['error' => $response['error']]]; + $decodedEmail = Encoding::base64DecodeUrlsafe(urldecode($email)); + if (!$decodedEmail) { + return ['mail/unsubscribe', ['error' => 'Invalid unsubscribe link']]; + } + + $response = LBRY::unsubscribe($decodedEmail); + return ['mail/unsubscribe', ['error' => $response['error']]]; } - return ['mail/subscribe', ['subscribeSuccess' => true, 'nextUrl' => $nextUrl]]; - } - - public static function executeSubscribed() - { - return ['mail/subscribe', ['confirmSuccess' => true, 'learnFooter' => true]]; - } - - - public static function prepareSubscribeFormPartial(array $vars) - { - $vars += ['btnClass' => 'btn-primary', 'returnUrl' => Request::getRelativeUri()]; - - $vars['error'] = Session::get(Session::KEY_LIST_SUB_ERROR); - Session::unsetKey(Session::KEY_LIST_SUB_ERROR); - - return $vars; - } - - public static function executeUnsubscribe(string $email) - { - $decodedEmail = Encoding::base64DecodeUrlsafe(urldecode($email)); - if (!$decodedEmail) + public static function editEmailSettings(string $token) { - return ['mail/unsubscribe', ['error' => 'Invalid unsubscribe link']]; + $response = LBRY::emailStatus($token); + $responseData = $response['data'] ?? []; + return ['mail/settings', [ + 'emails' => $responseData['emails'] ?? [], + 'tags' => $responseData['tags'] ?? [], + 'token' => $token, + 'error' => $response['error'] ?? false + ]]; } - $response = LBRY::unsubscribe($decodedEmail); - return ['mail/unsubscribe', ['error' => $response['error']]]; - } -} \ No newline at end of file + public static function prepareSettingsFormPartial(array $vars) + { + return $vars + [ + 'tagMetadata' => [ + '3d-printing' => [ + 'label' => '3D Printing', + 'description' => 'Receive updates, tips, and new content suggestions related to 3D Printing.' + ], + 'android' => [ + 'label' => 'Android', + 'description' => 'Be an Android beta tester, earn LBC, and receive notification when the app goes live!' + ], + 'college' => [ + 'label' => 'University', + 'description' => 'LBRY has special programs and opportunities for people in school.' + ], + 'creator' => [ + 'label' => 'Creator', + 'description' => 'Get the most out of the stuff you create with tips and feedback from LBRY.' + ], + 'consumer' => [ + 'label' => 'Content Lover', + 'description' => 'Learn how to get the most out of LBRY as someone who just wants to find cool stuff.' + ], + 'developer' => [ + 'label' => 'Developer', + 'description' => 'Receive technical updates and other news intended for those who are familiar with software engineering.' + ], + 'ios' => [ + 'label' => 'iPhone', + 'description' => 'Be an iOS alpha tester, earn LBC, and receive notification when the app goes live!' + ], + ] + ]; + } +} diff --git a/controller/action/NavActions.class.php b/controller/action/NavActions.class.php index 5ddc575a..e56ef0fa 100644 --- a/controller/action/NavActions.class.php +++ b/controller/action/NavActions.class.php @@ -2,48 +2,48 @@ class NavActions extends Actions { - protected static $navUri; + protected static $navUri; - public static function setNavUri($uri) - { - static::$navUri = $uri; - } + public static function setNavUri($uri) + { + static::$navUri = $uri; + } - public static function getNavUri() - { - return static::$navUri ?: Request::getRelativeUri(); - } + public static function getNavUri() + { + return static::$navUri ?: Request::getRelativeUri(); + } - public static function prepareFooterPartial(array $vars) - { - return $vars + [ + public static function prepareFooterPartial(array $vars) + { + return $vars + [ 'isDark' => false, 'showLearnFooter' => false, ]; - } + } - public static function prepareGlobalItemsPartial(array $vars) - { - return $vars += [ + public static function prepareGlobalItemsPartial(array $vars) + { + return $vars += [ 'selectedItem' => static::getNavUri(), 'selectedCulture' => i18n::getLanguage() . '_' . i18n::getCountry(), 'cultures' => i18n::getAllCultures() ]; - } + } - public static function execute400(array $vars) - { - Response::setStatus(400); - return ['page/400', ['error' => $vars['error'] ?? null]]; - } + public static function execute400(array $vars) + { + Response::setStatus(400); + return ['page/400', ['error' => $vars['error'] ?? null]]; + } - public static function execute404() - { + public static function execute404() + { // $uri = Request::getRelativeUri(); // Controller::queueToRunAfterResponse(function() use($uri) { // Slack::sendErrorIfProd('404 for url ' . $uri, false); // }); - Response::setStatus(404); - return ['page/404']; - } + Response::setStatus(404); + return ['page/404']; + } } diff --git a/controller/action/OpsActions.class.php b/controller/action/OpsActions.class.php index 08e5580f..e64d1ea6 100644 --- a/controller/action/OpsActions.class.php +++ b/controller/action/OpsActions.class.php @@ -2,103 +2,89 @@ class OpsActions extends Actions { - public static function executeClearCache(): array - { - if (!ini_get('apc.enabled') || !function_exists('apc_clear_cache')) + public static function executeClearCache(): array { - return View::renderJson(['success' => false, 'error' => 'Cache not enabled']); + if (!ini_get('apc.enabled') || !function_exists('apc_clear_cache')) { + return View::renderJson(['success' => false, 'error' => 'Cache not enabled']); + } + + apc_clear_cache(); + apc_clear_cache('user'); + apc_clear_cache('opcode'); + + return View::renderJson(['success' => true]); } - apc_clear_cache(); - apc_clear_cache('user'); - apc_clear_cache('opcode'); - - return View::renderJson(['success' => true]); - } - - public static function executePostCommit(): array - { - $payload = Request::getParam('payload'); - if (!$payload) + public static function executePostCommit(): array { - return NavActions::execute400(['error' => 'No payload']); + $payload = Request::getParam('payload'); + if (!$payload) { + return NavActions::execute400(['error' => 'No payload']); + } + + $payload = json_decode($payload, true); + if ($payload['ref'] === 'refs/heads/master') { + $sig = Request::getHttpHeader('X-Hub-Signature'); + if (!$sig) { + return NavActions::execute400(['error' => "HTTP header 'X-Hub-Signature' is missing."]); + } + + list($algo, $hash) = explode('=', Request::getHttpHeader('X-Hub-Signature'), 2) + ['', '']; + if (!in_array($algo, hash_algos(), true)) { + return NavActions::execute400(['error' => 'Invalid hash algorithm "' . htmlspecialchars($algo) . '"']); + } + + $rawPost = file_get_contents('php://input'); + $secret = Config::get(Config::GITHUB_KEY); + if ($hash !== hash_hmac($algo, $rawPost, $secret)) { + return NavActions::execute400(['error' => 'Hash does not match.']); + } + + file_put_contents(ROOT_DIR . '/data/writeable/NEEDS_UPDATE', ''); + } + + return [null, []]; } - $payload = json_decode($payload, true); - if ($payload['ref'] === 'refs/heads/master') + public static function executeLogUpload(): array { - $sig = Request::getHttpHeader('X-Hub-Signature'); - if (!$sig) - { - return NavActions::execute400(['error' => "HTTP header 'X-Hub-Signature' is missing."]); - } - - list($algo, $hash) = explode('=', Request::getHttpHeader('X-Hub-Signature'), 2) + ['', '']; - if (!in_array($algo, hash_algos(), true)) - { - return NavActions::execute400(['error' => 'Invalid hash algorithm "' . htmlspecialchars($algo) . '"']); - } - - $rawPost = file_get_contents('php://input'); - $secret = Config::get(Config::GITHUB_KEY); - if ($hash !== hash_hmac($algo, $rawPost, $secret)) - { - return NavActions::execute400(['error' => 'Hash does not match.']); - } - - file_put_contents(ROOT_DIR . '/data/writeable/NEEDS_UPDATE', ''); - } - - return [null, []]; - } - - public static function executeLogUpload(): array - { - $log = Request::getPostParam('log') ? urldecode(Request::getPostParam('log')) : null; - if (Request::getPostParam('name')) - { - $name = substr(trim(urldecode(Request::getPostParam('name'))), 0, 50); - } - elseif (Request::getPostParam('date')) - { - $name = substr(trim(urldecode(Request::getPostParam('date'))), 0, 20) . '_' . + $log = Request::getPostParam('log') ? urldecode(Request::getPostParam('log')) : null; + if (Request::getPostParam('name')) { + $name = substr(trim(urldecode(Request::getPostParam('name'))), 0, 50); + } elseif (Request::getPostParam('date')) { + $name = substr(trim(urldecode(Request::getPostParam('date'))), 0, 20) . '_' . substr(trim(urldecode(Request::getPostParam('hash'))), 0, 20) . '_' . substr(trim(urldecode(Request::getPostParam('sys'))), 0, 50) . '_' . substr(trim(urldecode(Request::getPostParam('type'))), 0, 20); + } else { + $name = null; + } + + $name = preg_replace('/[^A-Za-z0-9_-]+/', '', $name); + + if (!$log || !$name) { + return NavActions::execute400(['error' => "Required params: log, name"]); + } + + $awsKey = Config::get(Config::AWS_LOG_ACCESS_KEY); + $awsSecret = Config::get(Config::AWS_LOG_SECRET_KEY); + + if (!$log || !$name) { + throw new RuntimeException('Missing AWS credentials'); + } + + $tmpFile = tempnam(sys_get_temp_dir(), 'lbryinstalllog'); + file_put_contents($tmpFile, $log); + + if (filesize($tmpFile) > 1024 * 1024 * 2) { + return NavActions::execute400(['error' => 'Log file is too large']); + } + + S3::$useExceptions = true; + S3::setAuth($awsKey, $awsSecret); + S3::putObject(S3::inputFile($tmpFile, false), 'lbry-install-logs', $name); + unlink($tmpFile); + + return [null, []]; } - else - { - $name = null; - } - - $name = preg_replace('/[^A-Za-z0-9_-]+/', '', $name); - - if (!$log || !$name) - { - return NavActions::execute400(['error' => "Required params: log, name"]); - } - - $awsKey = Config::get(Config::AWS_LOG_ACCESS_KEY); - $awsSecret = Config::get(Config::AWS_LOG_SECRET_KEY); - - if (!$log || !$name) - { - throw new RuntimeException('Missing AWS credentials'); - } - - $tmpFile = tempnam(sys_get_temp_dir(), 'lbryinstalllog'); - file_put_contents($tmpFile, $log); - - if (filesize($tmpFile) > 1024 * 1024 * 2) - { - return NavActions::execute400(['error' => 'Log file is too large']); - } - - S3::$useExceptions = true; - S3::setAuth($awsKey, $awsSecret); - S3::putObject(S3::inputFile($tmpFile, false), 'lbry-install-logs', $name); - unlink($tmpFile); - - return [null, []]; - } } diff --git a/controller/action/ReportActions.class.php b/controller/action/ReportActions.class.php index 6ff6c68a..ddc12f9e 100644 --- a/controller/action/ReportActions.class.php +++ b/controller/action/ReportActions.class.php @@ -2,40 +2,35 @@ class ReportActions extends Actions { - public static function executeDmca() - { - if (!Request::isPost()) + public static function executeDmca() { - return ['report/dmca']; + Response::setHeader(Response::HEADER_CROSS_ORIGIN, "*"); + if (!Request::isPost()) { + return ['report/dmca']; + } + + $values = []; + $errors = []; + + foreach (['name', 'email', 'rightsholder', 'identifier'] as $field) { + $value = Request::getPostParam($field); + + if (!$value) { + $errors[$field] = __('form_error.required'); + } elseif ($field == 'email' && !filter_var($value, FILTER_VALIDATE_EMAIL)) { + $errors[$field] = __('form_error.invalid'); + } + + $values[$field] = $value; + } + + if (!$errors) { + $values['report_id'] = Encoding::base58Encode(random_bytes(6)); + Mailgun::sendDmcaReport($values); + Session::setFlash('success', '

Report Submitted

We will respond shortly.

This ID for this report is ' . $values['report_id'] . '. Please reference this ID when contacting us regarding this report.

'); + return Controller::redirect(Request::getRelativeUri(), 303); + } + + return ['report/dmca', ['errors' => $errors, 'values' => $values]]; } - - $values = []; - $errors = []; - - foreach (['name', 'email', 'rightsholder', 'identifier'] as $field) - { - $value = Request::getPostParam($field); - - if (!$value) - { - $errors[$field] = __('form_error.required'); - } - elseif($field == 'email' && !filter_var($value, FILTER_VALIDATE_EMAIL)) - { - $errors[$field] = __('form_error.invalid'); - } - - $values[$field] = $value; - } - - if (!$errors) - { - $values['report_id'] = Encoding::base58Encode(random_bytes(6)); - Mailgun::sendDmcaReport($values); - Session::setFlash('success', '

Report Submitted

We will respond shortly.

This ID for this report is ' . $values['report_id'] . '. Please reference this ID when contacting us regarding this report.

'); - return Controller::redirect(Request::getRelativeUri(), 303); - } - - return ['report/dmca', ['errors' => $errors, 'values' => $values]]; - } -} \ No newline at end of file +} diff --git a/controller/action/i18nActions.class.php b/controller/action/i18nActions.class.php index e8de8f8b..0e9ae2ce 100644 --- a/controller/action/i18nActions.class.php +++ b/controller/action/i18nActions.class.php @@ -2,28 +2,24 @@ class i18nActions extends Actions { - public static function setCulture() - { - $culture = Request::getPostParam('culture'); - - // Validate - if ($culture && !in_array($culture, i18n::getAllCultures())) + public static function setCulture() { - $culture = null; - } + $culture = Request::getPostParam('culture'); - if ($culture) - { - Session::set(Session::KEY_USER_CULTURE, $culture); - } - else - { - Session::unsetKey(Session::KEY_USER_CULTURE); - } + // Validate + if ($culture && !in_array($culture, i18n::getAllCultures())) { + $culture = null; + } - //if session changes update domain - //english language = www + if ($culture) { + Session::set(Session::KEY_USER_CULTURE, $culture); + } else { + Session::unsetKey(Session::KEY_USER_CULTURE); + } - return Controller::redirect(Request::getReferrer()); - } + //if session changes update domain + //english language = www + + return Controller::redirect(Request::getReferrer()); + } } diff --git a/data/i18n/en.yaml b/data/i18n/en.yaml index d3f2577d..c1d33453 100644 --- a/data/i18n/en.yaml +++ b/data/i18n/en.yaml @@ -49,14 +49,13 @@ download: unavailable: LBRY is not yet out on this platform, but will be soon. Enter your email to be notified when it is released. verb: Download windows: Download for Windows - works: "Works with Ubuntu, Debian, or any distro with apt or dpkg." email: address: Email code: Invite Code - confirm_email_body: Please confirm your subscription to LBRY updates by clicking the link below. + confirm_email_body: Please confirm your subscription by clicking the link below. confirm_email_button: Confirm Subscription confirm_email_subject: Confirm Your Subscription to LBRY - confirm_success: Great success! Welcome to LBRY. + confirm_success: You are subscribed. Thanks! confirm_unsubscribe: You are unsubscribed. disclaimer: You will receive 1-2 messages a month, only from LBRY Inc. and only about LBRY. You can easily unsubscribe at any time. go: Go @@ -90,6 +89,9 @@ learn: rebels: Learn more about the relentless rebels changing the internet. team: About The Team meetup: LBRY is looking for ambassadors to spread the word to College campuses and Meetup groups worldwide! + 3d-printing: 3D Printing meets Blockchain + 3d-contest: Get paid for uploading to LBRY + shirt-contest: Win Library Credits with a shirt design nav: get: Get home: Home @@ -113,6 +115,7 @@ page: header: Frequently Asked Questions funnier: One day this will be funnier but today is not that day. join: Join Email List + email_settings: Email Preferences unsubscribe: Unsubscribe refer: count0: Don't fret, we still like you. @@ -200,7 +203,7 @@ press: used: Any information or media on this page or in our kit can be re-used or otherwise published without attribution. zip: Download ZIP publish: - abundance: Experience digital abundance. + abundance: Experience digital abundance allow: Allowance for us to promote availability of your content. control: Complete Creator Control credits: Receive $1,000 worth of LBRY credits to hold, use or sell. @@ -252,6 +255,9 @@ title: meetup: Have LBRY Join Your Next Meetup! guns: LBRY Fights For Free Speech android-alpha: Be an Android Alpha Tester! + 3d-printing: Print your first file from the Blockchain + 3d-contest: Win $100 for a Chess Design + shirt-contest: Win $100 for a Shirt Design roadmap: title: LBRY Roadmap description: See past and future progress on development of LBRY, the world's first user-controlled digital marketplace. diff --git a/data/redirect/permanent.yaml b/data/redirect/permanent.yaml new file mode 100644 index 00000000..5584c7da --- /dev/null +++ b/data/redirect/permanent.yaml @@ -0,0 +1,54 @@ +-- +/FLO: /news +/There: /news +/art: /what +/developer-program: /news +/dl/lbry_setup.sh: /get +/faq/Q1-17-CreditReport: /credit-reports/2017-Q1 +/faq/Q4-credit-report: /credit-reports/2016-Q4 +/faq/make-money: /faq/earn-income +/faq/no-auction-options: /faq/naming +/faq/quarterly-report-3q-2016: /credit-reports/2016-Q3 +/faq/quarterly-report-july-2016: /credit-reports/2016-Q2 +/faq/when-referral-payouts: /faq/referrals +/faq/why-care-about-lbry: /get +/feedback: /learn +/get.How+to+Create+an+Identity+LBRY+TutorialsFollow: /faq/identity-requirements +/getFree: /news/free-lbry-credits-come-and-get-em +/join-list: /list/subscribe +/joinus: /join-us +/lbry-linux-latest.deb: /get +/lbry-osx-latest.dmg: /get +/navframe?selectedItem=/news: /news +/news/$1.2b-market-cap-we-dont-care: /news/1.2b-market-cap-we-dont-care +/news/author/jeremy/2: /team +/news/author/jimmy-kiselak: /team +/news/author/sam/page/2: /team +/news/author/sam/page/3: /team +/news/author/samuel: /team +/news/boom: /news/boombust +/news/every-major-tech-company-hates-you: /news +/news/get: /get +/news/ios: /ios +/news/lbry-promo-video-raw-footage: /news +/news/meet-the-lbry-founders: /team +/news/page/2: /news +/news/page/5: /news +/news/tag/events: /roadmap +/news/tag/freedom-of-information: /news/crypto-freedom +/news/tag/team: /team +/news/team: /team +/news/what: /what +/news/windows: /news/lbry-windows +/news/www.maidsafe.com: /news +/news/www.porcfest.com: /news/lbry-at-porcfest-first-public-screening-film-via-blockchain +/nothing-here: /news +/publish: /youtube +/what-is-lbry: /faq/what-is-lbry +/youtube/cdSSo: /news +/youtube/sync: /faq/youtube +/faq/how-to-report-bugs: /faq/support +/faq/how-to-encrypt-wallet: https://lbry.tech/resources/encrypt-lbrycrd +/faq/proof-algorithm: https://lbry.tech/resources/pow +/faq/regtest-setup-guide: https://lbry.tech/resources/regtest-setup +/faq/claimtrie-implementation: https://lbry.tech/resources/lbry-claimtrie diff --git a/data/redirect/temporary.yaml b/data/redirect/temporary.yaml new file mode 100644 index 00000000..43c8422b --- /dev/null +++ b/data/redirect/temporary.yaml @@ -0,0 +1,7 @@ +--- +/LBRY-deck.pdf: "https://www.dropbox.com/s/0xj4vgucsbi8rtv/lbry-deck.pdf?dl=1" +/api: "https://lbryio.github.io/lbry" +/api-help: "https://lbryio.github.io/lbry" +/apple-touch-icon.png: /img/fav/apple-touch-icon.png +/deck.pdf: "https://www.dropbox.com/s/0xj4vgucsbi8rtv/lbry-deck.pdf?dl=1" +/security: /faq/security diff --git a/dev.sh b/dev.sh index e191de74..5c5e9c51 100755 --- a/dev.sh +++ b/dev.sh @@ -1,18 +1,23 @@ #!/bin/bash +set -e + PHPBIN=php7.2 DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" if [ ! -e "data/config.php" ]; then - cp "$DIR/data/config.php.example" "$DIR/data/config.php" + cp "$DIR/data/config.php.example" "$DIR/data/config.php" fi if ! which $PHPBIN 2>/dev/null; then - PHPBIN=php + PHPBIN=php fi +# Installing git hook +$DIR/hooks/install.sh + $PHPBIN composer.phar install -$PHPBIN --server localhost:8000 --docroot "$DIR/web" "$DIR/web/index.php" +$PHPBIN --server localhost:8000 --docroot "$DIR/web" "$DIR/web/index.php" \ No newline at end of file diff --git a/docker.sh b/docker.sh index 72f4572c..0fbf5f86 100755 --- a/docker.sh +++ b/docker.sh @@ -4,14 +4,18 @@ DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" if [ ! -e "$DIR/data/config.php" ]; then - cp "$DIR/data/config.php.example" "$DIR/data/config.php" + cp "$DIR/data/config.php.example" "$DIR/data/config.php" fi +# Installing git hook +$DIR/hooks/install.sh + docker run --rm -it --name "dev.lbry.io" \ - -v "$DIR:/usr/src/lbry.io" \ - -w "/usr/src/lbry.io" \ - -p "127.0.0.1:8000:8000" \ - -u "$(id -u):$(id -g)" \ - php:7-alpine \ - php composer.phar install - php --server "0.0.0.0:8000" --docroot "web/" "index.php" + -v "$DIR:/usr/src/lbry.io" \ + -w "/usr/src/lbry.io" \ + -p "127.0.0.1:8000:8000" \ + -u "$(id -u):$(id -g)" \ + php:7-alpine \ + php composer.phar install + + php --server "0.0.0.0:8000" --docroot "web/" "index.php" \ No newline at end of file diff --git a/gradient.jpg b/gradient.jpg new file mode 100644 index 00000000..0699245a Binary files /dev/null and b/gradient.jpg differ diff --git a/hooks/install.sh b/hooks/install.sh new file mode 100755 index 00000000..bae03719 --- /dev/null +++ b/hooks/install.sh @@ -0,0 +1,18 @@ +#!/bin/sh + +if [ -e .git/hooks/pre-commit ]; +then + PRE_COMMIT_EXISTS=1 +else + PRE_COMMIT_EXISTS=0 +fi + +cp ./hooks/pre-commit .git/hooks/pre-commit +chmod +x .git/hooks/pre-commit + +if [ "$PRE_COMMIT_EXISTS" = 0 ]; +then + echo "Pre-commit git hook is installed!" +else + echo "Pre-commit git hook is updated!" +fi \ No newline at end of file diff --git a/hooks/pre-commit b/hooks/pre-commit new file mode 100644 index 00000000..a9b3f5c4 --- /dev/null +++ b/hooks/pre-commit @@ -0,0 +1,40 @@ +#!/bin/sh + +PROJECT=`php -r "echo dirname(dirname(dirname(realpath('$0'))));"` +STAGED_FILES_CMD=`git diff --cached --name-only --diff-filter=ACMR HEAD | grep \\\\.php` + +# Determine if a file list is passed +if [ "$#" -eq 1 ] +then + oIFS=$IFS + IFS=' + ' + SFILES="$1" + IFS=$oIFS +fi +SFILES=${SFILES:-$STAGED_FILES_CMD} + +echo "Checking PHP Lint..." +for FILE in $SFILES +do + php -l -d display_errors=0 $PROJECT/$FILE + if [ $? != 0 ] + then + echo "Fix the error before commit." + exit 1 + fi + FILES="$FILES $PROJECT/$FILE" +done + +if [ "$FILES" != "" ] +then + echo "Running php-cs-fixer" + $PROJECT/php-cs-fixer fix $FILES + git add $FILES + if [ $? != 0 ] + then + echo "Error was found" + fi +fi + +exit $? \ No newline at end of file diff --git a/lib/cache/Apc.class.php b/lib/cache/Apc.class.php index af3fdc31..3a51a4e7 100644 --- a/lib/cache/Apc.class.php +++ b/lib/cache/Apc.class.php @@ -8,8 +8,8 @@ */ class Apc { - public static function isEnabled() - { - return extension_loaded('apc') && ini_get('apc.enabled'); - } -} \ No newline at end of file + public static function isEnabled() + { + return extension_loaded('apc') && ini_get('apc.enabled'); + } +} diff --git a/lib/exception/StopException.class.php b/lib/exception/StopException.class.php index ad3dc43d..d780b143 100644 --- a/lib/exception/StopException.class.php +++ b/lib/exception/StopException.class.php @@ -2,5 +2,4 @@ class StopException extends Exception { - } diff --git a/lib/i18n.class.php b/lib/i18n.class.php index 482d0d67..6314a6cc 100644 --- a/lib/i18n.class.php +++ b/lib/i18n.class.php @@ -5,134 +5,119 @@ */ function __($msg, $args = []) { - return strtr(i18n::translate($msg), $args); + return strtr(i18n::translate($msg), $args); } class i18n { - protected static - $language = null, - $translations = [], - $country = null, - $cultures = ['pt_PT', 'en_US']; + protected static $language = null; + protected static $translations = []; + protected static $country = null; + protected static $cultures = ['pt_PT', 'en_US']; - public static function register() /*needed to trigger class include, presumably setup would happen here*/ - { - $culture = static::deduceCulture(); - - list($language, $country) = explode('_', $culture); - static::$language = $language; - static::$country = $country; - - setlocale(LC_MONETARY, $culture . '.UTF-8'); - } - - public static function getLanguage() - { - return static::$language; - } - - public static function getCountry() - { - return static::$country; - } - - public static function getAllCultures() - { - return static::$cultures; - } - - public static function formatCurrency($amount, $currency = 'USD') - { - return '' . money_format('%.2n', $amount) . ''; - } - - public static function formatCredits($amount) - { - return '' . (is_numeric($amount) ? number_format($amount, 1) : $amount) . ' LBC'; - } - - public static function translate($token, $language = null) - { - $language = $language === null ? static::$language : $language; - if (!isset(static::$translations[$language])) + public static function register() /*needed to trigger class include, presumably setup would happen here*/ { - $path = ROOT_DIR . '/data/i18n/' . $language . '.yaml'; + $culture = static::deduceCulture(); - static::$translations[$language] = file_exists($path) ? Spyc::YAMLLoadString(file_get_contents($path)) : []; - } - $scope = static::$translations[$language]; - foreach (explode('.', $token) as $level) - { - if (isset($scope[$level])) - { - $scope = $scope[$level]; - } - else - { - $scope = []; - } - } - if (!$scope && $language != 'en') - { - return static::translate($token, 'en'); - } - return $scope ?: $token; - } + list($language, $country) = explode('_', $culture); + static::$language = $language; + static::$country = $country; - protected static function deduceCulture() - { - $candidates = []; - - //url trumps everything - $urlTokens = Request::getHost() ? explode('.', Request::getHost()) : []; - $code = $urlTokens ? reset($urlTokens) : null; - if ($code !== 'www') - { - $candidates[] = $code; + setlocale(LC_MONETARY, $culture . '.UTF-8'); } - //then session - $candidates[] = Session::get(Session::KEY_USER_CULTURE); - - // then headers - // http://www.thefutureoftheweb.com/blog/use-accept-language-header - if (Request::getHttpHeader('Accept-Language')) + public static function getLanguage() { - // break up string into pieces (languages and q factors) - preg_match_all('/([a-z]{1,8}(-[a-z]{1,8})?)\s*(;\s*q\s*=\s*(1|0\.[0-9]+))?/i', Request::getHttpHeader('Accept-Language'), $languages); + return static::$language; + } - if (isset($languages[1]) && count($languages[1])) - { - // create a list like "en" => 0.8 - $langs = array_combine($languages[1], $languages[4]); + public static function getCountry() + { + return static::$country; + } - // set default to 1 for any without q factor - foreach ($langs as $lang => $val) - { - if ($val === '') - { - $langs[$lang] = 1; - } + public static function getAllCultures() + { + return static::$cultures; + } + + public static function formatCurrency($amount, $currency = 'USD') + { + return '' . money_format('%.2n', $amount) . ''; + } + + public static function formatCredits($amount) + { + return '' . (is_numeric($amount) ? number_format($amount, 1) : $amount) . ' LBC'; + } + + public static function translate($token, $language = null) + { + $language = $language === null ? static::$language : $language; + if (!isset(static::$translations[$language])) { + $path = ROOT_DIR . '/data/i18n/' . $language . '.yaml'; + + static::$translations[$language] = file_exists($path) ? Spyc::YAMLLoadString(file_get_contents($path)) : []; + } + $scope = static::$translations[$language]; + foreach (explode('.', $token) as $level) { + if (isset($scope[$level])) { + $scope = $scope[$level]; + } else { + $scope = []; + } + } + if (!$scope && $language != 'en') { + return static::translate($token, 'en'); + } + return $scope ?: $token; + } + + protected static function deduceCulture() + { + $candidates = []; + + //url trumps everything + $urlTokens = Request::getHost() ? explode('.', Request::getHost()) : []; + $code = $urlTokens ? reset($urlTokens) : null; + if ($code !== 'www') { + $candidates[] = $code; } - arsort($langs, SORT_NUMERIC); + //then session + $candidates[] = Session::get(Session::KEY_USER_CULTURE); - $candidates = array_merge($candidates, array_keys($langs)); - } - } + // then headers + // http://www.thefutureoftheweb.com/blog/use-accept-language-header + if (Request::getHttpHeader('Accept-Language')) { + // break up string into pieces (languages and q factors) + preg_match_all('/([a-z]{1,8}(-[a-z]{1,8})?)\s*(;\s*q\s*=\s*(1|0\.[0-9]+))?/i', Request::getHttpHeader('Accept-Language'), $languages); - foreach ($candidates as $candidate) - { - foreach (static::getAllCultures() as $culture) - { - if ($candidate === $culture || substr($culture, 0, 2) === $candidate) - { - return $culture; + if (isset($languages[1]) && count($languages[1])) { + // create a list like "en" => 0.8 + $langs = array_combine($languages[1], $languages[4]); + + // set default to 1 for any without q factor + foreach ($langs as $lang => $val) { + if ($val === '') { + $langs[$lang] = 1; + } + } + + arsort($langs, SORT_NUMERIC); + + $candidates = array_merge($candidates, array_keys($langs)); + } } - } - } - return 'en_US'; - } + foreach ($candidates as $candidate) { + foreach (static::getAllCultures() as $culture) { + if ($candidate === $culture || substr($culture, 0, 2) === $candidate) { + return $culture; + } + } + } + + return 'en_US'; + } } diff --git a/lib/routing/BadRouteException.class.php b/lib/routing/BadRouteException.class.php index 33d07f1e..1b5d7240 100644 --- a/lib/routing/BadRouteException.class.php +++ b/lib/routing/BadRouteException.class.php @@ -2,4 +2,6 @@ namespace Routing; -class RouterBadRouteException extends \LogicException {} +class RouterBadRouteException extends \LogicException +{ +} diff --git a/lib/routing/Dispatcher.class.php b/lib/routing/Dispatcher.class.php index 44574109..e19fb4bf 100644 --- a/lib/routing/Dispatcher.class.php +++ b/lib/routing/Dispatcher.class.php @@ -4,227 +4,205 @@ namespace Routing; class Dispatcher { + private $staticRouteMap; + private $variableRouteData; + private $filters; + private $handlerResolver; + public $matchedRoute; - private $staticRouteMap; - private $variableRouteData; - private $filters; - private $handlerResolver; - public $matchedRoute; - - /** - * Create a new route dispatcher. - * - * @param RouteDataInterface $data - * @param HandlerResolverInterface $resolver - */ - public function __construct(RouteData $data, HandlerResolverInterface $resolver = null) - { - $this->staticRouteMap = $data->getStaticRoutes(); - - $this->variableRouteData = $data->getVariableRoutes(); - - $this->filters = $data->getFilters(); - - if ($resolver === null) + /** + * Create a new route dispatcher. + * + * @param RouteDataInterface $data + * @param HandlerResolverInterface $resolver + */ + public function __construct(RouteData $data, HandlerResolverInterface $resolver = null) { - $this->handlerResolver = new HandlerResolver(); - } - else - { - $this->handlerResolver = $resolver; - } - } + $this->staticRouteMap = $data->getStaticRoutes(); - /** - * Dispatch a route for the given HTTP Method / URI. - * - * @param $httpMethod - * @param $uri - * - * @return mixed|null - */ - public function dispatch($httpMethod, $uri) - { - list($handler, $filters, $vars) = $this->dispatchRoute($httpMethod, trim($uri, '/')); + $this->variableRouteData = $data->getVariableRoutes(); - list($beforeFilter, $afterFilter) = $this->parseFilters($filters); + $this->filters = $data->getFilters(); - if (($response = $this->dispatchFilters($beforeFilter)) !== null) - { - return $response; - } - - $resolvedHandler = $this->handlerResolver->resolve($handler); - - $response = call_user_func_array($resolvedHandler, $vars); - - return $this->dispatchFilters($afterFilter, $response); - } - - /** - * Dispatch a route filter. - * - * @param $filters - * @param null $response - * - * @return mixed|null - */ - private function dispatchFilters($filters, $response = null) - { - while ($filter = array_shift($filters)) - { - $handler = $this->handlerResolver->resolve($filter); - - if (($filteredResponse = call_user_func($handler, $response)) !== null) - { - return $filteredResponse; - } - } - - return $response; - } - - /** - * Normalise the array filters attached to the route and merge with any global filters. - * - * @param $filters - * - * @return array - */ - private function parseFilters($filters) - { - $beforeFilter = []; - $afterFilter = []; - - if (isset($filters[Route::BEFORE])) - { - $beforeFilter = array_intersect_key($this->filters, array_flip((array)$filters[Route::BEFORE])); - } - - if (isset($filters[Route::AFTER])) - { - $afterFilter = array_intersect_key($this->filters, array_flip((array)$filters[Route::AFTER])); - } - - return [$beforeFilter, $afterFilter]; - } - - /** - * Perform the route dispatching. Check static routes first followed by variable routes. - * - * @param $httpMethod - * @param $uri - * - * @throws Exception\HttpRouteNotFoundException - */ - private function dispatchRoute($httpMethod, $uri) - { - if (isset($this->staticRouteMap[$uri])) - { - return $this->dispatchStaticRoute($httpMethod, $uri); - } - - return $this->dispatchVariableRoute($httpMethod, $uri); - } - - /** - * Handle the dispatching of static routes. - * - * @param $httpMethod - * @param $uri - * - * @return mixed - * @throws Exception\HttpMethodNotAllowedException - */ - private function dispatchStaticRoute($httpMethod, $uri) - { - $routes = $this->staticRouteMap[$uri]; - - if (!isset($routes[$httpMethod])) - { - $httpMethod = $this->checkFallbacks($routes, $httpMethod); - } - - return $routes[$httpMethod]; - } - - /** - * Check fallback routes: HEAD for GET requests followed by the ANY attachment. - * - * @param $routes - * @param $httpMethod - * - * @throws Exception\HttpMethodNotAllowedException - */ - private function checkFallbacks($routes, $httpMethod) - { - $additional = [Route::ANY]; - - if ($httpMethod === Route::HEAD) - { - $additional[] = Route::GET; - } - - foreach ($additional as $method) - { - if (isset($routes[$method])) - { - return $method; - } - } - - $this->matchedRoute = $routes; - - throw new HttpMethodNotAllowedException(array_keys($routes), 'Method not allowed'); - } - - /** - * Handle the dispatching of variable routes. - * - * @param $httpMethod - * @param $uri - * - * @throws Exception\HttpMethodNotAllowedException - * @throws Exception\HttpRouteNotFoundException - */ - private function dispatchVariableRoute($httpMethod, $uri) - { - foreach ($this->variableRouteData as $data) - { - if (!preg_match($data['regex'], $uri, $matches)) - { - continue; - } - - $count = count($matches); - - while (!isset($data['routeMap'][$count++])) - { - ; - } - - $routes = $data['routeMap'][$count - 1]; - - if (!isset($routes[$httpMethod])) - { - $httpMethod = $this->checkFallbacks($routes, $httpMethod); - } - - foreach (array_values($routes[$httpMethod][2]) as $i => $varName) - { - if (!isset($matches[$i + 1]) || $matches[$i + 1] === '') - { - unset($routes[$httpMethod][2][$varName]); + if ($resolver === null) { + $this->handlerResolver = new HandlerResolver(); + } else { + $this->handlerResolver = $resolver; } - else - { - $routes[$httpMethod][2][$varName] = $matches[$i + 1]; - } - } - - return $routes[$httpMethod]; } - throw new HttpRouteNotFoundException('Route ' . $uri . ' does not exist'); - } + /** + * Dispatch a route for the given HTTP Method / URI. + * + * @param $httpMethod + * @param $uri + * + * @return mixed|null + */ + public function dispatch($httpMethod, $uri) + { + list($handler, $filters, $vars) = $this->dispatchRoute($httpMethod, trim($uri, '/')); + + list($beforeFilter, $afterFilter) = $this->parseFilters($filters); + + if (($response = $this->dispatchFilters($beforeFilter)) !== null) { + return $response; + } + + $resolvedHandler = $this->handlerResolver->resolve($handler); + + $response = call_user_func_array($resolvedHandler, $vars); + + return $this->dispatchFilters($afterFilter, $response); + } + + /** + * Dispatch a route filter. + * + * @param $filters + * @param null $response + * + * @return mixed|null + */ + private function dispatchFilters($filters, $response = null) + { + while ($filter = array_shift($filters)) { + $handler = $this->handlerResolver->resolve($filter); + + if (($filteredResponse = call_user_func($handler, $response)) !== null) { + return $filteredResponse; + } + } + + return $response; + } + + /** + * Normalise the array filters attached to the route and merge with any global filters. + * + * @param $filters + * + * @return array + */ + private function parseFilters($filters) + { + $beforeFilter = []; + $afterFilter = []; + + if (isset($filters[Route::BEFORE])) { + $beforeFilter = array_intersect_key($this->filters, array_flip((array)$filters[Route::BEFORE])); + } + + if (isset($filters[Route::AFTER])) { + $afterFilter = array_intersect_key($this->filters, array_flip((array)$filters[Route::AFTER])); + } + + return [$beforeFilter, $afterFilter]; + } + + /** + * Perform the route dispatching. Check static routes first followed by variable routes. + * + * @param $httpMethod + * @param $uri + * + * @throws Exception\HttpRouteNotFoundException + */ + private function dispatchRoute($httpMethod, $uri) + { + if (isset($this->staticRouteMap[$uri])) { + return $this->dispatchStaticRoute($httpMethod, $uri); + } + + return $this->dispatchVariableRoute($httpMethod, $uri); + } + + /** + * Handle the dispatching of static routes. + * + * @param $httpMethod + * @param $uri + * + * @return mixed + * @throws Exception\HttpMethodNotAllowedException + */ + private function dispatchStaticRoute($httpMethod, $uri) + { + $routes = $this->staticRouteMap[$uri]; + + if (!isset($routes[$httpMethod])) { + $httpMethod = $this->checkFallbacks($routes, $httpMethod); + } + + return $routes[$httpMethod]; + } + + /** + * Check fallback routes: HEAD for GET requests followed by the ANY attachment. + * + * @param $routes + * @param $httpMethod + * + * @throws Exception\HttpMethodNotAllowedException + */ + private function checkFallbacks($routes, $httpMethod) + { + $additional = [Route::ANY]; + + if ($httpMethod === Route::HEAD) { + $additional[] = Route::GET; + } + + foreach ($additional as $method) { + if (isset($routes[$method])) { + return $method; + } + } + + $this->matchedRoute = $routes; + + throw new HttpMethodNotAllowedException(array_keys($routes), 'Method not allowed'); + } + + /** + * Handle the dispatching of variable routes. + * + * @param $httpMethod + * @param $uri + * + * @throws Exception\HttpMethodNotAllowedException + * @throws Exception\HttpRouteNotFoundException + */ + private function dispatchVariableRoute($httpMethod, $uri) + { + foreach ($this->variableRouteData as $data) { + if (!preg_match($data['regex'], $uri, $matches)) { + continue; + } + + $count = count($matches); + + while (!isset($data['routeMap'][$count++])) { + ; + } + + $routes = $data['routeMap'][$count - 1]; + + if (!isset($routes[$httpMethod])) { + $httpMethod = $this->checkFallbacks($routes, $httpMethod); + } + + foreach (array_values($routes[$httpMethod][2]) as $i => $varName) { + if (!isset($matches[$i + 1]) || $matches[$i + 1] === '') { + unset($routes[$httpMethod][2][$varName]); + } else { + $routes[$httpMethod][2][$varName] = $matches[$i + 1]; + } + } + + return $routes[$httpMethod]; + } + + throw new HttpRouteNotFoundException('Route ' . $uri . ' does not exist'); + } } diff --git a/lib/routing/HandlerResolver.class.php b/lib/routing/HandlerResolver.class.php index 9918d2f7..7421d098 100644 --- a/lib/routing/HandlerResolver.class.php +++ b/lib/routing/HandlerResolver.class.php @@ -4,13 +4,12 @@ namespace Routing; class HandlerResolver implements HandlerResolverInterface { - public function resolve($handler) - { - if (is_array($handler) && is_string($handler[0])) + public function resolve($handler) { - $handler[0] = new $handler[0]; - } + if (is_array($handler) && is_string($handler[0])) { + $handler[0] = new $handler[0]; + } - return $handler; - } -} \ No newline at end of file + return $handler; + } +} diff --git a/lib/routing/HandlerResolverInterface.class.php b/lib/routing/HandlerResolverInterface.class.php index 059c8d0f..7bc8d15d 100644 --- a/lib/routing/HandlerResolverInterface.class.php +++ b/lib/routing/HandlerResolverInterface.class.php @@ -4,5 +4,5 @@ namespace Routing; interface HandlerResolverInterface { - public function resolve($handler); + public function resolve($handler); } diff --git a/lib/routing/HttpException.class.php b/lib/routing/HttpException.class.php index e3e13232..c09f88f8 100644 --- a/lib/routing/HttpException.class.php +++ b/lib/routing/HttpException.class.php @@ -2,4 +2,6 @@ namespace Routing; -class HttpException extends \Exception {} +class HttpException extends \Exception +{ +} diff --git a/lib/routing/HttpMethodNotAllowedException.class.php b/lib/routing/HttpMethodNotAllowedException.class.php index cc393a3c..dbc8fedb 100644 --- a/lib/routing/HttpMethodNotAllowedException.class.php +++ b/lib/routing/HttpMethodNotAllowedException.class.php @@ -4,16 +4,16 @@ namespace Routing; class HttpMethodNotAllowedException extends HttpException { - protected $allowedMethods; + protected $allowedMethods; - public function __construct(array $allowedMethods, $message = "", $code = 0, Exception $previous = null) - { - $this->allowedMethods = $allowedMethods; - parent::__construct($message, $code, $previous); - } + public function __construct(array $allowedMethods, $message = "", $code = 0, Exception $previous = null) + { + $this->allowedMethods = $allowedMethods; + parent::__construct($message, $code, $previous); + } - public function getAllowedMethods() - { - return $this->allowedMethods; - } + public function getAllowedMethods() + { + return $this->allowedMethods; + } } diff --git a/lib/routing/HttpRouteNotFoundException.class.php b/lib/routing/HttpRouteNotFoundException.class.php index b7dc994a..4b461750 100644 --- a/lib/routing/HttpRouteNotFoundException.class.php +++ b/lib/routing/HttpRouteNotFoundException.class.php @@ -2,5 +2,6 @@ namespace Routing; -class HttpRouteNotFoundException extends HttpException {} - +class HttpRouteNotFoundException extends HttpException +{ +} diff --git a/lib/routing/Route.class.php b/lib/routing/Route.class.php index de39eb55..ef8b9a6b 100644 --- a/lib/routing/Route.class.php +++ b/lib/routing/Route.class.php @@ -4,23 +4,22 @@ namespace Routing; class Route { - /** - * Constants for before and after filters - */ - const BEFORE = 'before'; - const AFTER = 'after'; - const PREFIX = 'prefix'; + /** + * Constants for before and after filters + */ + const BEFORE = 'before'; + const AFTER = 'after'; + const PREFIX = 'prefix'; - /** - * Constants for common HTTP methods - */ - const ANY = 'ANY'; - const GET = 'GET'; - const HEAD = 'HEAD'; - const POST = 'POST'; - const PUT = 'PUT'; - const PATCH = 'PATCH'; - const DELETE = 'DELETE'; - const OPTIONS = 'OPTIONS'; + /** + * Constants for common HTTP methods + */ + const ANY = 'ANY'; + const GET = 'GET'; + const HEAD = 'HEAD'; + const POST = 'POST'; + const PUT = 'PUT'; + const PATCH = 'PATCH'; + const DELETE = 'DELETE'; + const OPTIONS = 'OPTIONS'; } - diff --git a/lib/routing/RouteCollector.class.php b/lib/routing/RouteCollector.class.php index aead6524..ce60f0d7 100644 --- a/lib/routing/RouteCollector.class.php +++ b/lib/routing/RouteCollector.class.php @@ -4,266 +4,247 @@ namespace Routing; class RouteCollector { - const DEFAULT_CONTROLLER_ROUTE = 'index'; + const DEFAULT_CONTROLLER_ROUTE = 'index'; - const APPROX_CHUNK_SIZE = 10; + const APPROX_CHUNK_SIZE = 10; - /** - * @var RouteParser - */ - private $routeParser; - /** - * @var array - */ - private $filters = []; - /** - * @var array - */ - private $staticRoutes = []; - /** - * @var array - */ - private $regexToRoutesMap = []; - /** - * @var array - */ - private $reverse = []; + /** + * @var RouteParser + */ + private $routeParser; + /** + * @var array + */ + private $filters = []; + /** + * @var array + */ + private $staticRoutes = []; + /** + * @var array + */ + private $regexToRoutesMap = []; + /** + * @var array + */ + private $reverse = []; - /** - * @var array - */ - private $globalFilters = []; + /** + * @var array + */ + private $globalFilters = []; - /** - * @var string - */ - private $globalRoutePrefix = ''; + /** + * @var string + */ + private $globalRoutePrefix = ''; - public function __construct(RouteParser $routeParser = null) - { - $this->routeParser = $routeParser ?: new RouteParser(); - } - - public function hasRoute(string $name): bool - { - return isset($this->reverse[$name]); - } - - public function route(string $name, array $args = null): string - { - $url = []; - - $replacements = is_null($args) ? [] : array_values($args); - - $variable = 0; - - foreach ($this->reverse[$name] as $part) + public function __construct(RouteParser $routeParser = null) { - if (!$part['variable']) - { - $url[] = $part['value']; - } - elseif (isset($replacements[$variable])) - { - if ($part['optional']) - { - $url[] = '/'; + $this->routeParser = $routeParser ?: new RouteParser(); + } + + public function hasRoute(string $name): bool + { + return isset($this->reverse[$name]); + } + + public function route(string $name, array $args = null): string + { + $url = []; + + $replacements = is_null($args) ? [] : array_values($args); + + $variable = 0; + + foreach ($this->reverse[$name] as $part) { + if (!$part['variable']) { + $url[] = $part['value']; + } elseif (isset($replacements[$variable])) { + if ($part['optional']) { + $url[] = '/'; + } + + $url[] = $replacements[$variable++]; + } elseif (!$part['optional']) { + throw new BadRouteException("Expecting route variable '{$part['name']}'"); + } } - $url[] = $replacements[$variable++]; - } - elseif (!$part['optional']) - { - throw new BadRouteException("Expecting route variable '{$part['name']}'"); - } + return implode('', $url); } - return implode('', $url); - } - - public function addRoute(string $httpMethod, $route, $handler, array $filters = []): RouteCollector - { - - if (is_array($route)) + public function addRoute(string $httpMethod, $route, $handler, array $filters = []): RouteCollector { - list($route, $name) = $route; - } + if (is_array($route)) { + list($route, $name) = $route; + } - $route = $this->addPrefix($this->trim($route)); + $route = $this->addPrefix($this->trim($route)); - list($routeData, $reverseData) = $this->routeParser->parse($route); + list($routeData, $reverseData) = $this->routeParser->parse($route); - if (isset($name)) - { - $this->reverse[$name] = $reverseData; - } + if (isset($name)) { + $this->reverse[$name] = $reverseData; + } - $filters = array_merge_recursive($this->globalFilters, $filters); + $filters = array_merge_recursive($this->globalFilters, $filters); - isset($routeData[1]) ? + isset($routeData[1]) ? $this->addVariableRoute($httpMethod, $routeData, $handler, $filters) : $this->addStaticRoute($httpMethod, $routeData, $handler, $filters); - return $this; - } - - private function addStaticRoute(string $httpMethod, $routeData, $handler, $filters) - { - $routeStr = $routeData[0]; - - if (isset($this->staticRoutes[$routeStr][$httpMethod])) - { - throw new BadRouteException("Cannot register two routes matching '$routeStr' for method '$httpMethod'"); + return $this; } - foreach ($this->regexToRoutesMap as $regex => $routes) + private function addStaticRoute(string $httpMethod, $routeData, $handler, $filters) { - if (isset($routes[$httpMethod]) && preg_match('~^' . $regex . '$~', $routeStr)) - { - throw new BadRouteException("Static route '$routeStr' is shadowed by previously defined variable route '$regex' for method '$httpMethod'"); - } + $routeStr = $routeData[0]; + + if (isset($this->staticRoutes[$routeStr][$httpMethod])) { + throw new BadRouteException("Cannot register two routes matching '$routeStr' for method '$httpMethod'"); + } + + foreach ($this->regexToRoutesMap as $regex => $routes) { + if (isset($routes[$httpMethod]) && preg_match('~^' . $regex . '$~', $routeStr)) { + throw new BadRouteException("Static route '$routeStr' is shadowed by previously defined variable route '$regex' for method '$httpMethod'"); + } + } + + $this->staticRoutes[$routeStr][$httpMethod] = [$handler, $filters, []]; } - $this->staticRoutes[$routeStr][$httpMethod] = [$handler, $filters, []]; - } - - private function addVariableRoute(string $httpMethod, $routeData, $handler, $filters) - { - list($regex, $variables) = $routeData; - - if (isset($this->regexToRoutesMap[$regex][$httpMethod])) + private function addVariableRoute(string $httpMethod, $routeData, $handler, $filters) { - throw new BadRouteException("Cannot register two routes matching '$regex' for method '$httpMethod'"); + list($regex, $variables) = $routeData; + + if (isset($this->regexToRoutesMap[$regex][$httpMethod])) { + throw new BadRouteException("Cannot register two routes matching '$regex' for method '$httpMethod'"); + } + + $this->regexToRoutesMap[$regex][$httpMethod] = [$handler, $filters, $variables]; } - $this->regexToRoutesMap[$regex][$httpMethod] = [$handler, $filters, $variables]; - } + public function group(array $filters, \Closure $callback) + { + $oldGlobalFilters = $this->globalFilters; - public function group(array $filters, \Closure $callback) - { - $oldGlobalFilters = $this->globalFilters; + $oldGlobalPrefix = $this->globalRoutePrefix; - $oldGlobalPrefix = $this->globalRoutePrefix; - - $this->globalFilters = + $this->globalFilters = array_merge_recursive($this->globalFilters, array_intersect_key($filters, [Route::AFTER => 1, Route::BEFORE => 1])); - $newPrefix = isset($filters[Route::PREFIX]) ? $this->trim($filters[Route::PREFIX]) : null; + $newPrefix = isset($filters[Route::PREFIX]) ? $this->trim($filters[Route::PREFIX]) : null; - $this->globalRoutePrefix = $this->addPrefix($newPrefix); + $this->globalRoutePrefix = $this->addPrefix($newPrefix); - $callback($this); + $callback($this); - $this->globalFilters = $oldGlobalFilters; + $this->globalFilters = $oldGlobalFilters; - $this->globalRoutePrefix = $oldGlobalPrefix; - } + $this->globalRoutePrefix = $oldGlobalPrefix; + } - private function addPrefix(string $route) - { - return $this->trim($this->trim($this->globalRoutePrefix) . '/' . $route); - } - - public function filter(string $name, $handler) - { - $this->filters[$name] = $handler; - } - - - public function get($route, $handler, array $filters = []) - { - return $this->addRoute(Route::GET, $route, $handler, $filters); - } - - public function head($route, $handler, array $filters = []) - { - return $this->addRoute(Route::HEAD, $route, $handler, $filters); - } - - public function post($route, $handler, array $filters = []) - { - return $this->addRoute(Route::POST, $route, $handler, $filters); - } - - public function put($route, $handler, array $filters = []) - { - return $this->addRoute(Route::PUT, $route, $handler, $filters); - } - - public function patch($route, $handler, array $filters = []) - { - return $this->addRoute(Route::PATCH, $route, $handler, $filters); - } - - public function delete($route, $handler, array $filters = []) - { - return $this->addRoute(Route::DELETE, $route, $handler, $filters); - } - - public function options($route, $handler, array $filters = []) - { - return $this->addRoute(Route::OPTIONS, $route, $handler, $filters); - } - - public function any($route, $handler, array $filters = []) - { - return $this->addRoute(Route::ANY, $route, $handler, $filters); - } - - - public function controller(string $route, string $classname, array $filters = []): RouteCollector - { - $reflection = new ReflectionClass($classname); - - $validMethods = $this->getValidMethods(); - - $sep = $route === '/' ? '' : '/'; - - foreach ($reflection->getMethods(ReflectionMethod::IS_PUBLIC) as $method) + private function addPrefix(string $route) { - foreach ($validMethods as $valid) - { - if (stripos($method->name, $valid) === 0) - { - $methodName = $this->camelCaseToDashed(substr($method->name, strlen($valid))); + return $this->trim($this->trim($this->globalRoutePrefix) . '/' . $route); + } - $params = $this->buildControllerParameters($method); + public function filter(string $name, $handler) + { + $this->filters[$name] = $handler; + } - if ($methodName === self::DEFAULT_CONTROLLER_ROUTE) - { - $this->addRoute($valid, $route . $params, [$classname, $method->name], $filters); - } - $this->addRoute($valid, $route . $sep . $methodName . $params, [$classname, $method->name], $filters); + public function get($route, $handler, array $filters = []) + { + return $this->addRoute(Route::GET, $route, $handler, $filters); + } - break; + public function head($route, $handler, array $filters = []) + { + return $this->addRoute(Route::HEAD, $route, $handler, $filters); + } + + public function post($route, $handler, array $filters = []) + { + return $this->addRoute(Route::POST, $route, $handler, $filters); + } + + public function put($route, $handler, array $filters = []) + { + return $this->addRoute(Route::PUT, $route, $handler, $filters); + } + + public function patch($route, $handler, array $filters = []) + { + return $this->addRoute(Route::PATCH, $route, $handler, $filters); + } + + public function delete($route, $handler, array $filters = []) + { + return $this->addRoute(Route::DELETE, $route, $handler, $filters); + } + + public function options($route, $handler, array $filters = []) + { + return $this->addRoute(Route::OPTIONS, $route, $handler, $filters); + } + + public function any($route, $handler, array $filters = []) + { + return $this->addRoute(Route::ANY, $route, $handler, $filters); + } + + + public function controller(string $route, string $classname, array $filters = []): RouteCollector + { + $reflection = new ReflectionClass($classname); + + $validMethods = $this->getValidMethods(); + + $sep = $route === '/' ? '' : '/'; + + foreach ($reflection->getMethods(ReflectionMethod::IS_PUBLIC) as $method) { + foreach ($validMethods as $valid) { + if (stripos($method->name, $valid) === 0) { + $methodName = $this->camelCaseToDashed(substr($method->name, strlen($valid))); + + $params = $this->buildControllerParameters($method); + + if ($methodName === self::DEFAULT_CONTROLLER_ROUTE) { + $this->addRoute($valid, $route . $params, [$classname, $method->name], $filters); + } + + $this->addRoute($valid, $route . $sep . $methodName . $params, [$classname, $method->name], $filters); + + break; + } + } } - } + + return $this; } - return $this; - } - - private function buildControllerParameters(ReflectionMethod $method): string - { - $params = ''; - - foreach ($method->getParameters() as $param) + private function buildControllerParameters(ReflectionMethod $method): string { - $params .= "/{" . $param->getName() . "}" . ($param->isOptional() ? '?' : ''); + $params = ''; + + foreach ($method->getParameters() as $param) { + $params .= "/{" . $param->getName() . "}" . ($param->isOptional() ? '?' : ''); + } + + return $params; } - return $params; - } + private function camelCaseToDashed(string $string): string + { + return strtolower(preg_replace('/([A-Z])/', '-$1', lcfirst($string))); + } - private function camelCaseToDashed(string $string): string - { - return strtolower(preg_replace('/([A-Z])/', '-$1', lcfirst($string))); - } - - public function getValidMethods(): array - { - return [ + public function getValidMethods(): array + { + return [ Route::ANY, Route::GET, Route::POST, @@ -273,53 +254,51 @@ class RouteCollector Route::HEAD, Route::OPTIONS, ]; - } - - public function getData(): RouteData - { - return new RouteData($this->staticRoutes, $this->regexToRoutesMap ? $this->generateVariableRouteData() : [], $this->filters); - } - - private function trim(string $route): string - { - return trim($route, '/'); - } - - private function generateVariableRouteData(): array - { - $chunkSize = $this->computeChunkSize(count($this->regexToRoutesMap)); - $chunks = array_chunk($this->regexToRoutesMap, $chunkSize, true); - return array_map([$this, 'processChunk'], $chunks); - } - - private function computeChunkSize(int $count): float - { - $numParts = max(1, round($count / self::APPROX_CHUNK_SIZE)); - return ceil($count / $numParts); - } - - private function processChunk($regexToRoutesMap): array - { - $routeMap = []; - $regexes = []; - $numGroups = 0; - foreach ($regexToRoutesMap as $regex => $routes) - { - $firstRoute = reset($routes); - $numVariables = count($firstRoute[2]); - $numGroups = max($numGroups, $numVariables); - - $regexes[] = $regex . str_repeat('()', $numGroups - $numVariables); - - foreach ($routes as $httpMethod => $route) - { - $routeMap[$numGroups + 1][$httpMethod] = $route; - } - - $numGroups++; } - $regex = '~^(?|' . implode('|', $regexes) . ')$~'; - return ['regex' => $regex, 'routeMap' => $routeMap]; - } + public function getData(): RouteData + { + return new RouteData($this->staticRoutes, $this->regexToRoutesMap ? $this->generateVariableRouteData() : [], $this->filters); + } + + private function trim(string $route): string + { + return trim($route, '/'); + } + + private function generateVariableRouteData(): array + { + $chunkSize = $this->computeChunkSize(count($this->regexToRoutesMap)); + $chunks = array_chunk($this->regexToRoutesMap, $chunkSize, true); + return array_map([$this, 'processChunk'], $chunks); + } + + private function computeChunkSize(int $count): float + { + $numParts = max(1, round($count / self::APPROX_CHUNK_SIZE)); + return ceil($count / $numParts); + } + + private function processChunk($regexToRoutesMap): array + { + $routeMap = []; + $regexes = []; + $numGroups = 0; + foreach ($regexToRoutesMap as $regex => $routes) { + $firstRoute = reset($routes); + $numVariables = count($firstRoute[2]); + $numGroups = max($numGroups, $numVariables); + + $regexes[] = $regex . str_repeat('()', $numGroups - $numVariables); + + foreach ($routes as $httpMethod => $route) { + $routeMap[$numGroups + 1][$httpMethod] = $route; + } + + $numGroups++; + } + + $regex = '~^(?|' . implode('|', $regexes) . ')$~'; + return ['regex' => $regex, 'routeMap' => $routeMap]; + } } diff --git a/lib/routing/RouteData.class.php b/lib/routing/RouteData.class.php index 1a53709f..3f022ac6 100644 --- a/lib/routing/RouteData.class.php +++ b/lib/routing/RouteData.class.php @@ -4,56 +4,56 @@ namespace Routing; class RouteData { - /** - * @var array - */ - private $variableRoutes; + /** + * @var array + */ + private $variableRoutes; - /** - * @var array - */ - private $staticRoutes; + /** + * @var array + */ + private $staticRoutes; - /** - * @var array - */ - private $filters; + /** + * @var array + */ + private $filters; - /** - * @param array $staticRoutes - * @param array $variableRoutes - * @param array $filters - */ - public function __construct(array $staticRoutes, array $variableRoutes, array $filters) - { - $this->staticRoutes = $staticRoutes; + /** + * @param array $staticRoutes + * @param array $variableRoutes + * @param array $filters + */ + public function __construct(array $staticRoutes, array $variableRoutes, array $filters) + { + $this->staticRoutes = $staticRoutes; - $this->variableRoutes = $variableRoutes; + $this->variableRoutes = $variableRoutes; - $this->filters = $filters; - } + $this->filters = $filters; + } - /** - * @return array - */ - public function getStaticRoutes() - { - return $this->staticRoutes; - } + /** + * @return array + */ + public function getStaticRoutes() + { + return $this->staticRoutes; + } - /** - * @return array - */ - public function getVariableRoutes() - { - return $this->variableRoutes; - } + /** + * @return array + */ + public function getVariableRoutes() + { + return $this->variableRoutes; + } - /** - * @return mixed - */ - public function getFilters() - { - return $this->filters; - } + /** + * @return mixed + */ + public function getFilters() + { + return $this->filters; + } } diff --git a/lib/routing/RouteParser.class.php b/lib/routing/RouteParser.class.php index c6d9ad17..79a34323 100644 --- a/lib/routing/RouteParser.class.php +++ b/lib/routing/RouteParser.class.php @@ -22,7 +22,7 @@ class RouteParser * * Finally we look for an optional '?' which is used to signify an optional route. */ - const VARIABLE_REGEX = + const VARIABLE_REGEX = "~\{ \s* ([a-zA-Z0-9_]*) \s* (?: @@ -30,185 +30,176 @@ class RouteParser )? \}\??~x"; - /** - * The default parameter character restriction (One or more characters that is not a '/'). - */ - const DEFAULT_DISPATCH_REGEX = '[^/]+'; + /** + * The default parameter character restriction (One or more characters that is not a '/'). + */ + const DEFAULT_DISPATCH_REGEX = '[^/]+'; - private $parts; + private $parts; - private $reverseParts; + private $reverseParts; - private $partsCounter; + private $partsCounter; - private $variables; + private $variables; - private $regexOffset; + private $regexOffset; - /** - * Handy parameter type restrictions. - * - * @var array - */ - private $regexShortcuts = [ + /** + * Handy parameter type restrictions. + * + * @var array + */ + private $regexShortcuts = [ ':i}' => ':[0-9]+}', ':a}' => ':[0-9A-Za-z]+}', ':h}' => ':[0-9A-Fa-f]+}', ':c}' => ':[a-zA-Z0-9+_\-\.]+}' ]; - /** - * Parse a route returning the correct data format to pass to the dispatch engine. - * - * @param $route - * - * @return array - */ - public function parse($route) - { - $this->reset(); - - $route = strtr($route, $this->regexShortcuts); - - if (!$matches = $this->extractVariableRouteParts($route)) + /** + * Parse a route returning the correct data format to pass to the dispatch engine. + * + * @param $route + * + * @return array + */ + public function parse($route) { - $reverse = [ + $this->reset(); + + $route = strtr($route, $this->regexShortcuts); + + if (!$matches = $this->extractVariableRouteParts($route)) { + $reverse = [ 'variable' => false, 'value' => $route ]; - return [[$route], [$reverse]]; - } + return [[$route], [$reverse]]; + } - foreach ($matches as $set) - { + foreach ($matches as $set) { + $this->staticParts($route, $set[0][1]); - $this->staticParts($route, $set[0][1]); + $this->validateVariable($set[1][0]); - $this->validateVariable($set[1][0]); + $regexPart = (isset($set[2]) ? trim($set[2][0]) : self::DEFAULT_DISPATCH_REGEX); - $regexPart = (isset($set[2]) ? trim($set[2][0]) : self::DEFAULT_DISPATCH_REGEX); + $this->regexOffset = $set[0][1] + strlen($set[0][0]); - $this->regexOffset = $set[0][1] + strlen($set[0][0]); + $match = '(' . $regexPart . ')'; - $match = '(' . $regexPart . ')'; + $isOptional = substr($set[0][0], -1) === '?'; - $isOptional = substr($set[0][0], -1) === '?'; + if ($isOptional) { + $match = $this->makeOptional($match); + } - if ($isOptional) - { - $match = $this->makeOptional($match); - } - - $this->reverseParts[$this->partsCounter] = [ + $this->reverseParts[$this->partsCounter] = [ 'variable' => true, 'optional' => $isOptional, 'name' => $set[1][0] ]; - $this->parts[$this->partsCounter++] = $match; + $this->parts[$this->partsCounter++] = $match; + } + + $this->staticParts($route, strlen($route)); + + return [[implode('', $this->parts), $this->variables], array_values($this->reverseParts)]; } - $this->staticParts($route, strlen($route)); - - return [[implode('', $this->parts), $this->variables], array_values($this->reverseParts)]; - } - - /** - * Reset the parser ready for the next route. - */ - private function reset() - { - $this->parts = []; - - $this->reverseParts = []; - - $this->partsCounter = 0; - - $this->variables = []; - - $this->regexOffset = 0; - } - - /** - * Return any variable route portions from the given route. - * - * @param $route - * - * @return mixed - */ - private function extractVariableRouteParts($route) - { - if (preg_match_all(self::VARIABLE_REGEX, $route, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER)) + /** + * Reset the parser ready for the next route. + */ + private function reset() { - return $matches; + $this->parts = []; + + $this->reverseParts = []; + + $this->partsCounter = 0; + + $this->variables = []; + + $this->regexOffset = 0; } - } - /** - * @param $route - * @param $nextOffset - */ - private function staticParts($route, $nextOffset) - { - $static = preg_split('~(/)~u', substr($route, $this->regexOffset, $nextOffset - $this->regexOffset), 0, PREG_SPLIT_DELIM_CAPTURE); - - foreach ($static as $staticPart) + /** + * Return any variable route portions from the given route. + * + * @param $route + * + * @return mixed + */ + private function extractVariableRouteParts($route) { - if ($staticPart) - { - $quotedPart = $this->quote($staticPart); + if (preg_match_all(self::VARIABLE_REGEX, $route, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER)) { + return $matches; + } + } - $this->parts[$this->partsCounter] = $quotedPart; + /** + * @param $route + * @param $nextOffset + */ + private function staticParts($route, $nextOffset) + { + $static = preg_split('~(/)~u', substr($route, $this->regexOffset, $nextOffset - $this->regexOffset), 0, PREG_SPLIT_DELIM_CAPTURE); - $this->reverseParts[$this->partsCounter] = [ + foreach ($static as $staticPart) { + if ($staticPart) { + $quotedPart = $this->quote($staticPart); + + $this->parts[$this->partsCounter] = $quotedPart; + + $this->reverseParts[$this->partsCounter] = [ 'variable' => false, 'value' => $staticPart ]; - $this->partsCounter++; - } + $this->partsCounter++; + } + } } - } - /** - * @param $varName - */ - private function validateVariable($varName) - { - if (isset($this->variables[$varName])) + /** + * @param $varName + */ + private function validateVariable($varName) { - throw new BadRouteException("Cannot use the same placeholder '$varName' twice"); + if (isset($this->variables[$varName])) { + throw new BadRouteException("Cannot use the same placeholder '$varName' twice"); + } + + $this->variables[$varName] = $varName; } - $this->variables[$varName] = $varName; - } - - /** - * @param $match - * - * @return string - */ - private function makeOptional($match) - { - $previous = $this->partsCounter - 1; - - if (isset($this->parts[$previous]) && $this->parts[$previous] === '/') + /** + * @param $match + * + * @return string + */ + private function makeOptional($match) { - $this->partsCounter--; - $match = '(?:/' . $match . ')'; + $previous = $this->partsCounter - 1; + + if (isset($this->parts[$previous]) && $this->parts[$previous] === '/') { + $this->partsCounter--; + $match = '(?:/' . $match . ')'; + } + + return $match . '?'; } - return $match . '?'; - } - - /** - * @param $part - * - * @return string - */ - private function quote($part) - { - return preg_quote($part, '~'); - } + /** + * @param $part + * + * @return string + */ + private function quote($part) + { + return preg_quote($part, '~'); + } } diff --git a/lib/thirdparty/Asana.class.php b/lib/thirdparty/Asana.class.php index 953d8334..6f969332 100644 --- a/lib/thirdparty/Asana.class.php +++ b/lib/thirdparty/Asana.class.php @@ -2,100 +2,77 @@ class Asana { - protected static $curlOptions = ['json_response' => true, 'cache' => true, 'timeout' => 10]; + protected static $curlOptions = ['json_response' => true, 'cache' => true, 'timeout' => 10]; - public static function listRoadmapTasks($cache = true) - { - // Use print_r(static::get('/projects')) to get project IDs - - $projects = [ - 158602294500138 => ['LBRY Browser', 'https://github.com/lbryio/lbry-web-ui'], - 158602294500137 => ['LBRY Data Network', 'https://github.com/lbryio/lbry'], - 161514803479899 => ['Blockchain and Wallets', 'https://github.com/lbryio/lbrycrd'], - 136290697597644 => ['Integration and Building', null], - 158602294500249 => ['Documentation', null], - ]; - - $tasks = []; - - $tags = [ - 192699565737944 => 'Open Beta', - 542803886522122 => 'Upcoming', - 542803886522120 => 'Future' - ]; - - foreach ($tags as $tagId => $tagLabel) + public static function listRoadmapTasks($cache = true) { - $taggedTasks = static::get('/tags/' . $tagId . '/tasks', ['completed_since' => 'now'], $cache); - foreach ($taggedTasks as $task) - { - $fullTask = static::get('/tasks/' . $task['id'], [], $cache); - $projectId = $fullTask['memberships'][0]['project']['id'] ?? null; - if ($fullTask['name']) - { - if ($projectId && isset($projects[$projectId])) - { - list($projectName, $projectUrl) = $projects[$projectId]; + // Use print_r(static::get('/projects')) to get project IDs + + $roadmapProjectId = 502841492992874; + $tasks = []; + + $allTasks = array_reduce( + static::get('/projects/' . $roadmapProjectId . '/tasks', [], $cache), + function ($carry, $task) use ($cache) { + $fullTask = static::get('/tasks/' . $task['id'], [], $cache); + if ($fullTask['name']) { + $carry[] = $fullTask; } - else - { - $projectName = 'Other'; - $projectId = null; - } - $tasks[$tagLabel][] = array_intersect_key($fullTask, ['name' => null]) + [ - 'badge' => $projectName, - 'date' => $fullTask['due_on'] ?? null, - 'body' => nl2br($fullTask['notes']), - 'group' => $tagLabel, - 'project_id' => $projectId, - 'assignee' => $fullTask['assignee'] ? ucwords($fullTask['assignee']['name']) : '', - 'quarter_date' => 'Q' . static::dateToQuarter($fullTask['due_on']) . ' ' . (string) date('Y', strtotime($fullTask['due_on'])) - ]; + return $carry; + }, + [] + ); + + foreach ($allTasks as $task) { + $badge = "Planned"; + if ($task['completed']) { + $badge = "Complete"; + } elseif (in_array("In Progress", array_map(function ($tag) { + return $tag['name']; + }, $task['tags'] ?? []))) { + $badge = "In Progress"; + } + $taskDueTime = strtotime($task['due_on']); + $year = date('Y', $taskDueTime); + $tasks[' ' . $year . ' '][] = array_intersect_key($task, ['name' => null]) + [ + 'badge' => $badge, + 'date' => $task['due_on'] ?? null, + 'body' => nl2br($task['notes']), +// 'assignee' => $fullTask['assignee'] ? ucwords($fullTask['assignee']['name']) : '', + 'quarter_date' => 'Q' . static::dateToQuarter($task['due_on']) . ' ' . $year + ]; } - } + + foreach ($tasks as &$groupTasks) { + usort($groupTasks, function ($tA, $tB) { + if ($tA['date'] xor $tB['date']) { + return $tA['date'] ? -1 : 1; + } + return $tA['date'] < $tB['date'] ? -1 : 1; + }); + } + + return $tasks; } - foreach ($tasks as &$groupTasks) + protected static function get($endpoint, array $data = [], $cache = true) { - usort($groupTasks, function ($tA, $tB) - { - if ($tA['group'] != $tB['group']) - { - return $tA['group'] <= $tB['group'] ? -1 : 1; - } - if ($tA['date'] xor $tB['date']) - { - return $tA['date'] ? -1 : 1; - } - if ($tA['project_id'] xor $tB['project_id']) - { - return $tA['project_id'] ? -1 : 1; - } - return $tA['date'] < $tB['date'] ? -1 : 1; - }); - } + $apiKey = Config::get(Config::ASANA_KEY); - return $tasks; - } - - protected static function get($endpoint, array $data = [], $cache = true) - { - $apiKey = Config::get(Config::ASANA_KEY); - - $options = [ + $options = [ 'headers' => ['Authorization: Bearer ' . $apiKey], 'cache' => $cache ] + static::$curlOptions; - $responseData = CurlWithCache::get('https://app.asana.com/api/1.0' . $endpoint, $data, $options); - return $responseData['data'] ?? []; - } + $responseData = CurlWithCache::get('https://app.asana.com/api/1.0' . $endpoint, $data, $options); + return $responseData['data'] ?? []; + } - // Converts date to quarter - protected static function dateToQuarter($date) - { - return (string)ceil(date('m', strtotime($date))/3); - } + // Converts date to quarter + protected static function dateToQuarter($date) + { + return (string)ceil(date('m', strtotime($date))/3); + } } class AsanaException extends Exception diff --git a/lib/thirdparty/Github.class.php b/lib/thirdparty/Github.class.php index 61147e82..9e85dee0 100644 --- a/lib/thirdparty/Github.class.php +++ b/lib/thirdparty/Github.class.php @@ -2,155 +2,135 @@ class Github { - protected static function findReleaseAssetForOs(array $release, string $os) - { - if (!in_array($os, array_keys(OS::getAll()))) + protected static function findReleaseAssetForOs(array $release, string $os) { - throw new DomainException('Unknown OS'); - } + if (!in_array($os, array_keys(OS::getAll()))) { + throw new DomainException('Unknown OS'); + } - foreach ($release['assets'] as $asset) - { - $ext = substr($asset['name'], -4); - if ( + foreach ($release['assets'] as $asset) { + $ext = substr($asset['name'], -4); + if ( ($os == OS::OS_LINUX && ($ext == '.deb' || in_array($asset['content_type'], ['application/x-debian-package', 'application/x-deb']))) || ($os == OS::OS_OSX && ($ext == '.dmg' || in_array($asset['content_type'], ['application/x-diskcopy', 'application/x-apple-diskimage']))) || ($os == OS::OS_WINDOWS && $ext == '.exe') - ) - { - return $asset; - } - } - } - - public static function getAppRelease($cache = true) - { - try - { - return static::get('/repos/lbryio/lbry-app/releases/latest', [], $cache); - } - catch (Exception $e) - { + ) { + return $asset; + } + } } - return null; - } - - public static function getAppAsset($os, $cache = true) - { - $release = static::getAppRelease($cache); - return $release ? static::findReleaseAssetForOs($release, $os) : null; - } - - - public static function getAppDownloadUrl($os, $cache = true) - { - $asset = static::getAppAsset($os, $cache); - return $asset ? $asset['browser_download_url'] : null; - } - - public static function getAppPrereleaseDownloadUrl($os, $cache = true) - { - try + public static function getAppRelease($cache = true) { - $releases = static::get('/repos/lbryio/lbry-app/releases', [], $cache); - if (count($releases)) - { - $asset = static::findReleaseAssetForOs($releases[0], $os); + try { + return static::get('/repos/lbryio/lbry-desktop/releases/latest', [], $cache); + } catch (Exception $e) { + } + + return null; + } + + public static function getAppAsset($os, $cache = true) + { + $release = static::getAppRelease($cache); + return $release ? static::findReleaseAssetForOs($release, $os) : null; + } + + + public static function getAppDownloadUrl($os, $cache = true) + { + $asset = static::getAppAsset($os, $cache); return $asset ? $asset['browser_download_url'] : null; - } - } - catch (Exception $e) - { } - return null; - } - - - public static function getDaemonReleaseProperty($os, $property, $isAssetProperty = false, $cache = true) - { - if (!in_array($os, array_keys(OS::getAll()))) + public static function getAppPrereleaseDownloadUrl($os, $cache = true) { - throw new DomainException('Unknown OS'); + try { + $releases = static::get('/repos/lbryio/lbry-desktop/releases', [], $cache); + if (count($releases)) { + $asset = static::findReleaseAssetForOs($releases[0], $os); + return $asset ? $asset['browser_download_url'] : null; + } + } catch (Exception $e) { + } + + return null; } - try + + public static function getDaemonReleaseProperty($os, $property, $isAssetProperty = false, $cache = true) { - $releaseData = static::get('/repos/lbryio/lbry/releases/latest', [], $cache); - foreach ($releaseData['assets'] as $asset) - { - if ( + if (!in_array($os, array_keys(OS::getAll()))) { + throw new DomainException('Unknown OS'); + } + + try { + $releaseData = static::get('/repos/lbryio/lbry/releases/latest', [], $cache); + foreach ($releaseData['assets'] as $asset) { + if ( ($os == OS::OS_LINUX && stripos($asset['browser_download_url'], 'linux') !== false) || ($os == OS::OS_OSX && stripos($asset['browser_download_url'], 'macos') !== false) || ($os == OS::OS_WINDOWS && strpos($asset['browser_download_url'], 'windows') !== false) - ) - { - return $isAssetProperty ? $asset[$property] : $releaseData[$property]; + ) { + return $isAssetProperty ? $asset[$property] : $releaseData[$property]; + } + } + } catch (Exception $e) { } - } - } - catch (Exception $e) - { + + return null; } - return null; - } - - public static function getDaemonDownloadUrl($os, $cache = true) - { - return static::getDaemonReleaseProperty($os, 'browser_download_url', true); - } - - public static function get($endpoint, array $params = [], $cache = true) - { - $twoHoursInSeconds = 7200; - return CurlWithCache::get('https://api.github.com' . $endpoint . '?' . http_build_query($params), [], - ['headers' => ['Accept: application/vnd.github.v3.html+json'],'user_agent' => 'LBRY', 'json_response' => true, 'cache' => $cache === true ? $twoHoursInSeconds : $cache]); - } - - public static function listRoadmapChangesets($cache = true) - { - $sets = []; - $allReleases = []; - - $projects = ['lbry' => 'LBRY Protocol', 'lbry-app' => 'LBRY App']; - - foreach($projects as $project => $projectLabel) + public static function getDaemonDownloadUrl($os, $cache = true) { - $page = 1; - do - { - $releases = static::get('/repos/lbryio/' . $project . '/releases', ['page' => $page], $cache); - $page++; - $allReleases = array_merge($allReleases, array_map(function ($release) use ($project, $projectLabel) - { - return $release + ['project' => $projectLabel]; - }, array_filter($releases, function ($release) - { - return isset($release['tag_name']) && isset($release['published_at']) && $release['published_at']; - }))); - } while (count($releases) >= 30); + return static::getDaemonReleaseProperty($os, 'browser_download_url', true); } - /** - * This logic is likely overly convoluted at this point. It used to group releases by project before going - * to strictly by time. - Jeremy - */ - - foreach ($allReleases as $release) + public static function get($endpoint, array $params = [], $cache = true) { - $group = null; - $matches = null; - if (preg_match('/^v(\d+)\.(\d+)\./', $release['tag_name'] ?? '', $matches)) - { - $group = $release['project'] . ' v' . $matches[1] . '.' . $matches[2]; - } - if ($group) - { - $sets[$group][] = array_intersect_key($release, [ + $twoHoursInSeconds = 7200; + return CurlWithCache::get( + 'https://api.github.com' . $endpoint . '?' . http_build_query($params), + [], + ['headers' => ['Accept: application/vnd.github.v3.html+json'],'user_agent' => 'LBRY', 'json_response' => true, 'cache' => $cache === true ? $twoHoursInSeconds : $cache] + ); + } + + public static function listRoadmapChangesets($cache = true) + { + $sets = []; + $allReleases = []; + + $projects = ['lbry' => 'LBRY Protocol', 'lbry-desktop' => 'LBRY App']; + + foreach ($projects as $project => $projectLabel) { + $page = 1; + do { + $releases = static::get('/repos/lbryio/' . $project . '/releases', ['page' => $page], $cache); + $page++; + $allReleases = array_merge($allReleases, array_map(function ($release) use ($project, $projectLabel) { + return $release + ['project' => $projectLabel]; + }, array_filter($releases, function ($release) { + return isset($release['tag_name']) && isset($release['published_at']) && $release['published_at']; + }))); + } while (count($releases) >= 30); + } + + /** + * This logic is likely overly convoluted at this point. It used to group releases by project before going + * to strictly by time. - Jeremy + */ + + foreach ($allReleases as $release) { + $group = null; + $matches = null; + if (preg_match('/^v(\d+)\.(\d+)\./', $release['tag_name'] ?? '', $matches)) { + $group = $release['project'] . ' v' . $matches[1] . '.' . $matches[2]; + } + if ($group) { + $sets[$group][] = array_intersect_key($release, [ 'prerelease' => null, 'tag_name' => null, 'published_at' => null, 'project' => null ]) + [ 'created_at' => strtotime($release['created_at']), @@ -165,23 +145,22 @@ class Github 'version' => $matches[1] . '.' . $matches[2] . '.' . (isset($matches[3]) ? $matches[3] : ''), 'body' => $release['body_html'] ]; - } + } + } + + uasort($sets, function ($sA, $sB) { + if ($sA[0]['project'] != $sB[0]['project']) { + return $sA[0]['project'] < $sB[0]['project'] ? -1 : 1; + } + return $sB[0]['sort_key'] <=> $sA[0]['sort_key']; + }); + + foreach ($sets as $group => &$groupSet) { + usort($groupSet, function ($rA, $rB) { + return $rB['created_at'] <=> $rA['created_at']; + }); + } + + return $sets; } - - uasort($sets, function ($sA, $sB) - { - if ($sA[0]['project'] != $sB[0]['project']) - { - return $sA[0]['project'] < $sB[0]['project'] ? -1 : 1; - } - return $sB[0]['sort_key'] <=> $sA[0]['sort_key']; - }); - - foreach ($sets as $group => &$groupSet) - { - usort($groupSet, function ($rA, $rB) { return $rB['created_at'] <=> $rA['created_at']; }); - } - - return $sets; - } -} \ No newline at end of file +} diff --git a/lib/thirdparty/LBRY.class.php b/lib/thirdparty/LBRY.class.php index 90ef6ae2..28284cb4 100644 --- a/lib/thirdparty/LBRY.class.php +++ b/lib/thirdparty/LBRY.class.php @@ -3,58 +3,71 @@ class LBRY { - public static function getApiUrl($endpoint) - { - return Config::get(Config::LBRY_API_SERVER) . $endpoint; - } + public static function getApiUrl($endpoint) + { + return Config::get(Config::LBRY_API_SERVER) . $endpoint; + } - public static function getLBCtoUSDRate() - { - $response = CurlWithCache::get(static::getApiUrl('/lbc/exchange_rate'), [], [ + public static function getLBCtoUSDRate() + { + $response = CurlWithCache::get(static::getApiUrl('/lbc/exchange_rate'), [], [ 'cache' => 3600, //one hour 'json_response' => true ]); - return $response['data']['lbc_usd'] ?? 0; - } + return $response['data']['lbc_usd'] ?? 0; + } - public static function subscribe($email, $tag = null) - { - return Curl::post(static::getApiUrl('/list/subscribe'), array_filter([ + public static function subscribe($email, $tag = null) + { + return Curl::post(static::getApiUrl('/list/subscribe'), array_filter([ 'email' => $email, 'tag' => $tag, ]), ['json_response' => true]); - } - - public static function unsubscribe($email) - { - return Curl::post(static::getApiUrl('/list/unsubscribe'), ['email' => $email], ['json_response' => true]); - } - - public static function connectYoutube($channel_name) - { - $type = 'sync'; - return Curl::post(static::getApiUrl('/yt/new'), ['desired_lbry_channel_name' => $channel_name, 'type' => $type], ['json_response' => true]); - } - - // Check the sync status - public static function statusYoutube($status_token) - { - return Curl::get(static::getApiUrl('/yt/status'), ['status_token' => $status_token], ['json_response' => true]); - } - - public static function youtubeReward() - { - return CurlWithCache::post(static::getApiUrl('/yt/rewards'), [], ['cache' => 3600, 'json_response' => true]); - } - - public static function editYouTube($status_token, $channel_name, $email, $sync_consent) - { - if ($email == null){ - return Curl::post(static::getApiUrl("/yt/update"),['status_token' => $status_token, 'new_preferred_channel' => $channel_name, 'sync_consent' => $sync_consent],['json_response' => true]); } - else{ - return Curl::post(static::getApiUrl("/yt/update"), ['status_token' => $status_token, 'new_email' => $email, 'new_preferred_channel' => $channel_name, 'sync_consent' => $sync_consent], ['json_response' => true]); + public static function editEmailSettings($token, $email, $isPrimary =null, $isEnabled = null) + { + return Curl::post(static::getApiUrl('/user/email/edit'),['auth_token' => $token],['email' => $email],['is_primary' => $isPrimary],['is_enabled' => $isEnabled]); + } + + public static function emailStatus($token) + { + return Curl::post(static::getApiUrl('/user/email/status'),['auth_token' => $token], ['json_response' => true]); + } + + public static function applyTags($type, $token, $tags) + { + return Curl::post(static::getApiUrl('/user/tag/edit'),['auth_token' => $token],[$type => $tags]); + } + + public static function unsubscribe($email) + { + return Curl::post(static::getApiUrl('/list/unsubscribe'), ['email' => $email], ['json_response' => true]); + } + + public static function connectYoutube($channel_name) + { + $type = 'sync'; + return Curl::post(static::getApiUrl('/yt/new'), ['desired_lbry_channel_name' => $channel_name, 'type' => $type], ['json_response' => true]); + } + + // Check the sync status + public static function statusYoutube($status_token) + { + return Curl::get(static::getApiUrl('/yt/status'), ['status_token' => $status_token], ['json_response' => true]); + } + + public static function youtubeReward() + { + return CurlWithCache::post(static::getApiUrl('/yt/rewards'), [], ['cache' => 3600, 'json_response' => true]); + } + + public static function editYouTube($status_token, $channel_name, $email, $sync_consent) + { + if ($email == null) { + return Curl::post(static::getApiUrl("/yt/update"), ['status_token' => $status_token, 'new_preferred_channel' => $channel_name, 'sync_consent' => $sync_consent], ['json_response' => true]); + } else { + return Curl::post(static::getApiUrl("/yt/update"), ['status_token' => $status_token, 'new_email' => $email, 'new_preferred_channel' => $channel_name, 'sync_consent' => $sync_consent], ['json_response' => true]); + } } - } } diff --git a/lib/thirdparty/Mailgun.class.php b/lib/thirdparty/Mailgun.class.php index cf898cda..293eb84b 100644 --- a/lib/thirdparty/Mailgun.class.php +++ b/lib/thirdparty/Mailgun.class.php @@ -2,30 +2,30 @@ class Mailgun { - const BASE_URL = 'https://api.mailgun.net/v3'; + const BASE_URL = 'https://api.mailgun.net/v3'; - const TOP_DOMAIN = 'lbry.io'; - const MAIL_DOMAIN = 'mail.lbry.io'; + const TOP_DOMAIN = 'lbry.io'; + const MAIL_DOMAIN = 'mail.lbry.io'; - const LIST_GENERAL = 'lbryians@lbry.io'; + const LIST_GENERAL = 'lbryians@lbry.io'; - public static function sendDmcaReport($data) - { - list($status, $headers, $body) = static::post('/' . static::MAIL_DOMAIN . '/messages', [ + public static function sendDmcaReport($data) + { + list($status, $headers, $body) = static::post('/' . static::MAIL_DOMAIN . '/messages', [ 'from' => 'LBRY ', - 'to' => 'help@lbry.io', + 'to' => 'hello@lbry.io', 'subject' => 'DMCA Report #' . $data['report_id'], 'html' => '
' . var_export($data, true) . '
', 'o:tracking-clicks' => 'no', 'o:tracking-opens' => 'no' ]); - return $status == 200; - } + return $status == 200; + } - public static function sendYouTubeWarmLead($data) - { - list($status, $headers, $body) = static::post('/' . static::MAIL_DOMAIN . '/messages', [ + public static function sendYouTubeWarmLead($data) + { + list($status, $headers, $body) = static::post('/' . static::MAIL_DOMAIN . '/messages', [ 'from' => 'LBRY ', 'to' => 'reilly@lbry.io', 'subject' => 'Interested YouTuber', @@ -34,32 +34,32 @@ class Mailgun 'o:tracking-opens' => 'no' ]); - return $status == 200; - } + return $status == 200; + } - protected static function post($endpoint, $data) - { - return static::request(Curl::POST, $endpoint, $data); - } + protected static function post($endpoint, $data) + { + return static::request(Curl::POST, $endpoint, $data); + } - protected static function put($endpoint, $data) - { - return static::request(Curl::PUT, $endpoint, $data); - } + protected static function put($endpoint, $data) + { + return static::request(Curl::PUT, $endpoint, $data); + } - protected static function request($method, $endpoint, $data) - { - return Curl::doCurl($method, self::BASE_URL . $endpoint, $data, [ + protected static function request($method, $endpoint, $data) + { + return Curl::doCurl($method, self::BASE_URL . $endpoint, $data, [ 'headers' => [ 'Authorization: Basic ' . base64_encode('api:' . Config::get(Config::MAILGUN_API_KEY)) ], 'retry' => 3, ]); - } + } - protected static function inlineCss($html, $css = '') - { - $e = new \Pelago\Emogrifier($html, $css); - return trim($e->emogrify()); - } + protected static function inlineCss($html, $css = '') + { + $e = new \Pelago\Emogrifier($html, $css); + return trim($e->emogrify()); + } } diff --git a/lib/thirdparty/Salesforce.class.php b/lib/thirdparty/Salesforce.class.php index fdb5f714..4601087b 100644 --- a/lib/thirdparty/Salesforce.class.php +++ b/lib/thirdparty/Salesforce.class.php @@ -2,21 +2,20 @@ class Salesforce { - const + const API_URL = 'https://api.salesforceiq.com/v2/', DEFAULT_LIST_ID = '58387a94e4b0a1fea2c76f4a'; - protected static - $curlOptions = [ + protected static $curlOptions = [ 'headers' => ['Accept: application/json', 'Content-type: application/json'], 'json_response' => true, 'json_data' => true, 'timeout' => 10 ]; - public static function createContact(string $email, string $initialListId = null, string $acquisitionChannel = null) - { - $contactData = [ + public static function createContact(string $email, string $initialListId = null, string $acquisitionChannel = null) + { + $contactData = [ 'properties' => [ 'email' => [[ 'value' => $email, @@ -25,54 +24,51 @@ class Salesforce ] ]; - $contactId = static::post('contacts?_upsert=email', $contactData)['id'] ?? null; + $contactId = static::post('contacts?_upsert=email', $contactData)['id'] ?? null; - if ($initialListId) - { - if (!$contactId) - { - throw new SalesforceException('Failed to generate or update contact'); - } + if ($initialListId) { + if (!$contactId) { + throw new SalesforceException('Failed to generate or update contact'); + } - static::post('lists/' . $initialListId . '/listitems?_upsert=contactIds', [ + static::post('lists/' . $initialListId . '/listitems?_upsert=contactIds', [ 'contactIds' => [$contactId], 'fieldValues' => (object)[ '2' => [['raw' => $acquisitionChannel]] ] ]); + } } - } - protected static function getApiUserPassword() - { - $userpw = Config::get(Config::SALESFORCE_KEY) . ':' . Config::get(Config::SALESFORCE_SECRET); - if ($userpw[0] === ':' || substr($userpw, -1) === ':') + protected static function getApiUserPassword() { - throw new SalesforceException('Salesforce key and/or secret not configured correctly'); + $userpw = Config::get(Config::SALESFORCE_KEY) . ':' . Config::get(Config::SALESFORCE_SECRET); + if ($userpw[0] === ':' || substr($userpw, -1) === ':') { + throw new SalesforceException('Salesforce key and/or secret not configured correctly'); + } + return $userpw; } - return $userpw; - } - public static function get($endpoint, array $data = []) - { - $options = [ + public static function get($endpoint, array $data = []) + { + $options = [ 'password' => static::getApiUserPassword(), ] + static::$curlOptions; - $responseData = Curl::get(static::API_URL . $endpoint, $data, $options); + $responseData = Curl::get(static::API_URL . $endpoint, $data, $options); - return $responseData ?? []; - } + return $responseData ?? []; + } - public static function post($endpoint, array $data = [], array $options = []) - { - $options += [ + public static function post($endpoint, array $data = [], array $options = []) + { + $options += [ 'password' => static::getApiUserPassword(), ] + static::$curlOptions; - $responseData = Curl::post(static::API_URL . $endpoint, $data, $options); - return $responseData ?? []; - } + $responseData = Curl::post(static::API_URL . $endpoint, $data, $options); + return $responseData ?? []; + } } class SalesforceException extends Exception diff --git a/lib/thirdparty/Slack.class.php b/lib/thirdparty/Slack.class.php index 616b4983..9264e89d 100644 --- a/lib/thirdparty/Slack.class.php +++ b/lib/thirdparty/Slack.class.php @@ -2,18 +2,15 @@ class Slack { - - public static function sendErrorIfProd($e, $alert = true) - { - if ($e instanceof Throwable) + public static function sendErrorIfProd($e, $alert = true) { - $e = Debug::exceptionToString($e); - } + if ($e instanceof Throwable) { + $e = Debug::exceptionToString($e); + } - $slackErrorNotificationUrl = Config::get(Config::SLACK_ERROR_NOTIFICATION_URL); - if ($slackErrorNotificationUrl) - { - Curl::post($slackErrorNotificationUrl, ['text' => ($alert ? ' ' : '') . Request::getRelativeUri() . "\n" . $e], ['json_data' => true]); + $slackErrorNotificationUrl = Config::get(Config::SLACK_ERROR_NOTIFICATION_URL); + if ($slackErrorNotificationUrl) { + Curl::post($slackErrorNotificationUrl, ['text' => ($alert ? ' ' : '') . Request::getRelativeUri() . "\n" . $e], ['json_data' => true]); + } } - } } diff --git a/lib/tools/Config.class.php b/lib/tools/Config.class.php index 32dfed23..da0610d7 100644 --- a/lib/tools/Config.class.php +++ b/lib/tools/Config.class.php @@ -2,45 +2,43 @@ class Config { - const HELP_CONTACT_EMAIL = 'josh@lbry.io'; + const HELP_CONTACT_EMAIL = 'josh@lbry.io'; - //Constant to help with managing strings - const IS_PROD = "is_prod"; - const GITHUB_KEY = "github_key"; - const GITHUB_DEVELOPER_CREDITS_CLIENT_ID = "github_developer_credits_client_id"; - const GITHUB_DEVELOPER_CREDITS_CLIENT_SECRET = "github_developer_credits_client_secret"; - const LBRY_API_SERVER = "lbry_api_server"; - const MAILCHIMP_KEY = "mailchimp_key"; - const ASANA_KEY = "asana_key"; - const AWS_LOG_ACCESS_KEY = "aws_log_access_key"; - const AWS_LOG_SECRET_KEY = "aws_log_secret_key"; - const MAILGUN_API_KEY = "mailgun_api_key"; - const SALESFORCE_KEY = "salesforce_key"; - const SALESFORCE_SECRET = "salesforce_secret"; - const SLACK_ERROR_NOTIFICATION_URL = "slack_error_notification_url"; + //Constant to help with managing strings + const IS_PROD = "is_prod"; + const GITHUB_KEY = "github_key"; + const GITHUB_DEVELOPER_CREDITS_CLIENT_ID = "github_developer_credits_client_id"; + const GITHUB_DEVELOPER_CREDITS_CLIENT_SECRET = "github_developer_credits_client_secret"; + const LBRY_API_SERVER = "lbry_api_server"; + const MAILCHIMP_KEY = "mailchimp_key"; + const ASANA_KEY = "asana_key"; + const AWS_LOG_ACCESS_KEY = "aws_log_access_key"; + const AWS_LOG_SECRET_KEY = "aws_log_secret_key"; + const MAILGUN_API_KEY = "mailgun_api_key"; + const SALESFORCE_KEY = "salesforce_key"; + const SALESFORCE_SECRET = "salesforce_secret"; + const SLACK_ERROR_NOTIFICATION_URL = "slack_error_notification_url"; - protected static $loaded = false; - protected static $data = []; + protected static $loaded = false; + protected static $data = []; - public static function get($name, $default = null) - { - static::load(); - return array_key_exists($name, static::$data) ? static::$data[$name] : $default; - } - - - protected static function load() - { - if (!static::$loaded) + public static function get($name, $default = null) { - $dataFile = ROOT_DIR.'/data/config.php'; - if (!is_readable($dataFile)) - { - throw new RuntimeException('config file is missing or not readable'); - } - static::$data = require $dataFile; - static::$loaded = true; + static::load(); + return array_key_exists($name, static::$data) ? static::$data[$name] : $default; + } + + + protected static function load() + { + if (!static::$loaded) { + $dataFile = ROOT_DIR.'/data/config.php'; + if (!is_readable($dataFile)) { + throw new RuntimeException('config file is missing or not readable'); + } + static::$data = require $dataFile; + static::$loaded = true; + } } - } } diff --git a/lib/tools/Curl.class.php b/lib/tools/Curl.class.php index 95efef12..2dc0ce6f 100644 --- a/lib/tools/Curl.class.php +++ b/lib/tools/Curl.class.php @@ -2,40 +2,40 @@ class Curl { - const + const GET = 'GET', POST = 'POST', PUT = 'PUT', DELETE = 'DELETE'; - public static function get($url, $params = [], $options = []) - { - list ($status, $headers, $body) = static::doCurl(static::GET, $url, $params, $options); - return $body; - } + public static function get($url, $params = [], $options = []) + { + list($status, $headers, $body) = static::doCurl(static::GET, $url, $params, $options); + return $body; + } - public static function post($url, $params = [], $options = []) - { - list ($status, $headers, $body) = static::doCurl(static::POST, $url, $params, $options); - return $body; - } + public static function post($url, $params = [], $options = []) + { + list($status, $headers, $body) = static::doCurl(static::POST, $url, $params, $options); + return $body; + } - public static function put($url, $params = [], $options = []) - { - list ($status, $headers, $body) = static::doCurl(static::PUT, $url, $params, $options); - return $body; - } + public static function put($url, $params = [], $options = []) + { + list($status, $headers, $body) = static::doCurl(static::PUT, $url, $params, $options); + return $body; + } - public static function delete($url, $params = [], $options = []) - { - list ($status, $headers, $body) = static::doCurl(static::DELETE, $url, $params, $options); - return $body; - } + public static function delete($url, $params = [], $options = []) + { + list($status, $headers, $body) = static::doCurl(static::DELETE, $url, $params, $options); + return $body; + } - public static function doCurl($method, $url, $params = [], $options = []) - { - $defaults = [ + public static function doCurl($method, $url, $params = [], $options = []) + { + $defaults = [ 'headers' => [], 'verify' => true, 'timeout' => 5, @@ -49,41 +49,36 @@ class Curl 'retry' => false, ]; - $invalid = array_diff_key($options, $defaults); - if ($invalid) - { - throw new DomainException('Invalid curl options: ' . join(', ', array_keys($invalid))); - } + $invalid = array_diff_key($options, $defaults); + if ($invalid) { + throw new DomainException('Invalid curl options: ' . join(', ', array_keys($invalid))); + } - if (!in_array($method, [static::GET, static::POST, static::PUT])) - { - throw new DomainException('Invalid method: ' . $method); - } + if (!in_array($method, [static::GET, static::POST, static::PUT])) { + throw new DomainException('Invalid method: ' . $method); + } - $options = array_merge($defaults, $options); + $options = array_merge($defaults, $options); - if ($options['headers'] && $options['headers'] !== array_values($options['headers'])) // associative array - { - throw new DomainException('Headers must not be an associative array. Its a simple array with values of the form "Header: value"'); - } + if ($options['headers'] && $options['headers'] !== array_values($options['headers'])) { // associative array + throw new DomainException('Headers must not be an associative array. Its a simple array with values of the form "Header: value"'); + } - $ch = curl_init(); + $ch = curl_init(); - curl_setopt($ch, CURLOPT_VERBOSE, true); - curl_setopt($ch, CURLOPT_STDERR, fopen(sys_get_temp_dir().'/curl-debug-'.date('Ymd-His'), 'w+')); + curl_setopt($ch, CURLOPT_VERBOSE, true); + curl_setopt($ch, CURLOPT_STDERR, fopen(sys_get_temp_dir().'/curl-debug-'.date('Ymd-His'), 'w+')); - if ($ch === false || $ch === null) - { - throw new LogicException('Unable to initialize cURL'); - } + if ($ch === false || $ch === null) { + throw new LogicException('Unable to initialize cURL'); + } - $urlWithParams = $url; - if ($method == static::GET && $params) - { - $urlWithParams .= (strpos($urlWithParams, '?') === false ? '?' : '&') . http_build_query($params); - } + $urlWithParams = $url; + if ($method == static::GET && $params) { + $urlWithParams .= (strpos($urlWithParams, '?') === false ? '?' : '&') . http_build_query($params); + } - curl_setopt_array($ch, [ + curl_setopt_array($ch, [ CURLOPT_URL => $urlWithParams, CURLOPT_HTTPHEADER => $options['headers'], CURLOPT_RETURNTRANSFER => true, @@ -96,97 +91,82 @@ class Curl CURLOPT_USERAGENT => $options['user_agent'], ]); - if ($method == static::POST) - { - curl_setopt($ch, CURLOPT_POST, true); - } - elseif ($method == static::PUT) - { - curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT'); - } + if ($method == static::POST) { + curl_setopt($ch, CURLOPT_POST, true); + } elseif ($method == static::PUT) { + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT'); + } - if (in_array($method, [static::PUT, static::POST])) - { - curl_setopt($ch, CURLOPT_POSTFIELDS, $options['json_data'] ? json_encode($params) : http_build_query($params)); - } + if (in_array($method, [static::PUT, static::POST])) { + curl_setopt($ch, CURLOPT_POSTFIELDS, $options['json_data'] ? json_encode($params) : http_build_query($params)); + } - if ($options['proxy']) - { - curl_setopt($ch, CURLOPT_PROXY, $options['proxy']); - curl_setopt($ch, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5); - } + if ($options['proxy']) { + curl_setopt($ch, CURLOPT_PROXY, $options['proxy']); + curl_setopt($ch, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5); + } - if ($options['password']) - { - curl_setopt($ch, CURLOPT_USERPWD, $options['password']); - } + if ($options['password']) { + curl_setopt($ch, CURLOPT_USERPWD, $options['password']); + } - if ($options['cookie']) - { - curl_setopt($ch, CURLOPT_COOKIE, $options['cookie']); - } + if ($options['cookie']) { + curl_setopt($ch, CURLOPT_COOKIE, $options['cookie']); + } - $startingResponse = false; - $headers = []; - curl_setopt($ch, CURLOPT_HEADERFUNCTION, function ($ch, $h) use (&$headers, &$startingResponse) - { - $value = trim($h); - if ($value === '') - { - $startingResponse = true; - } - elseif ($startingResponse) - { $startingResponse = false; - $headers = [$value]; - } - else - { - $headers[] = $value; - } - return strlen($h); - }); + $headers = []; + curl_setopt($ch, CURLOPT_HEADERFUNCTION, function ($ch, $h) use (&$headers, &$startingResponse) { + $value = trim($h); + if ($value === '') { + $startingResponse = true; + } elseif ($startingResponse) { + $startingResponse = false; + $headers = [$value]; + } else { + $headers[] = $value; + } + return strlen($h); + }); - $rawResponse = curl_exec($ch); + $rawResponse = curl_exec($ch); - if ($options['json_response']) - { - $responseContent = $rawResponse ? json_decode($rawResponse, true) : []; + if ($options['json_response']) { + $responseContent = $rawResponse ? json_decode($rawResponse, true) : []; + } else { + $responseContent = $rawResponse; + } + + $statusCode = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE); + + if (curl_errno($ch)) { + if ($options['retry'] && is_numeric($options['retry']) && $options['retry'] > 0) { + $options['retry'] -= 1; + return static::doCurl($method, $url, $params, $options); + } + throw new CurlException($ch); + } + + curl_close($ch); + + return [$statusCode, $headers, $responseContent]; } - else - { - $responseContent = $rawResponse; - } - - $statusCode = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE); - - if (curl_errno($ch)) - { - if ($options['retry'] && is_numeric($options['retry']) && $options['retry'] > 0) - { - $options['retry'] -= 1; - return static::doCurl($method, $url, $params, $options); - } - throw new CurlException($ch); - } - - curl_close($ch); - - return [$statusCode, $headers, $responseContent]; - } } class CurlException extends Exception { - protected $errno, $error, $info, $handle; + protected $errno; + protected $error; + protected $info; + protected $handle; - public function __construct($curlHandle, Exception $previous = null) - { - $this->handle = $curlHandle; - $this->errno = curl_errno($curlHandle); - $this->error = curl_error($curlHandle); - $this->info = curl_getinfo($curlHandle); + public function __construct($curlHandle, Exception $previous = null) + { + $this->handle = $curlHandle; + $this->errno = curl_errno($curlHandle); + $this->error = curl_error($curlHandle); + $this->info = curl_getinfo($curlHandle); - parent::__construct($this->error, $this->errno, $previous); - } + parent::__construct($this->error, $this->errno, $previous); + } } diff --git a/lib/tools/CurlWithCache.class.php b/lib/tools/CurlWithCache.class.php index f7b52d09..63b896c4 100644 --- a/lib/tools/CurlWithCache.class.php +++ b/lib/tools/CurlWithCache.class.php @@ -2,40 +2,34 @@ class CurlWithCache extends Curl { - const DEFAULT_CACHE = 600000; + const DEFAULT_CACHE = 600000; - public static function doCurl($method, $url, $params = [], $options = []) - { - $cacheAllowed = $options['cache'] ?? true; - $cacheEnabled = Apc::isEnabled(); - - $cacheTimeout = is_numeric($options['cache'] ?? null) ? $options['cache'] : static::DEFAULT_CACHE; - unset($options['cache']); - - $cacheKey = $cacheEnabled ? md5($url . $method . serialize($options) . serialize($params)) : null; - if ($cacheAllowed && $cacheKey) + public static function doCurl($method, $url, $params = [], $options = []) { - $cachedData = apc_fetch($cacheKey); - if ($cachedData) - { - return $cachedData; - } + $cacheAllowed = $options['cache'] ?? true; + $cacheEnabled = Apc::isEnabled(); + + $cacheTimeout = is_numeric($options['cache'] ?? null) ? $options['cache'] : static::DEFAULT_CACHE; + unset($options['cache']); + + $cacheKey = $cacheEnabled ? md5($url . $method . serialize($options) . serialize($params)) : null; + if ($cacheAllowed && $cacheKey) { + $cachedData = apc_fetch($cacheKey); + if ($cachedData) { + return $cachedData; + } + } + + $response = parent::doCurl($method, $url, $params, $options); + + if ($cacheEnabled) { + if ($cacheAllowed) { + apc_store($cacheKey, $response, $cacheTimeout); + } else { + apc_delete($cacheKey); + } + } + + return $response; } - - $response = parent::doCurl($method, $url, $params, $options); - - if ($cacheEnabled) - { - if ($cacheAllowed) - { - apc_store($cacheKey, $response, $cacheTimeout); - } - else - { - apc_delete($cacheKey); - } - } - - return $response; - } -} \ No newline at end of file +} diff --git a/lib/tools/Debug.class.php b/lib/tools/Debug.class.php index 6db68aac..5c71e7f6 100644 --- a/lib/tools/Debug.class.php +++ b/lib/tools/Debug.class.php @@ -2,74 +2,61 @@ class Debug { - public static function exceptionToString(Throwable $e) - { - return static::getExceptionMessageWithoutTrace($e) . "\n" . static::getFullTrace($e); - } - - public static function getExceptionMessageWithoutTrace(Throwable $e) - { - return 'exception \'' . get_class($e) . '\' with message \'' . $e->getMessage() . '\' in ' . $e->getFile() . ':' . $e->getLine(); - } - - /** - * Same as the normal getTraceAsString(), but does not truncate long lines. - * @param Throwable $exception - * @return string - * @see http://stackoverflow.com/questions/1949345/how-can-i-get-the-full-string-of-phps-gettraceasstring/6076667#6076667 - * @see https://gist.github.com/1437966 - */ - public static function getFullTrace(Throwable $exception) - { - $rtn = ''; - foreach ($exception->getTrace() as $count => $frame) + public static function exceptionToString(Throwable $e) { - $args = isset($frame['args']) ? static::exceptionFrameArgsToString($frame['args']) : ''; + return static::getExceptionMessageWithoutTrace($e) . "\n" . static::getFullTrace($e); + } - $rtn .= sprintf("#%s %s(%s): %s(%s)\n", + public static function getExceptionMessageWithoutTrace(Throwable $e) + { + return 'exception \'' . get_class($e) . '\' with message \'' . $e->getMessage() . '\' in ' . $e->getFile() . ':' . $e->getLine(); + } + + /** + * Same as the normal getTraceAsString(), but does not truncate long lines. + * @param Throwable $exception + * @return string + * @see http://stackoverflow.com/questions/1949345/how-can-i-get-the-full-string-of-phps-gettraceasstring/6076667#6076667 + * @see https://gist.github.com/1437966 + */ + public static function getFullTrace(Throwable $exception) + { + $rtn = ''; + foreach ($exception->getTrace() as $count => $frame) { + $args = isset($frame['args']) ? static::exceptionFrameArgsToString($frame['args']) : ''; + + $rtn .= sprintf( + "#%s %s(%s): %s(%s)\n", $count, $frame['file'] ?? 'unknown file', $frame['line'] ?? 'unknown line', isset($frame['class']) ? $frame['class'].$frame['type'].$frame['function'] : $frame['function'], - $args); + $args + ); + } + return $rtn; } - return $rtn; - } - public static function exceptionFrameArgsToString(array $args) - { - $ret = []; - foreach ($args as $arg) + public static function exceptionFrameArgsToString(array $args) { - if (is_string($arg)) - { - $ret[] = "'" . $arg . "'"; - } - elseif (is_array($arg)) - { - $ret[] = 'Array(' . count($arg) . ')'; - } - elseif (is_null($arg)) - { - $ret[] = 'NULL'; - } - elseif (is_bool($arg)) - { - $ret[] = ($arg) ? 'true' : 'false'; - } - elseif (is_object($arg)) - { - $ret[] = get_class($arg) . (!($arg instanceof Closure) && isset($arg->id) ? "({$arg->id})" : ''); - } - elseif (is_resource($arg)) - { - $ret[] = get_resource_type($arg); - } - else - { - $ret[] = $arg; - } + $ret = []; + foreach ($args as $arg) { + if (is_string($arg)) { + $ret[] = "'" . $arg . "'"; + } elseif (is_array($arg)) { + $ret[] = 'Array(' . count($arg) . ')'; + } elseif (is_null($arg)) { + $ret[] = 'NULL'; + } elseif (is_bool($arg)) { + $ret[] = ($arg) ? 'true' : 'false'; + } elseif (is_object($arg)) { + $ret[] = get_class($arg) . (!($arg instanceof Closure) && isset($arg->id) ? "({$arg->id})" : ''); + } elseif (is_resource($arg)) { + $ret[] = get_resource_type($arg); + } else { + $ret[] = $arg; + } + } + return join(', ', $ret); } - return join(', ', $ret); - } } diff --git a/lib/tools/Encoding.class.php b/lib/tools/Encoding.class.php index 3f6b6dc7..b30ab762 100644 --- a/lib/tools/Encoding.class.php +++ b/lib/tools/Encoding.class.php @@ -2,63 +2,58 @@ class Encoding { - public static function base64EncodeUrlsafe($data) - { - return rtrim(strtr(base64_encode($data), '+/', '-_'), '='); // equals sign is just for padding, can be safely removed - } - - public static function base64DecodeUrlsafe($data) - { - return base64_decode(strtr($data, '-_', '+/')); // dont worry about replacing equals sign - } - - public static function base58Encode($byteString) - { - $alphabet = '123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ'; - return static::convertBase($byteString, join('', array_map('chr', range(0, 255))), $alphabet); - } - - protected static function convertBase($numberInput, $sourceAlphabet, $targetAlphabet) - { - if ($sourceAlphabet == $targetAlphabet) + public static function base64EncodeUrlsafe($data) { - return $numberInput; + return rtrim(strtr(base64_encode($data), '+/', '-_'), '='); // equals sign is just for padding, can be safely removed } - $decimalAlphabet = '0123456789'; - - $fromBase = str_split($sourceAlphabet); - $toBase = str_split($targetAlphabet); - $number = str_split($numberInput); - - $fromLen = strlen($sourceAlphabet); - $toLen = strlen($targetAlphabet); - $numberLen = strlen($numberInput); - - if ($targetAlphabet == $decimalAlphabet) + public static function base64DecodeUrlsafe($data) { - $decimal = 0; - for ($i = 1; $i <= $numberLen; $i++) - { - $decimal = bcadd($decimal, bcmul(array_search($number[$i - 1], $fromBase), bcpow($fromLen, $numberLen - $i))); - } - return $decimal; + return base64_decode(strtr($data, '-_', '+/')); // dont worry about replacing equals sign } - $base10 = $sourceAlphabet == $decimalAlphabet ? $numberInput : static::convertBase($numberInput, $sourceAlphabet, $decimalAlphabet); - - if ($base10 < strlen($targetAlphabet)) + public static function base58Encode($byteString) { - return $toBase[$base10]; + $alphabet = '123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ'; + return static::convertBase($byteString, join('', array_map('chr', range(0, 255))), $alphabet); } - $retval = ''; - while ($base10 != '0') + protected static function convertBase($numberInput, $sourceAlphabet, $targetAlphabet) { - $retval = $toBase[bcmod($base10, $toLen)] . $retval; - $base10 = bcdiv($base10, $toLen, 0); - } + if ($sourceAlphabet == $targetAlphabet) { + return $numberInput; + } - return $retval; - } -} \ No newline at end of file + $decimalAlphabet = '0123456789'; + + $fromBase = str_split($sourceAlphabet); + $toBase = str_split($targetAlphabet); + $number = str_split($numberInput); + + $fromLen = strlen($sourceAlphabet); + $toLen = strlen($targetAlphabet); + $numberLen = strlen($numberInput); + + if ($targetAlphabet == $decimalAlphabet) { + $decimal = 0; + for ($i = 1; $i <= $numberLen; $i++) { + $decimal = bcadd($decimal, bcmul(array_search($number[$i - 1], $fromBase), bcpow($fromLen, $numberLen - $i))); + } + return $decimal; + } + + $base10 = $sourceAlphabet == $decimalAlphabet ? $numberInput : static::convertBase($numberInput, $sourceAlphabet, $decimalAlphabet); + + if ($base10 < strlen($targetAlphabet)) { + return $toBase[$base10]; + } + + $retval = ''; + while ($base10 != '0') { + $retval = $toBase[bcmod($base10, $toLen)] . $retval; + $base10 = bcdiv($base10, $toLen, 0); + } + + return $retval; + } +} diff --git a/lib/tools/Gzip.class.php b/lib/tools/Gzip.class.php index 260d4dcd..50488b4d 100644 --- a/lib/tools/Gzip.class.php +++ b/lib/tools/Gzip.class.php @@ -2,34 +2,29 @@ class Gzip { - public static function compressFile($source, $level = 1) - { - $compressedPath = $source . '.gz'; - $mode = 'wb' . $level; - - $fpOut = gzopen($compressedPath, $mode); - if (!$fpOut) + public static function compressFile($source, $level = 1) { - return false; - } + $compressedPath = $source . '.gz'; + $mode = 'wb' . $level; - $fpIn = fopen($source, 'rb'); - $error = false; - if ($fpIn) - { - while (!feof($fpIn)) - { - gzwrite($fpOut, fread($fpIn, 1024 * 512)); - } - fclose($fpIn); - } - else - { - $error = true; - } + $fpOut = gzopen($compressedPath, $mode); + if (!$fpOut) { + return false; + } - gzclose($fpOut); + $fpIn = fopen($source, 'rb'); + $error = false; + if ($fpIn) { + while (!feof($fpIn)) { + gzwrite($fpOut, fread($fpIn, 1024 * 512)); + } + fclose($fpIn); + } else { + $error = true; + } - return $error ? false : $compressedPath; - } -} \ No newline at end of file + gzclose($fpOut); + + return $error ? false : $compressedPath; + } +} diff --git a/lib/tools/Lock.class.php b/lib/tools/Lock.class.php index 4c5affbe..4f7b78b5 100644 --- a/lib/tools/Lock.class.php +++ b/lib/tools/Lock.class.php @@ -16,55 +16,50 @@ else { class Lock { - /** - * Creates a lockfile and acquires an exclusive lock on it. - * - * @param string $name The name of the lockfile. - * @param boolean $blocking Block until lock becomes available (default: don't block, just fail) - * @return mixed Returns the lockfile, or FALSE if a lock could not be acquired. - */ - public static function getLock($name, $blocking = false) - { - if (!preg_match('/^[A-Za-z0-9\.\-_]+$/', $name)) + /** + * Creates a lockfile and acquires an exclusive lock on it. + * + * @param string $name The name of the lockfile. + * @param boolean $blocking Block until lock becomes available (default: don't block, just fail) + * @return mixed Returns the lockfile, or FALSE if a lock could not be acquired. + */ + public static function getLock($name, $blocking = false) { - throw new InvalidArgumentException('Invalid lock name: "' . $name . '"'); + if (!preg_match('/^[A-Za-z0-9\.\-_]+$/', $name)) { + throw new InvalidArgumentException('Invalid lock name: "' . $name . '"'); + } + + $filename = static::getLockDir() . '/' . $name; + if (!preg_match('/\.lo?ck$/', $filename)) { + $filename .= '.lck'; + } + if (!file_exists($filename)) { + file_put_contents($filename, ''); + chmod($filename, 0666); // if the file cant be opened for writing later, getting the lock will fail + } + $lockFile = fopen($filename, 'w+'); + if (!flock($lockFile, $blocking ? LOCK_EX : LOCK_EX|LOCK_NB)) { + fclose($lockFile); + return false; + } + return $lockFile; } - $filename = static::getLockDir() . '/' . $name; - if (!preg_match('/\.lo?ck$/', $filename)) + /** + * Free a lock. + * + * @param resource $lockFile + */ + public static function freeLock($lockFile) { - $filename .= '.lck'; + if ($lockFile) { + flock($lockFile, LOCK_UN); + fclose($lockFile); + } } - if (!file_exists($filename)) - { - file_put_contents($filename, ''); - chmod($filename, 0666); // if the file cant be opened for writing later, getting the lock will fail - } - $lockFile = fopen($filename, 'w+'); - if (!flock($lockFile, $blocking ? LOCK_EX : LOCK_EX|LOCK_NB)) - { - fclose($lockFile); - return false; - } - return $lockFile; - } - /** - * Free a lock. - * - * @param resource $lockFile - */ - public static function freeLock($lockFile) - { - if ($lockFile) + public static function getLockDir() { - flock($lockFile, LOCK_UN); - fclose($lockFile); + return sys_get_temp_dir(); } - } - - public static function getLockDir() - { - return sys_get_temp_dir(); - } -} \ No newline at end of file +} diff --git a/lib/tools/OS.class.php b/lib/tools/OS.class.php index 64249608..bf5df116 100644 --- a/lib/tools/OS.class.php +++ b/lib/tools/OS.class.php @@ -2,32 +2,31 @@ class OS { - /* - * if changing below constants, you should add permanent redirects for old OS names used in URLs - */ - const OS_ANDROID = 'android', + /* + * if changing below constants, you should add permanent redirects for old OS names used in URLs + */ + const OS_ANDROID = 'android', OS_IOS = 'ios', OS_LINUX = 'linux', OS_OSX = 'osx', OS_WINDOWS = 'windows'; - public static function getAll() - { - //url, English name, icon class, partial name - //yes, this is probably a bad pattern - return [ + public static function getAll() + { + //url, English name, icon class, partial name + //yes, this is probably a bad pattern + return [ OS::OS_WINDOWS => ['/windows', 'Windows', 'icon-windows', __("Download for Windows"), "Windows"], OS::OS_OSX => ['/osx', 'macOS', 'icon-apple', __("Download for macOS"), "OSX"], OS::OS_LINUX => ['/linux', 'Linux', 'icon-linux', __("Download .deb"), "Linux"], OS::OS_ANDROID => ['/android', 'Android', 'icon-android', false, false], OS::OS_IOS => ['/ios', 'iOS', 'icon-mobile', false, false] ]; - } + } - public static function getOsForExtension($ext) - { - switch ($ext) + public static function getOsForExtension($ext) { + switch ($ext) { case 'deb': return OS::OS_LINUX; @@ -42,5 +41,5 @@ class OS default: throw new LogicException("Unknown ext $ext"); } - } -} \ No newline at end of file + } +} diff --git a/lib/tools/Shell.class.php b/lib/tools/Shell.class.php index ff2036bd..f2a51394 100644 --- a/lib/tools/Shell.class.php +++ b/lib/tools/Shell.class.php @@ -14,140 +14,121 @@ */ class Shell { - /** - * Execute a command. Returns the command's exit code, output, and error output. - * - * @param string $cmd The command to execute - * @param array $options Available options: - * - echo (bool) - If true, print the command's output/error to stdout/stderr of this process - * - echo_errors (bool) - If you want to control printing to stderr separately, use this. If not provided, will - * default to the value of 'echo' - * - live_callback (callable) - Will be called as soon as data is read. Use this for custom handling - * of live output. Callable signature: fn(string $text, bool $isError) - * - * @return array [exit code, output, errorOutput] - */ - public static function exec($cmd, array $options = []) - { - $options = array_merge([ + /** + * Execute a command. Returns the command's exit code, output, and error output. + * + * @param string $cmd The command to execute + * @param array $options Available options: + * - echo (bool) - If true, print the command's output/error to stdout/stderr of this process + * - echo_errors (bool) - If you want to control printing to stderr separately, use this. If not provided, will + * default to the value of 'echo' + * - live_callback (callable) - Will be called as soon as data is read. Use this for custom handling + * of live output. Callable signature: fn(string $text, bool $isError) + * + * @return array [exit code, output, errorOutput] + */ + public static function exec($cmd, array $options = []) + { + $options = array_merge([ 'echo' => false, 'echo_errors' => null, 'live_callback' => null, ], $options); - if ($options['echo_errors'] === null) - { - $options['echo_errors'] = $options['echo']; - } + if ($options['echo_errors'] === null) { + $options['echo_errors'] = $options['echo']; + } - if ($options['live_callback'] && !is_callable($options['live_callback'])) - { - throw new InvalidArgumentException('live_callback option must be a valid callback'); - } + if ($options['live_callback'] && !is_callable($options['live_callback'])) { + throw new InvalidArgumentException('live_callback option must be a valid callback'); + } - $descriptorSpec = [ + $descriptorSpec = [ 1 => ['pipe', 'w'], // stdout 2 => ['pipe', 'w'], // stderr ]; - $process = proc_open($cmd, $descriptorSpec, $pipes); - if (!is_resource($process)) - { - throw new RuntimeException('proc_open failed'); + $process = proc_open($cmd, $descriptorSpec, $pipes); + if (!is_resource($process)) { + throw new RuntimeException('proc_open failed'); + } + + stream_set_blocking($pipes[1], false); + stream_set_blocking($pipes[2], false); + + $output = ''; + $err = ''; + + while (!feof($pipes[1]) || !feof($pipes[2])) { + foreach ($pipes as $key => $pipe) { + $data = fread($pipe, 1024); + if (!$data) { + continue; + } + + if (1 == $key) { + $output .= $data; + if ($options['echo']) { + fprintf(STDOUT, "%s", $data); + } + if ($options['live_callback']) { + call_user_func($options['live_callback'], $data, false); + } + } else { + $err .= $data; + if ($options['echo_errors']) { + fprintf(STDERR, "%s", $data); + } + if ($options['live_callback']) { + call_user_func($options['live_callback'], $data, true); + } + } + } + + usleep(100000); + } + + fclose($pipes[1]); + fclose($pipes[2]); + + $exitCode = proc_close($process); + + return [$exitCode, $output, $err]; } - stream_set_blocking($pipes[1], false); - stream_set_blocking($pipes[2], false); - - $output = ''; - $err = ''; - - while (!feof($pipes[1]) || !feof($pipes[2])) + /** + * Convenience method to build a command to execute. Will escape everything as necessary. + * + * @param string $executable The path to the executable + * @param array $arguments An array of arguments + * @param array $options An associative array of flags in the form flagName => flagValue. + * Short and long flags are supported. + * If flagValue === true, the flag have no value. + * If flagValue === false, the flag will be skipped. + * + * @return string An executable command + */ + public static function buildCmd($executable, array $arguments = [], array $options = []) { - foreach ($pipes as $key => $pipe) - { - $data = fread($pipe, 1024); - if (!$data) - { - continue; + $shellArgs = []; + + foreach ($options as $key => $value) { + if ($value === false) { + continue; + } + + if (strlen($key) === 1) { + $shellArgs[] = '-'.$key; + if ($value !== true) { + $shellArgs[] = $value; + } + } else { + $shellArgs[] = '--' . $key . ($value !== true ? '=' . $value : ''); + } } - if (1 == $key) - { - $output .= $data; - if ($options['echo']) - { - fprintf(STDOUT, "%s", $data); - } - if ($options['live_callback']) - { - call_user_func($options['live_callback'], $data, false); - } - } - else - { - $err .= $data; - if ($options['echo_errors']) - { - fprintf(STDERR, "%s", $data); - } - if ($options['live_callback']) - { - call_user_func($options['live_callback'], $data, true); - } - } - } + $shellArgs = array_merge($shellArgs, array_values($arguments)); - usleep(100000); + return $executable . ' ' . join(' ', array_map('escapeshellarg', $shellArgs)); } - - fclose($pipes[1]); - fclose($pipes[2]); - - $exitCode = proc_close($process); - - return [$exitCode, $output, $err]; - } - - /** - * Convenience method to build a command to execute. Will escape everything as necessary. - * - * @param string $executable The path to the executable - * @param array $arguments An array of arguments - * @param array $options An associative array of flags in the form flagName => flagValue. - * Short and long flags are supported. - * If flagValue === true, the flag have no value. - * If flagValue === false, the flag will be skipped. - * - * @return string An executable command - */ - public static function buildCmd($executable, array $arguments = [], array $options = []) - { - $shellArgs = []; - - foreach ($options as $key => $value) - { - if ($value === false) - { - continue; - } - - if (strlen($key) === 1) - { - $shellArgs[] = '-'.$key; - if ($value !== true) - { - $shellArgs[] = $value; - } - } - else - { - $shellArgs[] = '--' . $key . ($value !== true ? '=' . $value : ''); - } - } - - $shellArgs = array_merge($shellArgs, array_values($arguments)); - - return $executable . ' ' . join(' ', array_map('escapeshellarg', $shellArgs)); - } -} \ No newline at end of file +} diff --git a/lib/tools/Smaz.class.php b/lib/tools/Smaz.class.php index c27b7bee..3a2b8783 100644 --- a/lib/tools/Smaz.class.php +++ b/lib/tools/Smaz.class.php @@ -13,14 +13,14 @@ */ class Smaz { - const CODEBOOK_DEFAULT = 'default'; - const CODEBOOK_EMAIL = 'email'; + const CODEBOOK_DEFAULT = 'default'; + const CODEBOOK_EMAIL = 'email'; - const VERBATIM_CHAR = 254; - const VERBATIM_STR = 255; + const VERBATIM_CHAR = 254; + const VERBATIM_STR = 255; - protected static $encodeBooks = []; - protected static $decodeBooks = [ + protected static $encodeBooks = []; + protected static $decodeBooks = [ self::CODEBOOK_DEFAULT => [ " ", "the", "e", "t", "a", "of", "o", "and", "i", "n", "s", "e ", "r", " th", " t", "in", "he", "th", "h", "he ", "to", "\r\n", "l", "s ", "d", " a", "an", @@ -67,145 +67,123 @@ class Smaz ] ]; - protected static function getEncodeBook($codebook) - { - if (!isset(static::$encodeBooks[$codebook])) + protected static function getEncodeBook($codebook) { - static::$encodeBooks[$codebook] = array_flip(static::getDecodeBook($codebook)); + if (!isset(static::$encodeBooks[$codebook])) { + static::$encodeBooks[$codebook] = array_flip(static::getDecodeBook($codebook)); + } + return static::$encodeBooks[$codebook]; } - return static::$encodeBooks[$codebook]; - } - protected static function getDecodeBook($codebook) - { - if (!static::$decodeBooks[$codebook]) + protected static function getDecodeBook($codebook) { - throw new Exception('decodebook ' . $codebook . ' does not exist'); + if (!static::$decodeBooks[$codebook]) { + throw new Exception('decodebook ' . $codebook . ' does not exist'); + } + if (count(static::$decodeBooks[$codebook]) > static::VERBATIM_CHAR) { + throw new Exception('decodebook ' . $codebook . ' must be at most ' . static::VERBATIM_CHAR . 'entries'); + } + return static::$decodeBooks[$codebook]; } - if (count(static::$decodeBooks[$codebook]) > static::VERBATIM_CHAR) + + + /** + * @param string $str + * @param string $codebook + * + * @return string + */ + public static function encode($str, $codebook = self::CODEBOOK_DEFAULT) { - throw new Exception('decodebook ' . $codebook . ' must be at most ' . static::VERBATIM_CHAR . 'entries'); - } - return static::$decodeBooks[$codebook]; - } + $encodeBook = static::getEncodeBook($codebook); + $inLen = strlen($str); + $inIdx = 0; + $output = ''; + $verbatim = ''; + $maxItemLen = max(array_map('strlen', array_keys($encodeBook))); - /** - * @param string $str - * @param string $codebook - * - * @return string - */ - public static function encode($str, $codebook = self::CODEBOOK_DEFAULT) - { - $encodeBook = static::getEncodeBook($codebook); + while ($inIdx < $inLen) { + $encode = false; - $inLen = strlen($str); - $inIdx = 0; - $output = ''; - $verbatim = ''; - $maxItemLen = max(array_map('strlen', array_keys($encodeBook))); - - while ($inIdx < $inLen) - { - $encode = false; - - for ($j = min($maxItemLen, $inLen - $inIdx); $j > 0; $j--) - { - $code = isset($encodeBook[substr($str, $inIdx, $j)]) ? $encodeBook[substr($str, $inIdx, $j)] : null; - if ($code !== null) - { + for ($j = min($maxItemLen, $inLen - $inIdx); $j > 0; $j--) { + $code = isset($encodeBook[substr($str, $inIdx, $j)]) ? $encodeBook[substr($str, $inIdx, $j)] : null; + if ($code !== null) { // echo substr($str, $inIdx, $j) . " = $code\n"; - if (strlen($verbatim)) - { - $output .= static::flushVerbatim($verbatim); - $verbatim = ''; - } - $output .= chr($code); - $inIdx += $j; - $encode = true; - break; - } - } + if (strlen($verbatim)) { + $output .= static::flushVerbatim($verbatim); + $verbatim = ''; + } + $output .= chr($code); + $inIdx += $j; + $encode = true; + break; + } + } - if (!$encode) - { + if (!$encode) { // echo "VERBATIM\n"; - $verbatim .= $str[$inIdx]; - $inIdx++; - if (strlen($verbatim) == 255) // any longer, and we can't represent the length as a single char - { - $output .= static::flushVerbatim($verbatim); - $verbatim = ''; + $verbatim .= $str[$inIdx]; + $inIdx++; + if (strlen($verbatim) == 255) { // any longer, and we can't represent the length as a single char + $output .= static::flushVerbatim($verbatim); + $verbatim = ''; + } + } } - } + + if (strlen($verbatim)) { + $output .= static::flushVerbatim($verbatim); + } + return $output; } - if (strlen($verbatim)) + /** + * @param string $str + * @param string $codebook + * + * @return string + */ + public static function decode($str, $codebook = self::CODEBOOK_DEFAULT) { - $output .= static::flushVerbatim($verbatim); + $decodeBook = static::getDecodeBook($codebook); + + $output = ''; + $i = 0; + + while ($i < strlen($str)) { + $code = ord($str[$i]); + if ($code == static::VERBATIM_CHAR) { + $output .= $str[$i + 1]; + $i += 2; + } elseif ($code == static::VERBATIM_STR) { + $len = ord($str[$i + 1]); + $output .= substr($str, $i + 2, $len); + $i += 2 + $len; + } elseif (!isset($decodeBook[$code])) { + return null; // decode error. throw exception? + } else { + $output .= $decodeBook[$code]; + $i++; + } + } + return $output; } - return $output; - } - /** - * @param string $str - * @param string $codebook - * - * @return string - */ - public static function decode($str, $codebook = self::CODEBOOK_DEFAULT) - { - $decodeBook = static::getDecodeBook($codebook); - - $output = ''; - $i = 0; - - while ($i < strlen($str)) + protected static function flushVerbatim($verbatim) { - $code = ord($str[$i]); - if ($code == static::VERBATIM_CHAR) - { - $output .= $str[$i + 1]; - $i += 2; - } - elseif ($code == static::VERBATIM_STR) - { - $len = ord($str[$i + 1]); - $output .= substr($str, $i + 2, $len); - $i += 2 + $len; - } - elseif (!isset($decodeBook[$code])) - { - return null; // decode error. throw exception? - } - else - { - $output .= $decodeBook[$code]; - $i++; - } - } - return $output; - } + $output = ''; + if (!strlen($verbatim)) { + return $output; + } - protected static function flushVerbatim($verbatim) - { - $output = ''; - if (!strlen($verbatim)) - { - return $output; + if (strlen($verbatim) > 1) { + $output .= chr(static::VERBATIM_STR); + $output .= chr(strlen($verbatim)); + } else { + $output .= chr(static::VERBATIM_CHAR); + } + $output .= $verbatim; + return $output; } - - if (strlen($verbatim) > 1) - { - $output .= chr(static::VERBATIM_STR); - $output .= chr(strlen($verbatim)); - } - else - { - $output .= chr(static::VERBATIM_CHAR); - } - $output .= $verbatim; - return $output; - } -} \ No newline at end of file +} diff --git a/model/Post.class.php b/model/Post.class.php index f54a6a88..93811d83 100644 --- a/model/Post.class.php +++ b/model/Post.class.php @@ -1,312 +1,310 @@ path = $path; - $this->postType = $postType; - $this->slug = $slug; - $this->markdown = $markdown; - $this->metadata = $frontMatter; - $this->title = $frontMatter['title'] ?? null; - $this->author = $frontMatter['author'] ?? null; - $this->date = isset($frontMatter['date']) ? new DateTime($frontMatter['date']) : null; - $this->cover = $frontMatter['cover'] ?? null; - $this->isCoverLight = isset($frontMatter['cover-light']) && $frontMatter['cover-light'] == 'true'; - $this->category = $frontMatter['category'] ?? null; - } - - public static function find($folder, $sort = null) - { - $posts = []; - foreach(glob(rtrim($folder, '/') . '/*.md') as $file) - { - $posts[] = static::load($file); + $this->path = $path; + $this->postType = $postType; + $this->slug = $slug; + $this->markdown = $markdown; + $this->metadata = $frontMatter; + $this->title = $frontMatter['title'] ?? null; + $this->author = $frontMatter['author'] ?? null; + $this->date = isset($frontMatter['date']) ? new DateTime($frontMatter['date']) : null; + $this->cover = $frontMatter['cover'] ?? null; + $this->isCoverLight = isset($frontMatter['cover-light']) && $frontMatter['cover-light'] == 'true'; + $this->category = $frontMatter['category'] ?? null; } - if ($sort) + public static function find($folder, $sort = null) { - switch ($sort) - { + $posts = []; + foreach (glob(rtrim($folder, '/') . '/*.md') as $file) { + $posts[] = static::load($file); + } + + if ($sort) { + switch ($sort) { case static::SORT_DATE_DESC: - usort($posts, function(Post $a, Post $b) { - return strcasecmp($b->getDate()->format('Y-m-d'), $a->getDate()->format('Y-m-d')); + usort($posts, function (Post $a, Post $b) { + return strcasecmp($b->getDate()->format('Y-m-d'), $a->getDate()->format('Y-m-d')); }); break; case static::SORT_ORD_ASC: - usort($posts, function(Post $a, Post $b) { - $aMeta = $a->getMetadata(); - $bMeta = $b->getMetadata(); - if (!isset($aMeta['order']) && !isset($bMeta['order'])) - { - return $a->getTitle() < $b->getTitle() ? -1 : 1; - } - if (isset($aMeta['order']) && isset($bMeta['order'])) - { - return $aMeta['order'] < $bMeta['order'] ? -1 : 1; - } - return isset($aMeta['order']) ? -1 : 1; + usort($posts, function (Post $a, Post $b) { + $aMeta = $a->getMetadata(); + $bMeta = $b->getMetadata(); + if (!isset($aMeta['order']) && !isset($bMeta['order'])) { + return $a->getTitle() < $b->getTitle() ? -1 : 1; + } + if (isset($aMeta['order']) && isset($bMeta['order'])) { + return $aMeta['order'] < $bMeta['order'] ? -1 : 1; + } + return isset($aMeta['order']) ? -1 : 1; }); break; } + } + return $posts; } - return $posts; - } - public static function filter(array $posts, array $filters) - { - return array_filter($posts, function(Post $post) use($filters) { - $metadata = $post->getMetadata(); - foreach($filters as $filterAttr => $filterValue) - { - if (!isset($metadata[$filterAttr]) || ( + public static function filter(array $posts, array $filters) + { + return array_filter($posts, function (Post $post) use ($filters) { + $metadata = $post->getMetadata(); + foreach ($filters as $filterAttr => $filterValue) { + if (!isset($metadata[$filterAttr]) || ( ($metadata[$filterAttr] != $filterValue) && (!is_array($metadata[$filterAttr]) || !in_array($filterValue, $metadata[$filterAttr])) - )) - { - return false; + )) { + return false; + } + } + return true; + }); + } + + public function getMetadata() + { + return $this->metadata; + } + + public function getMetadataItem($key, $default = null) + { + return $this->metadata[$key] ?? $default; + } + + public function setMetadataItem($key, $value) + { + $this->metadata[$key] = $value; + } + + public function getRelativeUrl() + { + return '/' . $this->postType . '/' . $this->slug; + } + + public function getSlug() + { + return $this->slug; + } + + public function getTitle() + { + return $this->title; + } + + public function getAuthor() + { + return $this->author; + } + + public function getAuthorGithubID() + { + $post = ContentActions::prepareBioPartial(['person' =>$this->author]); + if (array_key_exists("github", $post)) { + return $post["github"]; } - } - return true; - }); - } - - public function getMetadata() - { - return $this->metadata; - } - - public function getMetadataItem($key, $default = null) - { - return $this->metadata[$key] ?? $default; - } - - public function setMetadataItem($key, $value) - { - $this->metadata[$key] = $value; - } - - public function getRelativeUrl() - { - return '/' . $this->postType . '/' . $this->slug; - } - - public function getSlug() - { - return $this->slug; - } - - public function getTitle() - { - return $this->title; - } - - public function getAuthor() - { - return $this->author; - } - - public function getAuthorGithubID() - { - $post = ContentActions::prepareBioPartial(['person' =>$this->author]); - if(array_key_exists("github", $post)){ - return $post["github"]; - } - } - -public function getAuthorTwitterID() -{ - $post = ContentActions::prepareBioPartial(['person' =>$this->author]); - if(array_key_exists("twitter", $post)){ - return $post["twitter"]; } -} -public function getAuthorEmail() -{ - $post = ContentActions::prepareBioPartial(['person' =>$this->author]); - if(array_key_exists("email", $post)){ - return $post["email"]; - } -} - - public function getDate() - { - return $this->date; - } - - public function getCover() - { - return $this->cover; - } - - public function getIsCoverLight() - { - return $this->isCoverLight; - } - - public function getCategory() - { - return $this->category; - } - - public function getContentText($wordLimit = null, $appendEllipsis = false) - { - if ($this->markdown && !$this->contentText) + public function getAuthorTwitterID() { + $post = ContentActions::prepareBioPartial(['person' =>$this->author]); + if (array_key_exists("twitter", $post)) { + return $post["twitter"]; + } + } + + public function getAuthorEmail() + { + $post = ContentActions::prepareBioPartial(['person' =>$this->author]); + if (array_key_exists("email", $post)) { + return $post["email"]; + } + } + + public function getDate() + { + return $this->date; + } + + public function getCover() + { + return $this->cover; + } + + public function getIsCoverLight() + { + return $this->isCoverLight; + } + + public function getCategory() + { + return $this->category; + } + + public function getContentText($wordLimit = null, $appendEllipsis = false) + { + if ($this->markdown && !$this->contentText) { // $this->contentText = $this->markdownToText(trim($this->markdown)); - $this->contentText = html_entity_decode(str_replace(' ', ' ', strip_tags($this->getContentHtml())), ENT_COMPAT, 'utf-8'); + $this->contentText = html_entity_decode(str_replace(' ', ' ', strip_tags($this->getContentHtml())), ENT_COMPAT, 'utf-8'); + } + + return $wordLimit === null ? $this->contentText : $this->limitWords($this->contentText, $wordLimit, $appendEllipsis); } - return $wordLimit === null ? $this->contentText : $this->limitWords($this->contentText, $wordLimit, $appendEllipsis); - } - - public function getContentHtml() - { - if ($this->markdown && !$this->contentHtml) + public function getContentHtml() { - $this->contentHtml = ParsedownExtra::instance()->text(trim($this->markdown)); + if ($this->markdown && !$this->contentHtml) { + $this->contentHtml = ParsedownExtra::instance()->text(trim($this->markdown)); + } + return $this->contentHtml; } - return $this->contentHtml; - } - public function getPostNum() - { - return array_search($this->getSlug(), array_keys(static::getSlugMap($this->postType))); - } - - public function getPrevPost() - { - $slugs = array_keys(Post::getSlugMap($this->postType)); - $postNum = $this->getPostNum(); - return $postNum === false || $postNum === 0 ? null : Post::load($this->postType . '/' . $slugs[$postNum-1]); - } - - public function getNextPost() - { - $slugs = array_keys(Post::getSlugMap($this->postType)); - $postNum = $this->getPostNum(); - return $postNum === false || $postNum >= count($slugs)-1 ? null : Post::load($this->postType . '/' . $slugs[$postNum+1]); - } - - public function hasAuthor() - { - return $this->author !== null; - } - - public function hasDate() - { - return $this->date !== null; - } - - public function getAuthorName() - { - $post = ContentActions::prepareBioPartial(['person' =>$this->author]); - - return $post["name"]; - } - - public function getAuthorPostEmail() - { - $post = ContentActions::prepareBioPartial(['person' =>$this->author]); - - return $post["email"]; - - } - - public function getAuthorPhoto() - { - $post = ContentActions::prepareBioPartial(['person' =>$this->author]); - - return $post['imgSrc']; - } - - public function getAuthorBioHtml() - { - $post = ContentActions::prepareBioPartial(['person' =>$this->author]); - - return $post["bioHtml"]; - } - - public function getCoverBackgroundStyle($maxStyles) - { - return $this->getPostNum() % $maxStyles + 1; - } - - public function getImageUrls() - { - $urls = []; - - $cover = $this->getCover(); - if ($cover) + public function getPostNum() { - $urls[] = 'https://' . Request::getHost() . '/img/blog-covers/' . $cover; + return array_search($this->getSlug(), array_keys(static::getSlugMap($this->postType))); } - $matches = []; - preg_match_all('/!\[.*?\]\((.*?)\)/', $this->markdown, $matches); - - if ($matches) + public function getPrevPost() { - $urls = array_merge($urls, $matches[1]); + $slugs = array_keys(Post::getSlugMap($this->postType)); + $postNum = $this->getPostNum(); + return $postNum === false || $postNum === 0 ? null : Post::load($this->postType . '/' . $slugs[$postNum-1]); } - return $urls; - } + public function getNextPost() + { + $slugs = array_keys(Post::getSlugMap($this->postType)); + $postNum = $this->getPostNum(); + return $postNum === false || $postNum >= count($slugs)-1 ? null : Post::load($this->postType . '/' . $slugs[$postNum+1]); + } - protected function markdownToText($markdown) - { - $replacements = [ + public function hasAuthor() + { + return $this->author !== null; + } + + public function hasDate() + { + return $this->date !== null; + } + + public function getAuthorName() + { + $post = ContentActions::prepareBioPartial(['person' =>$this->author]); + + return $post["name"]; + } + + public function getAuthorPostEmail() + { + $post = ContentActions::prepareBioPartial(['person' =>$this->author]); + + return $post["email"]; + } + + public function getAuthorPhoto() + { + $post = ContentActions::prepareBioPartial(['person' =>$this->author]); + + return $post['imgSrc']; + } + + public function getAuthorBioHtml() + { + $post = ContentActions::prepareBioPartial(['person' =>$this->author]); + + return $post["bioHtml"]; + } + + public function getCoverBackgroundStyle($maxStyles) + { + return $this->getPostNum() % $maxStyles + 1; + } + + public function getImageUrls() + { + $urls = []; + + $cover = $this->getCover(); + if ($cover) { + $urls[] = 'https://' . Request::getHost() . '/img/blog-covers/' . $cover; + } + + $matches = []; + preg_match_all('/!\[.*?\]\((.*?)\)/', $this->markdown, $matches); + + if ($matches) { + $urls = array_merge($urls, $matches[1]); + } + + return $urls; + } + + protected function markdownToText($markdown) + { + $replacements = [ // '/<(.*?)>/' => '$1', // HTML tags '/^[=\-]{2,}\s*$/' => '', // setext-style headers '/\[\^.+?\](\: .*?$)?/' => '', // footnotes @@ -327,65 +325,61 @@ public function getAuthorEmail() '/\n{2,}/' => '\n\n', // multiple newlines ]; - return preg_replace(array_keys($replacements), array_values($replacements), strip_tags($markdown)); - } - - protected function limitWords($string, $wordLimit, $appendEllipsis = false) - { - $regexp = '/\s+/u'; - $words = preg_split($regexp, $string, $wordLimit + 1); - $numWords = count($words); - - # TBB: if there are $wordLimit words or less, this check is necessary - # to prevent the last word from being lost. - if ($numWords > $wordLimit) - { - array_pop($words); + return preg_replace(array_keys($replacements), array_values($replacements), strip_tags($markdown)); } - $string = implode(' ', $words); - - if ($appendEllipsis && $numWords > $wordLimit) + protected function limitWords($string, $wordLimit, $appendEllipsis = false) { - $ellipsis = '…'; - $string .= $ellipsis; + $regexp = '/\s+/u'; + $words = preg_split($regexp, $string, $wordLimit + 1); + $numWords = count($words); + + # TBB: if there are $wordLimit words or less, this check is necessary + # to prevent the last word from being lost. + if ($numWords > $wordLimit) { + array_pop($words); + } + + $string = implode(' ', $words); + + if ($appendEllipsis && $numWords > $wordLimit) { + $ellipsis = '…'; + $string .= $ellipsis; + } + + return $string; } - return $string; - } - - public static function getSlugFromFilename($filename) - { - return strtolower(preg_replace('#^\d{1,3}\-#', '', basename(trim($filename), '.md'))); - } - - public static function collectMetadata(array $posts, $field) - { - $values = array_unique(array_map(function(Post $post) use($field) { - $metadata = $post->getMetadata(); - return $metadata[$field] ?? null; - }, $posts)); - sort($values); - return array_combine($values, $values); - } - - public static function getSlugMap($postType) - { - if (!isset(static::$slugMap[$postType])) + public static function getSlugFromFilename($filename) { - static::$slugMap[$postType] = []; - $files = glob(ContentActions::CONTENT_DIR . '/' . $postType . '/*.md'); - usort($files, 'strnatcasecmp'); - foreach($files as $file) - { - static::$slugMap[$postType][static::getSlugFromFilename($file)] = $file; - } + return strtolower(preg_replace('#^\d{1,3}\-#', '', basename(trim($filename), '.md'))); } - return static::$slugMap[$postType]; - } - public function getGithubEditUrl() - { - return 'https://github.com/lbryio/lbry.io/tree/master' . str_replace(ROOT_DIR, '', $this->path); - } + public static function collectMetadata(array $posts, $field) + { + $values = array_unique(array_map(function (Post $post) use ($field) { + $metadata = $post->getMetadata(); + return $metadata[$field] ?? null; + }, $posts)); + sort($values); + return array_combine($values, $values); + } + + public static function getSlugMap($postType) + { + if (!isset(static::$slugMap[$postType])) { + static::$slugMap[$postType] = []; + $files = glob(ContentActions::CONTENT_DIR . '/' . $postType . '/*.md'); + usort($files, 'strnatcasecmp'); + foreach ($files as $file) { + static::$slugMap[$postType][static::getSlugFromFilename($file)] = $file; + } + } + return static::$slugMap[$postType]; + } + + public function getGithubEditUrl() + { + return 'https://github.com/lbryio/lbry.io/tree/master' . str_replace(ROOT_DIR, '', $this->path); + } } diff --git a/monero_trans.png b/monero_trans.png new file mode 100644 index 00000000..37b5c242 Binary files /dev/null and b/monero_trans.png differ diff --git a/php-cs-fixer b/php-cs-fixer new file mode 100755 index 00000000..e4df4dcf Binary files /dev/null and b/php-cs-fixer differ diff --git a/update.php b/update.php index 24806682..9a3216cb 100755 --- a/update.php +++ b/update.php @@ -7,10 +7,9 @@ $options = getopt('f'); $force = isset($options['f']); // update even if no NEEDS_UPDATE file exists $needsUpdateFile = ROOT_DIR . '/data/writeable/NEEDS_UPDATE'; -if (!$force && !file_exists($needsUpdateFile)) -{ - echo "No update necessary\n"; - return; +if (!$force && !file_exists($needsUpdateFile)) { + echo "No update necessary\n"; + return; } @unlink($needsUpdateFile); diff --git a/view/Response.class.php b/view/Response.class.php index c4abdf94..9b4526ba 100644 --- a/view/Response.class.php +++ b/view/Response.class.php @@ -2,320 +2,302 @@ class Response { - const HEADER_STATUS = 'Status'; - const HEADER_LOCATION = 'Location'; + const HEADER_STATUS = 'Status'; + const HEADER_LOCATION = 'Location'; - const HEADER_CACHE_CONTROL = 'Cache-Control'; - const HEADER_LAST_MODIFIED = 'Last-Modified'; - const HEADER_ETAG = 'Etag'; + const HEADER_CACHE_CONTROL = 'Cache-Control'; + const HEADER_LAST_MODIFIED = 'Last-Modified'; + const HEADER_ETAG = 'Etag'; - const HEADER_CONTENT_TYPE = 'Content-Type'; - const HEADER_CONTENT_LENGTH = 'Content-Length'; - const HEADER_CONTENT_DISPOSITION = 'Content-Disposition'; - const HEADER_CONTENT_TYPE_OPTIONS = 'X-Content-Type-Options'; - const HEADER_CONTENT_ENCODING = 'Content-Encoding'; + const HEADER_CONTENT_TYPE = 'Content-Type'; + const HEADER_CONTENT_LENGTH = 'Content-Length'; + const HEADER_CONTENT_DISPOSITION = 'Content-Disposition'; + const HEADER_CONTENT_TYPE_OPTIONS = 'X-Content-Type-Options'; + const HEADER_CONTENT_ENCODING = 'Content-Encoding'; + const HEADER_CROSS_ORIGIN = 'Access-Control-Allow-Origin'; - protected static - $metaDescription = '', - $metaTitle = '', - $jsCalls = [], - $assets = [ + protected static $metaDescription = ''; + protected static $metaTitle = ''; + protected static $jsCalls = []; + protected static $assets = [ 'js' => [ '/js/jquery-3.3.1.min.js', '/js/global.js' ], 'css' => ['/css/all.css'] - ], - $headers = [], - $headersSent = false, - $content = '', - $contentSent = false, - $isHeadersOnly = false, - $gzipResponseContent = true, - $metaImages = [], - $facebookAnalyticsType = "PageView"; + ]; + protected static $headers = []; + protected static $headersSent = false; + protected static $content = ''; + protected static $contentSent = false; + protected static $isHeadersOnly = false; + protected static $gzipResponseContent = true; + protected static $metaImages = []; + protected static $facebookAnalyticsType = "PageView"; - public static function setMetaDescription($description) - { - static::$metaDescription = $description; - } - - public static function addMetaImage($url) - { - static::$metaImages[] = $url; - } - - public static function addMetaImages(array $urls) - { - foreach ($urls as $url) + public static function setMetaDescription($description) { - static::addMetaImage($url); + static::$metaDescription = $description; } - } - public static function getMetaDescription() - { - return static::$metaDescription ?: 'A Content Revolution'; - } - - public static function getMetaImages() - { - return static::$metaImages ?: [Request::getHostAndProto() . '/img/lbry-green-meta-1200x900.png']; - } - - public static function setMetaTitle($title) - { - static::$metaTitle = $title; - } - - public static function getMetaTitle() - { - return static::$metaTitle; - } - - public static function guessMetaTitle($content) - { - $title = ''; - preg_match_all('/]*>([^<]+) $headerValue) + public static function addMetaImage($url) { - if ($headerValue == '1' || !$title) - { - $title = $titleMatches[2][$matchIndex]; - if ($headerValue == '1') - { - return $title; + static::$metaImages[] = $url; + } + + public static function addMetaImages(array $urls) + { + foreach ($urls as $url) { + static::addMetaImage($url); } - } } - return $title; - } - public static function getJsCalls() - { - return static::$jsCalls; - } - - public static function jsOutputCallback($js) - { - static::$jsCalls[] = $js; - return ''; - } - - public static function addJsAsset($src) - { - static::$assets['js'][$src] = $src; - } - - public static function addCssAsset($src) - { - static::$assets['css'][$src] = $src; - } - - public static function getJsAssets() - { - return static::$assets['js']; - } - - public static function getCssAssets() - { - return static::$assets['css']; - } - - public static function setCssAssets(array $assets = []){ - static::$assets['css'] = $assets; - } - public static function setGzipResponseContent($gzip = true) - { - static::$gzipResponseContent = $gzip; - } - - public static function gzipContentIfNotDisabled() - { - if (static::$gzipResponseContent) + public static function getMetaDescription() { - $content = static::getContent(); - if (strlen($content) > 256) // not worth it for really short content - { - $compressed = gzencode($content, 1); - static::setContent($compressed); - static::setHeader(static::HEADER_CONTENT_LENGTH, strlen($compressed)); - static::setHeader(static::HEADER_CONTENT_ENCODING, 'gzip'); - } + return static::$metaDescription ?: 'A Content Revolution'; } - } - public static function send() - { - static::sendHeaders(); - static::sendContent(); - } - - public static function setContent(string $content) - { - static::$content = $content; - } - - public static function getContent(): string - { - return static::$content; - } - - public static function sendContent() - { - if (static::$contentSent) + public static function getMetaImages() { - throw new LogicException('Content has already been sent. It cannot be sent twice'); + return static::$metaImages ?: [Request::getHostAndProto() . '/img/lbry-green-meta-1200x900.png']; } - if (!static::$isHeadersOnly) + public static function setMetaTitle($title) { - echo static::$content; + static::$metaTitle = $title; } - static::$contentSent = true; - } + public static function getMetaTitle() + { + return static::$metaTitle; + } - public static function setIsHeadersOnly(bool $isHeadersOnly = true) - { - static::$isHeadersOnly = $isHeadersOnly; - } + public static function guessMetaTitle($content) + { + $title = ''; + preg_match_all('/]*>([^<]+) $headerValue) { + if ($headerValue == '1' || !$title) { + $title = $titleMatches[2][$matchIndex]; + if ($headerValue == '1') { + return $title; + } + } + } + return $title; + } - public static function setDownloadHttpHeaders($name, $type = null, $size = null, $noSniff = true) - { - static::setBinaryHttpHeaders($type, $size, $noSniff); - static::setHeader('Content-Disposition', 'attachment;filename=' . $name); - } + public static function getJsCalls() + { + return static::$jsCalls; + } - public static function setBinaryHttpHeaders($type, $size = null, $noSniff = true) - { - static::setGzipResponseContent(false); // in case its already compressed - static::setHeaders(array_filter([ + public static function jsOutputCallback($js) + { + static::$jsCalls[] = $js; + return ''; + } + + public static function addJsAsset($src) + { + static::$assets['js'][$src] = $src; + } + + public static function addCssAsset($src) + { + static::$assets['css'][$src] = $src; + } + + public static function getJsAssets() + { + return static::$assets['js']; + } + + public static function getCssAssets() + { + return static::$assets['css']; + } + + public static function setCssAssets(array $assets = []) + { + static::$assets['css'] = $assets; + } + public static function setGzipResponseContent($gzip = true) + { + static::$gzipResponseContent = $gzip; + } + + public static function gzipContentIfNotDisabled() + { + if (static::$gzipResponseContent) { + $content = static::getContent(); + if (strlen($content) > 256) { // not worth it for really short content + $compressed = gzencode($content, 1); + static::setContent($compressed); + static::setHeader(static::HEADER_CONTENT_LENGTH, strlen($compressed)); + static::setHeader(static::HEADER_CONTENT_ENCODING, 'gzip'); + } + } + } + + public static function send() + { + static::sendHeaders(); + static::sendContent(); + } + + public static function setContent(string $content) + { + static::$content = $content; + } + + public static function getContent(): string + { + return static::$content; + } + + public static function sendContent() + { + if (static::$contentSent) { + throw new LogicException('Content has already been sent. It cannot be sent twice'); + } + + if (!static::$isHeadersOnly) { + echo static::$content; + } + + static::$contentSent = true; + } + + public static function setIsHeadersOnly(bool $isHeadersOnly = true) + { + static::$isHeadersOnly = $isHeadersOnly; + } + + public static function setDownloadHttpHeaders($name, $type = null, $size = null, $noSniff = true) + { + static::setBinaryHttpHeaders($type, $size, $noSniff); + static::setHeader('Content-Disposition', 'attachment;filename=' . $name); + } + + public static function setBinaryHttpHeaders($type, $size = null, $noSniff = true) + { + static::setGzipResponseContent(false); // in case its already compressed + static::setHeaders(array_filter([ static::HEADER_CONTENT_TYPE => $type, static::HEADER_CONTENT_LENGTH => $size ?: null, static::HEADER_CONTENT_TYPE_OPTIONS => $noSniff ? 'nosniff' : null, ])); - } - - public static function setContentEtag() - { - static::setHeader(static::HEADER_ETAG, md5(static::getContent())); - } - - public static function enableHttpCache(int $seconds = 300) - { - static::addCacheControlHeader('max-age', $seconds); - static::setHeader('Pragma', 'public'); - } - - public static function addCacheControlHeader(string $name, $value = null) - { - $cacheControl = static::getHeader(static::HEADER_CACHE_CONTROL); - $currentHeaders = []; - if ($cacheControl) - { - foreach (preg_split('/\s*,\s*/', $cacheControl) as $tmp) - { - $tmp = explode('=', $tmp); - $currentHeaders[$tmp[0]] = $tmp[1] ?? null; - } - } - $currentHeaders[strtr(strtolower($name), '_', '-')] = $value; - - $headers = []; - foreach ($currentHeaders as $key => $currentVal) - { - $headers[] = $key . ($currentVal !== null ? '=' . $currentVal : ''); } - static::setHeader(static::HEADER_CACHE_CONTROL, implode(', ', $headers)); - } - - public static function setHeader($name, $value) - { - static::$headers[$name] = $value; - } - - public static function setHeaders($headers, $overwrite = true) - { - foreach ($headers as $name => $value) + public static function setContentEtag() { - if ($overwrite || !static::getHeader($name)) - { - static::setHeader($name, $value); - } + static::setHeader(static::HEADER_ETAG, md5(static::getContent())); } - } - public static function getHeader($name, $default = null) - { - return static::$headers[$name] ?? $default; - } + public static function enableHttpCache(int $seconds = 300) + { + static::addCacheControlHeader('max-age', $seconds); + static::setHeader('Pragma', 'public'); + } - public static function getHeaders(): array - { - return static::$headers; - } + public static function addCacheControlHeader(string $name, $value = null) + { + $cacheControl = static::getHeader(static::HEADER_CACHE_CONTROL); + $currentHeaders = []; + if ($cacheControl) { + foreach (preg_split('/\s*,\s*/', $cacheControl) as $tmp) { + $tmp = explode('=', $tmp); + $currentHeaders[$tmp[0]] = $tmp[1] ?? null; + } + } + $currentHeaders[strtr(strtolower($name), '_', '-')] = $value; - public static function setStatus($status) - { - static::setHeader(static::HEADER_STATUS, $status); - } + $headers = []; + foreach ($currentHeaders as $key => $currentVal) { + $headers[] = $key . ($currentVal !== null ? '=' . $currentVal : ''); + } - public static function setDefaultSecurityHeaders() - { - $defaultHeaders = [ + static::setHeader(static::HEADER_CACHE_CONTROL, implode(', ', $headers)); + } + + public static function setHeader($name, $value) + { + static::$headers[$name] = $value; + } + + public static function setHeaders($headers, $overwrite = true) + { + foreach ($headers as $name => $value) { + if ($overwrite || !static::getHeader($name)) { + static::setHeader($name, $value); + } + } + } + + public static function getHeader($name, $default = null) + { + return static::$headers[$name] ?? $default; + } + + public static function getHeaders(): array + { + return static::$headers; + } + + public static function setStatus($status) + { + static::setHeader(static::HEADER_STATUS, $status); + } + + public static function setDefaultSecurityHeaders() + { + $defaultHeaders = [ 'Content-Security-Policy' => "frame-ancestors 'none'", 'X-Frame-Options' => 'DENY', 'X-XSS-Protection' => '1', ]; - if (IS_PRODUCTION) - { - $defaultHeaders['Strict-Transport-Security'] = 'max-age=31536000'; + if (IS_PRODUCTION) { + $defaultHeaders['Strict-Transport-Security'] = 'max-age=31536000'; + } + + static::setHeaders($defaultHeaders, false); } - static::setHeaders($defaultHeaders, false); - } - - public static function sendHeaders() - { - if (static::$headersSent) + public static function sendHeaders() { - throw new LogicException('Headers have already been sent. They cannot be sent twice'); + if (static::$headersSent) { + throw new LogicException('Headers have already been sent. They cannot be sent twice'); + } + + if (!static::getHeader(static::HEADER_CONTENT_TYPE)) { + static::setHeader(static::HEADER_CONTENT_TYPE, 'text/html'); + } + + $headers = static::getHeaders(); + + if (isset($headers[static::HEADER_STATUS])) { + $status = 'HTTP/1.0 ' . $headers[static::HEADER_STATUS] . ' ' . static::getStatusTextForCode($headers[static::HEADER_STATUS]); + header($status); + + if (substr(php_sapi_name(), 0, 3) == 'cgi') { + // fastcgi servers cannot send this status information because it was sent by them already due to the HTT/1.0 line + // so we can safely unset them. see ticket #3191 + unset($headers[static::HEADER_STATUS]); + } + } + + foreach ($headers as $name => $value) { + header($name . ': ' . $value); + } + + static::$headersSent = true; } - if (!static::getHeader(static::HEADER_CONTENT_TYPE)) + public static function getStatusTextForCode($code) { - static::setHeader(static::HEADER_CONTENT_TYPE, 'text/html'); - } - - $headers = static::getHeaders(); - - if (isset($headers[static::HEADER_STATUS])) - { - $status = 'HTTP/1.0 ' . $headers[static::HEADER_STATUS] . ' ' . static::getStatusTextForCode($headers[static::HEADER_STATUS]); - header($status); - - if (substr(php_sapi_name(), 0, 3) == 'cgi') - { - // fastcgi servers cannot send this status information because it was sent by them already due to the HTT/1.0 line - // so we can safely unset them. see ticket #3191 - unset($headers[static::HEADER_STATUS]); - } - } - - foreach ($headers as $name => $value) - { - header($name . ': ' . $value); - } - - static::$headersSent = true; - } - - public static function getStatusTextForCode($code) - { - $statusTexts = [ + $statusTexts = [ '100' => 'Continue', '101' => 'Switching Protocols', '200' => 'OK', @@ -363,28 +345,32 @@ class Response '505' => 'HTTP Version Not Supported', ]; - return $statusTexts[$code] ?? null; - } + return $statusTexts[$code] ?? null; + } - public static function setFacebookPixelAnalyticsType($type){ - static::$facebookAnalyticsType = $type; - } + public static function setFacebookPixelAnalyticsType($type) + { + static::$facebookAnalyticsType = $type; + } - public static function getFacebookPixelAnalyticsType(){ - return static::$facebookAnalyticsType; - } + public static function getFacebookPixelAnalyticsType() + { + return static::$facebookAnalyticsType; + } - protected static function normalizeHeaderName($name): string - { - return preg_replace_callback( + protected static function normalizeHeaderName($name): string + { + return preg_replace_callback( '/\-(.)/', - function ($matches) { return '-' . strtoupper($matches[1]); }, + function ($matches) { + return '-' . strtoupper($matches[1]); + }, strtr(ucfirst(strtolower($name)), '_', '-') ); - } + } -// public static function addBodyCssClass($classOrClasses) + // public static function addBodyCssClass($classOrClasses) // { // static::$bodyCssClasses = array_unique(array_merge(static::$bodyCssClasses, (array)$classOrClasses)); // } diff --git a/view/View.class.php b/view/View.class.php index 2a595e9a..34c1f29f 100644 --- a/view/View.class.php +++ b/view/View.class.php @@ -2,195 +2,179 @@ function js_start() { - ob_start('Response::jsOutputCallback'); + ob_start('Response::jsOutputCallback'); } function js_end() { - ob_end_flush(); + ob_end_flush(); } class View { - const LAYOUT_PARAMS = '_layout_params'; + const LAYOUT_PARAMS = '_layout_params'; - const WEB_DIR = ROOT_DIR . '/web'; - const SCSS_DIR = self::WEB_DIR . '/scss'; - const CSS_DIR = self::WEB_DIR . '/css'; - const JS_DIR = self::WEB_DIR . '/js'; + const WEB_DIR = ROOT_DIR . '/web'; + const SCSS_DIR = self::WEB_DIR . '/scss'; + const CSS_DIR = self::WEB_DIR . '/css'; + const JS_DIR = self::WEB_DIR . '/js'; - public static function render($template, array $vars = []): string - { - if (static::isMarkdown($template)) + public static function render($template, array $vars = []): string { - return static::markdownToHtml(static::getFullPath($template)); + if (static::isMarkdown($template)) { + return static::markdownToHtml(static::getFullPath($template)); + } + + if (!static::exists($template) || substr_count($template, '/') !== 1) { + throw new InvalidArgumentException(sprintf('The template "%s" does not exist or is unreadable.', $template)); + } + + list($module, $view) = explode('/', $template); + + $isPartial = $view[0] === '_'; + $actionClass = ucfirst($module) . 'Actions'; + $method = 'prepare' . ucfirst(ltrim($view, '_')) . ($isPartial ? 'Partial' : ''); + + if (method_exists($actionClass, $method)) { + $vars = $actionClass::$method($vars); + } + + if ($vars === null) { + return []; + } + + return static::interpolateTokens(static::getTemplateSafely($template, $vars)); } - if (!static::exists($template) || substr_count($template, '/') !== 1) + /** + * This is its own function because we don't want in-scope variables to leak into the template + * + * @param string $___template + * @param array $___vars + * + * @return string + * @throws Throwable + */ + protected static function getTemplateSafely(string $___template, array $___vars): string { - throw new InvalidArgumentException(sprintf('The template "%s" does not exist or is unreadable.', $template)); + extract($___vars); + ob_start(); + ob_implicit_flush(0); + + try { + require(static::getFullPath($___template)); + return ob_get_clean(); + } catch (Throwable $e) { + // need to end output buffering before throwing the exception + ob_end_clean(); + throw $e; + } } - list($module, $view) = explode('/', $template); - - $isPartial = $view[0] === '_'; - $actionClass = ucfirst($module) . 'Actions'; - $method = 'prepare' . ucfirst(ltrim($view, '_')) . ($isPartial ? 'Partial' : ''); - - if (method_exists($actionClass, $method)) + public static function markdownToHtml($path): string { - $vars = $actionClass::$method($vars); + return ParsedownExtra::instance()->text(trim(file_get_contents($path))); } - if ($vars === null) + public static function exists($template): bool { - return []; + return is_readable(static::getFullPath($template)); } - return static::interpolateTokens(static::getTemplateSafely($template, $vars)); - } - - /** - * This is its own function because we don't want in-scope variables to leak into the template - * - * @param string $___template - * @param array $___vars - * - * @return string - * @throws Throwable - */ - protected static function getTemplateSafely(string $___template, array $___vars): string - { - extract($___vars); - ob_start(); - ob_implicit_flush(0); - - try + protected static function isMarkdown($nameOrPath): bool { - require(static::getFullPath($___template)); - return ob_get_clean(); - } - catch (Throwable $e) - { - // need to end output buffering before throwing the exception - ob_end_clean(); - throw $e; - } - } - - public static function markdownToHtml($path): string - { - return ParsedownExtra::instance()->text(trim(file_get_contents($path))); - } - - public static function exists($template): bool - { - return is_readable(static::getFullPath($template)); - } - - protected static function isMarkdown($nameOrPath): bool - { - return strlen($nameOrPath) > 3 && substr($nameOrPath, -3) == '.md'; - } - - protected static function getFullPath($template): string - { - if ($template && $template[0] == '/') - { - return $template; + return strlen($nameOrPath) > 3 && substr($nameOrPath, -3) == '.md'; } - if (static::isMarkdown($template)) + protected static function getFullPath($template): string { - return ContentActions::CONTENT_DIR . '/' . $template; + if ($template && $template[0] == '/') { + return $template; + } + + if (static::isMarkdown($template)) { + return ContentActions::CONTENT_DIR . '/' . $template; + } + + return ROOT_DIR . '/view/template/' . $template . '.php'; } - return ROOT_DIR . '/view/template/' . $template . '.php'; - } - - public static function imagePath($image): string - { - return '/img/' . $image; - } - - public static function parseMarkdown($template): array - { - $path = static::getFullPath($template); - list($ignored, $frontMatter, $markdown) = explode('---', file_get_contents($path), 3); - $metadata = Spyc::YAMLLoadString(trim($frontMatter)); - $html = ParsedownExtra::instance()->text(trim($markdown)); - return [$metadata, $html]; - } - - public static function compileCss() - { - $scssCompiler = new \Leafo\ScssPhp\Compiler(); - - $scssCompiler->setImportPaths([self::SCSS_DIR]); - - $compress = true; - if ($compress) + public static function imagePath($image): string { - $scssCompiler->setFormatter('Leafo\ScssPhp\Formatter\Crunched'); - } - else - { - $scssCompiler->setFormatter('Leafo\ScssPhp\Formatter\Expanded'); - $scssCompiler->setLineNumberStyle(Leafo\ScssPhp\Compiler::LINE_COMMENTS); + return '/img/' . $image; } - $all_css = $scssCompiler->compile(file_get_contents(self::SCSS_DIR . '/all.scss')); - file_put_contents(self::CSS_DIR . '/all.css', $all_css); - - $youtube_css = $scssCompiler->compile(file_get_contents(self::SCSS_DIR . '/youtube.scss')); - file_put_contents(self::CSS_DIR . '/youtube.css', $youtube_css); - } - - public static function gzipAssets() - { - foreach ([self::CSS_DIR => 'css', self::JS_DIR => 'js'] as $dir => $ext) + public static function parseMarkdown($template): array { - foreach (glob("$dir/*.$ext") as $file) - { - Gzip::compressFile($file); - } + $path = static::getFullPath($template); + list($ignored, $frontMatter, $markdown) = explode('---', file_get_contents($path), 3); + $metadata = Spyc::YAMLLoadString(trim($frontMatter)); + $html = ParsedownExtra::instance()->text(trim($markdown)); + return [$metadata, $html]; } - } - protected static function interpolateTokens($html): string - { - return preg_replace_callback('/{{[\w\.]+}}/is', function ($m) + public static function compileCss() { - return i18n::translate(trim($m[0], '}{')); - }, $html); - } + $scssCompiler = new \Leafo\ScssPhp\Compiler(); - protected static function escapeOnce($value): string - { - return preg_replace('/&([a-z]+|(#\d+)|(#x[\da-f]+));/i', '&$1;', htmlspecialchars((string)$value, ENT_QUOTES, 'utf-8')); - } + $scssCompiler->setImportPaths([self::SCSS_DIR]); - protected static function attributesToHtml($attributes): string - { - return implode('', array_map(function ($k, $v) + $compress = true; + if ($compress) { + $scssCompiler->setFormatter('Leafo\ScssPhp\Formatter\Crunched'); + } else { + $scssCompiler->setFormatter('Leafo\ScssPhp\Formatter\Expanded'); + $scssCompiler->setLineNumberStyle(Leafo\ScssPhp\Compiler::LINE_COMMENTS); + } + + $all_css = $scssCompiler->compile(file_get_contents(self::SCSS_DIR . '/all.scss')); + file_put_contents(self::CSS_DIR . '/all.css', $all_css); + + $youtube_css = $scssCompiler->compile(file_get_contents(self::SCSS_DIR . '/youtube.scss')); + file_put_contents(self::CSS_DIR . '/youtube.css', $youtube_css); + } + + public static function gzipAssets() { - return $v === true ? " $k" : ($v === false || $v === null || ($v === '' && $k != 'value') ? '' : sprintf(' %s="%s"', $k, static::escapeOnce($v))); - }, array_keys($attributes), array_values($attributes))); - } + foreach ([self::CSS_DIR => 'css', self::JS_DIR => 'js'] as $dir => $ext) { + foreach (glob("$dir/*.$ext") as $file) { + Gzip::compressFile($file); + } + } + } - public static function renderTag($tag, $attributes = []): string - { - return $tag ? sprintf('<%s%s />', $tag, static::attributesToHtml($attributes)) : ''; - } + protected static function interpolateTokens($html): string + { + return preg_replace_callback('/{{[\w\.]+}}/is', function ($m) { + return i18n::translate(trim($m[0], '}{')); + }, $html); + } - public static function renderContentTag($tag, $content = null, $attributes = []): string - { - return $tag ? sprintf('<%s%s>%s', $tag, static::attributesToHtml($attributes), $content, $tag) : ''; - } + protected static function escapeOnce($value): string + { + return preg_replace('/&([a-z]+|(#\d+)|(#x[\da-f]+));/i', '&$1;', htmlspecialchars((string)$value, ENT_QUOTES, 'utf-8')); + } - public static function renderJson($data): array - { - Response::setHeader(Response::HEADER_CONTENT_TYPE, 'application/json'); - return ['internal/json', ['json' => $data, '_no_layout' => true]]; - } -} \ No newline at end of file + protected static function attributesToHtml($attributes): string + { + return implode('', array_map(function ($k, $v) { + return $v === true ? " $k" : ($v === false || $v === null || ($v === '' && $k != 'value') ? '' : sprintf(' %s="%s"', $k, static::escapeOnce($v))); + }, array_keys($attributes), array_values($attributes))); + } + + public static function renderTag($tag, $attributes = []): string + { + return $tag ? sprintf('<%s%s />', $tag, static::attributesToHtml($attributes)) : ''; + } + + public static function renderContentTag($tag, $content = null, $attributes = []): string + { + return $tag ? sprintf('<%s%s>%s', $tag, static::attributesToHtml($attributes), $content, $tag) : ''; + } + + public static function renderJson($data): array + { + Response::setHeader(Response::HEADER_CONTENT_TYPE, 'application/json'); + return ['internal/json', ['json' => $data, '_no_layout' => true]]; + } +} diff --git a/view/template/acquisition/verify.php b/view/template/acquisition/verify.php index bd497ab3..830715ba 100644 --- a/view/template/acquisition/verify.php +++ b/view/template/acquisition/verify.php @@ -16,7 +16,6 @@ })); magicLink = "lbry://?verify=" + payload; document.getElementById("magic-link-text").textContent = payload; - document.getElementById("magic-link-text-windows").value = payload; document.getElementById("success").style.display = "block"; document.getElementById("captcha-block").style.display = "none"; }; @@ -36,24 +35,12 @@
-
-

Now click the magic link below to verify your identity in app...

-
- Magic Link -

Does the magic link not work? Not on the same device as the app? Paste this text into the verification screen instead.

- -
- +

Now click the magic link below to verify your identity in app...

+
+ Magic Link +

Does the magic link not work? Not on the same device as the app? Paste this (very long) piece of text into the verification screen of the app to confirm your identity.

+
- diff --git a/view/template/acquisition/youtube-2.php b/view/template/acquisition/youtube-2.php new file mode 100644 index 00000000..63177ae5 --- /dev/null +++ b/view/template/acquisition/youtube-2.php @@ -0,0 +1,195 @@ + +
+ +
+
+ + + + + + + + + + +
+
+
+
+
+
+
+
+
+
+

Content Freedom.

+

Put your content on the blockchain and earn rewards.

+
Claim Your LBRY Channel
+
+
+
+
+
+
+

Sync & Earn Crypto

+

LBRY offers a single-click sync process
for existing YouTubers

+
+
+
+
+ + +
+
+
+
+
+ +
+
+
+ + + + + +
+ By syncing, you agree to mirror your content to the LBRY network for 1 year, and acknowledge these terms. +
+
+ +
+ +
+
+
+

LBRY is more fun with friends

+

Take your peers and your audience with you. Create without limits.

+
+
+
+
+ +
+ +
+

@3Blue1Brown

+
+
+
+
+
+ +
+ +
+

@CasuallyExplained

+
+
+
+
+
+ +
+
+

@ColinsLastStand

+
+
+
+
+
+
+
+
+
+

Migrating to LBRY

+

We will automatically mirror your existing YouTube channel to the LBRY Network.

+
+
+
+
+
+
1
+

Sync your channel

+
+
+
2
+

Download the LBRY App

+
+
+
3
+

Receive your LBRY Credits

+
+
+
+
+
+
+
+
+

LBRY Credits and Your Channel

+

After you sync, receive LBRY Credits for one year based on your current subscriber count.

+ The more you give to the network, the more it gives back.

+
+

Partner Programs

+

LBC

+
+
+
+

Subscribers

+

Yearly

+

Amount

+
+
+

1,000

+

+

+
+
+

10,000

+

+

+
+
+

100,000

+

+

+
+
+

1,000,000

+

+

+
+
+
+
+
+
+
+
+

Tell me more.

+

We have a guy that elaborates on things. Apply directly to the forehead.

+
+
+
+

Reilly Smith

+

Head of Content

+ Contact +
+
+
+
+
+
to top
+
diff --git a/view/template/acquisition/youtube-3.php b/view/template/acquisition/youtube-3.php new file mode 100644 index 00000000..f38aea12 --- /dev/null +++ b/view/template/acquisition/youtube-3.php @@ -0,0 +1,194 @@ + +
+ +
+
+ + + + + + + + + + +
+
+
+
+
+
+
+
+
+
+

Content Freedom.

+

Put your content on the blockchain and earn rewards.

+
Claim Your LBRY Channel
+
+
+
+
+ +
+ " . "The following error occurred: ". $error_message . " For support please send an email to hello@lbry.io" . "
"; + endif;?> +
+

Create on a stable platform. For real this time.

+ +
+
+
+ + + +
+ +
+ +
+
+ This will verify you are an active YouTuber, then instructions and your welcome credits will be emailed to you. + Learn more. +
+
+
+
+
+
+
+

LBRY is more fun with friends

+

Take your peers and your audience with you. Create without limits.

+
+
+
+
+ +
+ +
+

@3Blue1Brown

+
+
+
+
+
+ +
+ +
+

@CasuallyExplained

+
+
+
+
+
+ +
+
+

@ColinsLastStand

+
+
+
+
+
+
+
+
+
+

Migrating to LBRY

+

We will automatically mirror your most recent 1,000 YouTube videos to your channel on the LBRY Network.

+
+
+
+
+
+
1
+

Sync your channel

+
+
+
2
+

Download the LBRY App

+
+
+
3
+

Receive your LBRY Credits

+
+
+
+
+
+ +
+
+
+

LBRY Credits and Your Channel

+

After you sync, receive LBRY Credits for one year based on your current subscriber count.

+ The more you give to the network, the more it gives back.

+
+

Partner Programs

+

LBC

+
+
+
+

Subscribers

+

Yearly

+

Amount

+
+
+

1,000

+

+

+
+
+

10,000

+

+

+
+
+

100,000

+

+

+
+
+

1,000,000

+

+

+
+
+
+
+
+
+
+
+

Tell me more.

+

We have a guy that elaborates on things. Apply directly to the forehead.

+
+
+
+

Reilly Smith

+

Head of Content

+ Contact +
+
+
+
+
+
to top
+
diff --git a/view/template/acquisition/youtube.php b/view/template/acquisition/youtube.php index 6bb6607d..910541e3 100644 --- a/view/template/acquisition/youtube.php +++ b/view/template/acquisition/youtube.php @@ -8,6 +8,7 @@ Response::addJsAsset('/js/yt2/SyncStatus.js'); Response::addJsAsset('/js/yt2/youtube_video.js'); Response::setMetaTitle("LBRY YouTube Partner Program"); Response::setMetaDescription("Put your content on the blockchain, experience true content freedom, and earn rewards."); +Response::addMetaImage(Request::getHostAndProto() . '/img/lbry-partner.png'); ?>
@@ -62,7 +63,7 @@ Response::setMetaDescription("Put your content on the blockchain, experience tru
- This will verify you are an active YouTuber, then instructions and your welcome credits will be emailed to you. + This will verify you are an active YouTuber. Once verified instructions about how to claim credits, and technical details about your channel, will be emailed to you. Learn more.
@@ -77,7 +78,7 @@ Response::setMetaDescription("Put your content on the blockchain, experience tru
- +
@@ -87,7 +88,7 @@ Response::setMetaDescription("Put your content on the blockchain, experience tru
- +
@@ -97,7 +98,7 @@ Response::setMetaDescription("Put your content on the blockchain, experience tru
- +

@ColinsLastStand

@@ -111,7 +112,7 @@ Response::setMetaDescription("Put your content on the blockchain, experience tru

Migrating to LBRY

-

We will automatically mirror your existing YouTube channel to the LBRY Network.

+

We will automatically mirror your most recent 1,000 YouTube videos to your channel on the LBRY Network.

@@ -138,7 +139,7 @@ Response::setMetaDescription("Put your content on the blockchain, experience tru

Sync & Earn

LBRY offers a single-click sync process
for existing YouTubers

-
+
@@ -210,11 +211,11 @@ Response::setMetaDescription("Put your content on the blockchain, experience tru

Tell me more.

We have a guy that elaborates on things. Apply directly to the forehead.

-
+
-

Reilly Smith

-

Head of Content

- Contact +

Rob Smith

+

Head of Product Growth

+ Contact
diff --git a/view/template/acquisition/youtube_edit.php b/view/template/acquisition/youtube_edit.php index 74e26a40..ee0b202c 100644 --- a/view/template/acquisition/youtube_edit.php +++ b/view/template/acquisition/youtube_edit.php @@ -5,7 +5,7 @@ $email = Request::encodeStringFromUser($_POST['new_email']); $sync_consent = isset($_POST['sync_consent']); -if(!preg_match("/@[A-Za-z0-9_-]+$/", $channel_name)){ +if (!preg_match("/@[A-Za-z0-9-]+$/", $channel_name)) { $channel_name = "@" . $channel_name; } diff --git a/view/template/acquisition/youtube_status.php b/view/template/acquisition/youtube_status.php index bdfe26b0..e67fe638 100644 --- a/view/template/acquisition/youtube_status.php +++ b/view/template/acquisition/youtube_status.php @@ -104,7 +104,7 @@ endif;?>
- > + >
@@ -114,7 +114,7 @@
diff --git a/view/template/acquisition/youtube_token.php b/view/template/acquisition/youtube_token.php index 78bc9287..a1da4d37 100644 --- a/view/template/acquisition/youtube_token.php +++ b/view/template/acquisition/youtube_token.php @@ -1,10 +1,8 @@ @@ -41,12 +41,12 @@
- + getMetadata() ?>
" alt=""/>
-

+

- + diff --git a/view/template/content/_job.php b/view/template/content/_job.php index 74933d2b..a4eba742 100644 --- a/view/template/content/_job.php +++ b/view/template/content/_job.php @@ -1,8 +1,11 @@ -

+

"> + + +

diff --git a/view/template/content/_jobs.php b/view/template/content/_jobs.php index ddba2568..1e88b73f 100644 --- a/view/template/content/_jobs.php +++ b/view/template/content/_jobs.php @@ -1,5 +1,5 @@
- + $job[0], 'jobHtml' => $job[1] diff --git a/view/template/content/_postAuthor.php b/view/template/content/_postAuthor.php index bc742e71..e6c1f32e 100644 --- a/view/template/content/_postAuthor.php +++ b/view/template/content/_postAuthor.php @@ -10,13 +10,14 @@ - + +
diff --git a/view/template/content/_postList.php b/view/template/content/_postList.php index f723d56c..4b96b63c 100644 --- a/view/template/content/_postList.php +++ b/view/template/content/_postList.php @@ -1,7 +1,7 @@

Latest News

diff --git a/view/template/content/_postSocialShare.php b/view/template/content/_postSocialShare.php index 07baa08a..2ab69414 100644 --- a/view/template/content/_postSocialShare.php +++ b/view/template/content/_postSocialShare.php @@ -8,13 +8,13 @@ target="_blank" title="Tweet"> - + - + - getContentText(50,true)) ?>" + getContentText(50, true)) ?>" title="Email a Friend"> diff --git a/view/template/content/credit-reports.php b/view/template/content/credit-reports.php index c21b0081..703d333f 100644 --- a/view/template/content/credit-reports.php +++ b/view/template/content/credit-reports.php @@ -15,7 +15,7 @@ - + getSlug()) ?> Report diff --git a/view/template/content/faq.php b/view/template/content/faq.php index 19fc4262..4e257b1d 100644 --- a/view/template/content/faq.php +++ b/view/template/content/faq.php @@ -18,10 +18,10 @@ $('#faq-filter-form').change(function() { $(this).submit(); }); - $posts): ?> + $posts): ?>

- + diff --git a/view/template/content/news-post.php b/view/template/content/news-post.php index 98eb6c88..e94e558a 100644 --- a/view/template/content/news-post.php +++ b/view/template/content/news-post.php @@ -12,7 +12,7 @@ getAuthorName() ?> hasAuthor() && $post->hasDate() ? '•' : '' ?> hasDate()): ?> - getDate()->format('M j') ?> + getDate()->format('M j Y') ?>
diff --git a/view/template/content/news.php b/view/template/content/news.php index 100e7b04..ff247462 100644 --- a/view/template/content/news.php +++ b/view/template/content/news.php @@ -10,7 +10,7 @@
- +

getTitle() ?>

diff --git a/view/template/content/roadmap.php b/view/template/content/roadmap.php index 873d817e..16bc87c2 100644 --- a/view/template/content/roadmap.php +++ b/view/template/content/roadmap.php @@ -26,7 +26,7 @@
- $groupItems): ?> + $groupItems): ?>

"> @@ -40,7 +40,7 @@ $maxItems): ?> - +
> @@ -49,9 +49,14 @@ -
- - + +
+ +
+ +
+ +

@@ -72,7 +77,7 @@ No description' ?>

- +
diff --git a/view/template/content/rss.php b/view/template/content/rss.php index 477ec5ef..d4a95a22 100644 --- a/view/template/content/rss.php +++ b/view/template/content/rss.php @@ -6,7 +6,7 @@ {{rss.description}} https://github.com/lbryio/lbry.io {{rss.lang}} - Sat, 07 Sep 2002 09:42:31 GMT ?> + Sat, 07 Sep 2002 09:42:31 GMT?> diff --git a/view/template/developer/_quickstartCredits.php b/view/template/developer/_quickstartCredits.php index 3127b668..42259bcb 100644 --- a/view/template/developer/_quickstartCredits.php +++ b/view/template/developer/_quickstartCredits.php @@ -62,7 +62,7 @@

Try the UI

-

LBRY comes with a fully-featured UI so that normal people can use it too. You can download it here.

+

LBRY comes with a fully-featured UI so that normal people can use it too. You can download it here.

You Did It! What's Next?

Start building something awesome! LBRY works as a discovery and distribution backend for everything from films to CAD files. diff --git a/view/template/developer/quickstart.php b/view/template/developer/quickstart.php index 5f1ace82..21c47e2e 100644 --- a/view/template/developer/quickstart.php +++ b/view/template/developer/quickstart.php @@ -11,7 +11,7 @@

Quickstart

- +
@@ -24,7 +24,7 @@
    - $stepLabel): ?> + $stepLabel): ?>
  1. diff --git a/view/template/download/_downloadButton.php b/view/template/download/_downloadButton.php new file mode 100644 index 00000000..a67f8cf9 --- /dev/null +++ b/view/template/download/_downloadButton.php @@ -0,0 +1,40 @@ + +
    + + + + var anchor = document.getElementById('get-download-btn'); + ga('send', 'event', anchor.getAttribute('data-analytics-category'), anchor.getAttribute('data-analytics-action'), anchor.getAttribute('data-analytics-label')); + setTimeout(function() { window.location = anchor.getAttribute('href'); }, 500); + + +
    + +
    + Version , + MB, + built on + at UTC +
    + +
    + Works with Ubuntu, Debian, or any distro with apt or dpkg. + For other Linux flavors including Arch and Flatpak support or compiling from source, see our GitHub install page. +
    + +
    + Like source code? Go here.

    +

    + + + + Download LBRY
    + diff --git a/view/template/download/_list.php b/view/template/download/_list.php index 8be30acb..ae656726 100644 --- a/view/template/download/_list.php +++ b/view/template/download/_list.php @@ -4,7 +4,7 @@ - +

    @@ -14,7 +14,7 @@

    - +
    ', $bucketRow) ?>
    diff --git a/view/template/download/_videoIntro.php b/view/template/download/_videoIntro.php index b17b5268..1b37cedc 100644 --- a/view/template/download/_videoIntro.php +++ b/view/template/download/_videoIntro.php @@ -1,3 +1,3 @@
    - +
    diff --git a/view/template/download/get.php b/view/template/download/get.php index 22b7c661..ba0a5948 100644 --- a/view/template/download/get.php +++ b/view/template/download/get.php @@ -2,7 +2,6 @@ false]) ?> -
    @@ -14,38 +13,11 @@

    -

    - - - - var anchor = document.getElementById('get-download-btn'); - ga('send', 'event', anchor.getAttribute('data-analytics-category'), anchor.getAttribute('data-analytics-action'), anchor.getAttribute('data-analytics-label')); - setTimeout(function() { window.location = anchor.getAttribute('href'); }, 500); - - -
    - Latest Version : - , Download Size : - MB, - built on - at - -

    -
    - - {{download.works}} - - Like source code? Go here.

    - -

    + 'alt', + 'sourceLink' => true + ])?> +

    {{download.unavailable}}

    diff --git a/view/template/email_templates/_confirmHash.php b/view/template/email_templates/_confirmHash.php index 1750b9ea..c9b45091 100644 --- a/view/template/email_templates/_confirmHash.php +++ b/view/template/email_templates/_confirmHash.php @@ -13,4 +13,4 @@ - ob_get_clean()]); \ No newline at end of file + ob_get_clean()]); diff --git a/view/template/form/_select.php b/view/template/form/_select.php index dc3193a7..b4829d17 100644 --- a/view/template/form/_select.php +++ b/view/template/form/_select.php @@ -1,5 +1,5 @@ value="" /> + + +
    + + + + +
+
+

What do you want to receive email about?

+
+
+ + $enabled): ?> + +
+
+
+ +
+
+ + value="" /> + + +
+
+ +
+ +
+ +
+ +
+ + +
+
+
+ +
+
\ No newline at end of file diff --git a/view/template/mail/settings.php b/view/template/mail/settings.php new file mode 100644 index 00000000..edd8ce7c --- /dev/null +++ b/view/template/mail/settings.php @@ -0,0 +1,24 @@ + + + false]) ?> + +
+
+
+
+

{{page.email_settings}}

+ $tags, + 'emails' => $emails, + 'error' => $error, + 'token' => $token + ]) ?> +
+
+

{{social.also}}

+ +
+
+
+
+ false]) ?> diff --git a/view/template/nav/_footer.php b/view/template/nav/_footer.php index c9e2e3a3..2cee6b2c 100644 --- a/view/template/nav/_footer.php +++ b/view/template/nav/_footer.php @@ -10,12 +10,24 @@
+ + + +
diff --git a/view/template/nav/_globalItems.php b/view/template/nav/_globalItems.php index a2c7ac80..8871cfa3 100644 --- a/view/template/nav/_globalItems.php +++ b/view/template/nav/_globalItems.php @@ -1,4 +1,4 @@ - __('nav.get'), '/learn' => __('nav.learn'), '/news' => __("News") diff --git a/view/template/nav/_learnFooter.php b/view/template/nav/_learnFooter.php index 60d4720b..55c8d6f5 100644 --- a/view/template/nav/_learnFooter.php +++ b/view/template/nav/_learnFooter.php @@ -1,4 +1,4 @@ -
+
@@ -14,27 +14,30 @@

diff --git a/view/template/page/3d-contest.php b/view/template/page/3d-contest.php new file mode 100644 index 00000000..4996241e --- /dev/null +++ b/view/template/page/3d-contest.php @@ -0,0 +1,38 @@ + + + + true, 'isAbsolute' => true]) ?> + +
+
+
+

+ 3D Chess Design Contest +

+

+

+ +
+
+
+
+

This contest has ended.

+

Download the winning designs below!

+ + + +

You can download the LBRY app here. If you have any questions or need help, join our Discord community.

+ + +

Enter your email below and we'll send you updates on new releases from the best content creators.

+ '3d-contest', + 'submitLabel' => 'Sign me up', + 'hideDisclaimer' => true, + 'largeInput' => true, + 'btnClass' => 'btn-alt btn-large', + ]) ?> +
+
diff --git a/view/template/page/3d-printing.php b/view/template/page/3d-printing.php index d35e0c32..35596011 100644 --- a/view/template/page/3d-printing.php +++ b/view/template/page/3d-printing.php @@ -1,12 +1,12 @@ - - + + true, 'isAbsolute' => true]) ?>
-
+

- 3D printing on the blockchain + Get paid to 3D print!

@@ -17,35 +17,51 @@
-

Wanna earn crypto for your 3D files?

-

Now you can buy and sell files at ANY PRICE or share your files for free & earn microtips!

-

Like Bitcoin, LBRY (pronounced library) uses blockchain technology, enabling you to - earn money without a bank or 3rd party.

-

Sounds futuristic. How do I start earning?

-

You can download the LBRY app here. If you have any questions or need help, join our Discord community.

- - -
+

Want to earn rewards for 3D printing nifty items?

+

Scroll down, pick an object to print, then post a picture tagging us on social media (Facebook, Twitter or Instagram) and we'll pay you for each print that you share.

+

How do I start getting paid?

+

Download the LBRY App here to download these files. Click here for a short video on how to get paid.

+

+
+

How do rewards work?

+

You can earn one reward per 24 hour period by posting a picture of your exlcusive LBRY print with your wallet address to social and tagging our account.

-

Paste the links into the LBRY App to download!

- Bitcoin-Accepted-Here -

Bitcoin Accepted Here Tabletop Sign

-

lbry://@3dprinting#5aab622139e1fad2518c48fbead755a9f0e3f73a/bitcoinacceptedhere

- Ethereum-Accepted-Here -

Ethereum Accepted Here Tabletop Sign

-

lbry://@3dprinting#5aab622139e1fad2518c48fbead755a9f0e3f73a/ethereumacceptedhere

- Bitcoin-Cash-Accepted-Here -

Bitcoin Cash Accepted Here Tabletop Sign

-

lbry://@3dprinting#5aab622139e1fad2518c48fbead755a9f0e3f73a/bitcoincashtray

- Monero-Accepted-Here -

Monero Accepted Here Tabletop Sign

-

lbry://@3dprinting#5aab622139e1fad2518c48fbead755a9f0e3f73a/moneroacceptedhere

- Dash-Accepted-Here -

Dash Accepted Here Tabletop Sign

-

lbry://@3dprinting#5aab622139e1fad2518c48fbead755a9f0e3f73a/dashacceptedhere

- LBRY-Credits-Accepted-Here -

LBRY Credits Accepted Here Tabletop Sign

-

lbry://@3dprinting#5aab622139e1fad2518c48fbead755a9f0e3f73a/lbrycreditsacceptedhere

- +

Open the app & click an object to download.

+ Socrates Nightlight + Aristotle Nightlight + da Vinci Buddha Nightlight + Einstein Buddha Nightlight + Tesla Nightlight + Edison Nightlight + Einstein Nightlight + Genius Chess A + Genius Chess B + Greg's Intricate Chess A + Greg's Intricate Chess B + WW2 Chess Allies + WW2 Chess Axis + Chess Board A + Chess Board B + Cryptocurrency Chess (Non-mineable) + Cryptocurrency Chess (Mineable) + Abstract Chess + Russian Chess +
+
+

We want to see your first 3D upload!

+

Send the lbry:// address and your wallet address to james@lbry.io to receive 10 Library Credits on us.

+

Looking for more exclusive content?

+

Enter your email below and we'll send you updates on new releases from the best content creators.

+
+

Email james@lbry.io if you think you've got what it takes to be a LBRY designer.

+
+ '3d-printing', + 'submitLabel' => 'Sign me up', + 'hideDisclaimer' => true, + 'largeInput' => true, + 'btnClass' => 'btn-alt btn-large', + ]) ?>
+ diff --git a/view/template/page/LGBTQ.php b/view/template/page/LGBTQ.php index f62049b7..3f3a5900 100644 --- a/view/template/page/LGBTQ.php +++ b/view/template/page/LGBTQ.php @@ -1,5 +1,5 @@ - + true, 'isAbsolute' => true]) ?>
@@ -18,17 +18,17 @@

Companies like YouTube are afraid that advertisers will be offended by LGBTQ and sexual health content.

-

We think their behavior is what's embarrassing.

+

We think their censorship is what's really offensive.

LBRY is a free, open source platform that makes it easy to share your videos (and charge what you want for access to them).

Our system is decentralized - we care about our users, not advertisers trying to make a buck off of their hard work.

This is where you come in.

We're building LBRY for everyone, and we want you to join us! As long as it's legal, we'll never tell our users what they can and can't talk about.

-

If you're willing to give cutting edge technology (that may not work 100% of the time) a try, download the LBRY app and let us know what you think!

- - -

You can download the LBRY app here. If you have any questions or need help, join our Discord community.

- - +

Do you have a favorite LGBTQ creator?

+

Send them this page and ask them to sync their content today with our YouTube Partner Program.

+ +

If you'd like to get information and updates when new LGBTQ content goes live on our network and hear what's happening in the fight for free speech online, sign up for our mailing list below.

LBRY Warrant Canary

- Through April 1st, 2018, LBRY has received: + Through June 1st, 2018, LBRY has received:

  • Zero National Security Letters
  • diff --git a/view/template/page/forklist.php b/view/template/page/forklist.php new file mode 100644 index 00000000..c914eb8f --- /dev/null +++ b/view/template/page/forklist.php @@ -0,0 +1,25 @@ + + + +
    +
    +
    +

    + Blockchain Fork Updates +

    +
    +
    +
    +
    +

    Subscribe to be notified of fork-related updates

    + 'fork', + 'submitLabel' => 'Subscribe', + 'hideDisclaimer' => true, + 'largeInput' => true, + 'btnClass' => 'btn-alt btn-large', + ]) ?> +
    +
    +
    + diff --git a/view/template/page/home.php b/view/template/page/home.php index 34a69bde..8c485d8e 100644 --- a/view/template/page/home.php +++ b/view/template/page/home.php @@ -6,7 +6,7 @@

    Content Freedom

- Picture of LBRY Browser + Picture of LBRY Browser
@@ -15,7 +15,6 @@

Hollywood films, college lessons, amazing streamers and more are on the first media network ruled by you.

+ 'primary','meta' => false,])?>
diff --git a/view/template/page/join-us.php b/view/template/page/join-us.php index 165da92a..2e064a47 100644 --- a/view/template/page/join-us.php +++ b/view/template/page/join-us.php @@ -1,5 +1,5 @@ - - + + false]) ?>
@@ -13,7 +13,7 @@ curiosity, you've come to the right place.

- LBRY Team Photo from November 2017 + LBRY Team Photo from April 2018
Fortunately, photo shoots are not a regular job activity.
@@ -110,12 +110,12 @@ - lbry-desktop
+ lbry-desktop
lbry-android
spee.ch Lead Application Engineer - good first issues + good first issues (not public) @@ -139,7 +139,7 @@

Referrals

- Know someone who'd be a great fit? Tell them about us, send them a link this page, or show up at their house unexpectedly with a + Know someone who'd be a great fit? Tell them about us, send them a link to this page, or show up at their house unexpectedly with a box of candy and a persuasive pitch. If we hire them, we'll pay you $5,000. That's what we call a win-win.

diff --git a/view/template/page/learn.php b/view/template/page/learn.php index ff623649..2df0f747 100644 --- a/view/template/page/learn.php +++ b/view/template/page/learn.php @@ -24,20 +24,20 @@
@@ -45,13 +45,18 @@
+

For Creators

+ +

For Developers

-

LBRY is 100% open source in the Bazaar tradition.

-
@@ -61,7 +66,6 @@
-
- +
diff --git a/view/template/page/newslist.php b/view/template/page/newslist.php index d960d3cc..f243cb13 100644 --- a/view/template/page/newslist.php +++ b/view/template/page/newslist.php @@ -1,8 +1,8 @@ - true, 'isAbsolute' => true]) ?> +
-
+

Join Our List for LBRY Updates! diff --git a/view/template/page/press-kit.php b/view/template/page/press-kit.php index 1d2dff03..15d5de3d 100644 --- a/view/template/page/press-kit.php +++ b/view/template/page/press-kit.php @@ -20,7 +20,7 @@

{{press.logos}}

- +
@@ -32,7 +32,7 @@

{{press.team}}

- +
@@ -59,7 +59,7 @@

{{press.advisory}}

- +
diff --git a/view/template/page/privacypolicy.php b/view/template/page/privacypolicy.php new file mode 100644 index 00000000..a66e4fbd --- /dev/null +++ b/view/template/page/privacypolicy.php @@ -0,0 +1,22 @@ + + + false]) ?> +
+
+

LBRY Inc. Privacy Policy

+
Google AdSense
Fair Information Practices
Fair information
Practices
COPPA
CalOPPA
Our Contact Information
This privacy policy has been compiled to better serve those who are concerned with how their 'Personally Identifiable Information' (PII) is being used online. PII, as described in US privacy law and information security, is information that can be used on its own or with other information to identify, contact, or locate a single person, or to identify an individual in context. Please read our privacy policy carefully to get a clear understanding of how we collect, use, protect or otherwise handle your Personally Identifiable Information in accordance with our website.

What personal information do we collect from the people that visit our blog, website or app?

When ordering or registering on our site, as appropriate, you may be asked to enter your name, email address, mailing address, phone number, credit card information or other details to help you with your experience.

When do we collect information?

We collect information from you when you register on our site, place an order, subscribe to a newsletter, respond to a survey, fill out a form, open a support ticket or enter information on our site, or provide us with feedback on our products or services.

How do we use your information?

We may use the information we collect from you when you register, make a purchase, sign up for our newsletter, respond to a survey or marketing communication, surf the website, or use certain other site features in the following ways:

      To personalize your experience and to allow us to deliver the type of content and product offerings in which you are most interested.
      To improve our website in order to better serve you.
      To allow us to better service you in responding to your customer service requests.
      To administer a contest, promotion, survey or other site feature.
      To quickly process your transactions.
      To send periodic emails regarding your order or other products and services.
      To follow up with them after correspondence (live chat, email or phone inquiries)

How do we protect your information?

Our website is scanned on a regular basis for security holes and known vulnerabilities in order to make your visit to our site as safe as possible.

We use regular Malware Scanning.

Your personal information is contained behind secured networks and is only accessible by a limited number of persons who have special access rights to such systems, and are required to keep the information confidential. In addition, all sensitive/credit information you supply is encrypted via Secure Socket Layer (SSL) technology.

We implement a variety of security measures when a user places an order enters, submits, or accesses their information to maintain the safety of your personal information.

All transactions are processed through a gateway provider and are not stored or processed on our servers.

Do we use 'cookies'?

Yes. Cookies are small files that a site or its service provider transfers to your computer's hard drive through your Web browser (if you allow) that enables the site's or service provider's systems to recognize your browser and capture and remember certain information. For instance, we use cookies to help us remember and process the items in your shopping cart. They are also used to help us understand your preferences based on previous or current site activity, which enables us to provide you with improved services. We also use cookies to help us compile aggregate data about site traffic and site interaction so that we can offer better site experiences and tools in the future.

We use cookies to:
      Help remember and process the items in the shopping cart.
      Compile aggregate data about site traffic and site interactions in order to offer better site experiences and tools in the future. We may also use trusted third-party services that track this information on our behalf.

You can choose to have your computer warn you each time a cookie is being sent, or you can choose to turn off all cookies. You do this through your browser settings. Since browser is a little different, look at your browser's Help Menu to learn the correct way to modify your cookies.

If users disable cookies in their browser:

If you turn cookies off it will turn off some of the features of the site.

Third-party disclosure

We do not sell, trade, or otherwise transfer to outside parties your Personally Identifiable Information unless we provide users with advance notice. This does not include website hosting partners and other parties who assist us in operating our website, conducting our business, or serving our users, so long as those parties agree to keep this information confidential. We may also release information when it's release is appropriate to comply with the law, enforce our site policies, or protect ours or others' rights, property or safety.

However, non-personally identifiable visitor information may be provided to other parties for marketing, advertising, or other uses.

Third-party links

Occasionally, at our discretion, we may include or offer third-party products or services on our website. These third-party sites have separate and independent privacy policies. We therefore have no responsibility or liability for the content and activities of these linked sites. Nonetheless, we seek to protect the integrity of our site and welcome any feedback about these sites.

Google

Google's advertising requirements can be summed up by Google's Advertising Principles. They are put in place to provide a positive experience for users. https://support.google.com/adwordspolicy/answer/1316548?hl=en

We use Google AdSense Advertising on our website.

Google, as a third-party vendor, uses cookies to serve ads on our site. Google's use of the DART cookie enables it to serve ads to our users based on previous visits to our site and other sites on the Internet. Users may opt-out of the use of the DART cookie by visiting the Google Ad and Content Network privacy policy.

We have implemented the following:
      Remarketing with Google AdSense
      Google Display Network Impression Reporting
      Demographics and Interests Reporting

We, along with third-party vendors such as Google use first-party cookies (such as the Google Analytics cookies) and third-party cookies (such as the DoubleClick cookie) or other third-party identifiers together to compile data regarding user interactions with ad impressions and other ad service functions as they relate to our website.

Opting out:
Users can set preferences for how Google advertises to you using the Google Ad Settings page. Alternatively, you can opt out by visiting the Network Advertising Initiative Opt Out page or by using the Google Analytics Opt Out Browser add on.

California Online Privacy Protection Act

CalOPPA is the first state law in the nation to require commercial websites and online services to post a privacy policy. The law's reach stretches well beyond California to require any person or company in the United States (and conceivably the world) that operates websites collecting Personally Identifiable Information from California consumers to post a conspicuous privacy policy on its website stating exactly the information being collected and those individuals or companies with whom it is being shared. - See more at: http://consumercal.org/california-online-privacy-protection-act-caloppa/#sthash.0FdRbT51.dpuf

According to CalOPPA, we agree to the following:
Users can visit our site anonymously.
Once this privacy policy is created, we will add a link to it on our home page or as a minimum, on the first significant page after entering our website.
Our Privacy Policy link includes the word 'Privacy' and can easily be found on the page specified above.

You will be notified of any Privacy Policy changes:
      On our Privacy Policy Page
Can change your personal information:
      By emailing us
      By logging in to your account
      By chatting with us or by sending us a support ticket

How does our site handle Do Not Track signals?
We honor Do Not Track signals and Do Not Track, plant cookies, or use advertising when a Do Not Track (DNT) browser mechanism is in place.

Does our site allow third-party behavioral tracking?
It's also important to note that we do not allow third-party behavioral tracking

COPPA (Children Online Privacy Protection Act)

When it comes to the collection of personal information from children under the age of 13 years old, the Children's Online Privacy Protection Act (COPPA) puts parents in control. The Federal Trade Commission, United States' consumer protection agency, enforces the COPPA Rule, which spells out what operators of websites and online services must do to protect children's privacy and safety online.

We do not specifically market to children under the age of 13 years old.
Do we let third-parties, including ad networks or plug-ins collect PII from children under 13?

Fair Information Practices

The Fair Information Practices Principles form the backbone of privacy law in the United States and the concepts they include have played a significant role in the development of data protection laws around the globe. Understanding the Fair Information Practice Principles and how they should be implemented is critical to comply with the various privacy laws that protect personal information.

In order to be in line with Fair Information Practices we will take the following responsive action, should a data breach occur:
We will notify you via email
      Within 7 business days
We will notify the users via in-site notification
      Within 7 business days

We also agree to the Individual Redress Principle which requires that individuals have the right to legally pursue enforceable rights against data collectors and processors who fail to adhere to the law. This principle requires not only that individuals have enforceable rights against data users, but also that individuals have recourse to courts or government agencies to investigate and/or prosecute non-compliance by data processors.

CAN SPAM Act

The CAN-SPAM Act is a law that sets the rules for commercial email, establishes requirements for commercial messages, gives recipients the right to have emails stopped from being sent to them, and spells out tough penalties for violations.

We collect your email address in order to:
      Send information, respond to inquiries, and/or other requests or questions
      Process orders and to send information and updates pertaining to orders.
      Send you additional information related to your product and/or service
      Market to our mailing list or continue to send emails to our clients after the original transaction has occurred.

To be in accordance with CANSPAM, we agree to the following:
      Not use false or misleading subjects or email addresses.
      Identify the message as an advertisement in some reasonable way.
      Include the physical address of our business or site headquarters.
      Monitor third-party email marketing services for compliance, if one is used.
      Honor opt-out/unsubscribe requests quickly.
      Allow users to unsubscribe by using the link at the bottom of each email.

If at any time you would like to unsubscribe from receiving future emails, you can email us at
      Follow the instructions at the bottom of each email.
and we will promptly remove you from ALL correspondence.


Contacting Us

If there are any questions regarding this privacy policy, you may contact us using the information below.

LBRY Inc.
834 Elm St
Manchester, New Hampshire 03101
United States
hello@lbry.io

Last Edited on 2018-04-28
+
+
+ diff --git a/view/template/page/reefermadness.php b/view/template/page/reefermadness.php new file mode 100644 index 00000000..d54fedcc --- /dev/null +++ b/view/template/page/reefermadness.php @@ -0,0 +1,34 @@ + + + + true, 'isAbsolute' => true]) ?> +
+
+
+

+ YouTube Has Reefer Madness +

+

+ YouTube is erasing thousands of cannabis-related videos. Have you had enough? +

+
+ +
+
+
+

YouTube is purging drug-related content.

+

Over the past few months, YouTube has been quietly deleting hundreds of cannabis-related channels. Their vague content rules demand that drug use be “contextualized” (a term without any real definition), which means they get to pick and choose who’s allowed to talk about drugs.

+

It’s no surprise that the channels that are disappearing aren’t exactly household names - a psychonaut with a small audience or budding cannabis chef won’t get the benefit of the doubt that Doug Benson or Vice will.

+

As long as YouTube depends on revenue from advertisers, you can expect to see more and more of these periodic spasms of censorship.

+

We can do better.

+

At LBRY, we don’t want to pick winners and losers. We don’t rely on advertisers for revenue. Instead, we make it possible for creators to be compensated directly by their fans.

+

Ready to get off the YouTube merry go round? Use the link below to join our YouTube Partner Program and mirror your videos to LBRY.

+
+ +
+
+ diff --git a/view/template/page/rubin.php b/view/template/page/rubin.php index bb6c0cca..0d967419 100644 --- a/view/template/page/rubin.php +++ b/view/template/page/rubin.php @@ -11,9 +11,13 @@

Brought to you by LBRY -

+
- Try the App + "Try the App", + 'buttonStyle' => 'primary', + 'meta' => false, + ])?>
Desktop only. Mobile coming soon. @@ -29,7 +33,11 @@

Open-source and decentralized, LBRY is shaped entirely by the creators and community who use it. Free speech and censorship-resistance are baked into the design.

There are lots of nifty aspects to how LBRY works (pronounced, "library").
Learn from the veteran LBRYians on our Discord.

- Download LBRY + "Download LBRY", + 'buttonStyle' => 'alt', + 'meta' => false + ])?>
diff --git a/view/template/page/sexed.php b/view/template/page/sexed.php new file mode 100644 index 00000000..d0250f63 --- /dev/null +++ b/view/template/page/sexed.php @@ -0,0 +1,34 @@ + + + + true, 'isAbsolute' => true]) ?> +
+
+
+

+ YouTube is Afraid To Talk About Sex +

+

+ They've erased thousands of sexual education and sexual wellness videos. Have you had enough? +

+
+ +
+
+
+

YouTube is purging sex ed and sexuality-related content.

+

Over the past few months, sex ed, LGBTQ, and other sexuality-related channels have been disappearing from YouTube. YouTube's vague content rules mean that they get to pick and choose who’s allowed to talk about sex.

+

It’s no surprise that the channels that are disappearing aren’t typically household names - a sex ed guru with a small audience won’t get the benefit of the doubt that Dr. Laura or other well-known figures will.

+

As long as YouTube depends on revenue from advertisers, you can expect to see more and more of these periodic spasms of censorship.

+

We can do better.

+

At LBRY, we don’t want to pick winners and losers. We don’t rely on advertisers for revenue. Instead, we make it possible for creators to be compensated directly by their fans.

+

Ready to get off the YouTube merry go round? Use the link below to join our YouTube Partner Program and mirror your videos to LBRY.

+
+ +
+
+ diff --git a/view/template/page/shirt-contest.php b/view/template/page/shirt-contest.php new file mode 100644 index 00000000..2e1690df --- /dev/null +++ b/view/template/page/shirt-contest.php @@ -0,0 +1,35 @@ + + + + true, 'isAbsolute' => true]) ?> + +
+
+
+

+ Calling all design champions! +

+

+

+ +
+
+

This contest has ended.

+

Purchase the winning designs in our shop! (New designs coming soon)

+
+
+ +
+

Want to keep updated on future contests?

+

Enter your email below and we'll send you updates on new contests.

+ 'shirt-contest', + 'submitLabel' => 'Sign me up', + 'hideDisclaimer' => true, + 'largeInput' => true, + 'btnClass' => 'btn-alt btn-large', + ]) ?> +
+
diff --git a/view/template/page/speech-admin.php b/view/template/page/speech-admin.php index 400e8f2a..fd6258ce 100644 --- a/view/template/page/speech-admin.php +++ b/view/template/page/speech-admin.php @@ -16,14 +16,15 @@
-
+

You'd like us to stop being vaguely provocative and just explain?

Fine.

spee.ch is a free, open-source web portal for LBRY content. It both publishes content to the LBRY network and serves data from the LBRY network, but it does it over the web for improved usability.

Recently, spee.ch has been re-engineered to support self-hosting and custom skinning, as well as hosting only a portion of the network. You could run a spee.ch clone that's dedicated specifically to your favorite cat and has the appearance to show it.

This is where you come in.

This functionality is brand new and we're offering LBC bounties to those who can help us test and refine it. If you're capable of installing Wordpress, you're probably capable of installing spee.ch

-

So if you want to play around with a funky new technology, contribute to content freedom, and earn weird internet tokens, join us for an introductory session! We'll explain more about the program and walk you through the basics of how spee.ch works and how to set it up.

+

So if you want to play around with a funky new technology, contribute to content freedom, and earn weird internet tokens, you'll find everything you need to get started here.

+

Want to stay up to date on the latest news about Spee.ch and upcoming multisite training sessions? Join our mailing list!

'speech-admin', 'submitLabel' => 'Sign Me Up', diff --git a/view/template/page/team.php b/view/template/page/team.php index 977360f8..cadc0d5b 100644 --- a/view/template/page/team.php +++ b/view/template/page/team.php @@ -3,30 +3,38 @@ false]) ?>
-
+

{{page.team.header}}

-

Team work makes the dream work.

+

Teamwork makes the dream work.

+
+ Want to join this great group? + See our hiring page. +

Leadership

- +
$bioSlug]) ?>

Technical

- +
$bioSlug]) ?>

Business

+<<<<<<< HEAD >>>>>> master 'tom-zarebczan', 'brinck-slattery', 'rob-smith', @@ -36,11 +44,15 @@

{{page.team.advisory}}

- +
$bioSlug]) ?>
+

Join Us!

+

+ Our hiring page contains information about working at LBRY. +

diff --git a/view/template/page/termsofservice.php b/view/template/page/termsofservice.php index 210edd91..5e03a655 100644 --- a/view/template/page/termsofservice.php +++ b/view/template/page/termsofservice.php @@ -5,7 +5,7 @@

Terms of Service

-

This page is a stub. A full acknowledgement is coming before public launch.

+

This page is a stub. A full acknowledgement is coming soon.

In the interim, by publishing to LBRY, you affirm and acknowledge that:

  • You have the right to publish what you are publishing.
  • diff --git a/view/template/page/what.php b/view/template/page/what.php index 37576e5f..0a06c25f 100644 --- a/view/template/page/what.php +++ b/view/template/page/what.php @@ -35,9 +35,9 @@

    At the highest level, LBRY does something extraordinarily simple. LBRY creates an association between a unique name and a piece of digital content, such as a movie, book, or game. This is similar to the domain name system that you are most likely using to access this very post.

    - +
    - A user searches and prepares to stream and the film It’s a Wonderful Life, located at lbry://wonderfullife, via a completely decentralized network. Try it out for yourself at lbry.io/get. + A user searches for and prepares to stream the film It’s a Wonderful Life, located at lbry://wonderfullife, via a completely decentralized network. Try it out for yourself at lbry.io/get.
    @@ -225,7 +225,7 @@

    If LBRY succeeds, we will enter a world that is even more creative, connected, and conservatory. We will waste less and we make more. We will create a world where a teenager in Kenya and a reality star in Los Angeles use the same tool to search the same network and have access to the same results -- a world where information, knowledge, and imagination know no borders.

    -

    Build our dream with us! Download LBRY at lbry.io/get.

    +

    Build our dream with us! Download LBRY.

diff --git a/view/template/report/dmca.php b/view/template/report/dmca.php index a90bea20..816801f0 100644 --- a/view/template/report/dmca.php +++ b/view/template/report/dmca.php @@ -7,8 +7,7 @@

{{dmca.header}}

-
- +
@@ -50,7 +49,9 @@
- +
+ To learn more about reporting infringing or illegal content and DMCA producedures, please see our DMCA article.. +
diff --git a/view/template/social/_list.php b/view/template/social/_list.php index 52425ff5..0ee7d285 100644 --- a/view/template/social/_list.php +++ b/view/template/social/_list.php @@ -10,3 +10,10 @@ + + + diff --git a/view/template/social/_listDev.php b/view/template/social/_listDev.php index fd933b32..e1b81f32 100644 --- a/view/template/social/_listDev.php +++ b/view/template/social/_listDev.php @@ -15,6 +15,9 @@ + diff --git a/web/img/3d-einstein.jpg b/web/img/3d-einstein.jpg deleted file mode 100755 index d82ae45b..00000000 Binary files a/web/img/3d-einstein.jpg and /dev/null differ diff --git a/web/img/Gold-Crypto-Chess2.jpeg b/web/img/Gold-Crypto-Chess2.jpeg new file mode 100644 index 00000000..a3893320 Binary files /dev/null and b/web/img/Gold-Crypto-Chess2.jpeg differ diff --git a/web/img/JamesBillerLBRY.jpg b/web/img/JamesBillerLBRY.jpg new file mode 100644 index 00000000..5cb3785f Binary files /dev/null and b/web/img/JamesBillerLBRY.jpg differ diff --git a/web/img/ProtsNotPlats.jpg b/web/img/ProtsNotPlats.jpg new file mode 100644 index 00000000..3145d1eb Binary files /dev/null and b/web/img/ProtsNotPlats.jpg differ diff --git a/web/img/blog-covers/ASICS.jpg b/web/img/blog-covers/ASICS.jpg new file mode 100644 index 00000000..d7c0c1c9 Binary files /dev/null and b/web/img/blog-covers/ASICS.jpg differ diff --git a/web/img/blog-covers/Growing-Blockchain.png b/web/img/blog-covers/Growing-Blockchain.png new file mode 100644 index 00000000..67a4ab78 Binary files /dev/null and b/web/img/blog-covers/Growing-Blockchain.png differ diff --git a/web/img/blog-covers/Is Every Thing Better-01.png b/web/img/blog-covers/Is Every Thing Better-01.png new file mode 100644 index 00000000..9c826d7c Binary files /dev/null and b/web/img/blog-covers/Is Every Thing Better-01.png differ diff --git a/web/img/blog-covers/ProtsNotPlats.jpg b/web/img/blog-covers/ProtsNotPlats.jpg index 497cfe41..3145d1eb 100644 Binary files a/web/img/blog-covers/ProtsNotPlats.jpg and b/web/img/blog-covers/ProtsNotPlats.jpg differ diff --git a/web/img/blog-covers/amit-niko.jpg b/web/img/blog-covers/amit-niko.jpg new file mode 100644 index 00000000..fb306eeb Binary files /dev/null and b/web/img/blog-covers/amit-niko.jpg differ diff --git a/web/img/blog-covers/anne-frank-diary.jpg b/web/img/blog-covers/anne-frank-diary.jpg deleted file mode 100644 index 3fa8d75f..00000000 Binary files a/web/img/blog-covers/anne-frank-diary.jpg and /dev/null differ diff --git a/web/img/blog-covers/austen-min.jpg b/web/img/blog-covers/austen-min.jpg new file mode 100644 index 00000000..4262dd7e Binary files /dev/null and b/web/img/blog-covers/austen-min.jpg differ diff --git a/web/img/blog-covers/coder-tank.jpg b/web/img/blog-covers/coder-tank.jpg new file mode 100644 index 00000000..237d516f Binary files /dev/null and b/web/img/blog-covers/coder-tank.jpg differ diff --git a/web/img/blog-covers/crypt-zero-banner.PNG b/web/img/blog-covers/crypt-zero-banner.PNG deleted file mode 100644 index e5204d11..00000000 Binary files a/web/img/blog-covers/crypt-zero-banner.PNG and /dev/null differ diff --git a/web/img/blog-covers/database2.jpg b/web/img/blog-covers/database2.jpg new file mode 100644 index 00000000..49dc659d Binary files /dev/null and b/web/img/blog-covers/database2.jpg differ diff --git a/web/img/blog-covers/fireworks.jpg b/web/img/blog-covers/fireworks.jpg new file mode 100644 index 00000000..8233f8c9 Binary files /dev/null and b/web/img/blog-covers/fireworks.jpg differ diff --git a/web/img/blog-covers/gradient.png b/web/img/blog-covers/gradient.png new file mode 100644 index 00000000..cb221c82 Binary files /dev/null and b/web/img/blog-covers/gradient.png differ diff --git a/web/img/blog-covers/growgreens-banner.PNG b/web/img/blog-covers/growgreens-banner.PNG deleted file mode 100644 index 7bcd4c9c..00000000 Binary files a/web/img/blog-covers/growgreens-banner.PNG and /dev/null differ diff --git a/web/img/blog-covers/heckbendercover.png b/web/img/blog-covers/heckbendercover.png deleted file mode 100644 index a4cfd673..00000000 Binary files a/web/img/blog-covers/heckbendercover.png and /dev/null differ diff --git a/web/img/blog-covers/hirebossheader.jpg b/web/img/blog-covers/hirebossheader.jpg new file mode 100644 index 00000000..d11b0e1f Binary files /dev/null and b/web/img/blog-covers/hirebossheader.jpg differ diff --git a/web/img/blog-covers/jay-banner.png b/web/img/blog-covers/jay-banner.png deleted file mode 100644 index cd8ee1d7..00000000 Binary files a/web/img/blog-covers/jay-banner.png and /dev/null differ diff --git a/web/img/blog-covers/jingle-banner.PNG b/web/img/blog-covers/jingle-banner.PNG deleted file mode 100644 index bc7f89e1..00000000 Binary files a/web/img/blog-covers/jingle-banner.PNG and /dev/null differ diff --git a/web/img/blog-covers/juicemedia-banner.PNG b/web/img/blog-covers/juicemedia-banner.PNG deleted file mode 100644 index 978682d3..00000000 Binary files a/web/img/blog-covers/juicemedia-banner.PNG and /dev/null differ diff --git a/web/img/blog-covers/lbryopoly-min.jpg b/web/img/blog-covers/lbryopoly-min.jpg new file mode 100644 index 00000000..8a30bc0a Binary files /dev/null and b/web/img/blog-covers/lbryopoly-min.jpg differ diff --git a/web/img/blog-covers/lex-amit.jpg b/web/img/blog-covers/lex-amit.jpg new file mode 100644 index 00000000..5f5c9273 Binary files /dev/null and b/web/img/blog-covers/lex-amit.jpg differ diff --git a/web/img/blog-covers/nono-banner.png b/web/img/blog-covers/nono-banner.png deleted file mode 100644 index f5af3153..00000000 Binary files a/web/img/blog-covers/nono-banner.png and /dev/null differ diff --git a/web/img/blog-covers/nude.png b/web/img/blog-covers/nude.png deleted file mode 100644 index 55c2dcc7..00000000 Binary files a/web/img/blog-covers/nude.png and /dev/null differ diff --git a/web/img/blog-covers/redesign.png b/web/img/blog-covers/redesign.png new file mode 100644 index 00000000..2a7b4a5d Binary files /dev/null and b/web/img/blog-covers/redesign.png differ diff --git a/web/img/blog-covers/rubin-banner.png b/web/img/blog-covers/rubin-banner.png deleted file mode 100644 index 66b238a2..00000000 Binary files a/web/img/blog-covers/rubin-banner.png and /dev/null differ diff --git a/web/img/blog-covers/skate-banner.png b/web/img/blog-covers/skate-banner.png deleted file mode 100644 index de5f8c93..00000000 Binary files a/web/img/blog-covers/skate-banner.png and /dev/null differ diff --git a/web/img/blog-covers/srod-banner.png b/web/img/blog-covers/srod-banner.png deleted file mode 100644 index f76072a3..00000000 Binary files a/web/img/blog-covers/srod-banner.png and /dev/null differ diff --git a/web/img/cover-home-full.jpg b/web/img/cover-home-full.jpg deleted file mode 100644 index 6ac04c73..00000000 Binary files a/web/img/cover-home-full.jpg and /dev/null differ diff --git a/web/img/cover-home4.jpg b/web/img/cover-home4.jpg deleted file mode 100644 index a705d723..00000000 Binary files a/web/img/cover-home4.jpg and /dev/null differ diff --git a/web/img/cover-swartz.jpg b/web/img/cover-swartz.jpg deleted file mode 100644 index a1c13768..00000000 Binary files a/web/img/cover-swartz.jpg and /dev/null differ diff --git a/web/img/darkvenus.jpg b/web/img/darkvenus.jpg new file mode 100644 index 00000000..bc83928f Binary files /dev/null and b/web/img/darkvenus.jpg differ diff --git a/web/img/database2.jpg b/web/img/database2.jpg new file mode 100644 index 00000000..49dc659d Binary files /dev/null and b/web/img/database2.jpg differ diff --git a/web/img/dave-phil-lbryio.png b/web/img/dave-phil-lbryio.png deleted file mode 100644 index 04f19e62..00000000 Binary files a/web/img/dave-phil-lbryio.png and /dev/null differ diff --git a/web/img/dave-phillyd-background.png b/web/img/dave-phillyd-background.png deleted file mode 100644 index bca01e37..00000000 Binary files a/web/img/dave-phillyd-background.png and /dev/null differ diff --git a/web/img/dave-rucka-wide.png b/web/img/dave-rucka-wide.png deleted file mode 100644 index 66b238a2..00000000 Binary files a/web/img/dave-rucka-wide.png and /dev/null differ diff --git a/web/img/dave-rucka.png b/web/img/dave-rucka.png deleted file mode 100644 index e41593e1..00000000 Binary files a/web/img/dave-rucka.png and /dev/null differ diff --git a/web/img/fireworks.jpg b/web/img/fireworks.jpg deleted file mode 100644 index 7a601aa3..00000000 Binary files a/web/img/fireworks.jpg and /dev/null differ diff --git a/web/img/fireworks.png b/web/img/fireworks.png deleted file mode 100644 index 9ff2e4d9..00000000 Binary files a/web/img/fireworks.png and /dev/null differ diff --git a/web/img/greens_mini.jpg b/web/img/greens_mini.jpg new file mode 100644 index 00000000..e69a668f Binary files /dev/null and b/web/img/greens_mini.jpg differ diff --git a/web/img/lbry-partner.png b/web/img/lbry-partner.png new file mode 100644 index 00000000..cf4adefa Binary files /dev/null and b/web/img/lbry-partner.png differ diff --git a/web/img/lbry-ui.png b/web/img/lbry-ui.png index dd145165..d5a31060 100644 Binary files a/web/img/lbry-ui.png and b/web/img/lbry-ui.png differ diff --git a/web/img/lbrybanner-min.jpg b/web/img/lbrybanner-min.jpg new file mode 100644 index 00000000..be37ff6c Binary files /dev/null and b/web/img/lbrybanner-min.jpg differ diff --git a/web/img/lbryopoly-min.jpg b/web/img/lbryopoly-min.jpg new file mode 100644 index 00000000..8a30bc0a Binary files /dev/null and b/web/img/lbryopoly-min.jpg differ diff --git a/web/img/monero_hash.png b/web/img/monero_hash.png new file mode 100644 index 00000000..82a6db2b Binary files /dev/null and b/web/img/monero_hash.png differ diff --git a/web/img/monero_trans.png b/web/img/monero_trans.png new file mode 100644 index 00000000..37b5c242 Binary files /dev/null and b/web/img/monero_trans.png differ diff --git a/web/img/news/.dave-phillyd-background.png.icloud b/web/img/news/.dave-phillyd-background.png.icloud new file mode 100644 index 00000000..6d3066d5 Binary files /dev/null and b/web/img/news/.dave-phillyd-background.png.icloud differ diff --git a/web/img/news/.montage-inline.png.icloud b/web/img/news/.montage-inline.png.icloud new file mode 100644 index 00000000..b7ca9d02 Binary files /dev/null and b/web/img/news/.montage-inline.png.icloud differ diff --git a/web/img/news/.ohgeegeo-inline1.png.icloud b/web/img/news/.ohgeegeo-inline1.png.icloud new file mode 100644 index 00000000..bf186975 Binary files /dev/null and b/web/img/news/.ohgeegeo-inline1.png.icloud differ diff --git a/web/img/news/dave-phillyd-background.png b/web/img/news/dave-phillyd-background.png deleted file mode 100644 index c20608f5..00000000 Binary files a/web/img/news/dave-phillyd-background.png and /dev/null differ diff --git a/web/img/news/montage-inline.png b/web/img/news/montage-inline.png deleted file mode 100644 index 806e7b46..00000000 Binary files a/web/img/news/montage-inline.png and /dev/null differ diff --git a/web/img/news/ohgeegeo-inline1.png b/web/img/news/ohgeegeo-inline1.png deleted file mode 100644 index 02d8ec23..00000000 Binary files a/web/img/news/ohgeegeo-inline1.png and /dev/null differ diff --git a/web/img/noun_Blockchain_1500174_000000.svg b/web/img/noun_Blockchain_1500174_000000.svg new file mode 100644 index 00000000..fc13f761 --- /dev/null +++ b/web/img/noun_Blockchain_1500174_000000.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/web/img/sexed2.jpg b/web/img/sexed2.jpg new file mode 100644 index 00000000..60270f24 Binary files /dev/null and b/web/img/sexed2.jpg differ diff --git a/web/img/venus.jpg b/web/img/venus.jpg new file mode 100644 index 00000000..a4a206b5 Binary files /dev/null and b/web/img/venus.jpg differ diff --git a/web/index.php b/web/index.php index 118d9395..4f06d8a7 100644 --- a/web/index.php +++ b/web/index.php @@ -1,9 +1,8 @@ '.Debug::exceptionToString($e).''; + http_response_code(500); + echo '
'.Debug::exceptionToString($e).'
'; } diff --git a/web/js/emailSettings.js b/web/js/emailSettings.js new file mode 100644 index 00000000..65fb4303 --- /dev/null +++ b/web/js/emailSettings.js @@ -0,0 +1,103 @@ +lbry.emailSettingsForm = function (formSelector, tags, userAuthToken) { + var + form = $(formSelector), + emailSection = form.find('.email-section'), + tagSection = form.find('.tag-section'), + hasError = false, + isEmailSubmitPending = false, + tagMap = new Map(), + isTagSubmitPending = false; + + $.each(tags, function(tag, enabled){ + tagMap[tag] = enabled; + }); + + form.submit(function(e) { + + e.preventDefault(); + + form.find('.notice').hide(); + hasError = false; + isEmailSubmitPending = true; + isTagSubmitPending = true; + + var url = 'https://api.lbry.io/user/email/edit?auth_token=' + userAuthToken + $.param($.map(emailSection.find("input"), function(element) { + url = url + "&email="+element.value+"&enabled="+element.checked.toString(); + fetch(url).then(function(value) { return value.json()}).then(jsonResponse => { + isEmailSubmitPending = false; + if (!jsonResponse.success){ + hasError = true + emailSection.find('.notice-error').html(jsonResponse.error).show(); + } + showSuccess(); + }).catch(function(value) { + isEmailSubmitPending = false; + hasError = true; + var error = "get actual error message from value"; + emailSection.find('.notice-error').html(error).show(); + }); + })); + + //do tag edit + var url = 'https://api.lbry.io/user/tag/edit?auth_token=' + userAuthToken + var addTags = new Array(), + removeTags = new Array(); + + tagSection.find('input').each(function () { + var tagName = this.value + var enabled = this.checked + if (enabled && !tagMap[tagName] ){ + addTags.push(tagName) + } else if (!enabled && tagMap[tagName]){ + removeTags.push(tagName) + } + }); + + var hasChanges = addTags[0] || removeTags[0] + var addTagsParam = addTags[0] + for (var i = 1; i < addTags.length; i++) { + hasChanges = true + addTagsParam = addTagsParam+","+addTags[i]; + } + var removeTagsParam = removeTags[0] + for (var i = 1; i < removeTags.length; i++) { + hasChanges = true + removeTagsParam = removeTagsParam +","+removeTags[i]; + } + if (addTagsParam && addTagsParam.length > 0){ + url = url + "&add="+addTagsParam + } + if ( removeTagsParam && removeTagsParam.length > 0){ + url = url + "&remove="+removeTagsParam + } + + if (hasChanges){ + fetch(url).then(response => { return response.json() }).then(jsonResponse =>{ + isTagSubmitPending = false; + if (jsonResponse.success){ + showSuccess(); + }else { + hasError = true; + tagSection.find('.notice-error').html(jsonResponse.error).show(); + } + }).catch(function(value) { + isTagSubmitPending = false; + hasError = true; + tagSection.find('.notice-error').html(value.error).show(); + }); + } else{ + isTagSubmitPending = false; + } + }); + + form.show(); + + function showSuccess() { + if (!isEmailSubmitPending && !isTagSubmitPending && !hasError) + { + form.find('.notice-success').show().get(0).scrollIntoView(); + } + } +} + diff --git a/web/js/yt2/FormValidation.js b/web/js/yt2/FormValidation.js index b9efb00b..1f85c939 100644 --- a/web/js/yt2/FormValidation.js +++ b/web/js/yt2/FormValidation.js @@ -63,7 +63,7 @@ function validateEmail(email) { } function validateLBRYName(lbry_channel_name){ - var re = /^[@A-Za-z0-9_-]*$/g; + var re = /^[@A-Za-z0-9-]*$/g; return re.test(lbry_channel_name); } @@ -75,4 +75,4 @@ function validateYoutubeChannelUrl(youtube_channel_url){ function validateEmailIsNotGooglePlus(email){ var re = /^[A-Za-z0-9._%+-]+@(?!plusgoogle.com)[A-Za-z0-9.-]+\.[A-Za-z]{2,4}$/; return re.test(email); -} \ No newline at end of file +} diff --git a/web/scss/_basic.scss b/web/scss/_basic.scss index 869d8c9f..cc41767b 100644 --- a/web/scss/_basic.scss +++ b/web/scss/_basic.scss @@ -87,7 +87,7 @@ section .spacer2 { margin-bottom: $spacing-vertical * 2; } .text-center { text-align: center; } .text-right { text-align: right; } -.meta { font-size: 0.8em; } +.meta { font-size: $font-size-meta; } .clear { clear: both; } .align-left { float: left; } .align-right { float: right; } @@ -170,6 +170,7 @@ a:hover img .btn-full-width { width: 100%; + height: 100%; } .no-label .btn-label { @@ -236,4 +237,4 @@ video { width: 100%; max-width: 800px; height: auto; -} \ No newline at end of file +} diff --git a/web/scss/_blog.scss b/web/scss/_blog.scss index ae3b0623..854b9669 100644 --- a/web/scss/_blog.scss +++ b/web/scss/_blog.scss @@ -118,6 +118,17 @@ line-height: 1.1; color: $color-meta-light; } + img + em /*img + em = caption*/ + { + display: block; + text-align: center; + color: $color-meta-light; + font-size: $font-size-meta; + margin-top: $spacing-vertical / 4; + margin-left: $spacing-vertical / 2; + margin-right: $spacing-vertical / 2; + margin-bottom: $spacing-vertical; + } } .post-content table, table.post-content-table { diff --git a/web/scss/_global.scss b/web/scss/_global.scss index 704a7c4b..40ed9027 100644 --- a/web/scss/_global.scss +++ b/web/scss/_global.scss @@ -33,6 +33,7 @@ $font-size-h1: 2.4em; $font-size-h2: 2.0em; $font-size-h3: 1.75em; $font-size-h4: 1.4em; +$font-size-meta: 0.8em; $content-side-padding: 15px; diff --git a/web/scss/_mail_settings.scss b/web/scss/_mail_settings.scss new file mode 100644 index 00000000..e69de29b diff --git a/web/scss/_quickstart.scss b/web/scss/_quickstart.scss index d1214e47..4c5bad69 100644 --- a/web/scss/_quickstart.scss +++ b/web/scss/_quickstart.scss @@ -5,14 +5,6 @@ max-width: 860px; } -.quickstart .content-dark -{ - ul, ol, li - { - list-style-position: outside !important; /*hack fix for CSS bug affecting headers inside LI tags, ask Grin*/ - } -} - .quickstart__table { margin: 0 auto $spacing-vertical; diff --git a/web/scss/_reset.scss b/web/scss/_reset.scss index 189bbb3d..4a1abf91 100644 --- a/web/scss/_reset.scss +++ b/web/scss/_reset.scss @@ -22,8 +22,8 @@ h1, h2, h3, h4, h5, h6 } ol, ul { - list-style-position: inside; - > li { list-style-position: inside; } + list-style-position: outside; + > li { list-style-position: outside; } } input, textarea, select { diff --git a/web/scss/_slider.scss b/web/scss/_slider.scss new file mode 100644 index 00000000..6518f507 --- /dev/null +++ b/web/scss/_slider.scss @@ -0,0 +1,71 @@ +$checked-color: #006148; +$unchecked-color: #888; + +.slider-checkbox { + position: relative; + input { + margin: 0px; + margin-top: 1px; + cursor: pointer; + opacity: 0; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; + filter: alpha(opacity=0); + -moz-opacity: 0; + -khtml-opacity: 0; + opacity: 0; + position: absolute; + z-index: 1; + top: 0px; + left: 0px; + background: red; + width: 40px; + height: 20px; + &:checked + .label { + &:before { + background-color: $checked-color; + content: "\f00c"; + padding-left: 6px; + } + &:after { + left: 21px; + } + } + } + .label { + position: relative; + padding-left: 46px; + &:before, &:after { + position: absolute; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + border-radius: 0px; + transition: background-color 0.3s, left 0.3s; + } + &:before { + content: "\f00d"; + color: #fff; + box-sizing: border-box; + font-family: 'FontAwesome', sans-serif; + padding-left: 23px; + font-size: 12px; + line-height: 20px; + background-color: $unchecked-color; + left: 0px; + top: 0px; + height: 20px; + width: 40px; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + border-radius: 0px; + } + &:after { + content: ""; + letter-spacing: 20px; + background: #fff; + left: 1px; + top: 1px; + height: 18px; + width: 18px; + } + } +} \ No newline at end of file diff --git a/web/scss/all.scss b/web/scss/all.scss index 838dc4b3..35433ed2 100644 --- a/web/scss/all.scss +++ b/web/scss/all.scss @@ -17,4 +17,6 @@ @import "roadmap"; @import "quickstart"; @import "social"; -@import "home"; \ No newline at end of file +@import "home"; +@import "mail_settings"; +@import "slider"; \ No newline at end of file