Upload Video using Graph QL

Hi!

I’m trying to upload a video using the Graph QL API. I successfully receive the answer with the mutation

stagedUploadsCreate. Then, I try to use that info to upload the file but I’m getting the following response:

<?xml version='1.0' encoding='UTF-8'?>AccessDeniedAccess denied.
Anonymous caller does not have storage.objects.create access to the Google Cloud Storage object. Permission 'storage.objects.create' denied on resource (or it may not exist).

I’m doing a POST for the provided URL resource . I already tryed to to use the mutation with FILE and VIDEO as resource. Depending on that parameter, I get a different number os parameters from Shopify. However, none of them work. Can anyone help me?

UPDATE: I managed to upload the file to Google Storage with CURL. Then I was able to create the video file using the resourceURL on the fileCreate mutation. However, I cannot add that video to the product. Accordingly to the documentation I must use the resourceUrl as the originalSource of the productCreateMedia mutation but I always get Invalid video url.

Does anyone know what is going on? Anyone faced the same situation and has able to figure it out?

Hey @AndreFonseca ,

Thanks for sharing that update. Can you share an example of your productCreateMedia mutation?

We have an example in our guide here, which may help to double check if there are any syntax errors with the URL. https://shopify.dev/docs/apps/online-store/media/products#step-2-add-media-to-a-product

  • Kyle G.

Hi @ShopifyDevSup

Here is the mutation:

mutation productCreateMedia($media: [CreateMediaInput!]!, $productId: ID!) {
productCreateMedia(media: $media, productId: $productId) {
media {
alt,
mediaContentType,
status,
id
}
product {
id
handle
}
mediaUserErrors {
code
field
message
}
}
}

Variables

{
“media”: [
{
“originalSource”: “https://storage.googleapis.com/shopify-staged-uploads/tmp/64696615085/files/6a2e0dc2-8ad7-4833-86a7-3aa9ccce42f9/XXXX.mp4”,
“alt”: “”,
“mediaContentType”: “VIDEO”
}
],
“productId”: “gid://shopify/Product/7545977077933”
}

I restarted the all process but now, I cant even play the video that I upload to my store. I’m feeling frustrated. I already spent so many time on this and I cannot understand what is going on :disappointed_face:

EDIT:

Let me recap just to be sure.

1st

Use the stagedUploadsCreate mutation witch return the parameters to POST the video to Google.

NOTE: The endpoint must be "https://shopify-staged-uploads.storage.googleapis.com" and not the full resourceUrl

2nd

Upload the video using the information os the first step

3rd

Create the file in Shopify using the fileCreate mutation. The originalSource link must be the resourceUrl of the first step

4th

Associate the video to the product using the productCreateMedia. The originalSouce is the resourceUrl of the first step and the mediaContentType is VIDEO

In the documentation (step 2) you specify that “The original source of the media object. Can be an external URL for images, YouTube videos, and Vimeo videos, or an upload URL for images, videos, and 3D models hosted by Shopify. For assets hosted by Shopify, use the resourceUrl value returned by the stagedUploadsCreate mutation.” just like I said. However, looking for the example, the originalUrl used seems to be the one that is returned after uploading the file to Google. I tried both of them, but none work.

Kyle, can you show me what am I doing wrong and why the video does not play? I tried the resourceUrl, Shopify Url (Content > Files) and the one returned after uploading the file to Google. Thanks in advance!

EDIT2:

On the productCreateMedia mutation, I changed the mediaContentType value form VIDEO to IMAGE and I was able to associate. I went to the product page and the media was processing. After a while it failed (as expected). This proves that the url that I’m providing as the originalSource is correct. Something else is missing or may not be working on the API

I still have the problem when I try to associate the video to the product (step 4). I tryed the URL given by the mutation

stagedUploadsCreate, the url given as anwser after uploading the video to google storage and the link of the video on Shopify (Content > Files). None of them works. I even tried to put EXTERNAL_VIDEO as mediaContentType but no luck. Can someone please help me?

