store height and width in File table #543
20 changed files with 473 additions and 186 deletions
|
@ -2,4 +2,7 @@ const path = require('path');
|
|||
|
||||
module.exports = {
|
||||
'config': path.resolve('devConfig', 'sequelizeCliConfig.js'),
|
||||
'models-path': path.resolve('server', 'models'),
|
||||
'seeders-path': path.resolve('server', 'seeders'),
|
||||
'migrations-path': path.resolve('server', 'migrations')
|
||||
}
|
||||
|
|
40
README.md
40
README.md
|
@ -9,6 +9,11 @@ To get started running your own version of spee.ch, visit [lbryio/www.spee.ch](h
|
|||
npm install spee.ch --save
|
||||
```
|
||||
|
||||
### Dependenceis
|
||||
Make sure the following are installed
|
||||
* [imagemagick](https://www.imagemagick.org/script/download.php)
|
||||
* [ffmpeg](https://www.ffmpeg.org/download.html)
|
||||
|
||||
## Development
|
||||
* the `server/` folder contains all of the server code
|
||||
* `index.js` is the entry point for the server. It creates the [express app](https://expressjs.com/), requires the routes, syncs the database, and starts the server listening on the `PORT` designated in the config file.
|
||||
|
@ -23,21 +28,7 @@ npm install spee.ch --save
|
|||
* To run only tests that do not require LBC, run `npm run test:no-lbc`
|
||||
|
||||
## API
|
||||
|
||||
#### GET
|
||||
* /api/claim/resolve/:name/:claimId
|
||||
* example: `curl https://spee.ch/api/claim/resolve/doitlive/xyz`
|
||||
* /api/claim/list/:name
|
||||
* example: `curl https://spee.ch/api/claim/list/doitlive`
|
||||
* /api/claim/availability/:name
|
||||
* returns the name if it is available
|
||||
* example: `curl https://spee.ch/api/claim/availability/doitlive`
|
||||
* /api/channel/availability/:name
|
||||
* returns the name if it is available
|
||||
* example: `curl https://spee.ch/api/channel/availability/@CoolChannel`
|
||||
|
||||
#### POST
|
||||
* /api/claim/publish
|
||||
* _(post)_ /api/claim/publish
|
||||
* example: `curl -F 'name=MyPictureName' -F 'file=@/path/to/myPicture.jpeg' https://spee.ch/api/claim/publish`
|
||||
* Parameters:
|
||||
* `name` (required)
|
||||
|
@ -49,6 +40,16 @@ npm install spee.ch --save
|
|||
* `thumbnail` url to thumbnail image, for .mp4 uploads only (optional)
|
||||
* `channelName`(optional)
|
||||
* `channelPassword` (optional,; required if `channelName` is provided)
|
||||
* _(get)_ /api/claim/resolve/:name/:claimId
|
||||
* example: `curl https://spee.ch/api/claim/resolve/doitlive/xyz`
|
||||
* _(get)_ /api/claim/list/:name
|
||||
* example: `curl https://spee.ch/api/claim/list/doitlive`
|
||||
* _(get)_ /api/claim/availability/:name
|
||||
* returns the name if it is available
|
||||
* example: `curl https://spee.ch/api/claim/availability/doitlive`
|
||||
* _(get)_ /api/channel/availability/:name
|
||||
* returns the name if it is available
|
||||
* example: `curl https://spee.ch/api/channel/availability/@CoolChannel`
|
||||
|
||||
## Bugs
|
||||
If you find a bug or experience a problem, please report your issue here on github and find us in the lbry discord!
|
||||
|
@ -69,4 +70,13 @@ Issues with spee.ch strong familiarity with the spee.ch code base and how the lb
|
|||
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 placed in this repo because of they are part of the spee.ch implementation
|
||||
|
||||
### Stack
|
||||
* server
|
||||
* [mysql](https://www.mysql.com/)
|
||||
* [express](https://www.npmjs.com/package/express)
|
||||
* [node](https://nodejs.org/)
|
||||
* [lbry](https://github.com/lbryio/lbry)
|
||||
* [imagemagick](https://www.imagemagick.org/)
|
||||
* [ffmpeg](https://www.ffmpeg.org/)
|
||||
* client
|
||||
* [react](https://reactjs.org/)
|
||||
|
||||
|
|
54
package-lock.json
generated
54
package-lock.json
generated
|
@ -953,6 +953,11 @@
|
|||
"resolved": "https://registry.npmjs.org/ansicolors/-/ansicolors-0.2.1.tgz",
|
||||
"integrity": "sha1-vgiVmQl7dKXJxKhKDNvNtivYeu8="
|
||||
},
|
||||
"any-promise": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
|
||||
"integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8="
|
||||
},
|
||||
"anymatch": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz",
|
||||
|
@ -3984,6 +3989,14 @@
|
|||
"integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=",
|
||||
"dev": true
|
||||
},
|
||||
"get-video-dimensions": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/get-video-dimensions/-/get-video-dimensions-1.0.0.tgz",
|
||||
"integrity": "sha1-/H5ayBw5JEH1uG1Q3XeDiptTFHo=",
|
||||
"requires": {
|
||||
"mz": "1.3.0"
|
||||
}
|
||||
},
|
||||
"getpass": {
|
||||
"version": "0.1.7",
|
||||
"resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
|
||||
|
@ -4377,6 +4390,16 @@
|
|||
"integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk=",
|
||||
"dev": true
|
||||
},
|
||||
"image-size": {
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/image-size/-/image-size-0.6.3.tgz",
|
||||
"integrity": "sha512-47xSUiQioGaB96nqtp5/q55m0aBQSQdyIloMOc/x+QVTDZLNmXE892IIDrJ0hM1A5vcNUDD5tDffkSP5lCaIIA=="
|
||||
},
|
||||
"imagemagick": {
|
||||
"version": "0.1.3",
|
||||
"resolved": "https://registry.npmjs.org/imagemagick/-/imagemagick-0.1.3.tgz",
|
||||
"integrity": "sha1-dIPOoJO02fLi85aFetyIIbU3xWo="
|
||||
},
|
||||
"import-lazy": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz",
|
||||
|
@ -5371,6 +5394,16 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"mz": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/mz/-/mz-1.3.0.tgz",
|
||||
"integrity": "sha1-BvCT/dmVagbTfhsegTROJ0eMQvA=",
|
||||
"requires": {
|
||||
"native-or-bluebird": "1.2.0",
|
||||
"thenify": "3.3.0",
|
||||
"thenify-all": "1.6.0"
|
||||
}
|
||||
},
|
||||
"named-placeholders": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.1.tgz",
|
||||
|
@ -5431,6 +5464,11 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"native-or-bluebird": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/native-or-bluebird/-/native-or-bluebird-1.2.0.tgz",
|
||||
"integrity": "sha1-OcR7/Xgl0fuf+tMiEK4l2q3xAck="
|
||||
},
|
||||
"natural-compare": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
|
||||
|
@ -7745,6 +7783,22 @@
|
|||
"integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",
|
||||
"dev": true
|
||||
},
|
||||
"thenify": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.0.tgz",
|
||||
"integrity": "sha1-5p44obq+lpsBCCB5eLn2K4hgSDk=",
|
||||
"requires": {
|
||||
"any-promise": "1.3.0"
|
||||
}
|
||||
},
|
||||
"thenify-all": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz",
|
||||
"integrity": "sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY=",
|
||||
"requires": {
|
||||
"thenify": "3.3.0"
|
||||
}
|
||||
},
|
||||
"through": {
|
||||
"version": "2.3.8",
|
||||
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
|
||||
|
|
|
@ -37,7 +37,10 @@
|
|||
"cookie-session": "^2.0.0-beta.3",
|
||||
"express": "^4.15.2",
|
||||
"express-handlebars": "^3.0.0",
|
||||
"get-video-dimensions": "^1.0.0",
|
||||
"helmet": "^3.8.1",
|
||||
"image-size": "^0.6.3",
|
||||
"imagemagick": "^0.1.3",
|
||||
"module-alias": "^2.0.6",
|
||||
"mysql2": "^1.3.5",
|
||||
"passport": "^0.4.0",
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
module.exports = (fileInfo, getResult) => {
|
||||
fileInfo.fileName = getResult.file_name;
|
||||
fileInfo.filePath = getResult.download_path;
|
||||
return fileInfo;
|
||||
};
|
|
@ -1,13 +0,0 @@
|
|||
module.exports = ({ name, claimId, outpoint, height, address, nsfw, contentType }) => {
|
||||
return {
|
||||
name,
|
||||
claimId,
|
||||
outpoint,
|
||||
height,
|
||||
address,
|
||||
fileName: '',
|
||||
filePath: '',
|
||||
fileType: contentType,
|
||||
nsfw,
|
||||
};
|
||||
};
|
|
@ -1,6 +1,5 @@
|
|||
const { getClaim } = require('../../../../lbrynet');
|
||||
const addGetResultsToFileData = require('./addGetResultsToFileData.js');
|
||||
const createFileData = require('./createFileData.js');
|
||||
const { createFileRecordDataAfterGet } = require('../../../../models/utils/createFileRecordData.js');
|
||||
const { handleErrorResponse } = require('../../../utils/errorHandlers.js');
|
||||
const db = require('../../../../models');
|
||||
|
||||
|
@ -13,23 +12,32 @@ const db = require('../../../../models');
|
|||
const claimGet = ({ ip, originalUrl, params }, res) => {
|
||||
const name = params.name;
|
||||
const claimId = params.claimId;
|
||||
let resolveResult;
|
||||
let getResult;
|
||||
// resolve the claim
|
||||
db.Claim.resolveClaim(name, claimId)
|
||||
.then(resolveResult => {
|
||||
// make sure a claim actually exists at that uri
|
||||
if (!resolveResult) {
|
||||
.then(result => {
|
||||
if (!result) {
|
||||
throw new Error('No matching uri found in Claim table');
|
||||
}
|
||||
let fileData = createFileData(resolveResult);
|
||||
// get the claim
|
||||
return Promise.all([fileData, getClaim(`${name}#${claimId}`)]);
|
||||
resolveResult = result;
|
||||
return getClaim(`${name}#${claimId}`);
|
||||
})
|
||||
.then(([ fileData, getResult ]) => {
|
||||
fileData = addGetResultsToFileData(fileData, getResult);
|
||||
return Promise.all([db.upsert(db.File, fileData, {name, claimId}, 'File'), getResult]);
|
||||
.then(result => {
|
||||
getResult = result;
|
||||
})
|
||||
.then(([ fileRecord, {message, completed} ]) => {
|
||||
res.status(200).json({ success: true, message, completed });
|
||||
.then(() => {
|
||||
const fileData = createFileRecordDataAfterGet(resolveResult, getResult);
|
||||
const upsertCriteria = { name, claimId};
|
||||
return db.upsert(db.File, fileData, upsertCriteria, 'File')
|
||||
})
|
||||
.then(() => {
|
||||
const { message, completed } = getResult;
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
message,
|
||||
completed,
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
handleErrorResponse(originalUrl, ip, error, res);
|
||||
|
|
|
@ -47,29 +47,38 @@ const authenticateChannelCredentials = (channelName, channelId, userPassword) =>
|
|||
};
|
||||
|
||||
const authenticateUser = (channelName, channelId, channelPassword, user) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
// case: no channelName or channel Id are provided (anonymous), regardless of whether user token is provided
|
||||
if (!channelName && !channelId) {
|
||||
return {
|
||||
resolve({
|
||||
channelName : null,
|
||||
channelClaimId: null,
|
||||
};
|
||||
});
|
||||
return;
|
||||
}
|
||||
// case: channelName or channel Id are provided with user token
|
||||
if (user) {
|
||||
if (channelName && channelName !== user.channelName) {
|
||||
throw new Error('the provided channel name does not match user credentials');
|
||||
reject(new Error('the provided channel name does not match user credentials'));
|
||||
return;
|
||||
}
|
||||
if (channelId && channelId !== user.channelClaimId) {
|
||||
throw new Error('the provided channel id does not match user credentials');
|
||||
reject(new Error('the provided channel id does not match user credentials'));
|
||||
return;
|
||||
}
|
||||
return {
|
||||
resolve({
|
||||
channelName : user.channelName,
|
||||
channelClaimId: user.channelClaimId,
|
||||
};
|
||||
});
|
||||
return;
|
||||
}
|
||||
// case: channelName or channel Id are provided with password instead of user token
|
||||
if (!channelPassword) throw new Error('no channel password provided');
|
||||
return authenticateChannelCredentials(channelName, channelId, channelPassword);
|
||||
if (!channelPassword) {
|
||||
reject(new Error('no channel password provided'));
|
||||
return;
|
||||
}
|
||||
resolve(authenticateChannelCredentials(channelName, channelId, channelPassword));
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = authenticateUser;
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
const logger = require('winston');
|
||||
const { details, publishing } = require('@config/siteConfig');
|
||||
|
||||
const createBasicPublishParams = (filePath, name, title, description, license, nsfw, thumbnail) => {
|
||||
logger.debug(`Creating Publish Parameters`);
|
||||
const createPublishParams = (filePath, name, title, description, license, nsfw, thumbnail, channelName, channelClaimId) => {
|
||||
// provide defaults for title
|
||||
if (title === null || title.trim() === '') {
|
||||
title = name;
|
||||
|
@ -15,7 +14,7 @@ const createBasicPublishParams = (filePath, name, title, description, license, n
|
|||
if (license === null || license.trim() === '') {
|
||||
license = ' '; // default to empty string
|
||||
}
|
||||
// create the publish params
|
||||
// create the basic publish params
|
||||
const publishParams = {
|
||||
name,
|
||||
file_path: filePath,
|
||||
|
@ -34,8 +33,15 @@ const createBasicPublishParams = (filePath, name, title, description, license, n
|
|||
if (thumbnail) {
|
||||
publishParams['metadata']['thumbnail'] = thumbnail;
|
||||
}
|
||||
// add channel details if publishing to a channel
|
||||
if (channelName && channelClaimId) {
|
||||
publishParams['channel_name'] = channelName;
|
||||
publishParams['channel_id'] = channelClaimId;
|
||||
}
|
||||
// log params
|
||||
logger.debug('publish params:', publishParams);
|
||||
// return
|
||||
return publishParams;
|
||||
};
|
||||
|
||||
module.exports = createBasicPublishParams;
|
||||
module.exports = createPublishParams;
|
|
@ -9,12 +9,14 @@ const { handleErrorResponse } = require('../../../utils/errorHandlers.js');
|
|||
const checkClaimAvailability = require('../availability/checkClaimAvailability.js');
|
||||
|
||||
const publish = require('./publish.js');
|
||||
const createBasicPublishParams = require('./createBasicPublishParams.js');
|
||||
const createPublishParams = require('./createPublishParams.js');
|
||||
const createThumbnailPublishParams = require('./createThumbnailPublishParams.js');
|
||||
const parsePublishApiRequestBody = require('./parsePublishApiRequestBody.js');
|
||||
const parsePublishApiRequestFiles = require('./parsePublishApiRequestFiles.js');
|
||||
const authenticateUser = require('./authentication.js');
|
||||
|
||||
const CLAIM_TAKEN = 'CLAIM_TAKEN';
|
||||
|
||||
/*
|
||||
|
||||
route to publish a claim through the daemon
|
||||
|
@ -50,18 +52,20 @@ const claimPublish = ({ body, files, headers, ip, originalUrl, user, tor }, res)
|
|||
return res.status(400).json({success: false, message: error.message});
|
||||
}
|
||||
// check channel authorization
|
||||
Promise
|
||||
.all([
|
||||
authenticateUser(channelName, channelId, channelPassword, user),
|
||||
authenticateUser(channelName, channelId, channelPassword, user)
|
||||
.then(({ channelName, channelClaimId }) => {
|
||||
return Promise.all([
|
||||
checkClaimAvailability(name),
|
||||
createBasicPublishParams(filePath, name, title, description, license, nsfw, thumbnail),
|
||||
createPublishParams(filePath, name, title, description, license, nsfw, thumbnail, channelName, channelClaimId),
|
||||
createThumbnailPublishParams(thumbnailFilePath, name, license, nsfw),
|
||||
])
|
||||
.then(([{channelName, channelClaimId}, validatedClaimName, publishParams, thumbnailPublishParams]) => {
|
||||
// add channel details to the publish params
|
||||
if (channelName && channelClaimId) {
|
||||
publishParams['channel_name'] = channelName;
|
||||
publishParams['channel_id'] = channelClaimId;
|
||||
})
|
||||
.then(([ claimAvailable, publishParams, thumbnailPublishParams ]) => {
|
||||
if (!claimAvailable) {
|
||||
throw {
|
||||
name: CLAIM_TAKEN,
|
||||
message: 'That claim name is already taken'
|
||||
};
|
||||
}
|
||||
// publish the thumbnail, if one exists
|
||||
if (thumbnailPublishParams) {
|
||||
|
@ -87,6 +91,12 @@ const claimPublish = ({ body, files, headers, ip, originalUrl, user, tor }, res)
|
|||
sendGATimingEvent('end-to-end', 'publish', fileType, gaStartTime, Date.now());
|
||||
})
|
||||
.catch(error => {
|
||||
if (error.name = CLAIM_TAKEN) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
message: error.message,
|
||||
});
|
||||
}
|
||||
handleErrorResponse(originalUrl, ip, error, res);
|
||||
});
|
||||
};
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
const logger = require('winston');
|
||||
const db = require('../../../../models');
|
||||
const { publishClaim } = require('../../../../lbrynet');
|
||||
const db = require('../../../../models');
|
||||
const { createFileRecordDataAfterPublish } = require('../../../../models/utils/createFileRecordData.js');
|
||||
const { createClaimRecordDataAfterPublish } = require('../../../../models/utils/createClaimRecordData.js');
|
||||
const deleteFile = require('./deleteFile.js');
|
||||
|
||||
const publish = (publishParams, fileName, fileType) => {
|
||||
|
@ -35,51 +37,34 @@ const publish = (publishParams, fileName, fileType) => {
|
|||
logger.debug(`certificateId: ${certificateId}`);
|
||||
})
|
||||
.then(() => {
|
||||
// create the File record
|
||||
const fileRecord = {
|
||||
name : publishParams.name,
|
||||
claimId : publishResults.claim_id,
|
||||
title : publishParams.metadata.title,
|
||||
description: publishParams.metadata.description,
|
||||
address : publishParams.claim_address,
|
||||
outpoint : `${publishResults.txid}:${publishResults.nout}`,
|
||||
height : 0,
|
||||
fileName,
|
||||
filePath : publishParams.file_path,
|
||||
fileType,
|
||||
nsfw : publishParams.metadata.nsfw,
|
||||
};
|
||||
// create the Claim record
|
||||
const claimRecord = {
|
||||
name : publishParams.name,
|
||||
claimId : publishResults.claim_id,
|
||||
title : publishParams.metadata.title,
|
||||
description: publishParams.metadata.description,
|
||||
address : publishParams.claim_address,
|
||||
thumbnail : publishParams.metadata.thumbnail,
|
||||
outpoint : `${publishResults.txid}:${publishResults.nout}`,
|
||||
height : 0,
|
||||
contentType: fileType,
|
||||
nsfw : publishParams.metadata.nsfw,
|
||||
amount : publishParams.bid,
|
||||
certificateId,
|
||||
channelName,
|
||||
};
|
||||
// upsert criteria
|
||||
const upsertCriteria = {
|
||||
name : publishParams.name,
|
||||
claimId: publishResults.claim_id,
|
||||
};
|
||||
return Promise.all([
|
||||
createFileRecordDataAfterPublish(fileName, fileType, publishParams, publishResults),
|
||||
createClaimRecordDataAfterPublish(certificateId, channelName, fileName, fileType, publishParams, publishResults),
|
||||
]);
|
||||
})
|
||||
.then(([fileRecord, claimRecord]) => {
|
||||
// upsert the records
|
||||
return Promise.all([db.upsert(db.File, fileRecord, upsertCriteria, 'File'), db.upsert(db.Claim, claimRecord, upsertCriteria, 'Claim')]);
|
||||
const {name, claim_id: claimId} = publishParams;
|
||||
const upsertCriteria = {
|
||||
name,
|
||||
claimId,
|
||||
};
|
||||
return Promise.all([
|
||||
db.upsert(db.File, fileRecord, upsertCriteria, 'File'),
|
||||
db.upsert(db.Claim, claimRecord, upsertCriteria, 'Claim'),
|
||||
]);
|
||||
})
|
||||
.then(([file, claim]) => {
|
||||
logger.debug('File and Claim records successfully created');
|
||||
return Promise.all([file.setClaim(claim), claim.setFile(file)]);
|
||||
return Promise.all([
|
||||
file.setClaim(claim),
|
||||
claim.setFile(file),
|
||||
]);
|
||||
})
|
||||
.then(() => {
|
||||
logger.debug('File and Claim records successfully associated');
|
||||
resolve(publishResults); // resolve the promise with the result from lbryApi publishClaim;
|
||||
// resolve the promise with the result from lbryApi publishClaim;
|
||||
resolve(publishResults);
|
||||
})
|
||||
.catch(error => {
|
||||
logger.error('PUBLISH ERROR', error);
|
||||
|
|
88
server/migrations/File_AddHeightAndWidthColumn.js
Normal file
88
server/migrations/File_AddHeightAndWidthColumn.js
Normal file
|
@ -0,0 +1,88 @@
|
|||
module.exports = {
|
||||
up: (queryInterface, { INTEGER }) => {
|
||||
// logic for transforming into the new state
|
||||
return Promise.all([
|
||||
queryInterface.addColumn(
|
||||
'File',
|
||||
'fileHeight',
|
||||
{
|
||||
type : INTEGER,
|
||||
allowNull: false,
|
||||
default : 0,
|
||||
}
|
||||
),
|
||||
queryInterface.addColumn(
|
||||
'File',
|
||||
'fileWidth',
|
||||
{
|
||||
type : INTEGER,
|
||||
allowNull: false,
|
||||
default : 0,
|
||||
}
|
||||
),
|
||||
queryInterface.removeColumn(
|
||||
'File',
|
||||
'address',
|
||||
),
|
||||
queryInterface.removeColumn(
|
||||
'File',
|
||||
'height',
|
||||
),
|
||||
queryInterface.removeColumn(
|
||||
'File',
|
||||
'nsfw',
|
||||
),
|
||||
queryInterface.removeColumn(
|
||||
'File',
|
||||
'trendingEligible',
|
||||
),
|
||||
]);
|
||||
},
|
||||
down: (queryInterface, { BOOLEAN, INTEGER, STRING }) => {
|
||||
return Promise.all([
|
||||
queryInterface.removeColumn(
|
||||
'File',
|
||||
'fileHeight',
|
||||
),
|
||||
queryInterface.removeColumn(
|
||||
'File',
|
||||
'fileWidth',
|
||||
),
|
||||
queryInterface.addColumn(
|
||||
'File',
|
||||
'address',
|
||||
{
|
||||
type : STRING,
|
||||
allowNull: false,
|
||||
}
|
||||
),
|
||||
queryInterface.addColumn(
|
||||
'File',
|
||||
'height',
|
||||
{
|
||||
type : INTEGER,
|
||||
allowNull: false,
|
||||
default : 0,
|
||||
}
|
||||
),
|
||||
queryInterface.addColumn(
|
||||
'File',
|
||||
'nsfw',
|
||||
{
|
||||
type : BOOLEAN,
|
||||
allowNull : false,
|
||||
defaultValue: false,
|
||||
}
|
||||
),
|
||||
queryInterface.addColumn(
|
||||
'File',
|
||||
'trendingEligible',
|
||||
{
|
||||
type : BOOLEAN,
|
||||
allowNull : false,
|
||||
defaultValue: true,
|
||||
}
|
||||
),
|
||||
]);
|
||||
},
|
||||
};
|
|
@ -10,15 +10,16 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER }) => {
|
|||
type : STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
address: {
|
||||
type : STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
outpoint: {
|
||||
type : STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
height: {
|
||||
fileHeight: {
|
||||
type : INTEGER,
|
||||
allowNull: false,
|
||||
default : 0,
|
||||
},
|
||||
fileWidth: {
|
||||
type : INTEGER,
|
||||
allowNull: false,
|
||||
default : 0,
|
||||
|
@ -34,16 +35,6 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER }) => {
|
|||
fileType: {
|
||||
type: STRING,
|
||||
},
|
||||
nsfw: {
|
||||
type : BOOLEAN,
|
||||
allowNull : false,
|
||||
defaultValue: false,
|
||||
},
|
||||
trendingEligible: {
|
||||
type : BOOLEAN,
|
||||
allowNull : false,
|
||||
defaultValue: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
freezeTableName: true,
|
||||
|
@ -51,17 +42,8 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER }) => {
|
|||
);
|
||||
|
||||
File.associate = db => {
|
||||
File.hasMany(db.Request);
|
||||
File.hasOne(db.Claim);
|
||||
};
|
||||
|
||||
File.getRecentClaims = function () {
|
||||
return this.findAll({
|
||||
where: { nsfw: false, trendingEligible: true },
|
||||
order: [['createdAt', 'DESC']],
|
||||
limit: 25,
|
||||
});
|
||||
};
|
||||
|
||||
return File;
|
||||
};
|
||||
|
|
|
@ -5,7 +5,6 @@ const Certificate = require('./certificate.js');
|
|||
const Channel = require('./channel.js');
|
||||
const Claim = require('./claim.js');
|
||||
const File = require('./file.js');
|
||||
const Request = require('./request.js');
|
||||
const User = require('./user.js');
|
||||
const Blocked = require('./blocked.js');
|
||||
const Tor = require('./tor.js');
|
||||
|
@ -48,7 +47,6 @@ db['Certificate'] = sequelize.import('Certificate', Certificate);
|
|||
db['Channel'] = sequelize.import('Channel', Channel);
|
||||
db['Claim'] = sequelize.import('Claim', Claim);
|
||||
db['File'] = sequelize.import('File', File);
|
||||
db['Request'] = sequelize.import('Request', Request);
|
||||
db['User'] = sequelize.import('User', User);
|
||||
db['Blocked'] = sequelize.import('Blocked', Blocked);
|
||||
db['Tor'] = sequelize.import('Tor', Tor);
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
module.exports = (sequelize, { STRING, BOOLEAN, TEXT }) => {
|
||||
const Request = sequelize.define(
|
||||
'Request',
|
||||
{
|
||||
action: {
|
||||
type : STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
url: {
|
||||
type : STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
ipAddress: {
|
||||
type : STRING,
|
||||
allowNull: true,
|
||||
},
|
||||
result: {
|
||||
type : TEXT('long'),
|
||||
allowNull: true,
|
||||
default : null,
|
||||
},
|
||||
},
|
||||
{
|
||||
freezeTableName: true,
|
||||
}
|
||||
);
|
||||
|
||||
Request.associate = db => {
|
||||
Request.belongsTo(db.File, {
|
||||
foreignKey: {
|
||||
allowNull: true,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
return Request;
|
||||
};
|
39
server/models/utils/createClaimRecordData.js
Normal file
39
server/models/utils/createClaimRecordData.js
Normal file
|
@ -0,0 +1,39 @@
|
|||
const createClaimRecordDataAfterPublish = (certificateId, channelName, fileName, fileType, publishParams, publishResults) => {
|
||||
const {
|
||||
name,
|
||||
metadata: {
|
||||
title,
|
||||
description,
|
||||
thumbnail,
|
||||
nsfw,
|
||||
},
|
||||
claim_address: address,
|
||||
bid: amount,
|
||||
} = publishParams;
|
||||
|
||||
const {
|
||||
claim_id: claimId,
|
||||
txid,
|
||||
nout,
|
||||
} = publishResults;
|
||||
|
||||
return {
|
||||
name,
|
||||
claimId,
|
||||
title,
|
||||
description,
|
||||
address,
|
||||
thumbnail,
|
||||
outpoint : `${txid}:${nout}`,
|
||||
height : 0,
|
||||
contentType: fileType,
|
||||
nsfw,
|
||||
amount,
|
||||
certificateId,
|
||||
channelName,
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
createClaimRecordDataAfterPublish,
|
||||
};
|
65
server/models/utils/createFileRecordData.js
Normal file
65
server/models/utils/createFileRecordData.js
Normal file
|
@ -0,0 +1,65 @@
|
|||
const getMediaDimensions = require('../../utils/getMediaDimensions.js');
|
||||
|
||||
async function createFileRecordDataAfterGet (resolveResult, getResult) {
|
||||
const {
|
||||
name,
|
||||
claimId,
|
||||
outpoint,
|
||||
contentType: fileType,
|
||||
} = resolveResult;
|
||||
|
||||
const {
|
||||
file_name: fileName,
|
||||
download_path: filePath,
|
||||
} = getResult;
|
||||
|
||||
const {
|
||||
height: fileHeight,
|
||||
width: fileWidth,
|
||||
} = await getMediaDimensions(fileType, filePath);
|
||||
|
||||
return {
|
||||
name,
|
||||
claimId,
|
||||
outpoint,
|
||||
fileHeight,
|
||||
fileWidth,
|
||||
fileName,
|
||||
filePath,
|
||||
fileType,
|
||||
};
|
||||
};
|
||||
|
||||
async function createFileRecordDataAfterPublish (fileName, fileType, publishParams, publishResults) {
|
||||
const {
|
||||
name,
|
||||
file_path: filePath,
|
||||
} = publishParams;
|
||||
|
||||
const {
|
||||
claim_id: claimId,
|
||||
txid,
|
||||
nout,
|
||||
} = publishResults;
|
||||
|
||||
const {
|
||||
height: fileHeight,
|
||||
width: fileWidth,
|
||||
} = await getMediaDimensions(fileType, filePath);
|
||||
|
||||
return {
|
||||
name,
|
||||
claimId,
|
||||
outpoint: `${txid}:${nout}`,
|
||||
fileHeight,
|
||||
fileWidth,
|
||||
fileName,
|
||||
filePath,
|
||||
fileType,
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
createFileRecordDataAfterGet,
|
||||
createFileRecordDataAfterPublish,
|
||||
};
|
30
server/utils/getMediaDimensions.js
Normal file
30
server/utils/getMediaDimensions.js
Normal file
|
@ -0,0 +1,30 @@
|
|||
const logger = require('winston');
|
||||
const { getImageHeightAndWidth } = require('./imageProcessing');
|
||||
const { getVideoHeightAndWidth } = require('./videoProcessing');
|
||||
|
||||
async function getMediaDimensions (fileType, filePath) {
|
||||
let height = 0;
|
||||
let width = 0;
|
||||
switch (fileType) {
|
||||
case 'image/jpeg':
|
||||
case 'image/jpg':
|
||||
case 'image/png':
|
||||
case 'image/gif':
|
||||
logger.debug('creating File data for an image');
|
||||
[ height, width ] = await getImageHeightAndWidth(filePath);
|
||||
break;
|
||||
case 'video/mp4':
|
||||
logger.debug('creating File data for a video');
|
||||
[ height, width ] = await getVideoHeightAndWidth(filePath);
|
||||
break;
|
||||
default:
|
||||
logger.error('unable to create File data for unspported file type:', fileType);
|
||||
break;
|
||||
}
|
||||
return {
|
||||
height,
|
||||
width,
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = getMediaDimensions;
|
41
server/utils/imageProcessing.js
Normal file
41
server/utils/imageProcessing.js
Normal file
|
@ -0,0 +1,41 @@
|
|||
const imageMagick = require('imagemagick');
|
||||
const sizeOf = require('image-size');
|
||||
|
||||
const getImageMetadata = (filePath) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
imageMagick.readMetadata(filePath, (err, metadata) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
}
|
||||
resolve(metadata);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const getImageDetails = (filePath) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
imageMagick.identify(filePath, (err, details) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
}
|
||||
resolve(details);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const getImageHeightAndWidth = (filePath) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
const { height, width } = sizeOf(filePath);
|
||||
resolve([height, width]);
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
getImageMetadata,
|
||||
getImageDetails,
|
||||
getImageHeightAndWidth,
|
||||
};
|
11
server/utils/videoProcessing.js
Normal file
11
server/utils/videoProcessing.js
Normal file
|
@ -0,0 +1,11 @@
|
|||
const getVideoDimensions = require('get-video-dimensions');
|
||||
|
||||
async function getVideoHeightAndWidth (filePath) {
|
||||
const videoDimensions = await getVideoDimensions(filePath);
|
||||
const { height, width } = videoDimensions;
|
||||
return [ height, width ];
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getVideoHeightAndWidth,
|
||||
};
|
Loading…
Reference in a new issue