Skip to content

Requirements of the optional features

Some features need a tool that is not part of Drupal. When one is missing, the feature stays on its page, disabled, with a short line that links here. The status report at /admin/reports/status has one entry for each missing piece.

FFmpeg and ffprobe

Make the labelled copies of audio and video, and check the clips of the spoken disclaimer. Without them the media keep their original files and no job is queued. Everything else works.

The module also checks that this FFmpeg build has the overlay and concat filters. Distribution packages and the static builds below have both.

sudo apt install ffmpeg
ffmpeg -version
ffprobe -version

Download a static build. It has no dependencies and needs no root.

mkdir -p ~/bin ~/ffmpeg-download
cd ~/ffmpeg-download
curl -LO https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz
tar -xf ffmpeg-release-amd64-static.tar.xz
cp ffmpeg-*-static/ffmpeg ffmpeg-*-static/ffprobe ~/bin/
chmod +x ~/bin/ffmpeg ~/bin/ffprobe
~/bin/ffmpeg -version
~/bin/ffprobe -version

Use ffmpeg-release-arm64-static.tar.xz on an ARM server. The builds are listed on johnvansickle.com.

Add this line to the Dockerfile of the PHP image, which must be based on Debian or Ubuntu, then rebuild the image.

RUN apt-get update && apt-get install -y --no-install-recommends ffmpeg && rm -rf /var/lib/apt/lists/*
docker compose build php
docker compose up -d php
docker compose exec php ffmpeg -version

With DDEV, add the package to .ddev/config.yaml, then restart.

webimage_extra_packages: [ffmpeg]
ddev restart
ddev exec ffmpeg -version

Where the binaries are not in the PATH of the web server, set their full paths in "FFmpeg path" and "ffprobe path" at /admin/config/content/ai-disclosure/video-audio, for example /home/user/bin/ffmpeg. Saving the form runs -version and refuses a path that prints no version.

On a small server that cannot render video, limit FFmpeg or render on another machine, see Low-resource servers.

The AI module and a text-to-speech provider

The "Generate with AI" button makes a disclaimer clip from the sentence of the row. Uploading or recording a clip needs none of this.

The button needs the AI module, a provider module with text-to-speech, and the API key of that provider, kept by the Key module. Anthropic (Claude) has no text-to-speech. A site that uses Claude for text still needs a speech provider.

1. Install the modules

The ElevenLabs provider and the OpenAI Provider both offer text-to-speech. Pick the one you have an account with. The examples use ElevenLabs. For OpenAI, use drupal/ai_provider_openai and ai_provider_openai instead. Composer installs the Key module with the AI module. The commands run as the site user and need no root.

composer require drupal/ai drupal/elevenlabs
drush en ai elevenlabs
composer require drupal/ai drupal/elevenlabs
vendor/bin/drush en ai elevenlabs

If the host gives you no shell, install the modules on your computer and upload the vendor and web/modules/contrib directories.

docker compose exec php composer require drupal/ai drupal/elevenlabs
docker compose exec php vendor/bin/drush en ai elevenlabs

With DDEV:

ddev composer require drupal/ai drupal/elevenlabs
ddev drush en ai elevenlabs

2. Store the API key

A key entity holds the API key, so the value stays out of the exported configuration.

  1. Open /admin/config/system/keys and add a key.
  2. As key provider, choose "Environment" or "File". Avoid "Configuration", which stores the value in the database.
  3. Save the key.

3. Configure the provider

  1. For ElevenLabs, open /admin/config/system/eleven-labs-settings. Set "ElevenLabs API Key" to the key, then save. Other providers are listed at /admin/config/ai/providers.
  2. Open /admin/config/ai/settings. The row "Text To Speech" has a provider list. Select the provider there, then save.

4. Check the status

At /admin/reports/ai-disclosure/status, the entry "Generate with AI (speech)" under "Spoken disclaimer" says "A provider is available". If it says that no provider was found, go back over steps 1 and 3.

5. Translate the two sentences

A clip says the sentence of its own language, and the English one when there is no translation. Translate the sentences before you make the clips.

  1. Enable the Configuration Translation module.
  2. Open /admin/config/content/ai-disclosure/video-audio/audio/translate, the "Translate" tab of the "Spoken disclaimer" page.
  3. Add the language and write both sentences. Repeat for each language.

6. Generate the clips

Clips are made one row at a time, from the sentence of the row.

  1. Open /admin/config/content/ai-disclosure/video-audio/audio.
  2. Press "Generate with AI" in a row.
  3. Listen in the player. Press the button again if you want another take.
  4. Press "Save configuration". Only then is the clip used.

7. Verify

The status page and a test file show that the clips work.

  1. On the status page, "Clips" says "All languages have clips".
  2. Upload an audio file marked as AI generated. Its labelled copy starts with the clip, so it is longer than the original by the length of the clip.

Recording in the browser

The "Record" button makes a disclaimer clip in the browser. It needs a secure page (HTTPS) and the permission to use the microphone. The browser refuses the microphone on a plain HTTP page, except on localhost.

Get a certificate from Let's Encrypt. Use the plugin for your web server.

sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d example.com

For Apache:

sudo apt install certbot python3-certbot-apache
sudo certbot --apache -d example.com

Certbot also renews the certificate by itself. To verify, open the site with https:// and look for the padlock.

Use the HTTPS or Let's Encrypt option in the control panel of the host. It is usually a switch for each domain. Then open the site with https://.

Put a proxy that ends TLS in front of the web container, such as Caddy, Traefik or nginx with a certificate. Or use DDEV, which serves https:// by default:

ddev start
ddev describe

Open the https:// address that ddev describe shows. If the browser does not trust the certificate, run mkcert -install once on your computer and restart the browser.

When the browser asks, allow the site to use the microphone. If you refused by mistake, click the padlock in the address bar, set "Microphone" to "Allow" and reload the page. The computer must have a microphone, and the operating system must let the browser use it.

image_effects

Needed by the ai_disclosure_image submodule to burn the icon into the derivatives of an image style. Without it the icon stays in the HTML around the image only.

composer require drupal/image_effects
drush en image_effects
composer require drupal/image_effects
vendor/bin/drush en image_effects
docker compose exec php composer require drupal/image_effects
docker compose exec php vendor/bin/drush en image_effects

With DDEV:

ddev composer require drupal/image_effects
ddev drush en image_effects

To verify, open Configuration, Media, Image styles. The list of effects of a style now offers the effects of Image Effects.

ImageMagick

ImageMagick is optional, because the icon can be burned in with the GD toolkit that comes with PHP. Use ImageMagick when you want it as the image toolkit of the site, for example for more image formats. It needs the ImageMagick module and the program.

sudo apt install imagemagick
composer require drupal/imagemagick
drush en imagemagick
convert -version

On ImageMagick 7 the command may be magick -version.

Many hosts have ImageMagick already. Check:

convert -version
magick -version

If one of them prints a version, install the module:

composer require drupal/imagemagick
vendor/bin/drush en imagemagick

If neither does, and you cannot install packages, stay on GD.

Add this line to the Dockerfile of the PHP image, which must be based on Debian or Ubuntu, then rebuild the image.

RUN apt-get update && apt-get install -y --no-install-recommends imagemagick && rm -rf /var/lib/apt/lists/*
docker compose build php
docker compose up -d php
docker compose exec php composer require drupal/imagemagick
docker compose exec php vendor/bin/drush en imagemagick

With DDEV, check whether the web container has it already, and add imagemagick to webimage_extra_packages in .ddev/config.yaml if not:

ddev exec convert -version

Then choose the toolkit at Configuration, Media, Image toolkit.

ExifTool

ExifTool matters only for AVIF and HEIC derivatives, which carry the IPTC digital source type with it and do not without it. JPEG, PNG and WebP derivatives are marked anyway.

Use ExifTool 12.24 or later. Version 12.24 fixed CVE-2021-22204, a remote code execution flaw in the DjVu parser of older releases. A current release is better, because the module passes it files from uploads.

sudo apt install libimage-exiftool-perl
exiftool -ver

ExifTool is a Perl script, so the server needs perl. Download the tarball from exiftool.org and extract it. Replace VERSION with the version on that page.

perl -v
mkdir -p ~/bin
cd ~/bin
curl -LO https://exiftool.org/Image-ExifTool-VERSION.tar.gz
tar -xzf Image-ExifTool-VERSION.tar.gz
~/bin/Image-ExifTool-VERSION/exiftool -ver

Add this line to the Dockerfile of the PHP image, which must be based on Debian or Ubuntu, then rebuild the image.

RUN apt-get update && apt-get install -y --no-install-recommends libimage-exiftool-perl && rm -rf /var/lib/apt/lists/*
docker compose build php
docker compose up -d php
docker compose exec php exiftool -ver

With DDEV, add libimage-exiftool-perl to webimage_extra_packages in .ddev/config.yaml, then run ddev restart.

Enter the path in the "Image derivatives" section of Media, Images, at /admin/config/content/ai-disclosure/media: exiftool when it is in the PATH, or the full path, such as /home/user/bin/Image-ExifTool-VERSION/exiftool. The form runs the binary with -ver to check it. The path is empty by default.

audiowmark

audiowmark is optional and meant only for sites that generate audio themselves. With audiowmark set up, every audio copy gets an inaudible watermark that carries a message, so the audio can be recognized as generated. Without it, nothing changes: the setting is empty and no watermark is added. The original upload is never changed, and if audiowmark fails the copy is kept without the mark. See Watermark for what the mark is for.

audiowmark is free software under the GPL, run as a separate command-line program. Build it from the upstream source, which lists the libraries it needs. The tabs below build it from source with those libraries.

Install the build tools and the libraries, then build.

sudo apt install build-essential autoconf autoconf-archive automake libtool pkg-config gettext git \
    libfftw3-dev libsndfile1-dev libgcrypt20-dev libzita-resampler-dev libmpg123-dev
git clone https://github.com/swesterfeld/audiowmark.git
cd audiowmark
./autogen.sh
make
sudo make install
audiowmark --version

Build it in a folder of your own and install it under your home directory. It needs the build tools and the development libraries from the Ubuntu tab: FFTW3, libsndfile, libgcrypt, zita-resampler and mpg123.

git clone https://github.com/swesterfeld/audiowmark.git
cd audiowmark
./autogen.sh --prefix=$HOME/audiowmark
make
make install
~/audiowmark/bin/audiowmark --version

If the libraries cannot be installed without root, build a static binary on a machine like the server and copy it, then make it executable. Enter the full path in the form, for example /home/user/audiowmark/bin/audiowmark.

Add the lines to the Dockerfile of the PHP image, then rebuild the image. The package names are those of Debian and Ubuntu. The upstream README.md is the source of the list.

RUN apt-get update && apt-get install -y --no-install-recommends build-essential autoconf autoconf-archive automake libtool pkg-config gettext git ca-certificates \
    libfftw3-dev libsndfile1-dev libgcrypt20-dev libzita-resampler-dev libmpg123-dev \
    && git clone --depth 1 https://github.com/swesterfeld/audiowmark.git /tmp/audiowmark \
    && cd /tmp/audiowmark && ./autogen.sh && make && make install \
    && rm -rf /tmp/audiowmark /var/lib/apt/lists/*
docker compose build php
docker compose up -d php
docker compose exec php audiowmark --version

Make a key before you turn the watermark on. Without a key, anyone can read the message and remove the watermark without loss of quality. The key is a 128-bit AES key in a small file:

audiowmark gen-key /path/outside/webroot/audiowmark.key

Keep the file outside the web root and back it up. Marks made with a key can be read only with the same key, so a lost or changed key makes the existing marks unreadable.

Set the path in "audiowmark path" at /admin/config/content/ai-disclosure/video-audio: the command name when it is in the PATH of the web server, or the full path. Saving the form runs --version and refuses a path that does not run. The path is empty by default, which is off. Enter the key file in "Key file", which must be readable by the web server.

Private file system

Labelled copies of audio and video are kept in the private file system by default, so that only people who may see the media can download them. Without it you can only store them in public files, where anyone who knows or guesses the address of a copy can download it.

Choose a directory outside the web root and make it writable by the web server user. Then add this to settings.php:

$settings['file_private_path'] = '../private';

The path is relative to the web root, so ../private is next to the web directory.

mkdir -p private
sudo chgrp www-data private
chmod 2775 private
mkdir -p private
chmod 755 private

On a shared host PHP usually runs as your own user, so this is enough. If it does not, use the permissions the host documents for upload directories.

Mount a volume at the place the setting points to, so the files survive a rebuild of the container. In docker-compose.yml, for a project at /var/www/html with the web root in web:

services:
  php:
    volumes:
      - ./private:/var/www/html/private
mkdir -p private
docker compose up -d php
docker compose exec php chown www-data:www-data private

DDEV mounts the whole project, so a private directory in it is enough.

To verify, open /admin/reports/status. The entry "AI disclosure renditions storage" says "Writable, private files".

Media overlay

A deep fake image needs a visible label on the page. With "Images with the icon" set to "None", it has none. Open /admin/config/content/ai-disclosure/media and choose "Deep fakes" or "All AI images shown".

The burn-in effect of an image style does not replace this: it labels only the copies made by that style.

To verify, open /admin/reports/ai-disclosure/status. The warning about deep fakes without a visible label is gone.

Low-resource servers

Rendering audio and video takes about as long as the file lasts, or longer. On a small server:

  • Lower "Longest file to label" at /admin/config/content/ai-disclosure/video-audio. It is in seconds and 3600 by default. A longer file is skipped and keeps its original.
  • Renders run on cron, one at a time, never while a page is built. The queue worker works for 120 seconds on each cron run. A render that is still going when the time is up can outlast it, so give the cron user enough time.
  • Run cron from the system crontab instead of the Automated cron module, so a visitor never pays for it. For example, every five minutes:
*/5 * * * * cd /var/www/example && vendor/bin/drush cron
  • Or work the queue by hand from a shell, with a limit:
vendor/bin/drush ai-disclosure-av:process --limit=1