Blog

🚀 Générez et optimisez l'image mise en avant d'un article grâce à l'IA, avec la nouvelle version Gato GraphQL v2

Leonardo Losoviz
Par Leonardo Losoviz ·

Nous sommes ravis d'annoncer que Gato GraphQL v2.0 est désormais disponible !

Avec cette nouvelle version et les extensions PRO, vous pouvez utiliser l'IA générative pour créer des images mises en avant pour les articles qui n'ont pas de miniature.

Voici les changements les plus importants apportés à v2.0 (pour voir tous les changements, rendez-vous sur les notes de version sur GitHub).

Ajout de la mutation createMediaItem

La mutation createMediaItem permet d'uploader des fichiers dans la Médiathèque. Elle offre 2 façons de fournir le fichier source :

  1. Via URL
  2. Directement par son contenu

L'exécution de cette requête :

mutation CreateMediaItems {
  fromURL: createMediaItem(input: {
    from: {
      url: {
        source: "https://gatographql.com/assets/GatoGraphQL-logo.png"
      }
    }
    caption: "Gato GraphQL logo"
    altText: "This is the Gato GraphQL logo"
  }) {
    mediaItemID
    status
    errors {
      __typename
      ...on ErrorPayload {
        message
      }
    }
    mediaItem {
      ...MediaItemData
    }
  }
 
  directlyByContents: createMediaItem(input: {
    from: {
      contents: {
        body: """
<html>
  <body>
    Hello world!
  </body>
</html>
        """
        filename: "hello-world.html"
      }
    }
    title: "Hello world!"
  }) {
    mediaItemID
    status
    errors {
      __typename
      ...on ErrorPayload {
        message
      }
    }
    mediaItem {
      ...MediaItemData
    }
  }
}
 
fragment MediaItemData on Media {
  altText
  caption
  mimeType
  slug
  src
  title
}

...produira :

{
  "data": {
    "fromURL": {
      "mediaItemID": 1380,
      "status": "SUCCESS",
      "errors": null,
      "mediaItem": {
        "altText": "This is the Gato GraphQL logo",
        "caption": "Gato GraphQL logo",
        "mimeType": "image/png",
        "slug": "gatographql-logo-png",
        "src": "https://mysite.com/wp-content/uploads/GatoGraphQL-logo.png",
        "title": "GatoGraphQL-logo.png"
      }
    },
    "directlyByContents": {
      "mediaItemID": 1381,
      "status": "SUCCESS",
      "errors": null,
      "mediaItem": {
        "altText": "",
        "caption": "",
        "mimeType": "text/html",
        "slug": "hello-world-html",
        "src": "https://mysite.com/wp-content/uploads/hello-world.html",
        "title": "Hello world!"
      }
    }
  }
}

Ajout des champs myMediaItemCount, myMediaItems et myMediaItem

Les utilisateurs connectés peuvent désormais récupérer tous leurs fichiers multimédias.

L'exécution de cette requête :

query GetMediaItems {
  me {
    slug
  }
  
  myMediaItemCount
 
  myMediaItems(pagination: {
    limit: 3
  }) {
    ...MediaItemData
  }
 
  myMediaItem(by: { id: 1380 }) {
    ...MediaItemData
  }
}
 
fragment MediaItemData on Media {
  id
  mimeType
  src
  author {
    slug
  }
}

...produira :

{
  "data": {
    "me": {
      "slug": "admin"
    },
    "myMediaItemCount": 2,
    "myMediaItems": [
      {
        "id": 1380,
        "mimeType": "image/png",
        "src": "https://mysite.com/wp-content/uploads/GatoGraphQL-logo.png",
        "author": {
          "slug": "admin"
        }
      },
      {
        "id": 1365,
        "mimeType": "image/png",
        "src": "https://mysite.com/wp-content/uploads/browser.png",
        "author": {
          "slug": "admin"
        }
      }
    ],
    "myMediaItem": {
      "id": 1380,
      "mimeType": "image/png",
      "src": "https://mysite.com/wp-content/uploads/GatoGraphQL-logo.png",
      "author": {
        "slug": "admin"
      }
    }
  }
}

(Cette fonctionnalité nécessite les extensions PRO.)

Une nouvelle requête persistée prédéfinie, intitulée "Generate a post's featured image using AI and optimize it", a été ajoutée.

