Visarc.Umbraco.Qencode 17.1.2
Visarc.Umbraco.Qencode
A Qencode video transcoding integration for Umbraco 17. Editors configure per-video processing options and trigger transcoding explicitly; the plugin pushes the upload to Qencode, captures the transcoded renditions, auto-generated thumbnails, and subtitles, and serves everything from CDN — with a ready-made responsive player and SEO structured data for the front end.
This is the package to install on an Umbraco website — it ships the backoffice
UI and references Visarc.Umbraco.Qencode.Core
for the backend models, services, migrations and rendering logic. If you only
need the backend (e.g. a separate Models project in a multi-project solution),
see that package's README instead.
Installation
dotnet add package Visarc.Umbraco.Qencode
The property editors, data types, and Video media-type properties self-install on first application start — no manual Settings → Data Types setup is required.
Add to appsettings.json under Visarc:Qencode — at minimum ApiKey and
CallbackSecret:
{
"Visarc": {
"Qencode": {
"ApiKey": "your-qencode-api-key",
"CallbackSecret": "a-long-random-string"
}
}
}
See Visarc.Umbraco.Qencode.Core's README
for the full configuration reference.
What it adds to the Video media type
Two visible tabs are added to the built-in Video media type:
"Video" tab (alongside Umbraco's own file/upload fields):
| Sub-group | Properties |
|---|---|
| Qencode | qencodeModes — the Qencode Video Manager property editor (template picker, subtitle/thumbnail toggles, Process/Re-process button); qencodePoster — manual poster override with three named crops (1x1, 4x3, 16x9) |
| SEO Metadata | videoName, videoDescription, videoUploadDate — feed the VideoObject JSON-LD; captionsFile — manual .vtt override |
"Qencode" tab (internal/read-only, populated automatically once processing starts):
| Property | Purpose |
|---|---|
qencodeStatus |
idle / transcoding / completed / failed |
qencodeTaskToken |
Qencode task token, validates the completion callback |
qencodePrimaryUrl |
Highest-resolution transcoded CDN URL |
qencodeDuration |
Duration in seconds |
qencodeCdnUrls |
JSON array of renditions and auto-generated thumbnails (QencodeCdnEntry[]) |
qencodeSubtitlesUrl |
Auto-generated VTT subtitles URL |
qencodeHasAudio |
Whether the transcoded output retains audio — true/false, or unset for videos processed before this property existed / templates with no recognizable mp4 format entry |
qencodeErrorMessage |
Why the last job failed, when qencodeStatus is failed — cleared automatically the next time processing starts |
qencodeOutputsJson |
Raw JSON dump of any Qencode status fields not otherwise modelled |
The Qencode Video Manager (on the Video media type)
Opened from a Video media item's own edit screen (qencodeModes property).
An editor:
- Picks a transcoding template from a dropdown, populated live from your
Qencode account (
GET .../templates). A template must be selected before processing can start. - Toggles Generate subtitles (VTT) on/off.
- Toggles Generate thumbnails on/off — when on, a slider sets the capture point as a percentage of the video's duration (default 50%).
- Clicks Process video (or Re-process video once already processed).
While transcoding, the editor sees a live pipeline view (Source → Transcoding → Ready) with real progress, polled every 5 seconds. Once complete, the transcoded duration is shown alongside the button.
If a job fails, the specific reason is shown in the pipeline (e.g. "Template
unavailable", a Qencode error description, or "Qencode reported the job
complete but returned no video URLs") rather than a generic failure message —
sourced from qencodeErrorMessage.
The Qencode Video Picker (for your own content types)
The Qencode Video Picker property editor lets any content document type reference a video from the Media Library (filtered to Video-type items only) and toggle whether native playback controls show on the front end. Add it to a document type the same way you'd add any other custom data type — via Settings → Document Types — the plugin registers the data type but deliberately does not attach it to any content type for you.
In the backoffice, once a video is picked, the editor also sees at-a-glance
status badges — Transcoded, Audio, Subtitles, Thumbnails —
each yes / no / unknown, with a refresh button.
Its stored value is exposed as a strongly-typed QencodeVideoPickerValue
(MediaKey, ShowControls), and is registered with a PropertyValueConverter
so it comes back pre-converted from Value<T>:
@using Visarc.Umbraco.Qencode.Core.Extensions
@using Visarc.Umbraco.Qencode.Core.Models
@{
var picker = Model.Value<QencodeVideoPickerValue>("qencodeFeaturedVideo");
var video = picker?.MediaKey is { } key && Guid.TryParse(key, out var mediaKey)
? Umbraco.Media(mediaKey)
: null;
}
@if (video is not null)
{
@await Html.PartialAsync("QencodeVideoPlayer", new QencodeVideoPlayerModel
{
Media = video,
ShowControls = picker!.ShowControls,
})
}
The same value can also be read manually with
content.GetQencodeVideoPicker("qencodeFeaturedVideo")if you're not using ModelsBuilder / the value converter for some reason — see the Core README.
The QencodeVideoPlayer partial
Ships from this package's RCL — no wiring beyond the snippet above. Given a
QencodeVideoPlayerModel { Media, ShowControls }, it renders:
- A responsive
<video>built from the video's actual transcoded renditions — one<source media="(max-width: …)">per breakpoint (ascending, largest as the catch-all fallback with nomediaattribute), so the browser picks the best rendition for the viewport - A
posterfromGetQencodePosterUrl()(manual override if set, else the best-matching auto-generated thumbnail) and an inlineaspect-ratiomatching the largest rendition, so there's no layout shift - A subtitles
<track>(manual.vttoverride if set, else auto-generated), withcrossorigin="anonymous"added automatically whenever subtitles exist - The
VideoObjectJSON-LD<script>tag, appended right after the video
Playback behavior follows ShowControls:
ShowControls |
Behavior |
|---|---|
true (default) |
preload="metadata", native controls, no autoplay |
false |
preload="auto", autoplay muted loop, no native controls — background-video style |
Renders nothing if the video hasn't finished transcoding yet, or if no
renditions could be found — but in both cases it emits a console.warn(...)
in the browser (e.g. [Qencode] Video player: "My Video" (media id ...) was not rendered — transcoding is not complete. qencodeStatus: transcoding.), so
an unexpectedly-empty video slot is easy to spot in devtools instead of being
a silent, unexplained gap.
.Url() never exposes the local file
Because Visarc.Umbraco.Qencode.Core registers a custom IMediaUrlProvider,
calling Umbraco's own .Url() on any Video-type media item (content type
alias contains "video") is intercepted — the local uploaded file path is
never returned, even as a fallback:
<video src="@video.Url(Umbraco)" controls></video>
| State | What .Url() returns |
|---|---|
| Transcoding complete | The Qencode CDN URL (qencodePrimaryUrl) |
| Not yet encoded / failed | The literal string "error:qencode-video-not-yet-encoded" |
This is deliberate: it prevents the raw, unoptimized local media file from
ever leaking into markup by accident just because someone called .Url()
before checking status. Always guard with IsTranscodingComplete() (or
compare against QencodeMediaUrlProvider.NotEncodedUrl) before using the
result — the QencodeVideoPlayer partial and the extension methods above
already do this for you, so prefer those over calling .Url() directly.
Compatibility
| Package | Version |
|---|---|
| Umbraco CMS | 17.x ([17.0.0,18.0.0)) |
| .NET | 10.0 |
Packages
| Package | Purpose |
|---|---|
Visarc.Umbraco.Qencode |
Full plugin: backoffice UI + player view (install this) |
Visarc.Umbraco.Qencode.Core |
Backend only: models, services, migrations, rendering helpers (referenced automatically) |
No packages depend on Visarc.Umbraco.Qencode.
.NET 10.0
- Visarc.Umbraco.Qencode.Core (>= 17.1.2)
- Umbraco.Cms.Web.Common (>= 17.0.0 && < 18.0.0)