partkeepr

fork of partkeepr
git clone https://git.e1e0.net/partkeepr.git
Log | Files | Refs | Submodules | README | LICENSE

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:
Mapp/config/config.yml | 38+++++++++++++++++++++++++++++++++-----
Msrc/PartKeepr/FrontendBundle/Resources/public/js/Components/Widgets/AttachmentGrid.js | 5+++--
Msrc/PartKeepr/FrontendBundle/Resources/public/js/Components/Widgets/WebcamPanel.js | 141+++++++++++++++++++++++++++++++++++++++++++++++++------------------------------
Msrc/PartKeepr/ImageBundle/Controller/TemporaryImageController.php | 28++++++++++++++++++++++++++--
Msrc/PartKeepr/UploadedFileBundle/Controller/TemporaryFileController.php | 24++++++++++++++++++++++++
Msrc/PartKeepr/UploadedFileBundle/Services/UploadedFileService.php | 21+++++++++++++--------
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)); } /**