In this guide we are building an integration that covers the following scenarios:
- Uploading audio files to Cyanite.ai
- Enqueuing a file analysis
- Setup a webhook for getting notified for when the file analysis has finished
- Fetching the analysis result.
The tutorial will mostly provide Node.js code. However, all listed GraphQL Operations can be used with any other programming language.
You can find the whole source code of the example integration over on Github: cyanite-ai/cyanite-integration-example.
First of all we create an empty project
Then we install all the required packages:
|validate environment variables|
|minimalist framework for creating a webhook listener|
|request body parsing middleware for |
|HTTP request module for sending requests/upload files to the Cyanite.ai API|
Follow Creating an integration for obtaining an access token and a webhook secret.
The create a new file
cyanite-integration/.env with the following contents:
Make sure you replace
YOUR_ACCESS_TOKEN with the corresponding values for your integration.
Scan your music collection for an
mp3 that you want to analyze. For receiving the best result your
mp3 files should have a bitrate of 320kbs.
You can easily convert your
wav (or any other audio format) files to
mp3 with ffmpeg.
The Upload can be divided into three steps:
- Requesting a file upload
- Uploading the file
- Create an InDepthAnalysis from an uploaded file
For requesting a file upload we use the
fileUploadRequest mutation field.
On the FileUploadRequest we select the
id and the
uploadUrl. We need both fields to proceed.
uploadUrl is the url to which we are uploading our file.
id is a unique identifier which we need for creating the InDepthAnalysis from our uploaded file.
Let's start by requesting our file upload!
Now that we have all the necessary information for uploading the file, we can perform our file upload.
For uploading we use the HTTP
PUT method. You can verify that the upload was successful by checking the HTTP status code of the response which should be equal to 200.
Now we have uploaded the file. Up next we now need to create an InDepthAnalysis from our uploaded file.
For creating the InDepthAnalysis via the Cyanite.ai API we use the
inDepthAnalysisCreate mutation field returns the union type
InDepthAnalysisCreateResult. A union type specifies that the mutation can return a variety of possible results.
Depending on the result we use InlineFragments for specifying our data requirements.
InDepthAnalysisCreateResultSuccess: The mutation finished like expected, we can query for the id, title, status or any other fields that belong to our InDepthAnalysis.
Error: An error occurred. The details will be included in a message attached to the error object. The errors can be one of the following types:
InDepthAnalysisRecordLimitExceededError: You've exceeded your analysis limit.
InDepthAnalysisInvalidTagError: You can attach a tag to your analysis (see tagging InDepthAnalysis). If the tag you are trying to add is invalid, this error will be returned.
Error interface type is implemented by all
Errors. Instead of writing a SelectionSet for each error type we can use an InlineFragment on
Error for gathering the error message. In case another error type provides some more detailed info that we want to use we can add additional SelectionSets. For now, we wanna keep things simple.
We also query for the type name (
__typename). Each ObjectType (e.g.
InDepthAnalysisRecordLimitExceededError) have a
__typename field. We use it for distinguishing between the single types.
Note: Despite us selecting data on the interface
__typename can never be
Error. An interface is an abstract type. However, all types that implement the
Error interface will end with
InDepthAnalysisRecordLimitExceededError). In the integration code, we can therefore simply check whether
__typename ends with
Let's execute this script for completing our first file upload to the Cyanite.ai API 🚀
The terminal output should look similar to this:
As you can see the file is now successfully uploaded but it's status is
InDepthAnalysis.status has the
It can be either one of the following:
|NOT_STARTED||File was successfully uploaded|
|ENQUEUED||File Analysis is enqueued and awaiting processing|
|PROCESSING||File is being processed|
|FINISHED||File Processing has finished successfully|
|FAILED||File processing has failed|
In order to process our uploaded file we need to send an additional mutation to the Cyanite.ai API. We can do this with the
inDepthAnalysisCreate mutation we also include InlineFragments for the expected result (
InDepthAnalysisEnqueueAnalysisResultSuccess) and unexpected results (
The latter are all covered by the
Let's enqueue the file from earlier. Please note that you must use the id of the file you have uploaded instead.
We have now successfully uploaded and enqueued our first file analysis! Hurray 🎉!
The process of analyzing our files is asynchronous, that means that there is no direct connection being kept alive until the file has finished processing. Instead of constantly polling the status of a file, the Cyanite.ai API allows registering a Webhook Endpoint that will be notified once a file has been processed/failed to process.
This approach is much more intuitive than sending unnecessary requests or keeping a connection alive.
Note: For your production integration you should have a server that is facing the public internet. For this demonstration we are going to use ngrok for exposing the local port of our machine to the internet.
npx ngrok http 8080
Open the integration section on Cyanite.ai and edit your integration by setting your webhook url
Your url will slightly differ from the one above
Up next, let's finally write our webhook code.
Afterwards we can start the server (
Before receiving any event, we first need to initiate an action that triggers an event. We can do that by re-enqueueing our previously uploaded file:
After a few seconds the webhook terminal should output something similar to this:
That means our event was delivered successfully 🚀!
Depending on what we need, we can fetch different parts of the analysis result. E.g. we could either query for similar sounding spotify tracks, the detected genre or both at the same time! You can explore all available fields using GraphiQL.
For this demonstration we are going to fetch the analyzed genres.
Feel free to explore the available data using GraphiQL.
Here you can find a more complex Query:
Also check out the Query builder which can help you writing queries for the classifier data you need.
We successfully built our first Cyanite.ai Integration!
In case you have any open questions or suggestions on how we could improve this guide contact us via firstname.lastname@example.org.