Describe the bug
A build of current develop cannot play a YouTube video, and 17 of the repository's own 60 demo pages throw a JavaScript error during player initialization. We hit this while preparing to adopt the 5.1.0 line and traced it to four independent regressions, none of which exist in the released 5.0.0. Filing them together because two share an origin commit and the fixes overlap; happy to split into separate issues if you prefer.
We have working fixes for all four (plus a fifth, smaller Vimeo one) running in our fork, each on its own branch cut from develop, and we're glad to open them as PRs in whatever form suits you — after #770/#771/#773, we didn't want to stack more unrequested PRs, so this is a report first.
Version tested
Develop (the next release). Verified NOT present in v5.0.0 (details per item). Also worth noting: the committed build/ directory currently predates all of these (its banner says V5.0.0, last rebuilt in June), so testing against the checked-in bundles masks every one of them — they only appear after a fresh npm run build.
1. initSignLanguage throws for any media element with no <source> children — this alone breaks every YouTube demo
scripts/sign.js:9:
var hasLocalSrc = ( this.sources[0].getAttribute('data-sign-src') !== undefined && … );
getSources() (scripts/initialize.js:377) returns [] when the media element has no <source> children — the normal markup for a YouTube-only embed, e.g. demos/youtube1.html:
<video id="video1" data-able-player preload="auto" data-youtube-nocookie="true"
data-youtube-id="dhwpLACAls8" data-youtube-desc-id="Ck2TdVMhWcU" playsinline></video>
So this.sources[0] is undefined and the call throws:
TypeError: Cannot read properties of undefined (reading 'getAttribute')
at AblePlayer.initSignLanguage
at AblePlayer.recreatePlayer
initSignLanguage() is called unconditionally from recreatePlayer() (scripts/initialize.js:433) before initPlayer(), so the player is never built: the IFrame API is never requested, window.YT stays undefined, and the page degrades to a bare <video>. scripts/description.js:38 has the same unguarded read and throws next if only sign.js is patched (note the audio early-exit at description.js:31–33 resolves its deferred but doesn't return, so it falls through to that line).
Origin: 834d003 converted these reads from this.$sources.first().attr(…) — which returns undefined on an empty jQuery set — to this.sources[0].getAttribute(…), which throws.
Same commit also changed the absent-attribute sentinel: .attr() returned undefined for a missing attribute, getAttribute() returns null. The surrounding !== undefined / typeof !== 'undefined' tests therefore now read a missing attribute as present — e.g. every video without a described version still sets hasOpenDesc = true once execution gets that far.
2. All data-youtube-* attributes are silently ignored (wrong dataset keys)
scripts/ableplayer-base.js reads:
let youTubeId = options.youTubeId ?? data.youTubeId;
where data = media[0].dataset. But dataset keys are derived from the attribute name — data-youtube-id is dataset.youtubeId (one word, lowercase y, no capital T). data.youTubeId would correspond to a data-you-tube-id attribute, which doesn't exist. Same for youTubeDescId, youTubeSignId (5.0.0 read this from data-youtube-sign-src, so the correct key is youtubeSignSrc), and youTubeNoCookie (data-youtube-nocookie → youtubeNocookie).
Result: even with item 1 patched, every YouTube demo loads with no errors but no video — the instance reports player: 'html5', youTubeId undefined, youTubeNoCookie false, and zero network requests toward the IFrame API. This one is easy to miss precisely because it's silent.
Origin: 2d4a1c2 ("Get data attributes from dataset instead of jQuery object") — $(media).data('youtube-id') resolved the hyphenated name correctly; the dataset reads kept the API's youTube* spelling.
3. Constructor throws for a selector string or bare element
Same commit, same file: let data = media[0].dataset; indexes the constructor argument directly. For a selector string (new AblePlayer('#video1'), used by demos/translations.html) media[0] is the character #; for a bare element it's undefined. Both throw on .dataset. Only a jQuery object works, though all three forms are documented and the constructor's own guard two lines up already uses $(media). (demos/search2.html and demos/translations.html fail on this today.) Fix is one call: $(media)[0].dataset, matching the width reads at lines 330/334 of the same file.
4. syncTrackLanguages throws for any player without a transcript
scripts/control.js:
if ( ( ! this.$transcriptArea.is(':visible') && source === 'captions' ) || source === 'init' || source === 'transcript' ) {
$transcriptArea is only assigned when a transcript is built, and && evaluates left-to-right, so the .is() call runs before source is examined — including for source === 'init', i.e. normal player construction via initDefaultCaption. Any player with captions but no transcript throws here. Reordering the test (and using the same typeof this.$transcriptArea === 'undefined' idiom refreshControls() already uses at control.js:1618) fixes it. Origin: 05e9a22 ("Update language switching"); the line doesn't exist in 5.0.0.
5. (Smaller, Vimeo) getVimeoId throws on a bare ID string
scripts/vimeo.js:269 runs new URL(url) with only a typeof url === 'number' guard above it. Every caller passes a string read from data-vimeo-id/.attr(), so the documented data-vimeo-id="76979871" markup reaches new URL('76979871') → TypeError: Failed to construct 'URL': Invalid URL. scripts/validate.js:244 already shows the safe pattern (try/catch + base origin). Both Vimeo demos fail on this today.
To Reproduce
- Fresh clone of
develop; npm ci && npm run build (important — the committed build/ predates these regressions).
- Serve the repo root and open
demos/youtube1.html with the console open.
- See the
initSignLanguage TypeError (item 1). Patch item 1 alone and reload: no errors, but also no YouTube iframe and window.YT undefined (item 2). demos/translations.html and demos/vimeo1.html show items 3 and 5.
Measured across all 60 demo pages in headless Chromium (autoplay-policy errors excluded): 17 pages throw on develop; 0 throw with the five fixes applied. The full per-page breakdown: all 6 youtube demos + both vimeo demos + other-noa11y, other-width, search2, translations and 5 others.
Expected behavior
YouTube/Vimeo demos initialize and play as they do on v5.0.0; media elements without <source> children construct normally; all documented constructor argument forms work.
Desktop:
- OS: Windows 11 / Linux (CI)
- Browser: Chromium 145 (headless and headed) — but all five are environment-independent logic errors.
If it's useful: our fixes are live and testable at https://ableplayer.perkslocker.com/demos/ (our fork's develop plus these guards — every page loads clean there). Say the word and I'll open them as PRs, individually or as one branch, whichever you'd rather review.
Describe the bug
A build of current
developcannot play a YouTube video, and 17 of the repository's own 60 demo pages throw a JavaScript error during player initialization. We hit this while preparing to adopt the 5.1.0 line and traced it to four independent regressions, none of which exist in the released 5.0.0. Filing them together because two share an origin commit and the fixes overlap; happy to split into separate issues if you prefer.We have working fixes for all four (plus a fifth, smaller Vimeo one) running in our fork, each on its own branch cut from
develop, and we're glad to open them as PRs in whatever form suits you — after #770/#771/#773, we didn't want to stack more unrequested PRs, so this is a report first.Version tested
Develop (the next release). Verified NOT present in v5.0.0 (details per item). Also worth noting: the committed
build/directory currently predates all of these (its banner says V5.0.0, last rebuilt in June), so testing against the checked-in bundles masks every one of them — they only appear after a freshnpm run build.1.
initSignLanguagethrows for any media element with no<source>children — this alone breaks every YouTube demoscripts/sign.js:9:getSources()(scripts/initialize.js:377) returns[]when the media element has no<source>children — the normal markup for a YouTube-only embed, e.g.demos/youtube1.html:So
this.sources[0]isundefinedand the call throws:initSignLanguage()is called unconditionally fromrecreatePlayer()(scripts/initialize.js:433) beforeinitPlayer(), so the player is never built: the IFrame API is never requested,window.YTstays undefined, and the page degrades to a bare<video>.scripts/description.js:38has the same unguarded read and throws next if only sign.js is patched (note the audio early-exit at description.js:31–33 resolves its deferred but doesn'treturn, so it falls through to that line).Origin: 834d003 converted these reads from
this.$sources.first().attr(…)— which returnsundefinedon an empty jQuery set — tothis.sources[0].getAttribute(…), which throws.Same commit also changed the absent-attribute sentinel:
.attr()returnedundefinedfor a missing attribute,getAttribute()returnsnull. The surrounding!== undefined/typeof !== 'undefined'tests therefore now read a missing attribute as present — e.g. every video without a described version still setshasOpenDesc = trueonce execution gets that far.2. All
data-youtube-*attributes are silently ignored (wrong dataset keys)scripts/ableplayer-base.jsreads:where
data = media[0].dataset. But dataset keys are derived from the attribute name —data-youtube-idisdataset.youtubeId(one word, lowercasey, no capital T).data.youTubeIdwould correspond to adata-you-tube-idattribute, which doesn't exist. Same foryouTubeDescId,youTubeSignId(5.0.0 read this fromdata-youtube-sign-src, so the correct key isyoutubeSignSrc), andyouTubeNoCookie(data-youtube-nocookie→youtubeNocookie).Result: even with item 1 patched, every YouTube demo loads with no errors but no video — the instance reports
player: 'html5',youTubeIdundefined,youTubeNoCookiefalse, and zero network requests toward the IFrame API. This one is easy to miss precisely because it's silent.Origin: 2d4a1c2 ("Get data attributes from dataset instead of jQuery object") —
$(media).data('youtube-id')resolved the hyphenated name correctly; the dataset reads kept the API'syouTube*spelling.3. Constructor throws for a selector string or bare element
Same commit, same file:
let data = media[0].dataset;indexes the constructor argument directly. For a selector string (new AblePlayer('#video1'), used bydemos/translations.html)media[0]is the character#; for a bare element it'sundefined. Both throw on.dataset. Only a jQuery object works, though all three forms are documented and the constructor's own guard two lines up already uses$(media). (demos/search2.htmlanddemos/translations.htmlfail on this today.) Fix is one call:$(media)[0].dataset, matching the width reads at lines 330/334 of the same file.4.
syncTrackLanguagesthrows for any player without a transcriptscripts/control.js:$transcriptAreais only assigned when a transcript is built, and&&evaluates left-to-right, so the.is()call runs beforesourceis examined — including forsource === 'init', i.e. normal player construction viainitDefaultCaption. Any player with captions but no transcript throws here. Reordering the test (and using the sametypeof this.$transcriptArea === 'undefined'idiomrefreshControls()already uses at control.js:1618) fixes it. Origin: 05e9a22 ("Update language switching"); the line doesn't exist in 5.0.0.5. (Smaller, Vimeo)
getVimeoIdthrows on a bare ID stringscripts/vimeo.js:269runsnew URL(url)with only atypeof url === 'number'guard above it. Every caller passes a string read fromdata-vimeo-id/.attr(), so the documenteddata-vimeo-id="76979871"markup reachesnew URL('76979871')→TypeError: Failed to construct 'URL': Invalid URL.scripts/validate.js:244already shows the safe pattern (try/catch + base origin). Both Vimeo demos fail on this today.To Reproduce
develop;npm ci && npm run build(important — the committedbuild/predates these regressions).demos/youtube1.htmlwith the console open.initSignLanguageTypeError (item 1). Patch item 1 alone and reload: no errors, but also no YouTube iframe andwindow.YTundefined (item 2).demos/translations.htmlanddemos/vimeo1.htmlshow items 3 and 5.Measured across all 60 demo pages in headless Chromium (autoplay-policy errors excluded): 17 pages throw on develop; 0 throw with the five fixes applied. The full per-page breakdown: all 6 youtube demos + both vimeo demos +
other-noa11y,other-width,search2,translationsand 5 others.Expected behavior
YouTube/Vimeo demos initialize and play as they do on v5.0.0; media elements without
<source>children construct normally; all documented constructor argument forms work.Desktop:
If it's useful: our fixes are live and testable at https://ableplayer.perkslocker.com/demos/ (our fork's develop plus these guards — every page loads clean there). Say the word and I'll open them as PRs, individually or as one branch, whichever you'd rather review.