Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,32 @@
## 4.0.3 - UNRELEASED
### Changed
* Remove the `bufsize` parameter entirely from the FFMPEG default command, which was preventing `WebM` files from being generated properly ([#72](https://github.com/nystudio107/craft-transcoder/issues/72))
* Organize video settings into separate queue, posters, and watermark tabs in the Craft control panel.
* Use Craft autosuggest fields for queue, subfolder, and watermark values, including environment-variable support.

### Added

* Queue video encoding when new video assets are uploaded, with a configurable delay.
* Queue GIF encoding when new GIF assets are uploaded, with a configurable delay.
* Queue audio encoding when new audio assets are uploaded, with a configurable delay.
* Queue configured video poster formats on asset upload without requiring full video encoding.
* Add source-asset and encoding-options video filename strategies while preserving bitrate-based filenames by default.
* Add configurable video watermark overlays.
* Generate configured video poster formats after queued encodes.
* Add blurred-background poster fitting to prevent black bars.
* Add `refreshVideoAsset()` for integrations that intentionally replace a video asset’s source file.
* Retry failed queued video encodes with configurable attempt and delay settings.

### Fixed

* Use one byte-identical output-path calculation for replacement cleanup and queued encoding.
* Fail refresh jobs when Craft cannot queue replacement encoding instead of reporting completion.
* Add modification-time cache versions to generated video and poster URLs after regeneration.
* Preserve configured video output subfolders when `getVideoUrl()` receives a string URL or path.
* Keep automatic Control Panel video thumbnails out of temporary upload storage and in the Asset's final subfolder.
* Forward the optional `generate` argument from Twig's `getVideoThumbnailUrl()` helper.
* Keep poster format handles and black-bar generation flags out of thumbnail filenames so queued posters and equivalent Twig requests reuse the same file.
* Correct legacy `transcoderUrl` migration and remove validation for the obsolete singular `transcoderPath` property.

## 4.0.2 - 2024.09.30
## Added
Expand Down
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,25 @@

Transcode video & audio files to various formats, and provide video thumbnails

## Media workflow features

- Queue newly uploaded video assets through Craft’s queue.
- Queue newly uploaded GIF assets through Craft’s queue.
- Queue newly uploaded audio Assets through Craft’s queue.
- Delay queued encodes when other asset-save handlers need time to finish.
- Retry failed queued video encodes after a configurable delay.
- Keep the original option-based filenames, including bitrate, or opt into stable source-asset filenames.
- Overlay a configurable watermark on encoded videos.
- Generate named poster formats after queued encodes.
- Queue configured video posters on upload without requiring full video encoding.
- Replace poster letterboxing with a blurred cover background.
- Refresh managed video derivatives after a third-party plugin replaces a source asset.
- Keep Asset and string-URL video calls in the same configured output subfolder.

Upload queueing and the other opt-in media features are disabled by default, while queued video jobs retry twice by default and the original `options` filename strategy remains unchanged. Configuration is available from the plugin settings screen, organized into media queue, poster, and watermark tabs, or `config/transcoder.php`. Text and numeric fields in these tabs support Craft environment-variable autosuggestions; the watermark path also supports aliases.

Third-party plugins that intentionally replace a video asset can call `Transcoder::$plugin->getTranscode()->refreshVideoAsset($asset)`. Cleanup and re-encoding run asynchronously through Craft’s queue; see the usage documentation for the integration contract.

![Screenshot](./docs/docs/resources/img/plugin-banner.jpg)

**Note**: _The license fee for this plugin is $59.00 via the Craft Plugin Store._
Expand Down
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@
"scripts": {
"phpstan": "phpstan --ansi --memory-limit=1G",
"check-cs": "ecs check --ansi",
"fix-cs": "ecs check --fix --ansi"
"fix-cs": "ecs check --fix --ansi",
"test-refresh": "php tests/refresh-video-asset-harness.php"
},
"config": {
"allow-plugins": {
Expand Down
127 changes: 126 additions & 1 deletion docs/docs/configuring.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ description: Configuring Transcoder documentation for the Transcoder plugin. The
---
# Configuring Transcoder

The only configuration for Transcoder is in the `config.php` file, which is a multi-environment friendly way to store the default settings. Don’t edit this file, instead copy it to `craft/config` as `transcoder.php` and make your changes there.
Configure the media workflow from Transcoder’s Craft control-panel settings or with a `craft/config/transcoder.php` file. Don’t edit the plugin’s bundled `config.php`; copy it to `craft/config` when settings should be managed in code.

The media queue and Video watermark tabs use Craft autosuggest fields for values that can come from environment variables. The watermark path also suggests Yii aliases. Numeric environment variables must resolve to integers within the same limits shown by their literal values.

You will also need [ffmpeg](https://ffmpeg.org/) installed for Transcoder to work. On Ubuntu 16.04, you can do just:

Expand All @@ -17,4 +19,127 @@ To install `ffmpeg` on Centos 6/7, you can follow the guide [How to Install FFmp

If you have managed hosting, contact your sysadmin to get `ffmpeg` installed.

## Video queue and filenames

Video uploads can be encoded by Craft’s queue instead of starting ffmpeg during a web request:

```php
return [
'queueVideosOnAssetUpload' => true,
'videoQueueDelaySeconds' => 5,
'videoEncodeMaxRetries' => 2,
'videoEncodeRetryDelaySeconds' => 120,
'queuedVideoOptions' => [
'videoBitRate' => '1200k',
'videoFrameRate' => 30,
'width' => 1280,
'height' => 720,
],
];
```

A working Craft queue runner is required. The queue job waits for ffmpeg to finish. When an attempt fails, Transcoder queues another attempt after `videoEncodeRetryDelaySeconds`, up to `videoEncodeMaxRetries` retries. Set the retry count to `0` to disable automatic retries; after the final attempt, the Craft queue job fails normally and remains available for manual retry.

## GIF queue

GIF uploads can use the same failure-visible Craft queue workflow without changing `getGifUrl()` or its string return value:

```php
return [
'queueGifsOnAssetUpload' => true,
'gifQueueDelaySeconds' => 5,
'queuedGifOptions' => [
'videoEncoder' => 'gif',
],
];
```

GIF queueing is disabled by default. The queue worker runs ffmpeg synchronously and only completes after a non-empty output has been created. Existing Twig calls keep their original on-demand behavior.

## Audio queue

Audio uploads can also be encoded by Craft’s queue without changing `getAudioUrl()` or its string return value:

```php
return [
'queueAudioOnAssetUpload' => true,
'audioQueueDelaySeconds' => 5,
'queuedAudioOptions' => [
'audioEncoder' => 'mp3',
'audioBitRate' => '128k',
'audioSampleRate' => '44100',
'audioChannels' => '2',
],
];
```

Audio queueing is disabled by default. The job overrides the internal `synchronous` option so Craft only marks it complete after ffmpeg exits successfully and creates a non-empty output. Existing Twig calls and configured filenames are unchanged.

`videoFilenameStrategy` supports:

- `options` (default): preserves Transcoder’s original parameterized filenames. Output-affecting options such as `videoBitRate` remain part of the filename.
- `source`: uses the source asset name plus the encoder suffix, producing one stable output name per source asset.

### Output subfolders

Passing the actual Craft `Asset` to video and thumbnail helpers is preferred. With `createSubfolders` enabled, Transcoder uses Craft’s Asset folder for both the generated filesystem path and public URL. Automatic Control Panel thumbnails wait until an uploaded Asset has reached its final folder.

When an integration can only pass a string URL or path, configure `subfolderUrlSegment` with the one-based path segment that contains the desired output folder. For example, segment `3` extracts `197915` from `/content/videos/197915/video.mp4`:

```php
return [
'createSubfolders' => true,
'subfolderUrlSegment' => 3,
];
```

Leave `subfolderUrlSegment` as `false` (the default) when string inputs should use their base output directories. Transcoder normalizes path and URL separators, so configured video and thumbnail paths do not depend on a trailing slash.

## Watermarks

Watermarking accepts a local path, Yii alias, environment value, or public image URL:

```php
return [
'enableVideoWatermark' => true,
'videoWatermarkPath' => '@webroot/assets/watermark.png',
'videoWatermarkWidth' => 180,
'videoWatermarkPosition' => 'bottom-right',
'videoWatermarkPadding' => 24,
'videoWatermarkOpacity' => 80,
];
```

Watermark settings are included in option-based output filenames through a short fingerprint. Changing the watermark therefore creates a new option-based output instead of reusing an incompatible encode.

## Video posters

Configured poster formats can be generated after a queued video encode or through their own upload job:

```php
return [
'enableVideoPosters' => true,
'queueVideoPostersOnAssetUpload' => true,
// Shared with uploaded video encoding jobs.
'videoQueueDelaySeconds' => 5,
'preventVideoPosterBlackBars' => true,
'videoPosterFormats' => [
'16_9' => [
'width' => 800,
'height' => 450,
'timeInSecs' => 3,
],
'square' => [
'width' => 800,
'height' => 800,
'timeInSecs' => 1,
],
],
];
```

`queueVideoPostersOnAssetUpload` is disabled by default. When full video upload encoding is enabled, its existing `EncodeVideo` job remains responsible for poster generation and no duplicate poster job is queued. When full video upload encoding is disabled, this setting queues only the configured poster formats. The job reloads the Asset by ID and fails visibly instead of writing into Craft’s temporary upload folder.

When `preventVideoPosterBlackBars` is enabled, the video frame is fitted over a blurred cover version of the same frame. Poster timestamps are clamped to the source duration for short videos.

Brought to you by [nystudio107](https://nystudio107.com)
58 changes: 54 additions & 4 deletions docs/docs/using.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ You can also pass in an URL:
}) %}
```

When passing a URL or string path, Transcoder cannot read Craft’s `folderPath` directly. Configure `subfolderUrlSegment` if the encoded output should retain a folder segment from that URL. Passing the actual `Asset` remains preferred because its subfolder is available without parsing the URL.

You can also pass in an `Asset`:

```twig
Expand Down Expand Up @@ -89,7 +91,39 @@ The file format setting `videoEncoder` is preset to what you’ll need to genera

![Screenshot](./resources/screenshots/admin-cp-video-thumbnails.png)

Transcoder will also automatically add video thumbnails in the Control Panel Asset index.
Transcoder will also automatically add video thumbnails in the Control Panel Asset index. During an upload, thumbnail generation waits until Craft has moved the Asset into its final folder so `createSubfolders` remains consistent.

## Reading Generated Video Posters

Queued poster generation does not need to be started from a template. Enable `queueVideoPostersOnAssetUpload` to generate configured formats when full video upload encoding is disabled, then read a poster by its format handle:

```twig
{% set video = entry.video.one() %}
{% set posterUrl = craft.transcoder.getVideoPosterUrl(video, '16_9') %}
{% set posterUrls = craft.transcoder.getVideoPosterUrls(video) %}
```

`getVideoPosterUrl()` returns an empty string until the poster exists. Passing `true` as its third argument keeps the original on-demand behavior and starts poster generation when needed.

## Refreshing Replaced Video Assets

Plugins that intentionally replace the source file of an existing video asset can ask Transcoder to invalidate its managed derivatives and encode the replacement again:

```php
use nystudio107\transcoder\Transcoder;

$result = Transcoder::$plugin
->getTranscode()
->refreshVideoAsset($asset);
```

The result contains `queued` and `jobId`. Cleanup and encoding are asynchronous, so a working Craft queue runner is required. The refresh is explicit and works independently of `queueVideosOnAssetUpload`; Transcoder does not listen globally for every asset replacement.

Only the encoded variant produced with `queuedVideoOptions`, its lock/progress files, and currently configured poster formats are managed. Arbitrary variants generated from Twig are not removed. Missing derivatives are a successful no-op. Transcoder waits when the asset is already being processed and never signals or terminates FFmpeg. No database state or migration is required.

The refresh and encoding jobs use the same output-path calculation, including subfolders, aliases, hashed names, filename strategy, encoding options, and watermark fingerprint. If cleanup or the follow-up queue push fails, Craft records a failed queue job rather than a completed refresh. Successful refreshes log the asset ID, removed-file count, and follow-up job ID without exposing filesystem paths.

Generated video and poster URLs include a `v` query parameter based on the output file’s actual modification time. This prevents a stable output URL from continuing to serve cached bytes after successful regeneration.

## Generating a Transcoded Audio File

Expand Down Expand Up @@ -155,6 +189,14 @@ The above example would cause it to not change the audio of the source audio fil

The file format setting `audioEncoder` is preset to what you’ll need to generate `mp3` audio files, but it can also generate `aac`, `ogg`, or any other format that `ffmpeg` supports. See the `config.php` file for details

## Queued Audio Encoding

When `queueAudioOnAssetUpload` is enabled, newly uploaded audio Assets are converted using Craft’s queue and `queuedAudioOptions`. The queue job executes ffmpeg synchronously so failures remain visible in Craft. Existing `craft.transcoder.getAudioUrl()` calls retain their original string response and on-demand behavior.

## Queued GIF Encoding

When `queueGifsOnAssetUpload` is enabled, newly uploaded GIF Assets are converted using Craft’s queue and `queuedGifOptions`. This does not change the existing `craft.transcoder.getGifUrl()` response or its on-demand behavior. Queue failures are reported as failed Craft jobs.

## Getting Transcoding Progress

Transcoding of video/audio files can take quite a bit of time, so Transcoder provides you with a way to get the status of any currently running transcoding operation via `craft.transcoder.getVideoProgressUrl()` or `craft.transcoder.getAudioProgressUrl()`. For example:
Expand Down Expand Up @@ -205,7 +247,7 @@ To generate a thumbnail from a video, do the following:
You can also pass in a URL:

```twig
{% set transVideoUrl = craft.transcoder.getVideoUrl('http://vjs.zencdn.net/v/oceans.mp4', {
{% set transVideoThumbUrl = craft.transcoder.getVideoThumbnailUrl('http://vjs.zencdn.net/v/oceans.mp4', {
"width": 300,
"height": 200,
"timeInSecs": 20,
Expand All @@ -216,14 +258,22 @@ You can also pass in an `Asset`:

```twig
{% set myAsset = entry.someAsset.one() %}
{% set transVideoUrl = craft.transcoder.getVideoUrl(myAsset, {
{% set transVideoThumbUrl = craft.transcoder.getVideoThumbnailUrl(myAsset, {
"width": 300,
"height": 200,
"timeInSecs": 20,
}) %}
```

It will return to you a URL to the thumbnail of the video, in the size you specify, from the timecode `timeInSecs` in the video. It creates this thumbnail immediately if it doesn’t already exist.
It will return a URL to the thumbnail of the video, in the size you specify, from the timecode `timeInSecs` in the video. It creates this thumbnail immediately if it doesn’t already exist. Pass `false` as the third argument to perform a read-only lookup without starting FFmpeg:

```twig
{% set transVideoThumbUrl = craft.transcoder.getVideoThumbnailUrl(myAsset, {
"width": 300,
"height": 200,
"timeInSecs": 20,
}, false) %}
```

In the array you pass in, the default values are used if the key-value pair does not exist:

Expand Down
Loading
Loading