Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Auto change quality #341

Open
wants to merge 22 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
e5c4ba9
added Resolution model
Yemeni Aug 23, 2020
db1cc56
Revert "added Resolution model"
Yemeni Aug 27, 2020
433b128
Merge branch 'master' into auto-change-quality
Yemeni Aug 27, 2020
6ee186a
added ffmpeg quality conversion to videos
Yemeni Aug 27, 2020
91819ef
added quality in upload model and made uploading push to mongodb
Yemeni Aug 28, 2020
753adb3
nasty stuff that are not checked yet
Yemeni Aug 28, 2020
e06f0fb
working example
mayeaux Aug 28, 2020
8199125
added loop over available video qualities
Yemeni Aug 29, 2020
892dc11
Added condition before converting qualities in upload
Yemeni Aug 31, 2020
aaa0f7d
Merge branch 'master' into auto-change-quality
Yemeni Sep 16, 2020
8f820b8
Merge branch 'master' into auto-change-quality
Yemeni Sep 16, 2020
5a3d80d
refactored coverting code + pending converts shows in media
Yemeni Sep 16, 2020
359359d
Removed extra spaces for eslint
Yemeni Sep 16, 2020
90adcd5
Update media.pug
Yemeni Sep 17, 2020
cb2a8d4
Merge branch 'master' into auto-change-quality
Yemeni Sep 19, 2020
a557926
Added bull and bull-board package
Yemeni Sep 19, 2020
711c50e
Added queue logic to converting
Yemeni Sep 19, 2020
77e89d2
Merge remote-tracking branch 'origin/auto-change-quality' into auto-c…
Yemeni Sep 19, 2020
c815416
added 480p video quality
Yemeni Sep 19, 2020
0a3b35a
added 480p video quality
Yemeni Sep 19, 2020
5d85a55
eslint removed unnecessary semi colon
Yemeni Sep 19, 2020
3511353
mark as complete and inform users that all video qualities are complete
Yemeni Sep 26, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions app.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const ngrok = require('ngrok');
const commandExists = require('command-exists');
const errorHandler = require('errorhandler');
const jsHelpers = require('./lib/helpers/js-helpers');
const { UI } = require('bull-board');

/** FOR FINDING ERRANT LOGS **/
if(process.env.SHOW_LOG_LOCATION == 'true' || 2 == 1){
Expand Down Expand Up @@ -161,6 +162,12 @@ if(cluster.isMaster){
path: '/hiddenStatus'
}));

// TODO: put this behind auth
/** bull-board queue monitor **/
if(process.env.NODE_ENV === 'development'){
app.use('/admin/queues', UI);
}

