Google Cloud Speech recognition API (PHP) - file encoding issue - php

I am playing with google speech recognition API and have some trouble with file encoding.
Firstly, I tried the API using the Google's HTML form ( https://cloud.google.com/speech-to-text/ ) by file uploading a mp3. It worked nicely, so I have created a Google Cloud project and tried to do that by API using PHP. However, I can't get it to work, it always return empty response.
I am using Google's example code:
$projectId = 'xx';
# Instantiates a client
$speech = new SpeechClient([
'projectId' => $projectId,
'languageCode' => 'sk-SK',
]);
# The name of the audio file to transcribe
$fileName = 'D:\ts\all.mp3';
# The audio file's encoding and sample rate
$options = [
'encoding' => 'LINEAR16',
'model' => 'default',
'sampleRateHertz' => 44100,
];
# Detects speech in the audio file
$results = $speech->recognize(fopen($fileName, 'r'), $options);
var_dump($results); // array(0)
foreach ($results as $result) {
echo 'Transcription: ' . $result->alternatives()[0]['transcript'] . PHP_EOL;
}
When I looked deeper, I noticed, that when I am uploading the mp3 file through the browser, the same file is encoded differently than when using the API.
These are the first 615 characters of encoded audio data sent to API by browser (working):
UklGRiQoEABXQVZFZm10IBAAAAABAAEARKwAAIhYAQACABAAZGF0YQAoEAAFAAQAAgD8//b/8P/q/+r/7P/u//L//f8EAAkAEQAaACMALgA1AD4ATABNAE0AUwBLADoALwAeAAoA/P/o/9n/z/+6/6D/iv9n/zr/FP/z/tv+1/7p/g7/Q/94/6z/4f8DAAwACADz/8z/p/+N/4D/hf+S/67/0P/c/9b/xf+l/37/Xf9Q/1z/Z/90/5j/wf/g//P//f8QACgAJwA4AF4AfgCmAMoA0ADBAHgA+/+B/+j+Mv6x/VD9//zl/Nj80fzd/ML8n/yh/IT8Yfx9/LT8E/2y/W3+Vf9dAEUBFwLHAjMDfQO8A/MDMwRoBJwEzgTLBJ8EYwQMBLgDbQM5A0wDZQNPA0MDLwPyAq0CTQL3Ad8BugGPAZoBkQFVAf0AfAAAAIj/6P54/mH+Zf5+/qL+yv4I/yH/Dv8T/y7/VP+R/+P/VwDNABUBSAFqAWUBQAH+ALYAcAAmAAoACADj/9X/3P+6/4b/SP/7/r3+Z/4W/gL+5P28/dD98/0P/kL+gf7M/gD/Bv8P//X+nv5E/tv9eP1D/RP9Cv08/V79jf3o/Sf
And this is made by Google's PHP library (not working):
SUQzBAAAAAABClRYWFgAAAASAAADbWFqb3JfYnJhbmQAaXNvbQBUWFhYAAAAEwAAA21pbm9yX3ZlcnNpb24ANTEyAFRYWFgAAAAkAAADY29tcGF0aWJsZV9icmFuZHMAaXNvbWlzbzJhdmMxbXA0MQBUU1NFAAAADwAAA0xhdmY1OC4xNy4xMDEAAAAAAAAAAAAAAP/7UAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEluZm8AAAAPAAABzQAC8XcAAwUICw0PExUXGh0fISQnKSwuMTQ2ODs+QEJGSEpNUFJUV1pcX2FkZ2lrbnFzdXl7fYCDhYeKjY+SlJeanJ6hpKaorK6wtLa4ur3AwsXHys3P0dTX2dvf4ePn6evt8fP1+Pv9AAAAAExhdmM1OC4yMQAAAAAAAAAAAAAAACQEQAAAAAAAAvF3QasUAQAAAAAAAAAAAAAAAAAAAAD/+5BkAAAC+xrGhWMAAAAADSCgAAEbxZlRuYwAEAAANIMAAACKNzL/lp0F4GTkLOAIBnGAjoLsTctnbL2xAAh3udXmkKV8XqUkofxnbO3fo4gwxdjqQGoAoI1x/HYaw1yTMrLkIONMqw2
The Google's PHP library is using base64_encode method to format the request, but I don't know what type of encoding uses the browser.
When I modify the Google's PHP libraries and force the API to send the correct data (as was sent by browser), it works and I am getting correct results.
So the question is, why is the Google's example not working for me and how to correctly encode the file?

