diff --git a/app.js b/app.js index 78326faf..67cb026a 100644 --- a/app.js +++ b/app.js @@ -227,6 +227,7 @@ if(cluster.isMaster){ requestPath === '/upload' || requestPath === '/account/profile' || requestPath === '/api/channel/thumbnail/delete' || + requestPath === '/api/uploadFileThumbnail' || requestPath.match(editUploadRegexp) || requestPath.match(deleteUploadThumbnailRegexp) || requestPath === '/livestream/on-live-auth' || diff --git a/controllers/backend/uploading.js b/controllers/backend/uploading.js index 800693d8..f768e277 100644 --- a/controllers/backend/uploading.js +++ b/controllers/backend/uploading.js @@ -10,6 +10,8 @@ const path = require('path'); const mkdirp = Promise.promisifyAll(require('mkdirp')); var randomstring = require('randomstring'); var CombinedStream = require('combined-stream'); +const FileType = require('file-type'); +const readChunk = require('read-chunk'); const redisClient = require('../../config/redis'); @@ -165,7 +167,8 @@ function testIfUserRestricted(user, logObject, res){ function aboutToProcess(res, channelUrl, uniqueTag){ res.send({ message: 'ABOUT TO PROCESS', - url: `/user/${channelUrl}/${uniqueTag}?u=t` + url: `/user/${channelUrl}/${uniqueTag}?u=t`, + uniqueTag }); } @@ -417,6 +420,8 @@ exports.postFileUpload = async(req, res) => { uploadLogger.info('Concat done', logObject); + // TODO: pull this out into its own function + let bitrate, codecName, codecProfile; if(upload.fileType !== 'image'){ @@ -587,25 +592,32 @@ exports.postFileUpload = async(req, res) => { uploadLogger.info('Upload marked as complete', logObject); - updateUsersUnreadSubscriptions(user); - - uploadLogger.info('Updated subscribed users subscriptions', logObject); - // this is admin upload for all alertAdminOfNewUpload(user, upload); uploadLogger.info('Alert admins of a new upload', logObject); - if(upload.visibility == 'public'){ - // update user push notifications - updateUsersPushNotifications(user, upload); + // if visibility is public, send push and email notifications + if(upload.visibility === 'public'){ + + // update the subscription amount of subscribing users + updateUsersUnreadSubscriptions(user); + + uploadLogger.info('Updated subscribed users subscriptions', logObject); + + // send push and email notifs if production + if(process.env.NODE_ENV === 'production'){ + // update user push notifications + updateUsersPushNotifications(user, upload); - uploadLogger.info('Update users push notifications', logObject); + uploadLogger.info('Update users push notifications', logObject); - // update user email notifications - updateUsersEmailNotifications(user, upload, req.host); + // update user email notifications + updateUsersEmailNotifications(user, upload, req.host); + + uploadLogger.info('Update users email notifications', logObject); + } - uploadLogger.info('Update users email notifications', logObject); } // upload is complete, send it off to user (aboutToProcess is a misnomer here) @@ -732,3 +744,92 @@ exports.adminUpload = async(req, res) => { // }); }; + +/** + * POST /api/uploadFileThumbnail + * Upload file thumbnail + */ +exports.postThumbnailUpload = async(req, res) => { + console.log('files'); + console.log(req.files); + + console.log('body'); + console.log(req.body); + + const uniqueTag = req.body.uploadUniqueTag; + + // check if there's a thumbnail + let thumbnailFile; + + // if req there are files and + if(req.files && req.files.thumbnailFile){ + thumbnailFile = req.files.thumbnailFile; + } else { + res.status(500); + return res.send('no thumbnail file'); + } + + const filename = thumbnailFile.originalFilename; + // size in bytes + const fileSize = thumbnailFile.size; + + // 5 MB + if(fileSize > 5242880){ + res.status(500); + return res.send('file too large'); + } + + const filePath = thumbnailFile.path; + + const buffer = readChunk.sync(filePath, 0, 4100); + + const bufferFileType = await FileType.fromBuffer(buffer); + + console.log('Buffer file type ' + bufferFileType); + + // comes back at 'mp4' to prepend . to make .mp4 + const extension = '.' + String(bufferFileType.ext); + + console.log('extension'); + console.log(extension); + + const fileType = getMediaType('hackforsetup' + extension); + + if(fileType !== 'image'){ + res.status(500); + return res.send('not an image'); + } + + console.log('File type'); + console.log(fileType); + + console.log(bufferFileType); + + const channelUrl = req.user.channelUrl; + + const saveFileDirectory = `${saveAndServeFilesDirectory}/${channelUrl}/${uniqueTag}-custom${extension}`; + + await fs.move(filePath, saveFileDirectory); + + const upload = await Upload.findOne({ uniqueTag }); + + if(!upload.thumbnails){ + upload.thumbnails = {}; + } + + upload.thumbnails.custom = `${uniqueTag}-custom${extension}`; + + if(process.env.UPLOAD_TO_B2 === 'true'){ + // TODO: check this + // await backblaze.editploadThumbnailToB2(req.user.channelUrl, upload.uniqueTag, extension, saveFileDirectory); + } + + // sendUploadThumbnailToB2(args) + + await upload.save(); + + res.status(200); + + return res.send('success'); + +}; diff --git a/controllers/frontend/account.js b/controllers/frontend/account.js index 6a378651..88cd759f 100644 --- a/controllers/frontend/account.js +++ b/controllers/frontend/account.js @@ -59,6 +59,12 @@ const forgotEmailFunctionalityOn = process.env.FORGOT_PASSWORD_EMAIL_FUNCTIONALI const { attachDataToUploadsAsUploads } = require('../../lib/helpers/addFieldsToUploads'); +const stripeToken = process.env.STRIPE_FRONTEND_TOKEN; + +const plusEnabled = process.env.PLUS_ENABLED === 'true'; + +const verifyEmailFunctionalityOn = process.env.CONFIRM_EMAIL_FUNCTIONALITY_ON === 'true'; + // TODO: pull this function out function removeTrailingSlash(requestPath){ if(requestPath.charAt(requestPath.length - 1) == '/'){ @@ -68,37 +74,6 @@ function removeTrailingSlash(requestPath){ return requestPath; } -// TODO: pull this function out -async function addValuesIfNecessary(upload, channelUrl){ - if(upload.fileType == 'video' || upload.fileType == 'audio'){ - if(!upload.durationInSeconds || !upload.formattedDuration){ - - var server = uploadServer; - if(server.charAt(0) == '/') // the slash confuses the file reading, because host root directory is not the same as machine root directory - server = server.substr(1); - - const uploadLocation = `${server}/${channelUrl}/${upload.uniqueTag + upload.fileExtension}`; - - try { - const duration = await getUploadDuration(uploadLocation, upload.fileType); - console.log(duration); - - let uploadDocument = await Upload.findOne({uniqueTag: upload.uniqueTag}); - - uploadDocument.durationInSeconds = duration.seconds; - uploadDocument.formattedDuration = duration.formattedTime; - - await uploadDocument.save(); - - } catch(err){ - /** if the file has been deleted then it won't blow up **/ - // console.log(err); - } - // console.log('have to add'); - } - } -} - /** * GET /upload * Page to facilitate user uploads @@ -124,7 +99,8 @@ exports.getFileUpload = async(req, res) => { categories, maxRatingAllowed: process.env.MAX_RATING_ALLOWED, userCanUploadContentOfThisRating, - secondsToFormattedTime + secondsToFormattedTime, + plusEnabled }); }; @@ -903,11 +879,6 @@ exports.getSignup = (req, res) => { * Account page. */ exports.getAccount = async(req, res) => { - const stripeToken = process.env.STRIPE_FRONTEND_TOKEN; - - const plusEnabled = process.env.PLUS_ENABLED == 'true'; - - const verifyEmailFunctionalityOn = process.env.CONFIRM_EMAIL_FUNCTIONALITY_ON == 'true'; // give user an upload token if(!req.user.uploadToken){ diff --git a/controllers/frontend/mediaPlayer.js b/controllers/frontend/mediaPlayer.js index 0570f696..7686af5b 100644 --- a/controllers/frontend/mediaPlayer.js +++ b/controllers/frontend/mediaPlayer.js @@ -74,10 +74,14 @@ function getFormattedFileSize(upload){ */ exports.getMedia = async(req, res) => { + // TODO: pull out plus redirect thing + // get the amount of slashes, to determine if the user is allowed // to access the vanity url version of this url let requestPath = req.path; + var queryParams = req.url.split('?')[1]; + if(requestPath.charAt(requestPath.length - 1) == '/'){ requestPath = requestPath.substr(0, requestPath.length - 1); } @@ -118,10 +122,17 @@ exports.getMedia = async(req, res) => { }); } + // console.log(req.path); + // TODO: make sure to add query params here // if it's three but you're plus, then move to shortened url if(amountOfSlashes === 3 && user.plan == 'plus'){ - return res.redirect(`/${user.channelUrl}/${upload.uniqueTag}`); + let redirectPath = `/${user.channelUrl}/${upload.uniqueTag}`; + if(queryParams){ + redirectPath = redirectPath + '?' + queryParams; + } + + return res.redirect(redirectPath); } // TODO: pull this thing out diff --git a/package-lock.json b/package-lock.json index e6cf1b97..92c1d79e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8716,6 +8716,15 @@ "mute-stream": "~0.0.4" } }, + "read-chunk": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/read-chunk/-/read-chunk-3.2.0.tgz", + "integrity": "sha512-CEjy9LCzhmD7nUpJ1oVOE6s/hBkejlcJEgLQHVnQznOSilOPb+kpKktlLfFDK3/WP43+F80xkUTM2VOkYoSYvQ==", + "requires": { + "pify": "^4.0.1", + "with-open-file": "^0.1.6" + } + }, "read-pkg": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", @@ -11572,6 +11581,23 @@ "resolved": "https://registry.npmjs.org/with-callback/-/with-callback-1.0.2.tgz", "integrity": "sha1-oJYpuakgAo1yFAT7Q1vc/1yRvCE=" }, + "with-open-file": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/with-open-file/-/with-open-file-0.1.7.tgz", + "integrity": "sha512-ecJS2/oHtESJ1t3ZfMI3B7KIDKyfN0O16miWxdn30zdh66Yd3LsRFebXZXq6GU4xfxLf6nVxp9kIqElb5fqczA==", + "requires": { + "p-finally": "^1.0.0", + "p-try": "^2.1.0", + "pify": "^4.0.1" + }, + "dependencies": { + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" + } + } + }, "word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", diff --git a/package.json b/package.json index f926a73c..359a3b93 100644 --- a/package.json +++ b/package.json @@ -108,6 +108,7 @@ "paypal-rest-sdk": "^1.8.1", "pug": "^2.0.3", "randomstring": "^1.1.5", + "read-chunk": "^3.2.0", "recaptcha2": "^1.3.3", "redis": "^2.8.0", "request": "^2.88.0", diff --git a/routes.js b/routes.js index 98319b90..c1615a5a 100644 --- a/routes.js +++ b/routes.js @@ -259,6 +259,7 @@ function frontendRoutes(app){ app.post('/api/upload/:uniqueTag/edit', passportConfig.isAuthenticated, internalApiController.editUpload); app.post('/api/upload/:uniqueTag/thumbnail/delete', passportConfig.isAuthenticated, internalApiController.deleteUploadThumbnail); app.post('/api/upload/:uniqueTag/captions/delete', passportConfig.isAuthenticated, internalApiController.deleteUploadCaption); + app.post('/api/uploadFileThumbnail', passportConfig.isAuthenticated, uploadingController.postThumbnailUpload); /** API ENDPOINTS **/ app.post('/api/react/:upload/:user', passportConfig.isAuthenticated, internalApiController.react); diff --git a/views/media.pug b/views/media.pug index 6d063f2d..a272d859 100644 --- a/views/media.pug +++ b/views/media.pug @@ -307,7 +307,7 @@ block content .secondLine { - border-bottom: 1px solid #3f3f3f !important; + border-bottom: 0px solid #3f3f3f !important; } .lampIcon { @@ -1728,29 +1728,105 @@ block extra_footer_js script. $(document).keyup(function (e) { - var mediaPlayer = document.getElementById("media_player"); - var activeElement = document.activeElement; - var inputs = ['input', 'textarea']; + var oneOfTheUsedKeys = e.keyCode === 37 || e.keyCode === 39 || e.keyCode === 32 || e.keyCode === 13 || e.keyCode === 70; - var plyrElementNotFocused = !document.activeElement.classList.contains('plyr') + if(oneOfTheUsedKeys){ + var mediaPlayer = document.getElementById("media_player"); - var inputElementNotFocused = inputs.indexOf(activeElement.tagName.toLowerCase()) === -1; + var activeElement = document.activeElement; + var inputs = ['input', 'textarea']; - if (activeElement && plyrElementNotFocused && inputElementNotFocused) { + // match against an input or text area input + var inputElementNotFocused = inputs.indexOf(activeElement.tagName.toLowerCase()) === -1; - if (e.keyCode === 37) { + var plyrElementNotFocused = !document.activeElement.classList.contains('plyr') - mediaPlayer.currentTime = mediaPlayer.currentTime - 5; + if (activeElement && plyrElementNotFocused && inputElementNotFocused) { - return false; + // left keypad + if (e.keyCode === 37) { - } else if (e.keyCode === 39) { + e.preventDefault() - mediaPlayer.currentTime = mediaPlayer.currentTime + 5; + $('button[data-plyr="rewind"]').click() - return false; + player.toggleControls(true); + setTimeout(function () { + player.toggleControls(true); + }, 150) + + // mediaPlayer.currentTime = mediaPlayer.currentTime - 5; + + return false; + + // right keypad + } else if (e.keyCode === 39) { + + e.preventDefault() + + $('button[data-plyr="fast-forward"]').click() + + player.toggleControls(true); + setTimeout(function () { + player.toggleControls(true); + }, 150) + + // mediaPlayer.currentTime = mediaPlayer.currentTime + 5; + + return false; + + // space button + } else if (e.keyCode === 32) { + // dont scroll down + e.preventDefault() + + // toggle play/pause button + if (mediaPlayer.paused) { + mediaPlayer.play(); + } else { + mediaPlayer.pause(); + } + + return false + + } + } + + if (inputElementNotFocused && ( e.keyCode === 13 || e.keyCode === 70)) { + mediaPlayer.requestFullscreen(); + } + + var leftOrRightKey = e.keyCode === 37 || e.keyCode === 39; + + if (!plyrElementNotFocused && leftOrRightKey) { + player.toggleControls(true); } } }); + script. + $(document).keypress(function (e) { + + if(e.keyCode === 32){ + var activeElement = document.activeElement; + var inputs = ['input', 'textarea']; + + var plyrElementNotFocused = !document.activeElement.classList.contains('plyr') + + var inputElementNotFocused = inputs.indexOf(activeElement.tagName.toLowerCase()) === -1; + + if (activeElement && plyrElementNotFocused && inputElementNotFocused) { + + // dont scroll down + e.preventDefault() + + return false + + } + } + + }); + + + diff --git a/views/mediaPlayerPartials/progressTrackerJs.pug b/views/mediaPlayerPartials/progressTrackerJs.pug index e5ff7a04..d39e2353 100644 --- a/views/mediaPlayerPartials/progressTrackerJs.pug +++ b/views/mediaPlayerPartials/progressTrackerJs.pug @@ -26,6 +26,7 @@ script. $('#processing').text(`${data.uploadProgress !== undefined ? data.uploadProgress : 'Completed'}`); if(parseInt(data) == 100){ + // TODO: this should be a u=t link location.reload(); } diff --git a/views/pugJavascript/uploadingJavascript.pug b/views/pugJavascript/uploadingJavascript.pug new file mode 100644 index 00000000..826fa3f1 --- /dev/null +++ b/views/pugJavascript/uploadingJavascript.pug @@ -0,0 +1,440 @@ +script(src='/js/lib/jquery-3.1.1.min.js') +script(src='/js/lib/resumable.js') +script. + + function checkIfFileSizeAcceptable() { + var fileInput = $('.upload-thumbnail-input'); + + var maxSize = fileInput.data('max-size'); + + console.log('max size'); + + console.log(maxSize); + + + if (fileInput.get(0).files.length) { + var fileSize = fileInput.get(0).files[0].size; // in bytes + + console.log('file size'); + + console.log(fileSize) + + if (fileSize > maxSize) { + return false; + } else { + return true + } + } else { + return true + } + } + + $(document).ready(function () { + + $('.upload-thumbnail-input').on('change', function () { + const fileSizeAcceptable = checkIfFileSizeAcceptable() + + if (!fileSizeAcceptable) { + $(this).val(null); + + return swal('Please select a thumbnail under 5MB'); + } + + document.getElementById('scrollToThumbnail').scrollIntoView({behavior: 'smooth'}); + + }); + + // focus the button so you can just click enter to start uploading + $('.resumable-browser').focus(); + + // scroll down and focus description when category is changed + $('.categoryValue').on('click', function(){ + $('#description').focus() + // have to do a setTimeout for event loop + setTimeout(function(){ + document.getElementById('categoryName').scrollIntoView({ behavior: 'smooth' }); + }, 1) + }) + + // scroll down and focus description when category is changed + $('.subcategoryOption').on('click', function () { + $('#description').focus() + // TODO: would be better if it scrolled down just a smidge to visually bring attention back to description + setTimeout(function () { + document.getElementById('description').scrollIntoView({behavior: 'smooth'}); + }, 1) + }) + + // progress bar stuff + var bar1 = new ldBar("#myItem1"); + + var selectedCategory; + var selectedSubcategory; + + // hide uploading gif + $(".upload-gif").hide(); + + // when a new category is selected + $('.categoryValue').click(function(){ + var value = $(this).data("value"); + + var displayName = $(this).data("displayname"); + + // hide all subcategories + $('.subcategories').hide(); + + selectedCategory = value; + + // display selected category + $('.categoryDisplayName').text('Category: ' + displayName) + + // show subcategories if they exist + $('.categoryName').show(); + $('.' + value).show(); + + + + + console.log(value, displayName); + }) + + var privs = #{user.privs} + + var maxSize; + + if(privs){ + maxSize = '#{user.privs.uploadSize}' || 500; + } else { + maxSize = 500; + } + + var maxSizeInBytes = maxSize * 1000000 + + console.log(maxSizeInBytes) + + // set chunk size to 25MB + var chunkSize = 1000 * 1000 * 25; + + // on submit, build a resumable, and then fire it off + + var r = new Resumable({ + target: '#{uploadUrl}', + chunkSize: chunkSize, + simultaneousUploads: 1, + testChunks: false, + throttleProgressCallbacks: 1, + maxFiles: 1, + maxFileSize: maxSizeInBytes, + maxFileSizeErrorCallback: function (file, errorCount) { + swal(`Please upload a file smaller than ${maxSize} MB`); + // console.log(file); + } + + }); + + // Resumable.js isn't supported, fall back on a different method + if (!r.support) { + swal('Sorry, your browser isn\'t supported currently, please update your browser'); + } else { + var setProgressAndTimeRemaining = null; + var barProgress = null; + var estimatedUploadTimeLeft = null; + var secondsElapsed = null; + + r.assignBrowse($('.resumable-browse')[0]); + + r.assignDrop(document.getElementById('dropTarget')); + + // Handle file add event + + r.on('fileAdded', function (file) { + + var fileType = file.file.type.split('/')[0]; + + if(fileType === 'video' || fileType === 'audio'){ + $('#customThumbnailDiv').show(); + } else if (fileType === 'image') { + $('#customThumbnailDiv').hide(); + } + + console.log('filetype'); + console.log(fileType); + + console.log(file); + + // replace underlines with spaces just to be nice + var fileName = file.fileName.replace(/_/g, " "); + + fileName = fileName.substring(0, fileName.lastIndexOf('.')); + + var title = $('#title').val(); + + console.log(title); + + if(!title || title == ""){ + $('#title').val(fileName); + } + + // scroll to the div when file selected + document.getElementById('scrollTo').scrollIntoView({behavior: 'smooth'}); + + + // have to use setTimeout for event loop + setTimeout(function(){ + + // focus and scroll down the div + $('#title').focus(); + }, 750) + + // auto fill in title + + // console.log(file); + + }); + + $('.submit-button').on('click', function (e) { + + var title = $('#title').val(); + var description = $('#description').val(); + + var visibility = $('input[name=visibility]:checked').val(); + var rating = $(".rating label input[type='radio']:checked").val(); + + var subcategory = $(".subcategory label input[type='radio']:checked").val(); + + if(!rating){ + return swal('Please select a rating') + } + + if(!visibility){ + visibility = 'public' + } + + $('.upload-title-name').text('File: ' + title); + + if (r.files.length == 0) { + swal('Please select a file') + return + } + + if (title == '') { + swal('Please enter a title') + return + } + + // HIDE FORM SHOW GIF + $(".upload-form").hide(); + + $('.upload-information').hide(); + + $(".upload-gif").show(); + + + + var uploadToken = '#{user.uploadToken}' + + r.opts.query = { + title, + description, + uploadToken, + visibility, + rating, + category: selectedCategory, + subcategory + }; + + r.upload(); + + $('.file-progress').html('BEGINNING UPLOAD...'); + }); + + + // when receiving a success response from the backend + r.on('fileSuccess', function (file, response) { + + console.log('message:') + console.log(response); + + response = JSON.parse(response); + + var redirectUrl = response.url + + if(response.message == 'ALREADY-UPLOADED'){ + // HIDE FORM SHOW GIF + $(".upload-form").show(); + $(".upload-gif").hide(); + + + $('.upload-information').show(); + + + // auto fill in title + $('#title').val(''); + return swal({ title: 'File Already Uploaded', text: 'Sorry, this file has already been uploaded, please try another'}, function(){ + window.location.href = '/upload' + }) + } + + // get the thumbnail file + var thumbnailFile = $('#filetoupload')[0].files[0] + + // if there's thumbnail file, just redirect + if(!thumbnailFile){ + + // alert('Redirecting to ' + redirectUrl); + + // alert('no thumbnail url'); + return window.location.href = redirectUrl; + + } else { + + // initiate a form to send for thumbnail upload + let files = new FormData(); + + files.append('thumbnailFile', thumbnailFile) + + files.append('uploadUniqueTag', response.uniqueTag) + + var addThumbnailUrl = '/api/uploadFileThumbnail'; + + // alert('thumbnail url'); + + $.ajax({ + type: 'POST', + url: addThumbnailUrl, + processData: false, + contentType: false, + data: files, + success: function (response) { + console.log(response); + + if (response === 'success') { + window.location.href = redirectUrl + } + + }, + error: function (err) { + // hopefully this never happens? + console.log(err); + + // alert('there was an error'); + + // no thumbnail added, just redirect to the url + window.location.href = redirectUrl + } + }); + + + } + + + // Reflect that the file upload has completed + $('.file-progress').html('UPLOAD COMPLETED'); + }); + + + // if receiving an error from the backend + r.on('fileError', function (file, response) { + + response = JSON.parse(response); + + console.log(response.message); + + console.log('file error'); + + if (response.message == 'UNKNOWN-FILETYPE') { + // HIDE FORM SHOW GIF + $(".upload-form").show(); + $(".upload-gif").hide(); + $('.upload-information').show(); + + + // auto fill in title + $('#title').val(''); + return swal({ + title: 'File Type Not Supported', + text: 'Sorry, this file type is not supported, please try another file' + }, function () { + window.location.href = '/upload' + }) + } + + if (response.message == 'ALREADY-UPLOADED') { + // HIDE FORM SHOW GIF + $(".upload-form").show(); + $(".upload-gif").hide(); + $('.upload-information').show(); + + + // auto fill in title + $('#title').val(''); + return swal({ + title: 'File Already Uploaded', + text: 'Sorry, this file has already been uploaded, please try another' + }, function () { + window.location.href = '/upload' + }) + } + + if (response.message == 'UPLOADS_OFF') { + // HIDE FORM SHOW GIF + $(".upload-form").show(); + $(".upload-gif").hide(); + + // auto fill in title + $('#title').val(''); + return swal({ + title: 'Uploads Temporarily Off', + text: 'Sorry uploads are temporarily turned off (only app moderator is offline)' + }, function () { + window.location.href = '/upload' + }) + } + + // Reflect that the file upload has resulted in error + $('.file-progress').html('File could not be uploaded: (' + response.message + ')'); + }); + + + r.on('fileProgress', function (file, response) { + + const progress = Math.floor(file.progress() * 100); + + if (progress === 0 || progress === '0') { + uploadStartTime = new Date() + $('.file-progress').html('BEGINNING UPLOAD...'); + } else { + secondsElapsed = Math.round((new Date() - uploadStartTime) / 1000) + const progressSpeed = (file.progress() * 100) / secondsElapsed + + estimatedUploadSecondsLeft = Math.ceil((100 - (file.progress() * 100)) / progressSpeed) + + barProgress = progress + } + }); + + setInterval(function() { + if (barProgress) { + $('.file-progress').html('PROGRESS: ' + barProgress + '%'); + + bar1.set(barProgress); + + var secondsToFormattedTime = #{secondsToFormattedTime} + estimatedUploadSecondsLeft = estimatedUploadSecondsLeft > 0 ? estimatedUploadSecondsLeft - 1 : 0 + secondsElapsed += 1 + + var formattedElapsedTime = secondsElapsed && secondsToFormattedTime(secondsElapsed) + var formattedTimeLeft = estimatedUploadSecondsLeft !== null && secondsToFormattedTime(estimatedUploadSecondsLeft) + var formattedTotalEstimatedProcessingTime = estimatedUploadSecondsLeft !== null && secondsToFormattedTime(estimatedUploadSecondsLeft + secondsElapsed) + + $('#uploadTimeElapsed').text(`Time elapsed: ${formattedElapsedTime ? formattedElapsedTime : "calculating ..."}`) + + $('#estimatedUploadTimeLeft').text(`Estimated time to completion: ${formattedTimeLeft ? formattedTimeLeft : "calculating ..." }`); + } + + $('#estimatedTotalUploadTime').text(`Total estimated upload time: ${formattedTotalEstimatedProcessingTime ? formattedTotalEstimatedProcessingTime : "calculating ..."}`) + + }, 1000 * 1) + } + }); + diff --git a/views/uploading.pug b/views/uploading.pug index a7540516..2ce201ee 100644 --- a/views/uploading.pug +++ b/views/uploading.pug @@ -30,6 +30,11 @@ block content font-weight: 300; } + .ldBar path.mainline { + stroke-width: 6 !important; + stroke: white !important; + } + .ldBar { margin: 0 auto; } @@ -73,9 +78,9 @@ block content //h3.fw(style="font-size: 30px;") Select Upload - if user.plan == 'free' && plus_enabled - p You can raise this limit to 2000 MB by subscribing for  - a(href="/account?to=pt-plus") #{brandName} Plus + if user.plan === 'free' && plusEnabled + p You can raise this limit to 2000 MB (2GB) by subscribing for  + a(href="/account?to=nt-plus") #{brandName} Plus if site_rating @@ -131,10 +136,11 @@ block content | Private br - div(style="margin:0 auto;text-align:center;margin-bottom:11px;") + div#scrollToThumbnail + div#customThumbnailDiv(style="margin:0 auto;text-align:center;margin-bottom:11px;display:none;") h4.fw(style="margin-bottom:3px;font-size:17px;") (Optional) - label.fw(for='title' style="font-size:20px") Select A Thumbnail - input.btn.btn-primary.center-block.text-center.upload-thumbnail-input(data-max-size="500000000" type="file" id="filetoupload" name="filetoupload" accept="image/*" style="width:211px;border-radius:6px;") + label.fw(for='title' style="font-size:20px") Custom Thumbnail + input.btn.btn-primary.center-block.text-center.upload-thumbnail-input(data-max-size="5000000" type="file" id="filetoupload" name="filetoupload" accept="image/*" style="width:176px;border-radius:6px;font-size:11px;") br label.fw(for='title' style="font-size:20px") Select A Category @@ -225,340 +231,4 @@ block content - script(src='/js/lib/jquery-3.1.1.min.js') - script(src='/js/lib/resumable.js') - script. - $(document).ready(function () { - - // focus the button so you can just click enter to start uploading - $('.resumable-browser').focus(); - - // scroll down and focus description when category is changed - $('.categoryValue').on('click', function(){ - $('#description').focus() - // have to do a setTimeout for event loop - setTimeout(function(){ - document.getElementById('categoryName').scrollIntoView({ behavior: 'smooth' }); - }, 1) - }) - - // scroll down and focus description when category is changed - $('.subcategoryOption').on('click', function () { - $('#description').focus() - // TODO: would be better if it scrolled down just a smidge to visually bring attention back to description - setTimeout(function () { - document.getElementById('description').scrollIntoView({behavior: 'smooth'}); - }, 1) - }) - - // progress bar stuff - var bar1 = new ldBar("#myItem1"); - - var selectedCategory; - var selectedSubcategory; - - // hide uploading gif - $(".upload-gif").hide(); - - // when a new category is selected - $('.categoryValue').click(function(){ - var value = $(this).data("value"); - - var displayName = $(this).data("displayname"); - - // hide all subcategories - $('.subcategories').hide(); - - selectedCategory = value; - - // display selected category - $('.categoryDisplayName').text('Category: ' + displayName) - - // show subcategories if they exist - $('.categoryName').show(); - $('.' + value).show(); - - - - - console.log(value, displayName); - }) - - - - var privs = #{user.privs} - - var maxSize; - - if(privs){ - maxSize = '#{user.privs.uploadSize}' || 500; - } else { - maxSize = 500; - - } - - var maxSizeInBytes = maxSize * 1000000 - - console.log(maxSizeInBytes) - - // set chunk size to 25MB - var chunkSize = 1000 * 1000 * 25; - - // on submit, build a resumable, and then fire it off - - var r = new Resumable({ - target: '#{uploadUrl}', - chunkSize: chunkSize, - simultaneousUploads: 1, - testChunks: false, - throttleProgressCallbacks: 1, - maxFiles: 1, - maxFileSize: maxSizeInBytes, - maxFileSizeErrorCallback: function (file, errorCount) { - swal(`Please upload a file smaller than ${maxSize} MB`); - // console.log(file); - } - - }); - - // Resumable.js isn't supported, fall back on a different method - if (!r.support) { - swal('Sorry, your browser isn\'t supported currently, please update your browser'); - } else { - var setProgressAndTimeRemaining = null; - var barProgress = null; - var estimatedUploadTimeLeft = null; - var secondsElapsed = null; - - r.assignBrowse($('.resumable-browse')[0]); - - r.assignDrop(document.getElementById('dropTarget')); - - // Handle file add event - - r.on('fileAdded', function (file) { - - // replace underlines with spaces just to be nice - var fileName = file.fileName.replace(/_/g, " "); - - fileName = fileName.substring(0, fileName.lastIndexOf('.')); - - var title = $('#title').val(); - - console.log(title); - - if(!title || title == ""){ - $('#title').val(fileName); - } - - // scroll to the div when file selected - document.getElementById('scrollTo').scrollIntoView({behavior: 'smooth'}); - - - // have to use setTimeout for event loop - setTimeout(function(){ - - // focus and scroll down the div - $('#title').focus(); - }, 750) - - // auto fill in title - - // console.log(file); - - }); - - $('.submit-button').on('click', function (e) { - - var title = $('#title').val(); - var description = $('#description').val(); - - var visibility = $('input[name=visibility]:checked').val(); - var rating = $(".rating label input[type='radio']:checked").val(); - - var subcategory = $(".subcategory label input[type='radio']:checked").val(); - - if(!rating){ - return swal('Please select a rating') - } - - if(!visibility){ - visibility = 'public' - } - - $('.upload-title-name').text('File: ' + title); - - if (r.files.length == 0) { - swal('Please select a file') - return - } - - if (title == '') { - swal('Please enter a title') - return - } - - // HIDE FORM SHOW GIF - $(".upload-form").hide(); - - $('.upload-information').hide(); - - $(".upload-gif").show(); - - - - var uploadToken = '#{user.uploadToken}' - - r.opts.query = { - title, - description, - uploadToken, - visibility, - rating, - category: selectedCategory, - subcategory - }; - - r.upload(); - - $('.file-progress').html('BEGINNING UPLOAD...'); - }); - - - // when receiving a success response from the backend - r.on('fileSuccess', function (file, response) { - - console.log('message:') - console.log(response); - - response = JSON.parse(response); - - if(response.message == 'ALREADY-UPLOADED'){ - // HIDE FORM SHOW GIF - $(".upload-form").show(); - $(".upload-gif").hide(); - - - $('.upload-information').show(); - - - // auto fill in title - $('#title').val(''); - return swal({ title: 'File Already Uploaded', text: 'Sorry, this file has already been uploaded, please try another'}, function(){ - window.location.href = '/upload' - }) - } - - // TODO: add a post here to add a thumbnail - if (response.url) { - window.location.href = response.url; - } - - - // Reflect that the file upload has completed - $('.file-progress').html('UPLOAD COMPLETED'); - }); - - - // if receiving an error from the backend - r.on('fileError', function (file, response) { - - response = JSON.parse(response); - - console.log(response.message); - - console.log('file error'); - - if (response.message == 'UNKNOWN-FILETYPE') { - // HIDE FORM SHOW GIF - $(".upload-form").show(); - $(".upload-gif").hide(); - $('.upload-information').show(); - - - // auto fill in title - $('#title').val(''); - return swal({ - title: 'File Type Not Supported', - text: 'Sorry, this file type is not supported, please try another file' - }, function () { - window.location.href = '/upload' - }) - } - - if (response.message == 'ALREADY-UPLOADED') { - // HIDE FORM SHOW GIF - $(".upload-form").show(); - $(".upload-gif").hide(); - $('.upload-information').show(); - - - // auto fill in title - $('#title').val(''); - return swal({ - title: 'File Already Uploaded', - text: 'Sorry, this file has already been uploaded, please try another' - }, function () { - window.location.href = '/upload' - }) - } - - if (response.message == 'UPLOADS_OFF') { - // HIDE FORM SHOW GIF - $(".upload-form").show(); - $(".upload-gif").hide(); - - // auto fill in title - $('#title').val(''); - return swal({ - title: 'Uploads Temporarily Off', - text: 'Sorry uploads are temporarily turned off (only app moderator is offline)' - }, function () { - window.location.href = '/upload' - }) - } - - // Reflect that the file upload has resulted in error - $('.file-progress').html('File could not be uploaded: (' + response.message + ')'); - }); - r.on('fileProgress', function (file, response) { - - const progress = Math.floor(file.progress() * 100); - - if (progress === 0 || progress === '0') { - uploadStartTime = new Date() - $('.file-progress').html('BEGINNING UPLOAD...'); - } else { - secondsElapsed = Math.round((new Date() - uploadStartTime) / 1000) - const progressSpeed = (file.progress() * 100) / secondsElapsed - - estimatedUploadSecondsLeft = Math.ceil((100 - (file.progress() * 100)) / progressSpeed) - - barProgress = progress - } - }); - - setInterval(function() { - if (barProgress) { - $('.file-progress').html('PROGRESS: ' + barProgress + '%'); - - bar1.set(barProgress); - - var secondsToFormattedTime = #{secondsToFormattedTime} - estimatedUploadSecondsLeft = estimatedUploadSecondsLeft > 0 ? estimatedUploadSecondsLeft - 1 : 0 - secondsElapsed += 1 - - var formattedElapsedTime = secondsElapsed && secondsToFormattedTime(secondsElapsed) - var formattedTimeLeft = estimatedUploadSecondsLeft !== null && secondsToFormattedTime(estimatedUploadSecondsLeft) - var formattedTotalEstimatedProcessingTime = estimatedUploadSecondsLeft !== null && secondsToFormattedTime(estimatedUploadSecondsLeft + secondsElapsed) - - $('#uploadTimeElapsed').text(`Time elapsed: ${formattedElapsedTime ? formattedElapsedTime : "calculating ..."}`) - - $('#estimatedUploadTimeLeft').text(`Estimated time to completion: ${formattedTimeLeft ? formattedTimeLeft : "calculating ..." }`); - } - - $('#estimatedTotalUploadTime').text(`Total estimated upload time: ${formattedTotalEstimatedProcessingTime ? formattedTotalEstimatedProcessingTime : "calculating ..."}`) - - }, 1000 * 1) - } - }); + include pugJavascript/uploadingJavascript