app.use(compression());
app.use(sass({
src: path.join(__dirname, 'public'),
Expand Down
145 changes: 139 additions & 6 deletions controllers/backend/uploading.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const { b2, bucket, hostUrl } = require('../../lib/uploading/backblaze');
const ffmpegHelper = require('../../lib/uploading/ffmpeg');
const {
markUploadAsComplete,
markConvertAsComplete,
updateUsersUnreadSubscriptions,
runTimeoutFunction,
userCanUploadContentOfThisRating
Expand All @@ -40,6 +41,8 @@ const backblaze = require('../../lib/uploading/backblaze');
// console.log(`SAVE AND SERVE FILES DIRECTORY: ${saveAndServeFilesDirectory}`);

var resumable = require('../../lib/uploading/resumable.js')(__dirname + '/upload');
var Queue = require('bull');
const { setQueues } = require('bull-board');

const moderationUpdatesToDiscord = process.env.MODERATION_UPDATES_TO_DISCORD == 'true';

Expand Down Expand Up @@ -537,7 +540,6 @@ exports.postFileUpload = async(req, res) => {
}

upload.fileType = 'video';

upload = await upload.save();
}

Expand All @@ -557,17 +559,148 @@ exports.postFileUpload = async(req, res) => {

uploadLogger.info('Upload marked as complete', logObject);

updateUsersUnreadSubscriptions(user);

uploadLogger.info('Updated subscribed users subscriptions', logObject);

if(!responseSent){
responseSent = true;
aboutToProcess(res, channelUrl, uniqueTag);
}

var videoQualityQueue = new Queue('Video Quality is going to convert');
setQueues([videoQualityQueue]);

videoQualityQueue.process(async function(job, done){

Upload.update({'videoQualities._id': job.data.videoQualitiesId}, {'$set': {
'videoQualities.$.status': 'converting',
'videoQualities.$.fileSizeInMb': 2
}}, function(err, result){

if(err){
res.send(err);
}
else {
res.send(result);
}

});

job.progress(5);

await ffmpegHelper.convertVideo({
uploadedPath: job.data.uploadedPath,
title: job.data.title,
bitrate: job.data.bitrate,
savePath: job.data.savePath,
uniqueTag: job.data.uniqueTag,
quality: job.data.quality
});

job.progress(42); // TODO: set progress based on convert percentage

Upload.update({'videoQualities._id': job.data.videoQualitiesId}, {'$set': {
'videoQualities.$.status': 'complete',
'videoQualities.$.fileSizeInMb': 3
}}, function(err, result){

if(err){
res.send(err);
}
// else {
// res.send(result);
// }

});

// Update if all qualities are converted
// TODO: needs some cleanup
// db.uploads.findOne({}).videoQualities.every(arr=> arr.status == 'complete');
await Upload.findOne({uniqueTag : job.data.uploadUniqueTag })
.select('videoQualities')
.exec(async function(err, txs){
if(txs.videoQualities.every(arr=> arr.status === 'complete')){
await markConvertAsComplete(uniqueTag, channelUrl, user);
uploadLogger.info('Quality all qualities are converted', logObject);
updateUsersUnreadSubscriptions(user);
uploadLogger.info('Updated subscribed users subscriptions', logObject);
}

});

job.progress(100);
done();

});
/*
all values in Kbps (kilobits per seconds)
2160p (4k) -> 13000
1440p 6000
1080p 3000
720p 2250
480p 500
360p 400
240p 300
144p 80

0-80 144p
81-299 360p
*/
// TODO: put qualities somewhere else
const qualities = [
{ name: '256 x 144p', quality: 144, bitrate: 80 },
{ name: '426 x 240p', quality: 240, bitrate: 300 },
{ name: '640 x 360p', quality: 360, bitrate: 400 },
{ name: '854 x 480p', quality: 480, bitrate: 500 },
{ name: '1280 x 720p', quality: 720, bitrate: 2250 }
];
// TODO: Do quality stuff
// TODO: loop quality stuff
// video will be available in original quality while this is happening in the background
// TODO: make a quality conversion counter/progress bar
for(var i = 0; i < qualities.length; i = i + 1){
if(bitrate >= qualities[i].bitrate){
// qualitySavePath = `${channelUrlFolder}/${uniqueTag}-${qualities[i].quality}.mp4`;

let qualityExists = upload.videoQualities.find(o => o.quality === qualities[i].quality);
if(!qualityExists){

upload.videoQualities.push({
quality: qualities[i].quality,
bitrate: qualities[i].bitrate,
fileSizeInMb: 1,
status: 'pending' // TODO: use enum here not string
});
}

}
}

await upload.save();

for(var i = 0; i < upload.videoQualities.length; i = i + 1){
if(bitrate >= upload.videoQualities[i].bitrate){
if(upload.videoQualities[i].status != 'complete'){
// TODO: this is kind of ugly lmao
qualitySavePath = `${channelUrlFolder}/${uniqueTag}-${upload.videoQualities[i].quality}.mp4`;

videoQualityQueue.add({
uploadId: upload._id,
videoQualitiesId: upload.videoQualities[i]._id,
uploadedPath: fileInDirectory,
title: title,
bitrate: upload.videoQualities[i].bitrate,
savePath: qualitySavePath,
uploadUniqueTag: uniqueTag,
uniqueTag: uniqueTag + '-' + upload.videoQualities[i].quality,
quality: upload.videoQualities[i].quality
});
}

await upload.save();
}

}

});

// });
}
});
} catch(err){
Expand Down
7 changes: 5 additions & 2 deletions lib/uploading/ffmpeg.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ function setRedisClient({ currentSeconds, uniqueTag, progress, force }){

// setRedisClient();

function convertVideo({ uploadedPath, title, bitrate, savePath, uniqueTag }){
function convertVideo({ uploadedPath, title, quality, bitrate, savePath, uniqueTag }){
return new Promise(function(resolve, reject) {

let command;
Expand All @@ -88,9 +88,12 @@ function convertVideo({ uploadedPath, title, bitrate, savePath, uniqueTag }){
command = ffmpeg(uploadedPath).videoCodec('libx264').audioCodec('aac').format('mp4')
.outputOptions([ '-preset faster', '-b:v 2500k' ]);

} else {
} else if(!quality){
command = ffmpeg(uploadedPath).videoCodec('libx264').audioCodec('aac').format('mp4')

} else{
command = ffmpeg(uploadedPath).videoCodec('libx264').audioCodec('aac').format('mp4')
.outputOptions([ '-preset faster', '-b:v ' + bitrate + 'k', '-vf scale=-2:' + quality ]);
}

let secondCounter = 0;
Expand Down
16 changes: 16 additions & 0 deletions lib/uploading/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,21 @@ async function markUploadAsComplete(uniqueTag, channelUrl, user, res){
return 'success'
}

async function markConvertAsComplete(uniqueTag, channelUrl, user, res){
// upload = await Upload.findOne({ uniqueTag });
// upload.status = 'completed';

// upload.processingCompletedAt = new Date();

// await upload.save();
console.log("quality converted")

// user.uploads.push(upload._id);
// await user.save();

return 'success'
}

async function updateUsersUnreadSubscriptions(user){
const subscriptions = await Subscription.find({ subscribedToUser: user._id, active: true });

Expand Down Expand Up @@ -84,6 +99,7 @@ const bytesToGb = (bytes, decimalPlaces = 4) => {

module.exports = {
markUploadAsComplete,
markConvertAsComplete,
updateUsersUnreadSubscriptions,
runTimeoutFunction,
userCanUploadContentOfThisRating,
Expand Down
9 changes: 9 additions & 0 deletions models/Upload.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,15 @@ const uploadSchema = new mongoose.Schema({
// pretty sure this is in bytes
fileSize: Number, // TODO: should support highQualityFileSize as well for compressions

videoQualities: [
{
quality: 'String',
fileSizeInMb: Number,
bitrate: Number,
status: {type: String, enum: ['pending', 'converting', 'complete'], default: 'pending'}
}
],

bitrateInKbps: Number,
dimensions: {
height: String,
Expand Down
Loading