Solved, the file needs to be converted to WAV. It's probably happening by javascript when uploading the mp3 by form.

Related

Images are always served with name "unnamed.jpg" in Google App Engine

I am working on a PHP app on Google App Engine standard environment.
I upload an image to the app's associated bucket, then use this code to serve the image:
use \google\appengine\api\cloud_storage\CloudStorageTools;
// ...
$path = "gs://my-app.appspot.com/the_image_name.jpg"
$imageUrl = CloudStorageTools::getImageServingUrl($path, ['secure_url' => true, 'size' => 0]);
header("Location: $imageUrl");
There is of course a redirection to a page like this:
https://lh3.googleusercontent.com/<some_hash>
The image displays correctly, but in the title of the browser it says "unnamed.jpg", and when I try to save the image to my computer the default name appears as "unnamed.jpg". This happens every time I upload an image, no matter its actual name, or if I do upload it through my app using the API or if I upload it directly to the bucket through the web cloud console.
Is there any way in which I can specify what name is used when serving the image?
Did you set the headers: 'Content-disposition', 'Content-Transfer-Encoding', & 'Content-Type'
CloudStorageTools::getImageServingUrl uses googleusercontent.com which is a "sandbox" and therefore you can't change the name of the file served.
I suggest you to take a look in CloudStorageTools::serve method where you will be able to set the name of the file with save_as parameter. Please note that the type is string and not boolean as it's mentioned in the documentation and serve will take some app engine compute time.
The code should look like this:
use \google\appengine\api\cloud_storage\CloudStorageTools;
// ...
$file_name = "gs://my-app.appspot.com/the_image_name.jpg"
$options = ['save_as' => 'the_image_name.jpg'];
CloudStorageTools::serve($file_name, $options);

S3 Force Download

I have a website here http://www.voclr.it/acapellas/ my files are hosting on my Amazon S3 Account, but when a visitor goes to download an MP3 from my website it forces them to stream it but what I actually want it to do is download it to there desktop.
I have disabled S3 on the website for now, so the downloads are working fine. but really I want S3 to search the MP3s
Basically, you have to tell S3 to override the content-disposition header of the response. You can do that by appending the response-content-disposition query string parameter to the S3 file url and setting it to the desired content-disposition value. To force download try:
<url>&response-content-disposition="attachment; filename=somefilename"
You can find this in the S3 docs. For information on the values that the content-disposition header can assume you can look here.
As an additional information this also works with Google Cloud Storage.
require_once '../sdk-1.4.2.1/sdk.class.php';
// Instantiate the class
$s3 = new AmazonS3();
// Copy object over itself and modify headers
$response = $s3->copy_object(
array( // Source
'bucket' => 'your_bucket',
'filename' => 'Key/To/YourFile'
),
array( // Destination
'bucket' => 'your_bucket',
'filename' => 'Key/To/YourFile'
),
array( // Optional parameters
**'headers' => array(
'Content-Type' => 'application/octet-stream',
'Content-Disposition' => 'attachment'**
)
)
);
// Success?
var_dump($response->isOK());
Amazon AWS S3 to Force Download Mp3 File instead of Stream It
I created a solution for doing this via CloudFront functions (no php required since it all runs at AWS by linking to the .mp3 file on Cloudfront with a ?title=TitleGoesHere querystring to force a downloaded file with that filename). This is a fairly recent way of doing things (as of August 2022). I documented my function and how I set up my "S3 bucket behind a CloudFront Distribution" here: https://stackoverflow.com/a/73451456/19823883

Get gps metadata from image in dropbox using PHP

