Skip to content
Merged
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
6 changes: 6 additions & 0 deletions packages/render-helper/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# @ecency/render-helper

## 2.5.13

### Patch Changes

- fix: don't autoplay videos in waves feed (#965)

## 2.5.12

### Patch Changes
Expand Down
19 changes: 13 additions & 6 deletions packages/render-helper/dist/browser/index.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/render-helper/dist/browser/index.js.map

Large diffs are not rendered by default.

19 changes: 13 additions & 6 deletions packages/render-helper/dist/node/index.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -1242,14 +1242,15 @@ function a(el, forApp, parentDomain = "ecency.com", seoContext, renderOptions) {
el.setAttribute("data-start-time", startTime);
}
if (renderOptions?.embedVideosDirectly) {
const directSrc = `https://www.youtube.com/embed/${vid}`;
const wrapper = el.ownerDocument.createElement("span");
wrapper.setAttribute("class", "er-youtube-frame");
wrapper.setAttribute("style", "display:block");
const iframe2 = el.ownerDocument.createElement("iframe");
iframe2.setAttribute("class", "youtube-player");
iframe2.setAttribute("src", embedSrc);
iframe2.setAttribute("src", directSrc);
iframe2.setAttribute("title", "YouTube video");
iframe2.setAttribute("allow", "accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture; web-share");
iframe2.setAttribute("allow", "accelerometer; encrypted-media; gyroscope; picture-in-picture; web-share");
iframe2.setAttribute("allowfullscreen", "");
wrapper.appendChild(iframe2);
el.appendChild(wrapper);
Expand Down Expand Up @@ -1386,12 +1387,13 @@ function a(el, forApp, parentDomain = "ecency.com", seoContext, renderOptions) {
el.textContent = "";
}
if (renderOptions?.embedVideosDirectly) {
const directSrc = `${videoHref}&autoplay=false`;
const wrapper = el.ownerDocument.createElement("span");
wrapper.setAttribute("class", "er-speak-frame");
wrapper.setAttribute("style", "display:block");
const iframe2 = el.ownerDocument.createElement("iframe");
iframe2.setAttribute("class", "speak-iframe");
iframe2.setAttribute("src", videoHref);
iframe2.setAttribute("src", directSrc);
iframe2.setAttribute("title", "3Speak video");
iframe2.setAttribute("allow", "accelerometer; encrypted-media; gyroscope; picture-in-picture; web-share");
iframe2.setAttribute("allowfullscreen", "");
Expand Down Expand Up @@ -1504,7 +1506,7 @@ function a(el, forApp, parentDomain = "ecency.com", seoContext, renderOptions) {
}

// src/methods/iframe.method.ts
function iframe(el, parentDomain = "ecency.com", forApp = false) {
function iframe(el, parentDomain = "ecency.com", forApp = false, renderOptions) {
if (!el || !el.parentNode) {
return;
}
Expand Down Expand Up @@ -1547,7 +1549,12 @@ function iframe(el, parentDomain = "ecency.com", forApp = false) {
normalizedSrc = `${normalizedSrc}&mode=iframe`;
}
const hasAutoplay = /[?&]autoplay=/.test(normalizedSrc);
let s = hasAutoplay ? normalizedSrc : `${normalizedSrc}&autoplay=true`;
let s;
if (renderOptions?.embedVideosDirectly) {
s = hasAutoplay ? normalizedSrc.replace(/([?&]autoplay=)[^&]*/i, "$1false") : `${normalizedSrc}&autoplay=false`;
} else {
s = hasAutoplay ? normalizedSrc : `${normalizedSrc}&autoplay=true`;
}
if (forApp && !/[?&]layout=/.test(s)) {
s = `${s}&layout=mobile`;
}
Expand Down Expand Up @@ -1861,7 +1868,7 @@ function traverse(node, forApp, depth = 0, state = { firstImageFound: false }, p
a(child, forApp, parentDomain, seoContext, renderOptions);
}
if (child.nodeName.toLowerCase() === "iframe") {
iframe(child, parentDomain, forApp);
iframe(child, parentDomain, forApp, renderOptions);
}
if (child.nodeName === "#text") {
text(child, forApp);
Expand Down
2 changes: 1 addition & 1 deletion packages/render-helper/dist/node/index.cjs.map

Large diffs are not rendered by default.

19 changes: 13 additions & 6 deletions packages/render-helper/dist/node/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -1213,14 +1213,15 @@ function a(el, forApp, parentDomain = "ecency.com", seoContext, renderOptions) {
el.setAttribute("data-start-time", startTime);
}
if (renderOptions?.embedVideosDirectly) {
const directSrc = `https://www.youtube.com/embed/${vid}`;
const wrapper = el.ownerDocument.createElement("span");
wrapper.setAttribute("class", "er-youtube-frame");
wrapper.setAttribute("style", "display:block");
const iframe2 = el.ownerDocument.createElement("iframe");
iframe2.setAttribute("class", "youtube-player");
iframe2.setAttribute("src", embedSrc);
iframe2.setAttribute("src", directSrc);
iframe2.setAttribute("title", "YouTube video");
iframe2.setAttribute("allow", "accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture; web-share");
iframe2.setAttribute("allow", "accelerometer; encrypted-media; gyroscope; picture-in-picture; web-share");
iframe2.setAttribute("allowfullscreen", "");
wrapper.appendChild(iframe2);
el.appendChild(wrapper);
Expand Down Expand Up @@ -1357,12 +1358,13 @@ function a(el, forApp, parentDomain = "ecency.com", seoContext, renderOptions) {
el.textContent = "";
}
if (renderOptions?.embedVideosDirectly) {
const directSrc = `${videoHref}&autoplay=false`;
const wrapper = el.ownerDocument.createElement("span");
wrapper.setAttribute("class", "er-speak-frame");
wrapper.setAttribute("style", "display:block");
const iframe2 = el.ownerDocument.createElement("iframe");
iframe2.setAttribute("class", "speak-iframe");
iframe2.setAttribute("src", videoHref);
iframe2.setAttribute("src", directSrc);
iframe2.setAttribute("title", "3Speak video");
iframe2.setAttribute("allow", "accelerometer; encrypted-media; gyroscope; picture-in-picture; web-share");
iframe2.setAttribute("allowfullscreen", "");
Expand Down Expand Up @@ -1475,7 +1477,7 @@ function a(el, forApp, parentDomain = "ecency.com", seoContext, renderOptions) {
}

// src/methods/iframe.method.ts
function iframe(el, parentDomain = "ecency.com", forApp = false) {
function iframe(el, parentDomain = "ecency.com", forApp = false, renderOptions) {
if (!el || !el.parentNode) {
return;
}
Expand Down Expand Up @@ -1518,7 +1520,12 @@ function iframe(el, parentDomain = "ecency.com", forApp = false) {
normalizedSrc = `${normalizedSrc}&mode=iframe`;
}
const hasAutoplay = /[?&]autoplay=/.test(normalizedSrc);
let s = hasAutoplay ? normalizedSrc : `${normalizedSrc}&autoplay=true`;
let s;
if (renderOptions?.embedVideosDirectly) {
s = hasAutoplay ? normalizedSrc.replace(/([?&]autoplay=)[^&]*/i, "$1false") : `${normalizedSrc}&autoplay=false`;
} else {
s = hasAutoplay ? normalizedSrc : `${normalizedSrc}&autoplay=true`;
}
if (forApp && !/[?&]layout=/.test(s)) {
s = `${s}&layout=mobile`;
}
Expand Down Expand Up @@ -1832,7 +1839,7 @@ function traverse(node, forApp, depth = 0, state = { firstImageFound: false }, p
a(child, forApp, parentDomain, seoContext, renderOptions);
}
if (child.nodeName.toLowerCase() === "iframe") {
iframe(child, parentDomain, forApp);
iframe(child, parentDomain, forApp, renderOptions);
}
if (child.nodeName === "#text") {
text(child, forApp);
Expand Down
2 changes: 1 addition & 1 deletion packages/render-helper/dist/node/index.mjs.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion packages/render-helper/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@ecency/render-helper",
"private": false,
"version": "2.5.12",
"version": "2.5.13",
"description": "Markdown+Html Render helper",
"repository": {
"type": "git",
Expand Down
39 changes: 39 additions & 0 deletions packages/render-helper/src/methods/a.method.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -799,6 +799,26 @@ describe('a() method - Link Processing', () => {
expect(el.getAttribute('data-youtube')).toBe('dQw4w9WgXcQ')
})

it('should embed inline without autoplay in waves (embedVideosDirectly)', () => {
const parent = doc.createElement('div')
const el = doc.createElement('a')
const href = 'https://www.youtube.com/watch?v=dQw4w9WgXcQ'
el.setAttribute('href', href)
el.textContent = href
parent.appendChild(el)

a(el, false, 'ecency.com', undefined, { embedVideosDirectly: true })

const iframe = el.getElementsByTagName('iframe')[0]
expect(iframe).toBeTruthy()
expect(iframe.getAttribute('src')).toBe('https://www.youtube.com/embed/dQw4w9WgXcQ')
expect(iframe.getAttribute('src')).not.toContain('autoplay')
expect(iframe.getAttribute('allow')).not.toContain('autoplay')
// click-to-play path (non-waves) still autoplays after the user clicks
expect(el.getAttribute('data-embed-src')).toBe('https://www.youtube.com/embed/dQw4w9WgXcQ?autoplay=1')
expect(el.getAttribute('class')).toContain('er-youtube')
})

it('should handle youtu.be short URLs', () => {
const parent = doc.createElement('div')
const el = doc.createElement('a')
Expand Down Expand Up @@ -1084,6 +1104,25 @@ describe('a() method - Link Processing', () => {
expect(el.getAttribute('data-embed-src')).toBe('https://play.3speak.tv/watch?v=username/permlink&mode=iframe')
})

it('should embed inline without autoplay in waves (embedVideosDirectly)', () => {
const parent = doc.createElement('div')
const el = doc.createElement('a')
const href = 'https://3speak.tv/watch?v=username/permlink'
el.setAttribute('href', href)
el.textContent = href
parent.appendChild(el)

a(el, false, 'ecency.com', undefined, { embedVideosDirectly: true })

const iframe = el.getElementsByTagName('iframe')[0]
expect(iframe).toBeTruthy()
expect(iframe.getAttribute('src')).toContain('autoplay=false')
expect(iframe.getAttribute('src')).not.toContain('autoplay=true')
// click-to-play path (non-waves) keeps the player default via data-embed-src
expect(el.getAttribute('data-embed-src')).toBe('https://play.3speak.tv/watch?v=username/permlink&mode=iframe')
expect(el.getAttribute('class')).toContain('er-speak')
})

it('should handle 3Speak with thumbnail', () => {
const parent = doc.createElement('div')
const el = doc.createElement('a')
Expand Down
17 changes: 14 additions & 3 deletions packages/render-helper/src/methods/a.method.ts
Original file line number Diff line number Diff line change
Expand Up @@ -610,14 +610,19 @@ export function a(el: HTMLElement | null, forApp: boolean, parentDomain: string
}

if (renderOptions?.embedVideosDirectly) {
// Waves/thread feed: embed inline but never autoplay. A feed scrolling
// past several videos that all start playing at once is disorienting, so
// load the player paused (no autoplay=1, no autoplay permission). The
// click-to-play path elsewhere keeps using embedSrc via data-embed-src.
const directSrc = `https://www.youtube.com/embed/${vid}`
const wrapper = el.ownerDocument.createElement('span')
wrapper.setAttribute('class', 'er-youtube-frame')
wrapper.setAttribute('style', 'display:block')
const iframe = el.ownerDocument.createElement('iframe')
iframe.setAttribute('class', 'youtube-player')
iframe.setAttribute('src', embedSrc)
iframe.setAttribute('src', directSrc)
iframe.setAttribute('title', 'YouTube video')
iframe.setAttribute('allow', 'accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture; web-share')
iframe.setAttribute('allow', 'accelerometer; encrypted-media; gyroscope; picture-in-picture; web-share')
iframe.setAttribute('allowfullscreen', '')
wrapper.appendChild(iframe)
el.appendChild(wrapper)
Expand Down Expand Up @@ -812,13 +817,19 @@ export function a(el: HTMLElement | null, forApp: boolean, parentDomain: string
}

if (renderOptions?.embedVideosDirectly) {
// Waves/thread feed: embed inline but never autoplay. The 3Speak
// player autoplays by default, which is disorienting in a feed of
// many videos, so request autoplay=false explicitly. The
// click-to-play path elsewhere keeps using videoHref via
// data-embed-src (autoplay after the user clicks is fine).
const directSrc = `${videoHref}&autoplay=false`
// Use span (not div) to avoid invalid nesting inside <p> tags
const wrapper = el.ownerDocument.createElement('span')
wrapper.setAttribute('class', 'er-speak-frame')
wrapper.setAttribute('style', 'display:block')
const iframe = el.ownerDocument.createElement('iframe')
iframe.setAttribute('class', 'speak-iframe')
iframe.setAttribute('src', videoHref)
iframe.setAttribute('src', directSrc)
iframe.setAttribute('title', '3Speak video')
iframe.setAttribute('allow', 'accelerometer; encrypted-media; gyroscope; picture-in-picture; web-share')
iframe.setAttribute('allowfullscreen', '')
Expand Down
31 changes: 31 additions & 0 deletions packages/render-helper/src/methods/iframe.method.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,37 @@ describe('iframe() method - Iframe Sanitization', () => {
expect(src).toContain('autoplay=false')
})

it('should default to autoplay=false in waves (embedVideosDirectly)', () => {
const parent = doc.createElement('div')
const el = doc.createElement('iframe')
el.setAttribute('src', 'https://3speak.co/embed?v=video123')
parent.appendChild(el)

iframe(el, 'ecency.com', false, { embedVideosDirectly: true })

const src = el.getAttribute('src')!
expect(src).toContain('play.3speak.tv')
expect(src).toContain('mode=iframe')
expect(src).toContain('autoplay=false')
expect(src).not.toContain('autoplay=true')
})

it('should override a baked-in autoplay=true in waves (embedVideosDirectly)', () => {
const parent = doc.createElement('div')
const el = doc.createElement('iframe')
el.setAttribute('src', 'https://3speak.co/embed?v=video123&autoplay=true')
parent.appendChild(el)

iframe(el, 'ecency.com', false, { embedVideosDirectly: true })

const src = el.getAttribute('src')!
expect(src).toContain('play.3speak.tv')
expect(src).toContain('autoplay=false')
expect(src).not.toContain('autoplay=true')
// still exactly one autoplay param, just flipped to false
expect(src.match(/autoplay=/g)).toHaveLength(1)
})

it('should handle 3speak URLs with multiple query parameters', () => {
const parent = doc.createElement('div')
const el = doc.createElement('iframe')
Expand Down
19 changes: 14 additions & 5 deletions packages/render-helper/src/methods/iframe.method.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { ARCH_REGEX, DAPPLR_REGEX, LBRY_REGEX, TRUVVL_REGEX, ODYSEE_REGEX, SKATEHIVE_IPFS_REGEX, SKATEHYPE_EMBED_REGEX, BITCHUTE_REGEX, RUMBLE_REGEX, BRIGHTEON_REGEX, VIMEO_EMBED_REGEX, SPEAK_EMBED_REGEX, SPEAK_AUDIO_EMBED_REGEX, VIMM_EMBED_REGEX, D_TUBE_EMBED_REGEX, SPOTIFY_EMBED_REGEX, SOUNDCLOUD_EMBED_REGEX, TWITCH_EMBED_REGEX, YOUTUBE_EMBED_REGEX, BRAND_NEW_TUBE_REGEX, LOOM_EMBED_REGEX, AUREAL_EMBED_REGEX } from '../consts'
import { stripQueryString } from '../helper'
import { RenderOptions } from '../types'

export function iframe(el: HTMLElement | null, parentDomain: string = 'ecency.com', forApp: boolean = false): void {
export function iframe(el: HTMLElement | null, parentDomain: string = 'ecency.com', forApp: boolean = false, renderOptions?: RenderOptions): void {
if (!el || !el.parentNode) {
return;
}
Expand Down Expand Up @@ -62,11 +63,19 @@ export function iframe(el: HTMLElement | null, parentDomain: string = 'ecency.co
normalizedSrc = `${normalizedSrc}&mode=iframe`;
}

// Add autoplay if not present
// Waves/thread feeds embed inline (embedVideosDirectly) and must never
// autoplay: force autoplay=false even when the src already carries
// autoplay=true. Everywhere else, add autoplay=true only when absent so
// an author-supplied value is preserved.
const hasAutoplay = /[?&]autoplay=/.test(normalizedSrc);
let s = hasAutoplay
? normalizedSrc
: `${normalizedSrc}&autoplay=true`;
let s: string;
if (renderOptions?.embedVideosDirectly) {
s = hasAutoplay
? normalizedSrc.replace(/([?&]autoplay=)[^&]*/i, '$1false')
: `${normalizedSrc}&autoplay=false`;
} else {
s = hasAutoplay ? normalizedSrc : `${normalizedSrc}&autoplay=true`;
}

// Add mobile layout parameter when rendering for app
if (forApp && !/[?&]layout=/.test(s)) {
Expand Down
2 changes: 1 addition & 1 deletion packages/render-helper/src/methods/traverse.method.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export function traverse(node: Node, forApp: boolean, depth = 0, state = { first
a(<HTMLElement>child, forApp, parentDomain, seoContext, renderOptions)
}
if (child.nodeName.toLowerCase() === 'iframe') {
iframe(<HTMLElement>child, parentDomain, forApp)
iframe(<HTMLElement>child, parentDomain, forApp, renderOptions)
}
if (child.nodeName === '#text') {
text(<HTMLElement>child, forApp, renderOptions)
Expand Down