Elle utilise l'IA générative pour produire des images pour les articles sans image mise en avant, en utilisant le titre de l'article comme prompt. Nous pouvons choisir parmi ces fournisseurs de services :

La requête vérifie d'abord si un article possède une image mise en avant. Si ce n'est pas le cas, elle en crée une en appelant le service d'IA générative. Nous devons fournir la clé API correspondant au service choisi.

Comme les images d'IA générative ne sont pas optimisées pour le web (les images d'OpenAI peuvent peser 3 Mo !), la requête envoie également l'image nouvellement générée à TinyPNG pour la compresser. Nous devons fournir la clé API pour utiliser ce service.

Enfin, la requête crée un nouvel élément multimédia avec l'image (en utilisant le titre de l'article comme nom de fichier pour la pièce jointe, tronqué à 20 caractères), et la définit comme image mise en avant de l'article.

Voici la requĂŞte GraphQL :

query InitializeVariables(
  $openAIAPIKey: String
  $stableDiffusionAPIKey: String
  $tinyPngAPIKey: String
)
  @configureWarningsOnExportingDuplicateVariable(enabled: false)
{
  isFeaturedImageMissing: _echo(value: false)
    @export(as: "isFeaturedImageMissing")
    @remove
 
  generatedImageURL: _echo(value: null)
    @export(as: "generatedImageURL")
    @remove
 
  isImageGenerated: _echo(value: false)
    @export(as: "isImageGenerated")
    @remove
 
  mimeType: _echo(value: null)
    @export(as: "mimeType")
    @remove
 
  isMediaItemCreated: _echo(value: false)
    @export(as: "isMediaItemCreated")
    @remove
 
  useOpenAI: _notEmpty(value: $openAIAPIKey)
    @export(as: "useOpenAI")
    @remove
 
  useStableDiffusion: _notEmpty(value: $stableDiffusionAPIKey)
    @export(as: "useStableDiffusion")
    @remove
 
  useTinyPng: _notEmpty(value: $tinyPngAPIKey)
    @export(as: "useTinyPng")
    @remove
}
 
query ExportPostData(
  $postId: ID!
)
  @depends(on: "InitializeVariables")
{
  post(by: { id: $postId }) {
    hasFeaturedImage
    isFeaturedImageMissing: hasFeaturedImage
      @boolOpposite
      @export(as: "isFeaturedImageMissing")
    title
      @export(as: "postTitle")
    mediaItemFilename: rawTitle
      @default(value: "untitled", condition: IS_EMPTY)
      @strLowerCase
      @strSubstr(offset: 0, length: 20)
      @export(as: "filename")
      @remove
  }
}
 
query MaybeGenerateImageUsingOpenAI(
  $openAIAPIKey: String
  $imageSize: String! = "1024x1024" # 256x256, 512x512, or 1024x1024 pixels
)
  @depends(on: "ExportPostData")
  @include(if: $isFeaturedImageMissing)
  @include(if: $useOpenAI)
{
  openAIResponse: _sendJSONObjectItemHTTPRequest(input: {
    url: "https://api.openai.com/v1/images/generations",
    method: POST,
    options: {
      auth: {
        password: $openAIAPIKey
      },
      json: {
        prompt: $postTitle,
        size: $imageSize,
        n: 1,
        response_format: "url",
      }
    }
  })
    @underJSONObjectProperty(by: { key: "data" })
      @underArrayItem(index: 0)
        @underJSONObjectProperty(by: { key: "url" })
          @export(as: "generatedImageURL")
  
  openAPIImageCaption: _sprintf(
    string: "Image created by DALL-E using prompt: '%s'",
    values: [$postTitle]
  )
      @export(as: "imageCaption")
  
  openAIMediaItemFilename: _sprintf(
    string: "%s.png",
    values: [$filename]
  )
    @export(as: "filename")
}
 