I am trying to get specific metadata of an image located in dropbox using PHP and the Dropbox API.
After I connect to dropbox and list the images, I do this:
$md = $dbxClient->getMetadata($path);
print_r ($md);
Where the $path is the directory to my image. This works perfectly but I need to get more metadata regarding GPS location. In the Dropbox API (general view for Python, PHP, Java etc.) it says I have to set "include_media_info" to true to get gps metadata.
Going to the PHP part it has the function GetMetaData() with only one parameter: string
($path The Dropbox path to a file or folder).
Is there a way to get detailed metadata using the Dropbox API for PHP?
As you noted, the PHP library doesn't support the include_media_info parameter, so you'd need to modify the source code of the library to add support.
E.g. you could add this method:
function getMetadataWithMediaInfo($path)
{
Path::checkArg("path", $path);
return $this->_getMetadata($path, array("include_media_info" => "true"));
}
I had a look at the source, try this:
$md = $dbxClient->_getMetadata($path,array("list" => "true"));
or edit the core getMetadata and change its hard coded parameter list to true

Google Drive SDK (PHP Client Library) - Upload file but unreadable on the Google Drive UI

Here is my code to upload a file:
private function addFile(File $file, $folderProject, User $user) {
$fileToUpload = new Google_DriveFile();
$fileToUpload->setTitle($file->getName());
//set mime type
$fileToUpload->setMimeType($file->getMimeType());
//build parent and set id
$parent = new Google_ParentReference();
$parent->setId($folderProject->getId());
//set parent to file
$fileToUpload->setParents(array($parent));
$data = file_get_contents($file->getPath());
$createdFile = $this->service->files->insert($fileToUpload, array(
'data' => $data,
'mimeType' => $file->getMimeType(),
));
}
When I upload for example a docx using this PHP code the file append to be unreadable on the Google Drive website (it shows up the XML structure of the docx) and it is showing up like this in my Google Drive folder:
And when i try to upload a docx directly through Google Drive UI it showing up like this (what i want):
I'm pretty sure that the problem is coming from the fact the Google
Drive UI tries to open the file in a different way because i uploaded
it through my app, but i don't want that :)
(i have the same problem with other kind of files)
I tried to set the parameter 'convert' = true in the array but then i got this error during the upload:
..convert=true&uploadType=multipart&key=AI39si49a86oqv9fhb9yzXp7FCWL-cSqwFiClHZ‌​qS3Y3r9Hw8ujtSNlZEWZyFKBp_id7LUyFaZTJ97pMeb0XqHznycK7JJ9o_Q:
(500) Internal Error
Documentation: https://developers.google.com/drive/v2/reference/files/insert
=> But anyway i don't think to convert is the right solution, i just want to be able to see the content as if i uploaded the file through the Google Drive UI
If i open the docx from my Google Drive Sync folders and I edit and save it, I will be able to see it through the Google Drive UI (showing with the W logo)
I also getting the same problem.
But the difference is that , I am uploading pdf and able to view pdf in google drive UI.
I need to automatically convert it to google doc format by api code after adding new pdf.
If I change $mimeType = 'application/vnd.google-apps.document'; then got below error
https://www.googleapis.com/upload/drive/v2/files?convert=true&uploadType=multipart: (400) Bad Request
if I add optional parameter
$createdFile = $service->files->insert($file, array(
'data' => $data,
'mimeType' => $mimeType,
'convert' => TRUE
));
Then no change , only pdf upload no conversion takes place.
do u have any suggestion , where I need to exactly add for convert parameter ?

Force-Download with php on Amazon S3

