iFrames
Many websites use iFrames to embed content from other sources, such as videos, music players, or interactive elements. This guide will show you how to gather information from iFrames and use it in your activity.
What are iFrames?
iFrames (Inline Frames) are HTML elements that allow you to embed another HTML document within the current document. They are commonly used to embed content from other websites, such as YouTube videos, SoundCloud players, or advertisements.
When to Use iFrames
You should use iFrames in your activity when:
- The website you're creating an activity for uses iFrames to display important content
- The information you need to gather is inside an iFrame
- You need to interact with elements inside an iFrame
Common examples include:
- Video streaming websites that embed videos in iFrames
- Music streaming websites that embed players in iFrames
- Websites that embed content from other platforms
Setting Up iFrames in metadata.json
To use iFrames in your activity, you need to set the iframe
property to true
in your metadata.json
file:
{
"author": {
"name": "Your Name",
"id": "your_discord_id"
},
"service": "Example",
"description": {
"en": "Example is a website that embeds videos in iFrames."
},
"url": "example.com",
"version": "1.0.0",
"logo": "https://example.com/logo.png",
"thumbnail": "https://example.com/thumbnail.png",
"color": "#FF0000",
"category": "videos",
"tags": ["example", "video"],
"iframe": true
}
You must also specify the iFrameRegExp
property to define which iFrames your activity should target:
{
"iframe": true,
"iFrameRegExp": "example\\.com/embed/.*"
}
This regular expression pattern determines which iFrames the script will be injected into. The iFrameRegExp
property is required when using iFrames, as it prevents your activity from unnecessarily injecting code into all iFrames on a page, which could cause performance issues or conflicts with other activities.
Creating an iframe.ts File
When you set iframe: true
in your metadata.json
file, you need to create an iframe.ts
file alongside your presence.ts
file. This file will contain the code that runs inside the iFrames.
Here's a basic example of an iframe.ts
file:
import { getTimestamps } from 'premid'
const iframe = new iFrame()
iframe.on('UpdateData', async () => {
// Get the video element
const video = document.querySelector('video')
if (video) {
// Send video information to the presence script
iframe.send({
video: {
paused: video.paused,
currentTime: video.currentTime,
duration: video.duration,
title: document.querySelector('.video-title')?.textContent
}
})
}
})
Receiving Data from iFrames
In your presence.ts
file, you need to listen for the iFrameData
event to receive data from the iFrames:
const presence = new Presence({
clientId: 'your_client_id'
})
const browsingTimestamp = Math.floor(Date.now() / 1000)
enum ActivityAssets {
Logo = 'https://example.com/logo.png',
}
// Store iFrame data
let iFrameData: {
video?: {
paused?: boolean
currentTime?: number
duration?: number
title?: string
}
} = {}
// Listen for iFrame data
presence.on('iFrameData', (data) => {
iFrameData = data
})
presence.on('UpdateData', async () => {
const presenceData: PresenceData = {
largeImageKey: ActivityAssets.Logo,
}
// Check if we have video data from the iFrame
if (iFrameData.video) {
const { paused, currentTime, duration, title } = iFrameData.video
presenceData.details = title || 'Watching a video'
if (paused) {
presenceData.state = 'Paused'
}
else {
presenceData.state = 'Playing'
// Calculate timestamps if we have currentTime and duration
if (currentTime && duration) {
[presenceData.startTimestamp, presenceData.endTimestamp] = getTimestamps(currentTime, duration)
}
}
}
else {
presenceData.details = 'Browsing'
presenceData.startTimestamp = browsingTimestamp
}
// Set the activity
presence.setActivity(presenceData)
})
Getting the iFrame URL
You can get the URL of the iFrame using the getUrl
method:
const iframe = new iFrame()
iframe.on('UpdateData', async () => {
// Get the iFrame URL
const url = await iframe.getUrl()
// Send the URL to the presence script
iframe.send({
url
})
})
This can be useful for determining which iFrame is sending the data or for extracting information from the URL.
Handling Multiple iFrames
If the website has multiple iFrames, you can identify them by their URL:
const iframe = new iFrame()
iframe.on('UpdateData', async () => {
// Get the iFrame URL
const url = await iframe.getUrl()
// Handle different types of iFrames
if (url.includes('youtube.com')) {
// Handle YouTube iFrame
const video = document.querySelector('video')
if (video) {
iframe.send({
youtube: {
paused: video.paused,
currentTime: video.currentTime,
duration: video.duration,
title: document.querySelector('.ytp-title-link')?.textContent
}
})
}
}
else if (url.includes('soundcloud.com')) {
// Handle SoundCloud iFrame
const playButton = document.querySelector('.playButton')
const isPlaying = playButton?.classList.contains('playing') || false
iframe.send({
soundcloud: {
playing: isPlaying,
title: document.querySelector('.soundTitle__title')?.textContent
}
})
}
})
In your presence.ts
file, you can handle the different types of data:
// Store iFrame data
let iFrameData: {
youtube?: {
paused?: boolean
currentTime?: number
duration?: number
title?: string
}
soundcloud?: {
playing?: boolean
title?: string
}
} = {}
// Listen for iFrame data
presence.on('iFrameData', (data) => {
// Merge the new data with the existing data
iFrameData = { ...iFrameData, ...data }
})
presence.on('UpdateData', async () => {
const presenceData: PresenceData = {
largeImageKey: ActivityAssets.Logo,
}
// Check if we have YouTube data
if (iFrameData.youtube) {
// Handle YouTube data
}
// Check if we have SoundCloud data
if (iFrameData.soundcloud) {
// Handle SoundCloud data
}
// Set the activity
presence.setActivity(presenceData)
})
Best Practices
- Always specify iFrameRegExp: The
iFrameRegExp
property is required when using iFrames. Make sure to define a specific pattern that only matches the iFrames you need to target. - Import getTimestamps from PreMiD: Always use
import { getTimestamps } from 'premid'
instead of creating your own implementation. - Manage browsing timestamps correctly: Initialize browsing timestamps outside the UpdateData event and only update them when changing back to a browsing state, not on every UpdateData event. This ensures consistent elapsed time display.
- Use destructuring for timestamps: Use the pattern
[presenceData.startTimestamp, presenceData.endTimestamp] = getTimestamps(currentTime, duration)
for cleaner code. - Only use iFrames when necessary: iFrames add complexity to your activity, so only use them when you need to gather information from iFrames.
Complete Example
Here's a complete example of an activity that uses iFrames to gather information from embedded YouTube videos:
metadata.json
{
"author": {
"name": "Your Name",
"id": "your_discord_id"
},
"service": "Example",
"description": {
"en": "Example is a website that embeds YouTube videos."
},
"url": "example.com",
"version": "1.0.0",
"logo": "https://example.com/logo.png",
"thumbnail": "https://example.com/thumbnail.png",
"color": "#FF0000",
"category": "videos",
"tags": ["example", "video"],
"iframe": true,
"iFrameRegExp": "youtube\\.com/embed/.*"
}
iframe.ts
const iframe = new iFrame()
iframe.on('UpdateData', async () => {
// Get the video element
const video = document.querySelector('video')
if (video) {
// Send video information to the presence script
iframe.send({
video: {
paused: video.paused,
currentTime: video.currentTime,
duration: video.duration,
title: document.querySelector('.ytp-title-link')?.textContent
}
})
}
})
presence.ts
import { Assets, getTimestamps } from 'premid'
const presence = new Presence({
clientId: 'your_client_id'
})
enum ActivityAssets {
Logo = 'https://example.com/logo.png',
}
// Define the type for iFrame data
interface IFrameData {
video?: {
paused?: boolean
currentTime?: number
duration?: number
title?: string
}
}
// Store iFrame data
let iFrameData: IFrameData = {}
// Create timestamps outside of UpdateData to maintain consistent browsing time
let browsingTimestamp = Math.floor(Date.now() / 1000)
// Track whether we were watching a video in the previous update
let wasWatchingVideo = false
// Listen for iFrame data
presence.on('iFrameData', (data: IFrameData) => {
iFrameData = data
})
presence.on('UpdateData', async () => {
// Get settings
const showButtons = await presence.getSetting<boolean>('showButtons')
const showTimestamp = await presence.getSetting<boolean>('showTimestamp')
// Create the base presence data
const presenceData: PresenceData = {
largeImageKey: ActivityAssets.Logo,
}
// Check if we have video data from the iFrame
if (iFrameData.video) {
const { paused, currentTime, duration, title } = iFrameData.video
// Set the activity type to Watching
presenceData.type = ActivityType.Watching
// Set the details and state
presenceData.details = title || 'Watching a video'
if (paused) {
presenceData.state = 'Paused'
presenceData.smallImageKey = Assets.Pause
presenceData.smallImageText = 'Paused'
}
else {
presenceData.state = 'Playing'
presenceData.smallImageKey = Assets.Play
presenceData.smallImageText = 'Playing'
// Add timestamps if enabled and we have currentTime and duration
if (showTimestamp && currentTime && duration) {
// Use destructuring assignment for timestamps
[presenceData.startTimestamp, presenceData.endTimestamp] = getTimestamps(currentTime, duration)
}
}
// Add buttons if enabled
if (showButtons) {
presenceData.buttons = [
{
label: 'Watch Video',
url: document.location.href
}
]
}
}
else {
// No video data, user is browsing the website
// If we were watching a video before but now we're browsing,
// reset the browsing timestamp to the current time
if (wasWatchingVideo) {
browsingTimestamp = Math.floor(Date.now() / 1000)
wasWatchingVideo = false
}
presenceData.details = 'Browsing'
presenceData.startTimestamp = browsingTimestamp
}
// Update wasWatchingVideo state for the next update
if (iFrameData.video) {
wasWatchingVideo = true
}
// Set the activity
presence.setActivity(presenceData)
})
Next Steps
Now that you understand how to use iFrames in your activity, you can learn more about:
- Slideshows: Learn how to create a slideshow that alternates between different presence data.
- Localization: Learn how to add support for multiple languages to your activity.
- Best Practices: Learn best practices for creating activities.