commit ba8c40a5afdbba28544ca4e3bc390415eb69970d
parent a1c41b35936d008298aaefbf012630c7ba278e38
Author: Felicitus <felicitus@felicitus.org>
Date: Sat, 18 Jul 2015 03:22:32 +0200
Implemented HTML5 video capture and fixed webcam image upload
Diffstat:
6 files changed, 186 insertions(+), 71 deletions(-)
diff --git a/app/config/config.yml b/app/config/config.yml
@@ -409,6 +409,21 @@ services:
"hydra:title": "A custom operation"
"returns": "xmls:string"
+ resource.tempimage.collection_operation.custom_post_webcam:
+ class: "Dunglas\ApiBundle\Api\Operation\Operation"
+ public: false
+ factory: [ "@api.operation_factory", "createCollectionOperation" ]
+ arguments:
+ - "@resource.tempimage" # Resource
+ - [ "POST" ] # Methods
+ - "/temp_images/webcamUpload" # Path
+ - "PartKeeprImageBundle:TemporaryImage:webcamUpload" # Controller
+ - "TemporaryImageUploadWebcam" # Route name
+ - # Context (will be present in Hydra documentation)
+ "@type": "hydra:Operation"
+ "hydra:title": "A custom operation"
+ "returns": "xmls:string"
+
resource.tempimage.item_operation.custom_get:
class: "Dunglas\ApiBundle\Api\Operation\Operation"
public: false
@@ -430,7 +445,7 @@ services:
tags: [ { name: "api.resource" } ]
calls:
- method: "initCollectionOperations"
- arguments: [ [ "@resource.tempimage.collection_operation.custom_post" ] ]
+ arguments: [ [ "@resource.tempimage.collection_operation.custom_post", "@resource.tempimage.collection_operation.custom_post_webcam" ] ]
- method: "initItemOperations"
arguments: [ [ "@resource.tempimage.item_operation.custom_get" ] ]
@@ -449,6 +464,21 @@ services:
"hydra:title": "A custom operation"
"returns": "xmls:string"
+ resource.tempfile.collection_operation.custom_post_webcam:
+ class: "Dunglas\ApiBundle\Api\Operation\Operation"
+ public: false
+ factory: [ "@api.operation_factory", "createCollectionOperation" ]
+ arguments:
+ - "@resource.tempfile" # Resource
+ - [ "POST" ] # Methods
+ - "/temp_uploaded_files/webcamUpload" # Path
+ - "PartKeeprUploadedFileBundle:TemporaryFile:webcamUpload" # Controller
+ - "TemporaryFileUploadWebcam" # Route name
+ - # Context (will be present in Hydra documentation)
+ "@type": "hydra:Operation"
+ "hydra:title": "A custom operation"
+ "returns": "xmls:string"
+
resource.tempfile.item_operation.get:
class: "Dunglas\ApiBundle\Api\Operation\Operation"
public: false
@@ -485,8 +515,6 @@ services:
"hydra:title": "A custom operation"
"returns": "xmls:string"
-
-
resource.tempfile:
parent: "api.resource"
arguments: [ "PartKeepr\\UploadedFileBundle\\Entity\\TempUploadedFile" ]
@@ -495,4 +523,4 @@ services:
- method: "initCollectionOperations"
arguments: [ [ "@resource.tempfile.collection_operation.custom_post" ] ]
- method: "initItemOperations"
- arguments: [ [ "@resource.tempfile.item_operation.get", "@resource.tempfile.item_operation.custom_get", "@resource.tempfile.item_operation.custom_get_mimetype" ] ]-
\ No newline at end of file
+ arguments: [ [ "@resource.tempfile.item_operation.get", "@resource.tempfile.item_operation.custom_get", "@resource.tempfile.item_operation.custom_get_mimetype", "@resource.tempfile.collection_operation.custom_post_webcam" ] ]+
\ No newline at end of file
diff --git a/src/PartKeepr/FrontendBundle/Resources/public/js/Components/Widgets/AttachmentGrid.js b/src/PartKeepr/FrontendBundle/Resources/public/js/Components/Widgets/AttachmentGrid.js
@@ -96,16 +96,17 @@ Ext.define('PartKeepr.AttachmentGrid', {
},
onWebcamClick: function () {
var wp = Ext.create("PartKeepr.WebcamPanel");
- wp.on("uploadComplete", this.onFileUploaded, this);
+ wp.on("fileUploaded", this.onFileUploaded, this);
var j = Ext.create("Ext.window.Window", {
title: i18n("Take Webcam Photo"),
+ layout: 'fit',
items: [
wp
]
});
- wp.on("uploadComplete", function () { j.close(); });
+ wp.on("fileUploaded", function () { j.close(); });
j.show();
},
diff --git a/src/PartKeepr/FrontendBundle/Resources/public/js/Components/Widgets/WebcamPanel.js b/src/PartKeepr/FrontendBundle/Resources/public/js/Components/Widgets/WebcamPanel.js
@@ -3,58 +3,91 @@
* a flash (jpegcam).
*/
Ext.define('PartKeepr.WebcamPanel', {
- extend: 'Ext.panel.Panel',
- alias: 'widget.WebcamPanel',
- initComponent: function () {
-
- this.takePhotoButton = Ext.create("Ext.button.Button", {
- text: i18n("Take picture and upload"),
- icon: 'bundles/brainbitsfugueicons/icons/fugue/16/webcam.png',
- handler: this.takePhoto
- });
-
- // Create a toolbar with the "take photo" button
- this.bbar = Ext.create("Ext.toolbar.Toolbar", {
- enableOverflow: true,
- items: [ this.takePhotoButton ]
- });
-
- // Render the SWF
- this.on("afterrender", this.renderWebcam, this);
-
- this.callParent();
- },
- /**
- * Renders the webcam swf.
- * @param e The element for this component
- */
- renderWebcam: function (e) {
- webcam.set_swf_url("resources/webcam.swf");
- webcam.set_quality(90);
- webcam.set_api_url(PartKeepr.getBasePath()+"?service=TempFile&call=uploadCam&session="+PartKeepr.getApplication().getSession());
- webcam.set_shutter_sound(false);
- webcam.set_hook('onComplete', Ext.bind(this.onUploadComplete, this));
-
- e.body.insertHtml('beforeEnd', webcam.get_html(640,480, 640, 480));
- },
- /**
- * Takes a photo using the webcam.
- */
- takePhoto: function () {
- webcam.snap();
- this.takePhotoButton.disable();
- this.takePhotoButton.setText(i18n("Uploading..."));
- },
- /**
- * Called when the upload is complete. Resumes webcam operation
- * and fires the event. 'uploadComplete'
- * @param message The server side message
- */
- onUploadComplete: function (message) {
- var response = Ext.decode(message);
-
- webcam.reset();
- this.fireEvent("uploadComplete", response.response);
-
- }
+ extend: 'Ext.panel.Panel',
+ alias: 'widget.WebcamPanel',
+
+ layout: 'fit',
+ width: 320,
+ height: 286,
+ items: [{
+ xtype: 'component',
+ itemId: 'video',
+ autoEl: {
+ tag: 'video',
+ autoplay: 'true'
+ }
+ },{
+ xtype: 'component',
+ itemId: 'canvas',
+ autoEl: {
+ tag: 'canvas',
+ }
+ }],
+ video: null,
+
+ initComponent: function ()
+ {
+ this.takePhotoButton = Ext.create("Ext.button.Button", {
+ text: i18n("Take picture and upload"),
+ icon: 'bundles/brainbitsfugueicons/icons/fugue/16/webcam.png',
+ handler: this.takePhoto,
+ scope: this
+ });
+
+ // Create a toolbar with the "take photo" button
+ this.bbar = Ext.create("Ext.toolbar.Toolbar", {
+ enableOverflow: true,
+ items: [this.takePhotoButton]
+ });
+
+
+ this.callParent();
+
+ this.on("afterrender", this._onAfterRender, this);
+ },
+ handleVideo: function (stream) {
+ this.video.src = window.URL.createObjectURL(stream);
+ },
+ videoError: function () {
+ // @todo: Implement video error handler
+ },
+ _onAfterRender: function () {
+ this.video = this.down("#video").getEl().dom;
+ this.canvas = this.down("#canvas").getEl().dom;
+
+ navigator.getUserMedia = navigator.getUserMedia ||
+ navigator.webkitGetUserMedia ||
+ navigator.mozGetUserMedia ||
+ navigator.msGetUserMedia;
+
+ if (navigator.getUserMedia) {
+ navigator.getUserMedia({video: true}, Ext.bind(this.handleVideo, this), Ext.bind(this.videoError, this));
+ }
+ },
+ /**
+ * Takes a photo using the webcam.
+ */
+ takePhoto: function ()
+ {
+ this.canvas.width = this.video.videoWidth;
+ this.canvas.height = this.video.videoHeight;
+
+ var ctx = this.canvas.getContext('2d');
+ ctx.drawImage(this.video, 0, 0, this.video.videoWidth, this.video.videoHeight);
+
+ Ext.Ajax.request({
+ // Might need to adjust the path, depending on if we are uploading a file or image
+ url: PartKeepr.getBasePath() + "/api/temp_uploaded_files/webcamUpload",
+ params: this.canvas.toDataURL(),
+ success: function (response) {
+ var responseObject = Ext.decode(response.responseText);
+ this.fireEvent("fileUploaded", responseObject);
+ },
+ //@todo implement failure handler
+ scope: this
+ });
+
+ this.takePhotoButton.disable();
+ this.takePhotoButton.setText(i18n("Uploading..."));
+ }
});
\ No newline at end of file
diff --git a/src/PartKeepr/ImageBundle/Controller/TemporaryImageController.php b/src/PartKeepr/ImageBundle/Controller/TemporaryImageController.php
@@ -57,10 +57,35 @@ class TemporaryImageController extends ImageController
}
/**
+ * Uploads a webcam image
+ *
+ * @param Request $request The request to process
+ * @return Response
+ */
+ public function webcamUploadAction(Request $request)
+ {
+ $image = new TempImage();
+ $imageService = $this->get("partkeepr_image_service");
+
+
+ $data = $request->getContent();
+
+ $base64 = explode(',', $data);
+ $imageService->replaceFromData($image, base64_decode($base64[1]), "webcam.png");
+
+ $this->getDoctrine()->getManager()->persist($image);
+ $this->getDoctrine()->getManager()->flush();
+
+ $resource = $this->getResource($request);
+
+ return $this->getSuccessResponse($resource, $image, 201);
+ }
+
+ /**
* @inheritdoc
*/
public function getEntityClass()
{
return "PartKeepr\\ImageBundle\\Entity\\TempImage";
}
-}-
\ No newline at end of file
+}
diff --git a/src/PartKeepr/UploadedFileBundle/Controller/TemporaryFileController.php b/src/PartKeepr/UploadedFileBundle/Controller/TemporaryFileController.php
@@ -56,6 +56,30 @@ class TemporaryFileController extends FileController
return new JsonResponse(new TemporaryImageUploadResponse($serializedData));
}
+ /**
+ * Uploads a webcam image
+ *
+ * @param Request $request The request to process
+ * @return Response
+ */
+ public function webcamUploadAction(Request $request)
+ {
+ $file = new TempUploadedFile();
+ $fileService = $this->get("partkeepr_uploadedfile_service");
+
+
+ $data = $request->getContent();
+
+ $base64 = explode(',', $data);
+ $fileService->replaceFromData($file, base64_decode($base64[1]), "webcam.png");
+
+ $this->getDoctrine()->getManager()->persist($file);
+ $this->getDoctrine()->getManager()->flush();
+
+ $resource = $this->getResource($request);
+
+ return $this->getSuccessResponse($resource, $file, 201);
+ }
/**
* @inheritdoc
diff --git a/src/PartKeepr/UploadedFileBundle/Services/UploadedFileService.php b/src/PartKeepr/UploadedFileBundle/Services/UploadedFileService.php
@@ -53,6 +53,18 @@ class UploadedFileService extends ContainerAware
}
}
+ public function replaceFromData(UploadedFile $file, $data, $filename)
+ {
+ $tempName = tempnam("/tmp", "PARTKEEPR");
+
+ file_put_contents($tempName, $data);
+
+ $this->replaceFromFilesystem($file, new File($tempName));
+ $file->setOriginalFilename($filename);
+
+ unlink($tempName);
+ }
+
/**
* Replaces an existing uploaded file with another uploaded file.
*
@@ -135,14 +147,7 @@ class UploadedFileService extends ContainerAware
curl_close($curl);
- $tempName = tempnam("/tmp", "PARTKEEPR");
-
- file_put_contents($tempName, $data);
-
- $this->replaceFromFilesystem($file, new File($tempName));
- $file->setOriginalFilename(basename($url));
-
- unlink($tempName);
+ $this->replaceFromData($file, $data, basename($url));
}
/**