Software: Apache. PHP/8.1.30 uname -a: Linux server1.tuhinhossain.com 5.15.0-163-generic #173-Ubuntu SMP Tue Oct 14 17:51:00 UTC uid=1002(picotech) gid=1003(picotech) groups=1003(picotech),0(root) Safe-mode: OFF (not secure) /home/picotech/domains/note.picotech.app/public_html/src/models/ drwxr-xr-x | |
| Viewing file: Select action/file-type: const { DataTypes } = require('sequelize');
const { sequelize } = require('../config/database');
const path = require('path');
const fs = require('fs').promises;
const Recording = sequelize.define('Recording', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
},
meeting_id: {
type: DataTypes.INTEGER,
allowNull: false,
references: {
model: 'meetings',
key: 'id',
},
onDelete: 'CASCADE',
},
file_path: {
type: DataTypes.STRING(500),
allowNull: false,
validate: {
notEmpty: true,
},
},
file_name: {
type: DataTypes.STRING(255),
allowNull: false,
validate: {
notEmpty: true,
},
},
original_name: {
type: DataTypes.STRING(255),
allowNull: true,
comment: 'Original filename from upload',
},
mime_type: {
type: DataTypes.STRING(100),
allowNull: false,
validate: {
isIn: [['audio/mpeg', 'audio/wav', 'audio/mp4', 'audio/m4a', 'audio/webm', 'audio/ogg']],
},
},
duration: {
type: DataTypes.INTEGER,
allowNull: false,
comment: 'Duration in milliseconds',
validate: {
min: 0,
},
},
file_size: {
type: DataTypes.BIGINT,
allowNull: false,
comment: 'File size in bytes',
validate: {
min: 0,
},
},
timestamp: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: DataTypes.NOW,
comment: 'When the recording was made',
},
status: {
type: DataTypes.ENUM('uploading', 'processing', 'completed', 'failed', 'deleted'),
defaultValue: 'uploading',
allowNull: false,
},
quality: {
type: DataTypes.ENUM('low', 'medium', 'high'),
defaultValue: 'medium',
allowNull: false,
},
sample_rate: {
type: DataTypes.INTEGER,
allowNull: true,
comment: 'Audio sample rate in Hz',
},
bit_rate: {
type: DataTypes.INTEGER,
allowNull: true,
comment: 'Audio bit rate in kbps',
},
channels: {
type: DataTypes.INTEGER,
allowNull: true,
defaultValue: 1,
comment: 'Number of audio channels (1=mono, 2=stereo)',
},
metadata: {
type: DataTypes.JSON,
allowNull: true,
defaultValue: {},
comment: 'Additional metadata like device info, location, etc.',
},
checksum: {
type: DataTypes.STRING(64),
allowNull: true,
comment: 'File checksum for integrity verification',
},
transcription_status: {
type: DataTypes.ENUM('pending', 'processing', 'completed', 'failed', 'skipped'),
defaultValue: 'pending',
allowNull: false,
},
chunking_status: {
type: DataTypes.ENUM('pending', 'processing', 'completed', 'failed', 'skipped'),
defaultValue: 'pending',
allowNull: false,
comment: 'Status of audio chunking process',
},
error_message: {
type: DataTypes.TEXT,
allowNull: true,
comment: 'Error message if processing failed',
},
}, {
tableName: 'recordings',
timestamps: true,
indexes: [
{
fields: ['meeting_id'],
},
{
fields: ['status'],
},
{
fields: ['transcription_status'],
},
{
fields: ['chunking_status'],
},
{
fields: ['timestamp'],
},
{
fields: ['meeting_id', 'status'],
},
],
});
// Instance methods
Recording.prototype.getFileExtension = function() {
return path.extname(this.file_name).toLowerCase();
};
Recording.prototype.getDurationInSeconds = function() {
return Math.floor(this.duration / 1000);
};
Recording.prototype.getDurationFormatted = function() {
const totalSeconds = this.getDurationInSeconds();
const minutes = Math.floor(totalSeconds / 60);
const seconds = totalSeconds % 60;
return `${minutes}:${seconds.toString().padStart(2, '0')}`;
};
Recording.prototype.getFileSizeFormatted = function() {
const bytes = this.file_size;
if (bytes === 0) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
};
Recording.prototype.isProcessed = function() {
return this.status === 'completed';
};
Recording.prototype.canBeTranscribed = function() {
return this.status === 'completed' &&
['pending', 'failed'].includes(this.transcription_status);
};
Recording.prototype.markAsCompleted = async function() {
this.status = 'completed';
return this.save();
};
Recording.prototype.markAsFailed = async function(errorMessage) {
this.status = 'failed';
this.error_message = errorMessage;
return this.save();
};
Recording.prototype.updateTranscriptionStatus = async function(status, errorMessage = null) {
this.transcription_status = status;
if (errorMessage) {
this.error_message = errorMessage;
}
return this.save();
};
Recording.prototype.updateChunkingStatus = async function(status, errorMessage = null) {
this.chunking_status = status;
if (errorMessage) {
this.error_message = errorMessage;
}
return this.save();
};
Recording.prototype.deleteFile = async function() {
try {
await fs.unlink(this.file_path);
this.status = 'deleted';
await this.save();
return true;
} catch (error) {
console.error('Error deleting file:', error);
return false;
}
};
// Class methods
Recording.findByMeeting = function(meetingId, options = {}) {
return this.findAll({
where: {
meeting_id: meetingId,
status: { [sequelize.Sequelize.Op.ne]: 'deleted' },
...options.where,
},
order: options.order || [['timestamp', 'ASC']],
...options,
});
};
Recording.findPendingTranscription = function(limit = 10) {
return this.findAll({
where: {
status: 'completed',
transcription_status: 'pending',
},
order: [['created_at', 'ASC']],
limit,
});
};
Recording.findPendingChunking = function(limit = 10) {
return this.findAll({
where: {
status: 'completed',
chunking_status: 'pending',
duration: { [sequelize.Sequelize.Op.gt]: 30000 }, // Only chunk recordings longer than 30 seconds
},
order: [['created_at', 'ASC']],
limit,
});
};
Recording.findByStatus = function(status, options = {}) {
return this.findAll({
where: {
status,
...options.where,
},
order: options.order || [['created_at', 'DESC']],
limit: options.limit,
});
};
Recording.getTotalSizeByMeeting = async function(meetingId) {
const result = await this.findOne({
where: {
meeting_id: meetingId,
status: { [sequelize.Sequelize.Op.ne]: 'deleted' },
},
attributes: [
[sequelize.fn('SUM', sequelize.col('file_size')), 'total_size'],
],
raw: true,
});
return parseInt(result.total_size) || 0;
};
Recording.getTotalDurationByMeeting = async function(meetingId) {
const result = await this.findOne({
where: {
meeting_id: meetingId,
status: { [sequelize.Sequelize.Op.ne]: 'deleted' },
},
attributes: [
[sequelize.fn('SUM', sequelize.col('duration')), 'total_duration'],
],
raw: true,
});
return parseInt(result.total_duration) || 0;
};
module.exports = Recording; |
:: Command execute :: | |
--[ c99shell v. 2.5 [PHP 8 Update] [24.05.2025] | Generation time: 0.0032 ]-- |