Current File : /home/escuelai/public_html/it/marketplace/screenshot/js/screenshot.js |
/*
-------------------------------------------------------------------------
Screenshot
Copyright (C) 2020-2021 by Curtis Conard
https://github.com/cconard96/glpi-screenshot-plugin
-------------------------------------------------------------------------
LICENSE
This file is part of Screenshot.
Screenshot is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
Screenshot is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Screenshot. If not, see <http://www.gnu.org/licenses/>.
--------------------------------------------------------------------------
*/
/* global CFG_GLPI */
/* global GLPI_PLUGINS_PATH */
window.GLPIMediaCapture = new function() {
/**
* Array storing the size used for the preview canvas in the format [width, height].
* @type {number[]}
*/
const preview_size = [200, 180];
let config = {};
function isMobileBrowser() {
const userAgent = navigator.userAgent.toLowerCase();
return userAgent.match(/ipad|iphone|ipod|android/i);
}
/**
* Check if the browser supports this feature. If not, this will hide the timeline button.
*/
this.evalTimelineAction = function() {
if (isMobileBrowser()) {
$('#attach_screenshot_timeline').hide();
$('#attach_screenrecording_timeline').hide();
}
}
/**
* Update a preview and full-size canvas based on the supplied image.
* Each canvas parameter is optional and can be skipped by setting it to null.
*
* @param {ImageBitmap|HTMLVideoElement} img The image.
* @param {HTMLCanvasElement} preview The canvas being used to preview the image/frame.
* @param {HTMLCanvasElement} full The full-size canvas that stores the image/frame. Not needed if doing a recording.
*/
function updateCanvases(img, preview = null, full = null) {
const sourceWidth = img.videoWidth ?? img.width;
const sourceHeight = img.videoHeight ?? img.height;
if (preview !== null) {
preview.width = preview_size[0];
preview.height = preview_size[1];
let ratio = Math.min(preview.width / sourceWidth, preview.height / sourceHeight);
let x = (preview.width - sourceWidth * ratio) / 2;
let y = (preview.height - sourceHeight * ratio) / 2;
preview.getContext('2d').clearRect(0, 0, preview.width, preview.height);
preview.getContext('2d').drawImage(img, 0, 0, sourceWidth, sourceHeight,
x, y, sourceWidth * ratio, sourceHeight * ratio);
}
if (full !== null) {
full.width = sourceWidth;
full.height = sourceHeight;
full.getContext('2d').clearRect(0, 0, sourceWidth, sourceHeight);
full.getContext('2d').drawImage(img, 0, 0, sourceWidth, sourceHeight,
0, 0, sourceWidth, sourceHeight);
}
}
/**
* Prompt the user to select a screen device, (re)-build the form, grab the first frame only, and update the preview and full-size image canvases.
* @param {jQuery} form_obj The form object that will be cleared and have the canvases and upload button added to.
* @param {string} itemtype The type of the item this recording would be attached to.
* @param {integer} items_id The ID of the item this recording would be attached to.
*/
function captureScreenshot(form_obj, itemtype, items_id) {
navigator.mediaDevices.getDisplayMedia({video: true})
.then(mediaStream => {
const track = mediaStream.getVideoTracks()[0];
// Clear any previous elements in case this is being reused
form_obj.empty();
// Remove any previous event handlers
form_obj.off();
form_obj.html(`
<table class="tab_cadre_fixe">
<tr class="headerRow"><th>New Item - Screenshot</th></tr>
<tr>
<td>
<canvas id="screenshotPreview" width="${preview_size[0]}" height="${preview_size[1]}"></canvas>
<canvas id="screenshotFull" width="200" height="180" style="display: none"></canvas>
</td>
</tr>
<tr>
<td>
<button type="submit" name="upload" class="vsubmit">${__('Upload', 'screenshot')}</button>
</td>
</tr>
</table>
`);
// Bind upload action handler
form_obj.on('click', 'button[name="upload"]', function(e) {
e.preventDefault();
const img_format = config['screenshot_format'];
const canvas = form_obj.find('#screenshotFull').get(0);
const base64 = canvas.toDataURL(img_format);
const ajax_data = {
itemtype: itemtype,
items_id: items_id,
format: img_format,
img: base64
};
$(this).attr('disabled', true);
$.ajax({
type: 'POST',
url: CFG_GLPI.root_doc+"/"+GLPI_PLUGINS_PATH.screenshot+"/ajax/screenshot.php",
data: ajax_data
}).done(function() {
location.reload();
});
});
if (typeof ImageCapture !== "undefined") {
imageCapture = new ImageCapture(track);
imageCapture.grabFrame().then(img => {
updateCanvases(img, form_obj.find('#screenshotPreview').get(0), form_obj.find('#screenshotFull').get(0));
track.stop();
})
} else {
const video = document.createElement('video');
video.srcObject = mediaStream;
return new Promise((resolve, reject) => {
try {
video.addEventListener('loadeddata', event => {
video.play().then(() => {
updateCanvases(video, form_obj.find('#screenshotPreview').get(0), form_obj.find('#screenshotFull').get(0));
track.stop();
});
});
} catch(error) {
track.stop();
reject(error);
}
});
}
});
}
function getPreferredBitrate(track) {
// Reference Bitrates based on YouTube recommendations
// 360@30 - 1 Mbps | 360@60 - 1.5 Mbps | Pixel Count - 230400
// 720@30 - 5 Mbps | 720@60 - 7.5 Mbps | Pixel Count - 921600
// 1080@30 - 8 Mbps | 1080@60 - 12 Mbps | Pixel Count - 2073600
// 1440@30 - 16 Mbps | 1440@60 - 24 Mbps | Pixel Count - 3686400
// 2160@30 - 40 Mbps | 2160@60 - 60 Mbps | Pixel Count - 8294400
const motion_factor = 0.5; // Weight value. How much activity we expect
// br = (pixels * f * motion_factor) / 10;
const settings = track.getSettings();
return ((settings.width * settings.height) * settings.frameRate * motion_factor) / 10;
}
function getRecordingCodec(requested_format) {
const codecs = ['vp9', 'vp8'];
return codecs.find(c => MediaRecorder.isTypeSupported(requested_format + ';codecs=' + c))
}
/**
* Prompt the user to select a screen device, (re)-build the form, and wait for the user to start the MediaRecorder.
* Then, this will continually grab frames from the video stream at a rate of 10 FPS and update the preview canvas until the user stops the recording.
* They can either restart the recording or upload the last recording.
* @param {jQuery} form_obj The form object that will be cleared and have the canvases and buttons added to.
* @param {string} itemtype The type of the item this recording would be attached to.
* @param {integer} items_id The ID of the item this recording would be attached to.
*/
function showRecordingForm(edit_panel, itemtype, items_id) {
edit_panel.empty();
navigator.mediaDevices.getDisplayMedia({video: true, frameRate: 10})
.then(mediaStream => {
const track = mediaStream.getVideoTracks()[0];
let recorder = new MediaRecorder(mediaStream, {
mimeType: 'video/webm;codecs='+getRecordingCodec('video/webm'),
videoBitsPerSecond: getPreferredBitrate(track),
});
let blob = null;
const stopRecording = function() {
recorder.stop();
const tracks = mediaStream.getTracks();
tracks.forEach(function(track) {
track.stop();
});
//$(this).parent().append(`<button type="button" name="restart" class="vsubmit">${__('Restart recording', 'screenshot')}</button>`);
$(this).parent().append(`<button type="button" name="upload" class="vsubmit">${__('Upload', 'screenshot')}</button>`);
$(this).remove();
}
const upload = function() {
if (blob === null) {
return;
}
$(this).attr('disabled', true);
const data = new FormData();
data.append('blob', blob);
data.append('itemtype', itemtype);
data.append('items_id', items_id);
data.append('format', 'video/webm');
$.ajax({
type: 'POST',
url: CFG_GLPI.root_doc+"/"+GLPI_PLUGINS_PATH.screenshot+"/ajax/screenshot.php",
data: data,
processData: false,
contentType: false
}).done(function() {
location.reload();
});
}
$(edit_panel).on('click', 'button[name="stop"]', {}, stopRecording);
$(edit_panel).on('click', 'button[name="upload"]', {}, upload);
let chunks = [];
recorder.ondataavailable = function(event) {
if (event.data.size > 0) {
chunks.push(event.data);
// Create blob
blob = new Blob(chunks, {
type: 'video/webm'
});
}
}
recorder.onstart = function() {
// No-op
}
// Start recording the video stream
const startRecording = function() {
edit_panel.find('button:not([name="start"])').remove();
$(this).parent().append(`<button type="button" name="stop" class="vsubmit">${__('Stop recording', 'screenshot')}</button>`);
$(this).remove();
recorder.start();
edit_panel.find('#screenshotPreview').get(0).srcObject = recorder.stream;
}
const restartRecording = function() {
if (recorder !== undefined) {
try {
recorder.stop();
} catch {}
}
blob = null;
showRecordingForm(edit_panel, itemtype, items_id);
}
$(edit_panel).on('click', 'button[name="start"]', {}, startRecording);
//$(edit_panel).on('click', 'button[name="restart"]', {}, restartRecording);
});
$(edit_panel).html(`
<table class="tab_cadre_fixe">
<tr class="headerRow"><th>New Item - Screen Recording</th></tr>
<tr>
<td>
<video id="screenshotPreview" width="${preview_size[0]}" height="${preview_size[1]}" autoplay muted></video>
</td>
</tr>
<tr>
<td>
<button type="button" name="start" class="vsubmit">${__('Start recording', 'screenshot')}</button>
</td>
</tr>
</table>
`);
}
function insertEditForm(type) {
const timeline_content = $('.itil-timeline');
return $(`
<div id="${type}EditPanel" class="itil-timeline-edit-panel">
<div class="timeline-item mb-3 ${type} collapse show" aria-expanded="true" data-bs-parent="#${type}EditPanel">
</div>
</div>`).appendTo(timeline_content);
}
// Get Config
$.ajax({
type: 'GET',
url: CFG_GLPI.root_doc+"/"+GLPI_PLUGINS_PATH.screenshot+"/ajax/config.php",
}).done(function(cfg) {
config = cfg;
});
$(document).on('click', '#attach_screenshot_timeline', function() {
let edit_panel = $($(this).data('editpanel'));
if (edit_panel.length === 0) {
edit_panel = insertEditForm('screenshot');
}
const itemtype = $(this).data('itemtype');
const items_id = $(this).data('items_id');
captureScreenshot(edit_panel, itemtype, items_id);
});
$(document).on('click', '#attach_screenrecording_timeline', function() {
let edit_panel = $($(this).data('editpanel'));
if (edit_panel.length === 0) {
edit_panel = insertEditForm('screenrecording');
}
const itemtype = $(this).data('itemtype');
const items_id = $(this).data('items_id');
showRecordingForm(edit_panel, itemtype, items_id);
});
}