query MaybeGenerateImageUsingStableDiffusion(
  $stableDiffusionAPIKey: String
  $width: Int! = 1024
  $height: Int! = 1024
)
  @depends(on: "ExportPostData")
  @include(if: $isFeaturedImageMissing)
  @include(if: $useStableDiffusion)
{
  stableDiffusionResponse: _sendJSONObjectItemHTTPRequest(input: {
    url: "https://stablediffusionapi.com/api/v3/text2img",
    method: POST,
    options: {
      json: {
        key: $stableDiffusionAPIKey
        prompt: $postTitle,
        width: $width
        height: $height
        samples: 1
      }
    }
  })
    @underJSONObjectProperty(by: { key: "output" })
      @underArrayItem(index: 0)
        @export(as: "generatedImageURL")
  
  stableDiffusionImageCaption: _sprintf(
    string: "Image created by Stable Diffusion using prompt: '%s'",
    values: [$postTitle]
  )
    @export(as: "imageCaption")
  
  stableDiffusionMediaItemFilename: _sprintf(
    string: "%s.png",
    values: [$filename]
  )
    @export(as: "filename")
}
 
query CheckIsImageGenerated
  @depends(on: [
    "MaybeGenerateImageUsingOpenAI",
    "MaybeGenerateImageUsingStableDiffusion"
  ])
  @include(if: $isFeaturedImageMissing)
{
  isImageGenerated: _notEmpty(value: $generatedImageURL)
    @export(as: "isImageGenerated")
}
 
query MaybeCompressGeneratedImage(
  $tinyPngAPIKey: String
)
  @depends(on: "CheckIsImageGenerated")
  @include(if: $isImageGenerated)
  @include(if: $useTinyPng)
{
  compressedImageResponse: _sendHTTPRequest(input: {
    url: "https://api.tinify.com/shrink",
    method: POST,
    options: {
      auth: {
        password: $tinyPngAPIKey
      },
      headers: [
        {
          name: "Content-Type",
          value: "application/json"
        }
      ],
      json: {
        source: {
          url: $generatedImageURL
        }
      }
    }
  }) {
    body
      @remove
    bodyJSONObject: _strDecodeJSONObject(string: $__body)
 
    mimeType: _objectProperty(
      object: $__bodyJSONObject
      by: { path: "output.type" }
    )
      @export(as: "mimeType")
 
    generatedImageURL: header(name: "Location")
      @export(as: "generatedImageURL")
  }
}
 
mutation CreateMediaItemFromGeneratedImage
  @depends(on: "MaybeCompressGeneratedImage")
  @include(if: $isImageGenerated)
{
  createMediaItem(input: {
    from: {
      url: {
        source: $generatedImageURL
        filename: $filename
      }
    }
    title: $postTitle
    caption: $imageCaption
    altText: $postTitle
    mimeType: $mimeType
  }) {
    mediaItemID
      @export(as: "mediaItemID")
    isMediaItemCreated: _notNull(value: $__mediaItemID)
      @export(as: "isMediaItemCreated")
      @remove
 
    status
    errors {
      __typename
      ...on ErrorPayload {
        message
      }
    }
    mediaItem {
      altText
      caption
      mimeType
      slug
      src
      title
    }
  }
}
 
mutation SetMediaItemAsPostFeaturedImage(
  $postId: ID!
)
  @depends(on: "CreateMediaItemFromGeneratedImage")
  @include(if: $isMediaItemCreated)
{
  setFeaturedImageOnCustomPost(input: {
    customPostID: $postId
    mediaItemBy: { id: $mediaItemID }
  }) {
    status
    errors {
      __typename
      ...on ErrorPayload {
        message
      }
    }
    customPost {
      __typename
      ...on CustomPost {
        featuredImage {
          id
          altText
          caption
          mimeType
          slug
          src
          title
        }
      }
    }
  }
}

[PRO] Ajout du champ _dataMatrixOutputAsCSV dans l'extension Helper Function Collection

(Cette fonctionnalité a été ajoutée aux extensions PRO.)

Le champ _dataMatrixOutputAsCSV a été ajouté à l'extension Helper Function Collection (et à tous les bundles contenant cette extension).

Ce champ prend une matrice de données et produit une chaîne CSV. Par exemple, cette requête :

csv: _dataMatrixOutputAsCSV(
  fields: 
    ["Name", "Surname", "Year"]
  data: [
    ["John", "Smith", 2003],
    ["Pedro", "Gonzales", 2012],
    ["Manuel", "Perez", 2008],
    ["Jose", "Pereyra", 1999],
    ["Jacinto", "Bloomberg", 1998],
    ["Jun-E", "Song", 1983],
    ["Juan David", "Santamaria", 1943],
    ["Luis Miguel", null, 1966],
  ]
)

