Cut staging from master #810
12 changed files with 77 additions and 47 deletions
50
README.md
50
README.md
|
@ -1,19 +1,21 @@
|
||||||
# Spee.ch
|
# Spee.ch
|
||||||
Spee.ch is a [NodeJS](https://nodejs.org) React web app that reads and publishes images and videos to and from the [LBRY](https://lbry.io/) blockchain.
|
|
||||||
|
|
||||||
You can create your own custom version of spee.ch by installing this code base and then creating your own custom components and styles to override the defaults. (More details/guide on how to do that coming soon.)
|
spee.ch provides a user-friendly, custom-designed, image and video hosting site backed by a decentralized network and
|
||||||
|
blockchain ([LBRY](https://lbry.tech/)). Via just a small set of config files, you can spin your an entire spee.ch site back up including assets.
|
||||||
|
|
||||||
Spee.ch depends on two other lbry technologies:
|
|
||||||
* [chainquery](https://github.com/lbryio/chainquery) - a normalized database of the blockchain data. We've provided credentials to use a public chainquery service. You can also install it on your own server to avoid being affected by the commons.
|
|
||||||
* [lbrynet](https://github.com/lbryio/lbry) - a daemon that handles your wallet and transactions.
|
|
||||||
![App GIF](https://spee.ch/e/speechgif.gif)
|
![App GIF](https://spee.ch/e/speechgif.gif)
|
||||||
|
|
||||||
## Install
|
For a completely open, unrestricted example of a spee.ch site, check out https://www.spee.ch.
|
||||||
|
|
||||||
### Ubuntu Step by Step
|
For a closed, custom-hosted and branded example, check out https://lbry.theantimedia.com/.
|
||||||
[Ubuntu Install Guide](./docs/ubuntuinstall.md)
|
|
||||||
|
|
||||||
### Quickstart Overview
|
## Installation
|
||||||
|
|
||||||
|
### Ubuntu Step-by-Step
|
||||||
|
|
||||||
|
[Step-by-step Ubuntu Install Guide](./docs/ubuntuinstall.md)
|
||||||
|
|
||||||
|
### Full Instructions
|
||||||
|
|
||||||
#### Get some information ready:
|
#### Get some information ready:
|
||||||
* mysqlusername
|
* mysqlusername
|
||||||
|
@ -21,7 +23,7 @@ Spee.ch depends on two other lbry technologies:
|
||||||
* domainname or 'http://localhost'
|
* domainname or 'http://localhost'
|
||||||
* speechport = 3000
|
* speechport = 3000
|
||||||
|
|
||||||
#### Install and Set Up System Dependencies:
|
#### Install and Set Up Dependencies
|
||||||
* Firewall open ports
|
* Firewall open ports
|
||||||
* 22
|
* 22
|
||||||
* 80
|
* 80
|
||||||
|
@ -49,7 +51,7 @@ Spee.ch depends on two other lbry technologies:
|
||||||
* _note: even running on http://localhost, you must redirect http or https to port 3000_
|
* _note: even running on http://localhost, you must redirect http or https to port 3000_
|
||||||
|
|
||||||
|
|
||||||
#### Clone a spee.ch repo (choose one)
|
#### Clone spee.ch
|
||||||
* release version for stable production
|
* release version for stable production
|
||||||
```
|
```
|
||||||
$ git clone -b release https://github.com/lbryio/spee.ch.git
|
$ git clone -b release https://github.com/lbryio/spee.ch.git
|
||||||
|
@ -95,8 +97,8 @@ Check out the [customization guide](https://github.com/lbryio/spee.ch/blob/readm
|
||||||
|
|
||||||
#### (optional) add custom components and update the styles
|
#### (optional) add custom components and update the styles
|
||||||
|
|
||||||
* Create custom components by creating React components in `site/custom/src/` (further instructions coming soon)
|
* Create custom components by creating React components in `site/custom/src/`
|
||||||
* Update the CSS by changing the files in `site/custom/scss` (further instructions and refactor coming soon)
|
* Update or override the CSS by changing the files in `site/custom/scss`
|
||||||
|
|
||||||
#### (optional) install your own chainquery
|
#### (optional) install your own chainquery
|
||||||
Instructions are coming at [lbry-docker] to install your own chainquery instance using docker-compose. This will require 50GB of preferably SSD space and at least 10 minutes to download, possibly much longer.
|
Instructions are coming at [lbry-docker] to install your own chainquery instance using docker-compose. This will require 50GB of preferably SSD space and at least 10 minutes to download, possibly much longer.
|
||||||
|
@ -222,7 +224,8 @@ Spee.ch also runs a sync tool, which decodes blocks from the `LBRY` blockchain a
|
||||||
* To run only tests that do not require LBC, run `npm run test:no-lbc`
|
* To run only tests that do not require LBC, run `npm run test:no-lbc`
|
||||||
|
|
||||||
### URL formats
|
### URL formats
|
||||||
Spee.ch has a few types of URL formats that return different assets from the LBRY network. Below is a list of all possible URLs for the content on spee.ch
|
Spee.ch has a few types of URL formats that return different assets from the LBRY network. Below is a list of all possible URLs for the content on spee.ch. You can learn more about LBRY URLs [here](https://lbry.tech/resources/uri).
|
||||||
|
|
||||||
* retrieve the controlling `LBRY` claim:
|
* retrieve the controlling `LBRY` claim:
|
||||||
* https://spee.ch/`claim`
|
* https://spee.ch/`claim`
|
||||||
* https://spee.ch/`claim`.`ext` (serve)
|
* https://spee.ch/`claim`.`ext` (serve)
|
||||||
|
@ -240,22 +243,15 @@ Spee.ch has a few types of URL formats that return different assets from the LBR
|
||||||
* https://spee.ch/`@channel`:`channel_id`/`claim`
|
* https://spee.ch/`@channel`:`channel_id`/`claim`
|
||||||
* https://spee.ch/`@channel`:`channel_id`/`claim`.`ext` (serve)
|
* https://spee.ch/`@channel`:`channel_id`/`claim`.`ext` (serve)
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
Spee.ch depends on two other lbry technologies:
|
||||||
|
* [chainquery](https://github.com/lbryio/chainquery) - a normalized database of the blockchain data. We've provided credentials to use a public chainquery service. You can also install it on your own server to avoid being affected by the commons.
|
||||||
|
* [lbrynet](https://github.com/lbryio/lbry) - a daemon that handles your wallet and transactions.
|
||||||
|
|
||||||
### Bugs
|
### Bugs
|
||||||
If you find a bug or experience a problem, please report your issue here on GitHub and find us in the lbry discord!
|
If you find a bug or experience a problem, please report your issue here on GitHub and find us in the lbry discord!
|
||||||
|
|
||||||
### Issue tags in this repo
|
|
||||||
#### level 1
|
|
||||||
Issues that anyone with basic web development can handle; little-to-no experience with the spee.ch codebase is required.
|
|
||||||
|
|
||||||
#### level 2
|
|
||||||
Familiarity with web apps is required, but little-to-no familiarity with the lbry daemon is necessary
|
|
||||||
|
|
||||||
#### level 3
|
|
||||||
Familiarity with the spee.ch code base and how the lbry daemon functions is required
|
|
||||||
|
|
||||||
#### level 4
|
|
||||||
Issues with lbry (e.g. the spee.ch wallet, lbrynet configuration, etc.) that require strong familiarity with the lbry daemon and/or network to fix. Generally these issues are best suited for the `lbry` `protocol team` but are reported in this repo because they are part of the spee.ch implementation
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
This project is MIT licensed. For the full license, see [LICENSE](LICENSE).
|
This project is MIT licensed. For the full license, see [LICENSE](LICENSE).
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
.asset-preview {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
.asset-preview__image {
|
.asset-preview__image {
|
||||||
width : 100%;
|
width : 100%;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
12
client/scss/_claim-pending.scss
Normal file
12
client/scss/_claim-pending.scss
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
.claim-pending {
|
||||||
|
display: inline-block;
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
left: 10px;
|
||||||
|
padding: 5px;
|
||||||
|
border-radius: 5px;
|
||||||
|
border: 2px solid red;
|
||||||
|
color: red;
|
||||||
|
font-weight: bold;
|
||||||
|
background-color: white;
|
||||||
|
}
|
|
@ -18,6 +18,7 @@
|
||||||
@import '~scss/_button';
|
@import '~scss/_button';
|
||||||
@import '~scss/_button-primary';
|
@import '~scss/_button-primary';
|
||||||
@import '~scss/_button-secondary';
|
@import '~scss/_button-secondary';
|
||||||
|
@import '~scss/_claim-pending';
|
||||||
@import '~scss/_click-to-copy';
|
@import '~scss/_click-to-copy';
|
||||||
@import '~scss/_form-feedback';
|
@import '~scss/_form-feedback';
|
||||||
@import '~scss/_horizontal-split';
|
@import '~scss/_horizontal-split';
|
||||||
|
|
|
@ -2,8 +2,14 @@ import React from 'react';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import createCanonicalLink from '../../../../utils/createCanonicalLink';
|
import createCanonicalLink from '../../../../utils/createCanonicalLink';
|
||||||
|
|
||||||
|
const ClaimPending = () => {
|
||||||
|
return (
|
||||||
|
<div className='claim-pending'>PENDING</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const AssetPreview = ({ defaultThumbnail, claimData }) => {
|
const AssetPreview = ({ defaultThumbnail, claimData }) => {
|
||||||
const {name, fileExt, contentType, thumbnail, title} = claimData;
|
const {name, fileExt, contentType, thumbnail, title, pending} = claimData;
|
||||||
const showUrl = createCanonicalLink({asset: {...claimData}});
|
const showUrl = createCanonicalLink({asset: {...claimData}});
|
||||||
const embedUrl = `${showUrl}.${fileExt}`;
|
const embedUrl = `${showUrl}.${fileExt}`;
|
||||||
switch (contentType) {
|
switch (contentType) {
|
||||||
|
|
|
@ -46,7 +46,7 @@ class AssetDisplay extends React.Component {
|
||||||
}
|
}
|
||||||
render () {
|
render () {
|
||||||
const { status, error, asset } = this.props;
|
const { status, error, asset } = this.props;
|
||||||
const { name, claimData: { claimId, contentType, thumbnail, outpoint } } = asset;
|
const { name, claimData: { claimId, contentType, thumbnail, outpoint, pending } } = asset;
|
||||||
// the outpoint is added to force the browser to re-download the asset after an update
|
// the outpoint is added to force the browser to re-download the asset after an update
|
||||||
// issue: https://github.com/lbryio/spee.ch/issues/607
|
// issue: https://github.com/lbryio/spee.ch/issues/607
|
||||||
let fileExt;
|
let fileExt;
|
||||||
|
@ -68,16 +68,22 @@ class AssetDisplay extends React.Component {
|
||||||
<p>Curious what magic is happening here? <a className='link--primary' target='blank' href='https://lbry.io/faq/what-is-lbry'>Learn more.</a></p>
|
<p>Curious what magic is happening here? <a className='link--primary' target='blank' href='https://lbry.io/faq/what-is-lbry'>Learn more.</a></p>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
{(status === ERROR) &&
|
{(status === ERROR) && (
|
||||||
<div>
|
pending ? (
|
||||||
<Row>
|
<div>
|
||||||
<p>Unfortunately, we couldn't download your asset from LBRY. You can help us out by sharing the following error message in the <a className='link--primary' href='https://chat.lbry.io' target='_blank'>LBRY discord</a>.</p>
|
<p>This content is pending confirmation on the LBRY blockchain. It should be available in the next few minutes, please check back or refresh.</p>
|
||||||
</Row>
|
</div>
|
||||||
<Row>
|
) : (
|
||||||
<p id='error-message'><i>{error}</i></p>
|
<div>
|
||||||
</Row>
|
<Row>
|
||||||
</div>
|
<p>Unfortunately, we couldn't download your asset from LBRY. You can help us out by sharing the following error message in the <a className='link--primary' href='https://chat.lbry.io' target='_blank'>LBRY discord</a>.</p>
|
||||||
}
|
</Row>
|
||||||
|
<Row>
|
||||||
|
<p id='error-message'><i>{error}</i></p>
|
||||||
|
</Row>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
)}
|
||||||
{(status === AVAILABLE) &&
|
{(status === AVAILABLE) &&
|
||||||
<AvailableContent
|
<AvailableContent
|
||||||
contentType={contentType}
|
contentType={contentType}
|
||||||
|
|
|
@ -120,7 +120,7 @@ class AssetInfo extends React.Component {
|
||||||
}
|
}
|
||||||
content={
|
content={
|
||||||
<ClickToCopy
|
<ClickToCopy
|
||||||
id={'short-link'}
|
id={'lbry-permanent-url'}
|
||||||
value={`${channelName}#${certificateId}/${name}`}
|
value={`${channelName}#${certificateId}/${name}`}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
|
|
@ -873,8 +873,7 @@ var claimQueries = (db, table, sequelize) => ({
|
||||||
logger$1.debug(`claim.getAllChannelClaims for ${channelClaimId}`);
|
logger$1.debug(`claim.getAllChannelClaims for ${channelClaimId}`);
|
||||||
|
|
||||||
const defaultWhereClauses = {
|
const defaultWhereClauses = {
|
||||||
bid_state:
|
bid_state: { [sequelize.Op.or]: ['Controlling', 'Active', 'Accepted'] }
|
||||||
{ [sequelize.Op.or]: ['Controlling', 'Active', 'Accepted'] }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const addWhereClauses = (whereClauses, params) => {
|
const addWhereClauses = (whereClauses, params) => {
|
||||||
|
@ -969,7 +968,7 @@ var claimQueries = (db, table, sequelize) => ({
|
||||||
getTopFreeClaimIdByClaimName: async (name) => {
|
getTopFreeClaimIdByClaimName: async (name) => {
|
||||||
return await table.findAll({
|
return await table.findAll({
|
||||||
// TODO: Limit 1
|
// TODO: Limit 1
|
||||||
where: { name },
|
where: { name, bid_state: { [sequelize.Op.or]: ['Controlling', 'Active', 'Accepted'] } },
|
||||||
order: [['effective_amount', 'DESC'], ['height', 'ASC']],
|
order: [['effective_amount', 'DESC'], ['height', 'ASC']],
|
||||||
}).then(result => {
|
}).then(result => {
|
||||||
if(result.length === 0) {
|
if(result.length === 0) {
|
||||||
|
|
|
@ -72,8 +72,7 @@ export default (db, table, sequelize) => ({
|
||||||
logger.debug(`claim.getAllChannelClaims for ${channelClaimId}`);
|
logger.debug(`claim.getAllChannelClaims for ${channelClaimId}`);
|
||||||
|
|
||||||
const defaultWhereClauses = {
|
const defaultWhereClauses = {
|
||||||
bid_state:
|
bid_state: { [sequelize.Op.or]: ['Controlling', 'Active', 'Accepted'] }
|
||||||
{ [sequelize.Op.or]: ['Controlling', 'Active', 'Accepted'] }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const addWhereClauses = (whereClauses, params) => {
|
const addWhereClauses = (whereClauses, params) => {
|
||||||
|
@ -168,7 +167,7 @@ export default (db, table, sequelize) => ({
|
||||||
getTopFreeClaimIdByClaimName: async (name) => {
|
getTopFreeClaimIdByClaimName: async (name) => {
|
||||||
return await table.findAll({
|
return await table.findAll({
|
||||||
// TODO: Limit 1
|
// TODO: Limit 1
|
||||||
where: { name },
|
where: { name, bid_state: { [sequelize.Op.or]: ['Controlling', 'Active', 'Accepted'] } },
|
||||||
order: [['effective_amount', 'DESC'], ['height', 'ASC']],
|
order: [['effective_amount', 'DESC'], ['height', 'ASC']],
|
||||||
}).then(result => {
|
}).then(result => {
|
||||||
if(result.length === 0) {
|
if(result.length === 0) {
|
||||||
|
|
|
@ -17,6 +17,12 @@ const getChannelClaims = async (channelName, channelShortId, page) => {
|
||||||
channelClaims = await chainquery.claim.queries.getAllChannelClaims(channelId, params);
|
channelClaims = await chainquery.claim.queries.getAllChannelClaims(channelId, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const split = channelClaims.reduce(
|
||||||
|
(acc, val) => val.dataValues.height === 0 ? { ...acc, zero: acc.zero.concat(val) } : { ...acc, nonzero: acc.nonzero.concat(val) },
|
||||||
|
{ zero: [], nonzero: [] }
|
||||||
|
);
|
||||||
|
channelClaims = split.zero.concat(split.nonzero);
|
||||||
|
|
||||||
const processingChannelClaims = channelClaims ? channelClaims.map((claim) => getClaimData(claim)) : [];
|
const processingChannelClaims = channelClaims ? channelClaims.map((claim) => getClaimData(claim)) : [];
|
||||||
const processedChannelClaims = await Promise.all(processingChannelClaims);
|
const processedChannelClaims = await Promise.all(processingChannelClaims);
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,7 @@ const claimGet = async ({ ip, originalUrl, params }, res) => {
|
||||||
try {
|
try {
|
||||||
await waitOn({
|
await waitOn({
|
||||||
resources: [ lbrynetResult.file_name ],
|
resources: [ lbrynetResult.file_name ],
|
||||||
delay : 100,
|
delay : 500,
|
||||||
timeout : 10000, // 10 seconds
|
timeout : 10000, // 10 seconds
|
||||||
});
|
});
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
|
|
@ -28,5 +28,6 @@ module.exports = async (data) => {
|
||||||
thumbnail : data.generated_thumbnail || data.thumbnail_url || data.thumbnail,
|
thumbnail : data.generated_thumbnail || data.thumbnail_url || data.thumbnail,
|
||||||
outpoint : data.transaction_hash_id || data.outpoint,
|
outpoint : data.transaction_hash_id || data.outpoint,
|
||||||
host,
|
host,
|
||||||
|
pending: Boolean(data.height === 0),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Reference in a new issue