diff --git a/.sequelizerc b/.sequelizerc index 757fef10..fe827ed6 100644 --- a/.sequelizerc +++ b/.sequelizerc @@ -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') } diff --git a/README.md b/README.md index d6fb1f7a..c883e6b8 100644 --- a/README.md +++ b/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/) diff --git a/package-lock.json b/package-lock.json index 2e9a4fd1..38f430f1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index 065928fb..545741e1 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/server/controllers/api/claim/get/addGetResultsToFileData.js b/server/controllers/api/claim/get/addGetResultsToFileData.js deleted file mode 100644 index 3fe3cc43..00000000 --- a/server/controllers/api/claim/get/addGetResultsToFileData.js +++ /dev/null @@ -1,5 +0,0 @@ -module.exports = (fileInfo, getResult) => { - fileInfo.fileName = getResult.file_name; - fileInfo.filePath = getResult.download_path; - return fileInfo; -}; diff --git a/server/controllers/api/claim/get/createFileData.js b/server/controllers/api/claim/get/createFileData.js deleted file mode 100644 index 71d295ab..00000000 --- a/server/controllers/api/claim/get/createFileData.js +++ /dev/null @@ -1,13 +0,0 @@ -module.exports = ({ name, claimId, outpoint, height, address, nsfw, contentType }) => { - return { - name, - claimId, - outpoint, - height, - address, - fileName: '', - filePath: '', - fileType: contentType, - nsfw, - }; -}; diff --git a/server/controllers/api/claim/get/index.js b/server/controllers/api/claim/get/index.js index 1a8757e8..8ab81844 100644 --- a/server/controllers/api/claim/get/index.js +++ b/server/controllers/api/claim/get/index.js @@ -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); diff --git a/server/controllers/api/claim/publish/authentication.js b/server/controllers/api/claim/publish/authentication.js index d41902ef..38bed36e 100644 --- a/server/controllers/api/claim/publish/authentication.js +++ b/server/controllers/api/claim/publish/authentication.js @@ -47,29 +47,38 @@ const authenticateChannelCredentials = (channelName, channelId, userPassword) => }; const authenticateUser = (channelName, channelId, channelPassword, user) => { - // case: no channelName or channel Id are provided (anonymous), regardless of whether user token is provided - if (!channelName && !channelId) { - return { - channelName : null, - channelClaimId: null, - }; - } - // 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'); + return new Promise((resolve, reject) => { + // case: no channelName or channel Id are provided (anonymous), regardless of whether user token is provided + if (!channelName && !channelId) { + resolve({ + channelName : null, + channelClaimId: null, + }); + return; } - if (channelId && channelId !== user.channelClaimId) { - throw new Error('the provided channel id does not match user credentials'); + // case: channelName or channel Id are provided with user token + if (user) { + if (channelName && channelName !== user.channelName) { + reject(new Error('the provided channel name does not match user credentials')); + return; + } + if (channelId && channelId !== user.channelClaimId) { + reject(new Error('the provided channel id does not match user credentials')); + return; + } + resolve({ + channelName : user.channelName, + channelClaimId: user.channelClaimId, + }); + return; } - return { - channelName : user.channelName, - channelClaimId: user.channelClaimId, - }; - } - // 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); + // case: channelName or channel Id are provided with password instead of user token + if (!channelPassword) { + reject(new Error('no channel password provided')); + return; + } + resolve(authenticateChannelCredentials(channelName, channelId, channelPassword)); + }); }; module.exports = authenticateUser; diff --git a/server/controllers/api/claim/publish/createBasicPublishParams.js b/server/controllers/api/claim/publish/createPublishParams.js similarity index 68% rename from server/controllers/api/claim/publish/createBasicPublishParams.js rename to server/controllers/api/claim/publish/createPublishParams.js index 6ab4acd8..facdc32f 100644 --- a/server/controllers/api/claim/publish/createBasicPublishParams.js +++ b/server/controllers/api/claim/publish/createPublishParams.js @@ -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; diff --git a/server/controllers/api/claim/publish/index.js b/server/controllers/api/claim/publish/index.js index b43995f5..6cf726f3 100644 --- a/server/controllers/api/claim/publish/index.js +++ b/server/controllers/api/claim/publish/index.js @@ -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), - checkClaimAvailability(name), - createBasicPublishParams(filePath, name, title, description, license, nsfw, thumbnail), - 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; + authenticateUser(channelName, channelId, channelPassword, user) + .then(({ channelName, channelClaimId }) => { + return Promise.all([ + checkClaimAvailability(name), + createPublishParams(filePath, name, title, description, license, nsfw, thumbnail, channelName, channelClaimId), + createThumbnailPublishParams(thumbnailFilePath, name, license, nsfw), + ]) + }) + .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); }); }; diff --git a/server/controllers/api/claim/publish/publish.js b/server/controllers/api/claim/publish/publish.js index b80dc0b7..5f0d01a1 100644 --- a/server/controllers/api/claim/publish/publish.js +++ b/server/controllers/api/claim/publish/publish.js @@ -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); diff --git a/server/migrations/File_AddHeightAndWidthColumn.js b/server/migrations/File_AddHeightAndWidthColumn.js new file mode 100644 index 00000000..c6ccc5be --- /dev/null +++ b/server/migrations/File_AddHeightAndWidthColumn.js @@ -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, + } + ), + ]); + }, +}; diff --git a/server/models/file.js b/server/models/file.js index 1ba0b5fa..0566384d 100644 --- a/server/models/file.js +++ b/server/models/file.js @@ -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; }; diff --git a/server/models/index.js b/server/models/index.js index a13d58ec..f15a453c 100644 --- a/server/models/index.js +++ b/server/models/index.js @@ -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); diff --git a/server/models/request.js b/server/models/request.js deleted file mode 100644 index 501fb441..00000000 --- a/server/models/request.js +++ /dev/null @@ -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; -}; diff --git a/server/models/utils/createClaimRecordData.js b/server/models/utils/createClaimRecordData.js new file mode 100644 index 00000000..91e25ca3 --- /dev/null +++ b/server/models/utils/createClaimRecordData.js @@ -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, +}; diff --git a/server/models/utils/createFileRecordData.js b/server/models/utils/createFileRecordData.js new file mode 100644 index 00000000..aa0802cf --- /dev/null +++ b/server/models/utils/createFileRecordData.js @@ -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, +}; diff --git a/server/utils/getMediaDimensions.js b/server/utils/getMediaDimensions.js new file mode 100644 index 00000000..fc5e79a1 --- /dev/null +++ b/server/utils/getMediaDimensions.js @@ -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; diff --git a/server/utils/imageProcessing.js b/server/utils/imageProcessing.js new file mode 100644 index 00000000..370530f6 --- /dev/null +++ b/server/utils/imageProcessing.js @@ -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, +}; diff --git a/server/utils/videoProcessing.js b/server/utils/videoProcessing.js new file mode 100644 index 00000000..508f6d10 --- /dev/null +++ b/server/utils/videoProcessing.js @@ -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, +};