store height and width in File table #543

Merged
bones7242 merged 14 commits from 527-height-and-width-for-images into master 2018-07-31 00:53:44 +02:00
20 changed files with 473 additions and 186 deletions

View file

@ -2,4 +2,7 @@ const path = require('path');
module.exports = { module.exports = {
'config': path.resolve('devConfig', 'sequelizeCliConfig.js'), 'config': path.resolve('devConfig', 'sequelizeCliConfig.js'),
'models-path': path.resolve('server', 'models'),
'seeders-path': path.resolve('server', 'seeders'),
'migrations-path': path.resolve('server', 'migrations')
} }

View file

@ -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 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 ## Development
* the `server/` folder contains all of the server code * 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. * `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` * To run only tests that do not require LBC, run `npm run test:no-lbc`
## API ## API
* _(post)_ /api/claim/publish
#### 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
* example: `curl -F 'name=MyPictureName' -F 'file=@/path/to/myPicture.jpeg' https://spee.ch/api/claim/publish` * example: `curl -F 'name=MyPictureName' -F 'file=@/path/to/myPicture.jpeg' https://spee.ch/api/claim/publish`
* Parameters: * Parameters:
* `name` (required) * `name` (required)
@ -49,6 +40,16 @@ npm install spee.ch --save
* `thumbnail` url to thumbnail image, for .mp4 uploads only (optional) * `thumbnail` url to thumbnail image, for .mp4 uploads only (optional)
* `channelName`(optional) * `channelName`(optional)
* `channelPassword` (optional,; required if `channelName` is provided) * `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 ## 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!
@ -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 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 ### 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
View file

@ -953,6 +953,11 @@
"resolved": "https://registry.npmjs.org/ansicolors/-/ansicolors-0.2.1.tgz", "resolved": "https://registry.npmjs.org/ansicolors/-/ansicolors-0.2.1.tgz",
"integrity": "sha1-vgiVmQl7dKXJxKhKDNvNtivYeu8=" "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": { "anymatch": {
"version": "1.3.2", "version": "1.3.2",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz",
@ -3984,6 +3989,14 @@
"integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=",
"dev": true "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": { "getpass": {
"version": "0.1.7", "version": "0.1.7",
"resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
@ -4377,6 +4390,16 @@
"integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk=", "integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk=",
"dev": true "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": { "import-lazy": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", "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": { "named-placeholders": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.1.tgz", "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": { "natural-compare": {
"version": "1.4.0", "version": "1.4.0",
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
@ -7745,6 +7783,22 @@
"integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",
"dev": true "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": { "through": {
"version": "2.3.8", "version": "2.3.8",
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",

View file

@ -37,7 +37,10 @@
"cookie-session": "^2.0.0-beta.3", "cookie-session": "^2.0.0-beta.3",
"express": "^4.15.2", "express": "^4.15.2",
"express-handlebars": "^3.0.0", "express-handlebars": "^3.0.0",
"get-video-dimensions": "^1.0.0",
"helmet": "^3.8.1", "helmet": "^3.8.1",
"image-size": "^0.6.3",
"imagemagick": "^0.1.3",
"module-alias": "^2.0.6", "module-alias": "^2.0.6",
"mysql2": "^1.3.5", "mysql2": "^1.3.5",
"passport": "^0.4.0", "passport": "^0.4.0",

View file

@ -1,5 +0,0 @@
module.exports = (fileInfo, getResult) => {
fileInfo.fileName = getResult.file_name;
fileInfo.filePath = getResult.download_path;
return fileInfo;
};

View file

@ -1,13 +0,0 @@
module.exports = ({ name, claimId, outpoint, height, address, nsfw, contentType }) => {
return {
name,
claimId,
outpoint,
height,
address,
fileName: '',
filePath: '',
fileType: contentType,
nsfw,
};
};

View file

@ -1,6 +1,5 @@
const { getClaim } = require('../../../../lbrynet'); const { getClaim } = require('../../../../lbrynet');
const addGetResultsToFileData = require('./addGetResultsToFileData.js'); const { createFileRecordDataAfterGet } = require('../../../../models/utils/createFileRecordData.js');
const createFileData = require('./createFileData.js');
const { handleErrorResponse } = require('../../../utils/errorHandlers.js'); const { handleErrorResponse } = require('../../../utils/errorHandlers.js');
const db = require('../../../../models'); const db = require('../../../../models');
@ -13,23 +12,32 @@ const db = require('../../../../models');
const claimGet = ({ ip, originalUrl, params }, res) => { const claimGet = ({ ip, originalUrl, params }, res) => {
const name = params.name; const name = params.name;
const claimId = params.claimId; const claimId = params.claimId;
let resolveResult;
let getResult;
// resolve the claim // resolve the claim
db.Claim.resolveClaim(name, claimId) db.Claim.resolveClaim(name, claimId)
.then(resolveResult => { .then(result => {
// make sure a claim actually exists at that uri if (!result) {
if (!resolveResult) {
throw new Error('No matching uri found in Claim table'); throw new Error('No matching uri found in Claim table');
} }
let fileData = createFileData(resolveResult); resolveResult = result;
// get the claim return getClaim(`${name}#${claimId}`);
return Promise.all([fileData, getClaim(`${name}#${claimId}`)]);
}) })
.then(([ fileData, getResult ]) => { .then(result => {
fileData = addGetResultsToFileData(fileData, getResult); getResult = result;
return Promise.all([db.upsert(db.File, fileData, {name, claimId}, 'File'), getResult]);
}) })
.then(([ fileRecord, {message, completed} ]) => { .then(() => {
res.status(200).json({ success: true, message, completed }); 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 => { .catch(error => {
handleErrorResponse(originalUrl, ip, error, res); handleErrorResponse(originalUrl, ip, error, res);

View file

@ -47,29 +47,38 @@ const authenticateChannelCredentials = (channelName, channelId, userPassword) =>
}; };
const authenticateUser = (channelName, channelId, channelPassword, user) => { 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 // case: no channelName or channel Id are provided (anonymous), regardless of whether user token is provided
if (!channelName && !channelId) { if (!channelName && !channelId) {
return { resolve({
channelName : null, channelName : null,
channelClaimId: null, channelClaimId: null,
}; });
return;
} }
// case: channelName or channel Id are provided with user token // case: channelName or channel Id are provided with user token
if (user) { if (user) {
if (channelName && channelName !== user.channelName) { 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) { 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, channelName : user.channelName,
channelClaimId: user.channelClaimId, channelClaimId: user.channelClaimId,
}; });
return;
} }
// case: channelName or channel Id are provided with password instead of user token // case: channelName or channel Id are provided with password instead of user token
if (!channelPassword) throw new Error('no channel password provided'); if (!channelPassword) {
return authenticateChannelCredentials(channelName, channelId, channelPassword); reject(new Error('no channel password provided'));
return;
}
resolve(authenticateChannelCredentials(channelName, channelId, channelPassword));
});
}; };
module.exports = authenticateUser; module.exports = authenticateUser;