I am trying to use http://code.google.com/p/amazon-s3-php-class/ to force-dowload files from AWS S3. I have an mp3 that I want people to "play" or "download." By default the when you access the file directly on s3 it begins to play in the browser. I need to add an option to actually download. I have Googled and found came up with nothing. I conceptually know what needs to happen but don't know how to produce it php. I know I need to modify the headers to Content-Disposition: attachment. Any help would be greatly appreciated.
Thanks,
Michael
Amazon has now solved this problem and allows overriding of headers on a per-request basis with signed requests:
http://docs.amazonwebservices.com/AmazonS3/latest/API/index.html?RESTObjectGET.html
w00t!
The php scripts that have been mentioned so far will work ok, but the main downside is that every time a visitor on your site requests a file, your own servers will load it from the S3 and then relay that data to the browser. For low traffic sites, it's probably not a big deal, but for high traffic ones, you definitely want to avoid running everything through your own servers.
Luckily, there's a fairly straight-forward way to set your files to be forced to download from the S3. And you're exactly right - you just want to set the content-type and content-disposition (just setting content-disposition will work in some browsers, but setting both should work in all browsers).
This code is assuming that you're using the Amazon S3 PHP class from Undesigned:
<?php
// set S3 auth and get your bucket listing
// then loop through the bucket and copy each file over itself, replacing the "request headers":
S3::copyObject($bucketName, $filename, $bucketName, $filename, "public-read", array(), array("Content-Type" => "application/octet-stream", "Content-Disposition" => "attachment"));
?>
Now all your files will be forced to download. You may need to clear your cache to see the change. And obviously, don't do that on any file that you actually do want to be loaded "inline" in the browser.
The nice part with this solution is that applications that load media files directly (like let's say an mp3 player in Flash) don't care about the content-type or content-disposition, so you can still play your files in the browser and then link to download that same file. If the user already finished loading the file in flash, they'll most likely still have it in their cache, which means their download will be super quick and it won't even cost you any extra bandwidth charges from the S3.
Just add this to your file's metadata on s3:
Content-Disposition: attachment; filename=FILENAME.EXT
Content-Type: application/octet-stream
Just wanting to post a contribution to this, Alex Neth is correct on this reference, but i do not feel a link is sufficient information, using amazon's own AWS PHP SDK2. Below I've outlined a basic (untested) method for calling data this way, you can use either S3 Factory Method or AWS Service Builder to make the S3 Client.
<?php
// S3 Factory Method
/*use Aws\S3\S3Client;
$s3= S3Client::factory(array(
'key' => '<aws access key>',
'secret' => '<aws secret key>'
));*/
// OR AWS Service Builder
use Aws\Common\Aws;
// Create a service builder using a configuration file
$aws = Aws::factory('/path/to/my_config.json');
// Get the client from the builder by namespace
$s3 = $aws->get('S3');
// Now lets create our request object.
$command = $s3->getCommand('GetObject',array(
'Bucket' => 'your-bucket-name',
'Key' => 'keyname',
'ResponseContentType' => 'application/octet-stream',
'ResponseContentDisposition' => 'attachment; filename="filename.mp3',
));
$url = $command->createPresignedUrl('+1 days');
?>
You can then use PHP's header("Location: $url"); in order to redirect the visitor to the MP3 file with a force download, this should prevent it from playing in the browser, Please note, i use ResponseContentType quite frequently but I've never used ResponseContentDisposition with AWS (it should work according to the docs).
Converting this sample into a function should be easy, you could even pass in $bucket, $key, $force_download as such
<?php
use Aws\Common\Aws;
function gen_url($bucket,$key,$force_download=false){
// OR AWS Service Builder (personal method to do this)
// Create a service builder using a configuration file
$aws = Aws::factory('/path/to/my_config.json');
// Get the client from the builder by namespace
$s3 = $aws->get('S3');
$params = array(
'Bucket' => $bucket,
'Key' => 'keyname',
'ResponseContentType' => 'application/octet-stream',
'ResponseContentDisposition' => 'attachment; filename="filename.mp3',
);
if($force_download){
$params['ResponseContentType'] = 'application/octet-stream';
$params['ResponseContentDisposition'] = 'attachment; filename="'.basename($key).'"';
}
$command = $s3->getCommand('GetObject',$params);
return $command->createPresignedUrl('+1 days');
}
// Location redirection to an MP3 force downlaod
header("Location: ".gen_url("recordings","my-file.mp3",true));
// Location redirection to a MP3 that lets the browser decide what to do.
header("Location: ".gen_url("recordings","my-file.mp3"));
?>
WARNING, if you haven't figured it out, this requires the AWS PHP SDK 2 currently (April 7th 2014) found here http://aws.amazon.com/sdkforphp/ This code is mostly pseudo code and may require some additional tweaking to actually make work as i'm referencing this from memory.
So modify my example above to be like this
<?php
header('Content-Type: audio/mpeg');
header("Content-Disposition: attachment; filename={$_GET['file']};");
readfile("url to the file/{$_GET['file']}");
exit();
?>
Now you will want to put some validation in there so that you not giving the world access to every file you put on S3, but this should work.
If you are using a library like Tarzan AWS, you can add meta headers, that amazon will include when the file is retrieved. Check out the meta parameter in the update_object function here, for example:
http://tarzan-aws.com/docs/2.0/files/s3-class-php.html#AmazonS3.update_object
Also worth mentioning is you are able to hard-set the headers for files in S3. For example, if you need to force-download a certain file you can set the appropriate headers for that file. Such as defaulting to stream, and either having a secondary file set to force-download or spend the bandwidth to use php/fputs and force-download via PHP.
<?php
require_once __DIR__.'/vendor/autoload.php';
use Aws\Common\Aws;
function gen_url($bucket,$key,$force_download=false){
// OR AWS Service Builder (personal method to do this)
$config = array(
'key' => '',
'secret' => '',
);
// Create a service builder using a configuration file
$aws = Aws::factory($config);
// Get the client from the builder by namespace
$s3 = $aws->get('S3');
$params = array(
'Bucket' => $bucket,
'Key' => $key,
'ResponseContentType' => 'application/octet-stream',
'ResponseContentDisposition' => 'attachment; filename="'.$key,
);
if($force_download){
$params['ResponseContentType'] = 'application/octet-stream';
$params['ResponseContentDisposition'] = 'attachment; filename="'.basename($key).'"';
}
$command = $s3->getCommand('GetObject',$params);
return $command->createPresignedUrl('+1 days');
}
$bucket = '';
$filename = '';
$url = gen_url($bucket,$filename,true);
echo "\n".$url."\n\n";
The code above works, you just need to install the S3 composer dependency and link in the autoload file, put your key/secret into config and then supply the bucket/filename.
php would just download the file to the server, not the client. remember, php doesn't do anything on the client, it just returns content (html, javascript, css, xml, whatever...)
[edit: added for clarity]: php can serve audio content, but you want to serve a web page and audio at the same time. To get that client behaviour, you have to get the client to request the file based on the web page's html or javascript.
So you have to get the client to download the file. For instance, have an iframe on the page with the url of the file on s3. Use css to make the iframe invisible. It should render the page and download and play the mp3.
Otherwise, look into using javascript to kick of a download when the page loads. I'm not sure if that's possible.
This is now possible by overwriting the S3 headers using signed requests.
Request Parameters
There are times when you want to override certain response header values in a GET response. For example, you might override the Content-Disposition response header value in your GET request.
You can override values for a set of response headers using the query parameters listed in the following table.
response-content-type - Sets the Content-Type header of the response
response-content-disposition - Sets the Content-Disposition header of the response.
Note
You must sign the request, either using an Authorization header or a pre-signed URL, when using these parameters. They cannot be used with an unsigned (anonymous) request.
So, you would set those headers to:
response-content-disposition: attachment; filename=FILENAME.EXT
response-content-type: application/octet-stream
Found answer here: https://stackoverflow.com/a/5903710/922522
<?php
// PHP solution (for OP and others), works with public and private files
// Download url expires after 30 minutes (no experation after the download initiates, for large files)
// ***Forces client download***
$signed_url = $s3_client->getObjectUrl($s3_bucket_name, $s3_filename_key, '+30 minutes', array(
'ResponseContentType' => 'application/octet-stream',
'ResponseContentDisposition' => 'attachment; filename="your-file-name-here.mp4"'
));
redirect($signed_url);
I never tried Amazon's S3 hosting, but don't you have access to using .htaccess files there? Then you can set Content-Type and Content-Disposition for an entire directory with this entry:
<IfModule mod_headers.c>
<FilesMatch "\.(mp3)$">
ForceType audio/mpeg
Header set Content-Disposition attachment
</FilesMatch>
</IfModule>

Categories