+module.exports = function (sequelize, DataTypes) {
+ const Video = sequelize.define('Video',
+ {
+ id: {
+ type: DataTypes.UUID,
+ defaultValue: DataTypes.UUIDV4,
+ primaryKey: true,
+ validate: {
+ isUUID: 4
+ }
+ },
+ name: {
+ type: DataTypes.STRING,
+ allowNull: false,
+ validate: {
+ nameValid: function (value) {
+ const res = customVideosValidators.isVideoNameValid(value)
+ if (res === false) throw new Error('Video name is not valid.')
+ }
+ }
+ },
+ extname: {
+ type: DataTypes.ENUM(values(constants.CONSTRAINTS_FIELDS.VIDEOS.EXTNAME)),
+ allowNull: false
+ },
+ remoteId: {
+ type: DataTypes.UUID,
+ allowNull: true,
+ validate: {
+ isUUID: 4
+ }
+ },
+ description: {
+ type: DataTypes.STRING,
+ allowNull: false,
+ validate: {
+ descriptionValid: function (value) {
+ const res = customVideosValidators.isVideoDescriptionValid(value)
+ if (res === false) throw new Error('Video description is not valid.')
+ }
+ }
+ },
+ infoHash: {
+ type: DataTypes.STRING,
+ allowNull: false,
+ validate: {
+ infoHashValid: function (value) {
+ const res = customVideosValidators.isVideoInfoHashValid(value)
+ if (res === false) throw new Error('Video info hash is not valid.')
+ }
+ }
+ },
+ duration: {
+ type: DataTypes.INTEGER,
+ allowNull: false,
+ validate: {
+ durationValid: function (value) {
+ const res = customVideosValidators.isVideoDurationValid(value)
+ if (res === false) throw new Error('Video duration is not valid.')
+ }
+ }
+ },
+ views: {
+ type: DataTypes.INTEGER,
+ allowNull: false,
+ defaultValue: 0,
+ validate: {
+ min: 0,
+ isInt: true
+ }
+ }
+ },
+ {
+ indexes: [
+ {
+ fields: [ 'authorId' ]
+ },
+ {
+ fields: [ 'remoteId' ]
+ },
+ {
+ fields: [ 'name' ]
+ },
+ {
+ fields: [ 'createdAt' ]
+ },
+ {
+ fields: [ 'duration' ]
+ },
+ {
+ fields: [ 'infoHash' ]
+ },
+ {
+ fields: [ 'views' ]
+ }
+ ],
+ classMethods: {
+ associate,
+
+ generateThumbnailFromData,
+ getDurationFromFile,
+ list,
+ listForApi,
+ listOwnedAndPopulateAuthorAndTags,
+ listOwnedByAuthor,
+ load,
+ loadByHostAndRemoteId,
+ loadAndPopulateAuthor,
+ loadAndPopulateAuthorAndPodAndTags,
+ searchAndPopulateAuthorAndPodAndTags
+ },
+ instanceMethods: {
+ generateMagnetUri,
+ getVideoFilename,
+ getThumbnailName,
+ getPreviewName,
+ getTorrentName,
+ isOwned,
+ toFormatedJSON,
+ toAddRemoteJSON,
+ toUpdateRemoteJSON
+ },
+ hooks: {
+ beforeValidate,
+ beforeCreate,
+ afterDestroy
+ }
+ }
+ )
+
+ return Video
+}
+
+function beforeValidate (video, options, next) {
+ // Put a fake infoHash if it does not exists yet
+ if (video.isOwned() && !video.infoHash) {
+ // 40 hexa length
+ video.infoHash = '0123456789abcdef0123456789abcdef01234567'
+ }
+
+ return next(null)
+}
+
+function beforeCreate (video, options, next) {
+ const tasks = []
+
+ if (video.isOwned()) {
+ const videoPath = pathUtils.join(constants.CONFIG.STORAGE.VIDEOS_DIR, video.getVideoFilename())
+
+ tasks.push(
+ function createVideoTorrent (callback) {
+ const options = {
+ announceList: [
+ [ constants.CONFIG.WEBSERVER.WS + '://' + constants.CONFIG.WEBSERVER.HOSTNAME + ':' + constants.CONFIG.WEBSERVER.PORT + '/tracker/socket' ]
+ ],
+ urlList: [
+ constants.CONFIG.WEBSERVER.URL + constants.STATIC_PATHS.WEBSEED + video.getVideoFilename()
+ ]
+ }
+
+ createTorrent(videoPath, options, function (err, torrent) {
+ if (err) return callback(err)
+
+ const filePath = pathUtils.join(constants.CONFIG.STORAGE.TORRENTS_DIR, video.getTorrentName())
+ fs.writeFile(filePath, torrent, function (err) {
+ if (err) return callback(err)
+
+ const parsedTorrent = parseTorrent(torrent)
+ video.set('infoHash', parsedTorrent.infoHash)
+ video.validate().asCallback(callback)
+ })
+ })
+ },
+
+ function createVideoThumbnail (callback) {
+ createThumbnail(video, videoPath, callback)
+ },
+
+ function createVIdeoPreview (callback) {
+ createPreview(video, videoPath, callback)
+ }
+ )
+
+ return parallel(tasks, next)