...produira :

{
  "data": {
    "csv": "Name,Surname,Year\nJohn,Smith,2003\nPedro,Gonzales,2012\nManuel,Perez,2008\nJose,Pereyra,1999\nJacinto,Bloomberg,1998\nJun-E,Song,1983\nJuan David,Santamaria,1943\nLuis Miguel,,1966\n"
  }
}

Cette fonctionnalité nous permet d'exporter des données de notre site WordPress vers Google Sheets ou d'autres outils.

Par exemple, cette requête récupérera les données de 100 articles et créera un fichier CSV qui sera uploadé dans la Médiathèque, avec les colonnes ID, Title, Slug, Author name, Published date, URL et Content :

query ExportPostData(
  $limit: Int! = 100,
  $offset: Int! = 0
) {
  posts(
    pagination: { limit: $limit, offset: $offset },
    sort: { by: ID, order: ASC }
  ) {
    id @export(as: "postIds", type: LIST)
    title @export(as: "postTitles", type: LIST)
    slug @export(as: "postSlugs", type: LIST)
    author {
      name @export(as: "postAuthorNames", type: LIST)
    }
    dateStr(format: "d/m/Y") @export(as: "postPublishedDates", type: LIST)
    url @export(as: "postUrls", type: LIST)
    content @export(as: "postContents", type: LIST)
  }
}
 
query CreateDataMatrix
  @depends(on: "ExportPostData")
{
  csvDataMatrix: _echo(value: $postIds)
    @underEachArrayItem(
      passIndexOnwardsAs: "key"
      passValueOnwardsAs: "postId"
      affectDirectivesUnderPos: [1, 2, 3, 4, 5, 6, 7]
    )
      @applyField(
        name: "_arrayItem",
        arguments: {
          array: $postTitles,
          position: $key,
        },
        passOnwardsAs: "postTitle"
      )
      @applyField(
        name: "_arrayItem",
        arguments: {
          array: $postSlugs,
          position: $key,
        },
        passOnwardsAs: "postSlug"
      )
      @applyField(
        name: "_arrayItem",
        arguments: {
          array: $postAuthorNames,
          position: $key,
        },
        passOnwardsAs: "postAuthorName"
      )
      @applyField(
        name: "_arrayItem",
        arguments: {
          array: $postPublishedDates,
          position: $key,
        },
        passOnwardsAs: "postPublishedDate"
      )
      @applyField(
        name: "_arrayItem",
        arguments: {
          array: $postUrls,
          position: $key,
        },
        passOnwardsAs: "postUrl"
      )
      @applyField(
        name: "_arrayItem",
        arguments: {
          array: $postContents,
          position: $key,
        },
        passOnwardsAs: "postContent"
      )
      @applyField(
        name: "_echo",
        arguments: {
          value: [
            $postId,
            $postTitle,
            $postSlug,
            $postAuthorName,
            $postPublishedDate,
            $postUrl,
            $postContent
          ]
        },
        setResultInResponse: true
      )
    @export(as: "csvDataMatrix")
}
 
query OutputCSV
  @depends(on: "CreateDataMatrix")
{
  csvString: _dataMatrixOutputAsCSV(
    fields: [
      "ID",
      "Title",
      "Slug",
      "Author name",
      "Published date",
      "URL",
      "Content",
    ]
    data: $csvDataMatrix
  )
    @export(as: "csvString")
}
 
mutation CreateMediaItem
  @depends(on: "OutputCSV")
{
  createMediaItem(input: {
    from: {
      contents: {
        body: $csvString
        filename: "posts.csv"
      }
    }
    title: "Post data as CSV"
  }) {
    mediaItemID
    status
    errors {
      __typename
      ...on ErrorPayload {
        message
      }
    }
    mediaItem {
      mimeType
      slug
      src
      title
    }
  }
}

Préparation pour v3.0

Nous espérons que vous apprécierez les nouvelles fonctionnalités de cette dernière version.

Y a-t-il une nouvelle fonctionnalité que vous aimeriez voir dans Gato GraphQL ? Envoyez-nous un message et faites-le nous savoir.

Profitez-en !


Abonnez-vous Ă  notre newsletter

Restez au courant de toutes les nouveautés de Gato GraphQL.