EDIT: I used the mutation as is in the second step of the following tutorial (https://shopify.dev/docs/apps/online-store/media/products#requirements)) but it doesn’t work. I always says Invalid video URL

Hey @AndreFonseca , thanks for sharing those additional details.

I went through to test on my own, and it was working properly. Let me what what I did and maybe we can narrow down where it’s failing for you.

Step 1. Staged Uploads create

This was my mutation

mutation generateStagedUploads {> stagedUploadsCreate(> input: [> {> filename: “testForMedia.mp4”> mimeType: “video/mp4”> resource: VIDEO> fileSize: “17404311”> }> ]> ) {> stagedTargets {> url> resourceUrl> parameters {> name> value> }> }> userErrors {> field> message> }> }> }

That returned the staged targets the same as in the example here https://shopify.dev/docs/apps/online-store/media/products#generate-the-upload-url-and-parameters

Noting in particular the resourceURL returned. Looking at the example above, this may be where it’s going wrong.

“resourceUrl”: “https://shopify-video-production-core-originals.storage.googleapis.com?external_video_id=8490719”,

Step 2, I created a CURL POST request with the details from above response. If successful, this will return a 204 status with no content

curl -v > -F “GoogleAccessId=REDACTED” > -F “key=REDACTED” > -F “policy=REDACTED” > -F “signature=REDACTED” > -F [email removed] > “https://shopify-video-production-core-originals.storage.googleapis.com

Step 3, is productCreateMedia mutation using the product GID that I want to apply to the video and the resourceURL from step 1 as the original source.

mutation createProductMedia {> productCreateMedia(productId: “gid://shopify/Product/[redacted]”, media: [> {> originalSource: “https://shopify-video-production-core-originals.storage.googleapis.com?external_video_id=8490719”,> alt: “Video showing nothing really”,> mediaContentType: VIDEO> }> ]) {> media {> … fieldsForMediaTypes> mediaErrors {> code> details> message> }> mediaWarnings {> code> message> }> }> product {> id> }> mediaUserErrors {> code> field> message> }> }> }> > fragment fieldsForMediaTypes on Media {> alt> mediaContentType> preview {> image {> id> }> }> status> … on Video {> id> sources {> format> height> mimeType> url> width> }> }> }

The response here returns the video, with the status field likely to be uploaded meaning it’s uploaded, but not yet processed or ready.

Step 4. is retrieve media object with a product query. For this step I’m mainly interested in the status of the video.

Here’s a simplified query that will return the product title and the status of the media.

{> product(id:“gid://shopify/Product/[redacted]”) {> title> media(first:5) {> edges {> node {> status> }> }> }> }> }

This returned the following status of Ready, confirming the video has completed processing. If any other status is returned, the video is not yet processed. :

“data”: {> “product”: {> “title”: “Coffee Maker”,> “media”: {> “edges”: [> {> “node”: {> “status”: “READY”> }> }> ]> }> }> },

Step 5, I visit my product page and the video is available.

Let me know if that helps!

  • Kyle G.

Hey @AndreFonseca , thanks for sharing those additional details.

I went through to test on my own, and it was working properly. Let me what what I did and maybe we can narrow down where it’s failing for you.

Step 1. Staged Uploads create

This was my mutation

mutation generateStagedUploads {> stagedUploadsCreate(> input: [> {> filename: “testForMedia.mp4”> mimeType: “video/mp4”> resource: VIDEO> fileSize: “17404311”> }> ]> ) {> stagedTargets {> url> resourceUrl> parameters {> name> value> }> }> userErrors {> field> message> }> }> }

That returned the staged targets the same as in the example here https://shopify.dev/docs/apps/online-store/media/products#generate-the-upload-url-and-parameters

Noting in particular the resourceURL returned. Looking at the example above, this may be where it’s going wrong.

“resourceUrl”: “https://shopify-video-production-core-originals.storage.googleapis.com?external_video_id=8490719”,

Step 2, I created a CURL POST request with the details from above response. If successful, this will return a 204 status with no content

*curl -v *> *-F “GoogleAccessId=REDACTED” *> *-F “key=REDACTED” *> *-F “policy=REDACTED” *> *-F “signature=REDACTED” *> *-F [email removed] *> https://shopify-video-production-core-originals.storage.googleapis.com

Step 3, is productCreateMedia mutation using the product GID that I want to apply to the video and the resourceURL from step 1 as the original source.

mutation createProductMedia {> productCreateMedia(productId: “gid://shopify/Product/[redacted]”, media: [> {> originalSource: “https://shopify-video-production-core-originals.storage.googleapis.com?external_video_id=8490719”,> alt: “Video showing nothing really”,> mediaContentType: VIDEO> }> ]) {> media {> … fieldsForMediaTypes> mediaErrors {> code> details> message> }> mediaWarnings {> code> message> }> }> product {> id> }> mediaUserErrors {> code> field> message> }> }> }> > fragment fieldsForMediaTypes on Media {> alt> mediaContentType> preview {> image {> id> }> }> status> … on Video {> id> sources {> format> height> mimeType> url> width> }> }> }

The response here returns the video, with the status field likely to be uploaded meaning it’s uploaded, but not yet processed or ready.

Step 4. is retrieve media object with a product query. For this step I’m mainly interested in the status of the video.

Here’s a simplified query that will return the product title and the status of the media.

{> product(id:“gid://shopify/Product/[redacted]”) {> title> media(first:5) {> edges {> node {> status> }> }> }> }> }

This returned the following status of Ready, confirming the video has completed processing. If any other status is returned, the video is not yet processed. :

“data”: {> “product”: {> “title”: “Coffee Maker”,> “media”: {> “edges”: [> {> “node”: {> “status”: “READY”> }> }> ]> }> }> },

Step 5, I visit my product page and the video is available.

Let me know if that helps!

  • Kyle G.

Hey @AndreFonseca , thanks for sharing those additional details.

I went through to test on my own, and it was working properly. Let me what what I did and maybe we can narrow down where it’s failing for you.

Step 1. Staged Uploads create

This was my mutation

mutation generateStagedUploads {> stagedUploadsCreate(> input: [> {> filename: “testForMedia.mp4”> mimeType: “video/mp4”> resource: VIDEO> fileSize: “17404311”> }> ]> ) {> stagedTargets {> url> resourceUrl> parameters {> name> value> }> }> userErrors {> field> message> }> }> }

That returned the staged targets the same as in the example here https://shopify.dev/docs/apps/online-store/media/products#generate-the-upload-url-and-parameters

Noting in particular the resourceURL returned. Looking at the example above, this may be where it’s going wrong.

“resourceUrl”: “https://shopify-video-production-core-originals.storage.googleapis.com?external_video_id=8490719”,

Step 2, I created a CURL POST request with the details from above response. If successful, this will return a 204 status with no content

*curl -v *> *-F “GoogleAccessId=REDACTED” *> *-F “key=REDACTED” *> *-F “policy=REDACTED” *> *-F “signature=REDACTED” *> *-F “file=@/path/to/your/file.mp4” *> https://shopify-video-production-core-originals.storage.googleapis.com

Step 3, is productCreateMedia mutation using the product GID that I want to apply to the video and the resourceURL from step 1 as the original source.

mutation createProductMedia {> productCreateMedia(productId: “gid://shopify/Product/[redacted]”, media: [> {> originalSource: “https://shopify-video-production-core-originals.storage.googleapis.com?external_video_id=8490719”,> alt: “Video showing nothing really”,> mediaContentType: VIDEO> }> ]) {> media {> … fieldsForMediaTypes> mediaErrors {> code> details> message> }> mediaWarnings {> code> message> }> }> product {> id> }> mediaUserErrors {> code> field> message> }> }> }> > fragment fieldsForMediaTypes on Media {> alt> mediaContentType> preview {> image {> id> }> }> status> … on Video {> id> sources {> format> height> mimeType> url> width> }> }> }

The response here returns the video, with the status field likely to be uploaded meaning it’s uploaded, but not yet processed or ready.

Step 4. is retrieve media object with a product query. For this step I’m mainly interested in the status of the video.

Here’s a simplified query that will return the product title and the status of the media.

{> product(id:“gid://shopify/Product/[redacted]”) {> title> media(first:5) {> edges {> node {> status> }> }> }> }> }

This returned the following status of Ready, confirming the video has completed processing. If any other status is returned, the video is not yet processed. :

“data”: {> “product”: {> “title”: “Coffee Maker”,> “media”: {> “edges”: [> {> “node”: {> “status”: “READY”> }> }> ]> }> }> },

Step 5, I visit my product page and the video is available.

Let me know if that helps!

  • Kyle G.

It works. Thanks!