View file

@ -1,8 +1,7 @@
const logger = require('winston'); const logger = require('winston');
const { details, publishing } = require('@config/siteConfig'); const { details, publishing } = require('@config/siteConfig');
const createBasicPublishParams = (filePath, name, title, description, license, nsfw, thumbnail) => { const createPublishParams = (filePath, name, title, description, license, nsfw, thumbnail, channelName, channelClaimId) => {
logger.debug(`Creating Publish Parameters`);
// provide defaults for title // provide defaults for title
if (title === null || title.trim() === '') { if (title === null || title.trim() === '') {
title = name; title = name;
@ -15,7 +14,7 @@ const createBasicPublishParams = (filePath, name, title, description, license, n
if (license === null || license.trim() === '') { if (license === null || license.trim() === '') {
license = ' '; // default to empty string license = ' '; // default to empty string
} }
// create the publish params // create the basic publish params
const publishParams = { const publishParams = {
name, name,
file_path: filePath, file_path: filePath,
@ -34,8 +33,15 @@ const createBasicPublishParams = (filePath, name, title, description, license, n
if (thumbnail) { if (thumbnail) {
publishParams['metadata']['thumbnail'] = 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); logger.debug('publish params:', publishParams);
// return
return publishParams; return publishParams;
}; };
module.exports = createBasicPublishParams; module.exports = createPublishParams;

View file

@ -9,12 +9,14 @@ const { handleErrorResponse } = require('../../../utils/errorHandlers.js');
const checkClaimAvailability = require('../availability/checkClaimAvailability.js'); const checkClaimAvailability = require('../availability/checkClaimAvailability.js');
const publish = require('./publish.js'); const publish = require('./publish.js');
const createBasicPublishParams = require('./createBasicPublishParams.js'); const createPublishParams = require('./createPublishParams.js');
const createThumbnailPublishParams = require('./createThumbnailPublishParams.js'); const createThumbnailPublishParams = require('./createThumbnailPublishParams.js');
const parsePublishApiRequestBody = require('./parsePublishApiRequestBody.js'); const parsePublishApiRequestBody = require('./parsePublishApiRequestBody.js');
const parsePublishApiRequestFiles = require('./parsePublishApiRequestFiles.js'); const parsePublishApiRequestFiles = require('./parsePublishApiRequestFiles.js');
const authenticateUser = require('./authentication.js'); const authenticateUser = require('./authentication.js');
const CLAIM_TAKEN = 'CLAIM_TAKEN';
/* /*
route to publish a claim through the daemon 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}); return res.status(400).json({success: false, message: error.message});
} }
// check channel authorization // check channel authorization
Promise authenticateUser(channelName, channelId, channelPassword, user)
.all([ .then(({ channelName, channelClaimId }) => {
authenticateUser(channelName, channelId, channelPassword, user), return Promise.all([
checkClaimAvailability(name), 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), createThumbnailPublishParams(thumbnailFilePath, name, license, nsfw),
]) ])
.then(([{channelName, channelClaimId}, validatedClaimName, publishParams, thumbnailPublishParams]) => { })
// add channel details to the publish params .then(([ claimAvailable, publishParams, thumbnailPublishParams ]) => {
if (channelName && channelClaimId) { if (!claimAvailable) {
publishParams['channel_name'] = channelName; throw {
publishParams['channel_id'] = channelClaimId; name: CLAIM_TAKEN,
message: 'That claim name is already taken'
};
} }
// publish the thumbnail, if one exists // publish the thumbnail, if one exists
if (thumbnailPublishParams) { 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()); sendGATimingEvent('end-to-end', 'publish', fileType, gaStartTime, Date.now());
}) })
.catch(error => { .catch(error => {
if (error.name = CLAIM_TAKEN) {
res.status(400).json({
success: false,
message: error.message,
});
}
handleErrorResponse(originalUrl, ip, error, res); handleErrorResponse(originalUrl, ip, error, res);
}); });
}; };

View file

@ -1,6 +1,8 @@
const logger = require('winston'); const logger = require('winston');
const db = require('../../../../models');
const { publishClaim } = require('../../../../lbrynet'); 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 deleteFile = require('./deleteFile.js');
const publish = (publishParams, fileName, fileType) => { const publish = (publishParams, fileName, fileType) => {
@ -35,51 +37,34 @@ const publish = (publishParams, fileName, fileType) => {
logger.debug(`certificateId: ${certificateId}`); logger.debug(`certificateId: ${certificateId}`);
}) })
.then(() => { .then(() => {
// create the File record return Promise.all([
const fileRecord = { createFileRecordDataAfterPublish(fileName, fileType, publishParams, publishResults),
name : publishParams.name, createClaimRecordDataAfterPublish(certificateId, channelName, fileName, fileType, publishParams, publishResults),
claimId : publishResults.claim_id, ]);
title : publishParams.metadata.title, })
description: publishParams.metadata.description, .then(([fileRecord, claimRecord]) => {
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,
};
// upsert the records // 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]) => { .then(([file, claim]) => {
logger.debug('File and Claim records successfully created'); 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(() => { .then(() => {
logger.debug('File and Claim records successfully associated'); 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 => { .catch(error => {
logger.error('PUBLISH ERROR', error); logger.error('PUBLISH ERROR', error);

View 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,
}
),
]);
},
};

View file

@ -10,15 +10,16 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER }) => {
type : STRING, type : STRING,
allowNull: false, allowNull: false,
}, },
address: {
type : STRING,
allowNull: false,
},
outpoint: { outpoint: {
type : STRING, type : STRING,
allowNull: false, allowNull: false,
}, },
height: { fileHeight: {
type : INTEGER,
allowNull: false,
default : 0,
},
fileWidth: {
type : INTEGER, type : INTEGER,
allowNull: false, allowNull: false,
default : 0, default : 0,
@ -34,16 +35,6 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER }) => {
fileType: { fileType: {
type: STRING, type: STRING,
}, },
nsfw: {
type : BOOLEAN,
allowNull : false,
defaultValue: false,
},
trendingEligible: {
type : BOOLEAN,
allowNull : false,
defaultValue: true,
},
}, },
{ {
freezeTableName: true, freezeTableName: true,
@ -51,17 +42,8 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER }) => {
); );
File.associate = db => { File.associate = db => {
File.hasMany(db.Request);
File.hasOne(db.Claim); File.hasOne(db.Claim);
}; };
File.getRecentClaims = function () {
return this.findAll({
where: { nsfw: false, trendingEligible: true },
order: [['createdAt', 'DESC']],
limit: 25,
});
};
return File; return File;
}; };

View file

@ -5,7 +5,6 @@ const Certificate = require('./certificate.js');
const Channel = require('./channel.js'); const Channel = require('./channel.js');
const Claim = require('./claim.js'); const Claim = require('./claim.js');
const File = require('./file.js'); const File = require('./file.js');
const Request = require('./request.js');
const User = require('./user.js'); const User = require('./user.js');
const Blocked = require('./blocked.js'); const Blocked = require('./blocked.js');
const Tor = require('./tor.js'); const Tor = require('./tor.js');
@ -48,7 +47,6 @@ db['Certificate'] = sequelize.import('Certificate', Certificate);
db['Channel'] = sequelize.import('Channel', Channel); db['Channel'] = sequelize.import('Channel', Channel);
db['Claim'] = sequelize.import('Claim', Claim); db['Claim'] = sequelize.import('Claim', Claim);
db['File'] = sequelize.import('File', File); db['File'] = sequelize.import('File', File);
db['Request'] = sequelize.import('Request', Request);
db['User'] = sequelize.import('User', User); db['User'] = sequelize.import('User', User);
db['Blocked'] = sequelize.import('Blocked', Blocked); db['Blocked'] = sequelize.import('Blocked', Blocked);
db['Tor'] = sequelize.import('Tor', Tor); db['Tor'] = sequelize.import('Tor', Tor);

View file

@ -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;
};

View 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,
};

View 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,
};

View 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;

View 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,
};

View 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,
};