Skip to content

Commit

Permalink
feat: implement media loading with p2p-image and p2p-video components
Browse files Browse the repository at this point in the history
  • Loading branch information
akhileshthite committed Jun 13, 2024
1 parent 675329f commit 58366b8
Show file tree
Hide file tree
Showing 9 changed files with 153 additions and 45 deletions.
10 changes: 4 additions & 6 deletions actor-mini-profile.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { db } from './dbInstance.js'
import { resolveP2PUrl } from './db.js'

class ActorMiniProfile extends HTMLElement {
static get observedAttributes () {
Expand Down Expand Up @@ -49,11 +48,10 @@ class ActorMiniProfile extends HTMLElement {
}

// Actor icon
const img = document.createElement('img')
img.className = 'profile-mini-icon'
img.src = resolveP2PUrl(iconUrl)
img.alt = actorInfo.name ? actorInfo.name : 'Actor icon'
clickableContainer.appendChild(img)
const p2pImage = document.createElement('p2p-image')
p2pImage.className = 'profile-mini-icon'
p2pImage.alt = actorInfo.name ? actorInfo.name : 'Actor icon'
clickableContainer.appendChild(p2pImage)

// Actor name
if (actorInfo.name) {
Expand Down
11 changes: 5 additions & 6 deletions actor-profile.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { db } from './dbInstance.js'
import { resolveP2PUrl } from './db.js'

class ActorProfile extends HTMLElement {
static get observedAttributes () {
Expand Down Expand Up @@ -70,11 +69,11 @@ class ActorProfile extends HTMLElement {
}
}

const img = document.createElement('img')
img.classList.add('profile-icon')
img.src = resolveP2PUrl(iconUrl)
img.alt = actorInfo.name ? actorInfo.name : 'Actor icon'
actorContainer.appendChild(img) // Append to the actor container
const p2pImage = document.createElement('p2p-image')
p2pImage.setAttribute('src', iconUrl)
p2pImage.classList.add('profile-icon')
p2pImage.alt = actorInfo.name ? actorInfo.name : 'Actor icon'
actorContainer.appendChild(p2pImage) // Append to the actor container

if (actorInfo.name) {
const pName = document.createElement('div')
Expand Down
11 changes: 10 additions & 1 deletion db.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,16 @@ export function isP2P (url) {
return url.startsWith(HYPER_PREFIX) || url.startsWith(IPNS_PREFIX)
}

export async function supportsP2P (url) {
try {
const response = await fetch(url)
return response.ok
} catch (error) {
console.log('P2P URL loading failed:', error)
return false
}
}

export function resolveP2PUrl (url) {
if (!url) return url

Expand Down Expand Up @@ -115,7 +125,6 @@ export class ActivityPubDB extends EventTarget {
if (url && typeof url === 'object') {
return url
}
url = resolveP2PUrl(url)

let response
// Try fetching directly for all URLs (including P2P URLs)
Expand Down
1 change: 1 addition & 0 deletions followed-accounts.html
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,5 @@
<script type="module" src="./sidebar.js"></script>
<script type="module" src="./followed-accounts.js"></script>
<script type="module" src="./actor-mini-profile.js"></script>
<script type="module" src="./p2p-media.js"></script>
<script type="module" src="./theme-selector.js"></script>
1 change: 1 addition & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
<script type="module" src="./timeline.js"></script>
<script type="module" src="./outbox.js"></script>
<script type="module" src="./post.js"></script>
<script type="module" src="./p2p-media.js"></script>
<script type="module" src="./followed-accounts.js"></script>
<script type="module" src="./theme-selector.js"></script>
<script type="module" src="./error-message.js"></script>
102 changes: 102 additions & 0 deletions p2p-media.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { supportsP2P, resolveP2PUrl } from './db.js'

class P2PImage extends HTMLElement {
constructor () {
super()

this.img = document.createElement('img')
this.attachShadow({ mode: 'open' })
this.shadowRoot.appendChild(this.img)
this.img.addEventListener('error', () => this.handleError())
}

static get observedAttributes () {
return ['src']
}

connectedCallback () {
if (this.hasAttribute('src')) {
this.loadImage(this.getAttribute('src'))
}
}

attributeChangedCallback (name, oldValue, newValue) {
if (name === 'src' && newValue !== oldValue) {
this.loadImage(newValue)
}
}

async loadImage (src) {
try {
const p2pSupported = await supportsP2P(src)
if (p2pSupported) {
this.img.src = src // Attempt to load the original P2P URL
} else {
this.handleError()
}
} catch (error) {
this.handleError()
}
}

async handleError () {
const fallbackSrc = resolveP2PUrl(this.getAttribute('src'))
console.log(`Failed to load image. Resolving to gateway URL: ${fallbackSrc}`)
this.img.src = fallbackSrc
}
}

customElements.define('p2p-image', P2PImage)

class P2PVideo extends HTMLElement {
constructor () {
super()
this.video = document.createElement('video')
this.video.controls = true
this.attachShadow({ mode: 'open' })
this.shadowRoot.appendChild(this.video)
}

static get observedAttributes () {
return ['src']
}

connectedCallback () {
if (this.hasAttribute('src')) {
this.loadVideo(this.getAttribute('src'))
}
}

attributeChangedCallback (name, oldValue, newValue) {
if (name === 'src' && newValue !== oldValue) {
this.loadVideo(newValue)
}
}

async loadVideo (src) {
this.video.innerHTML = '' // Clear any existing sources
const source = document.createElement('source')
source.src = src

try {
const p2pSupported = await supportsP2P(src)
if (!p2pSupported) {
throw new Error('P2P not supported for this URL')
}
} catch (error) {
this.handleError(source)
return // Skip setting the source if not supported
}

source.onerror = () => this.handleError(source)
this.video.appendChild(source)
}

handleError (source) {
const fallbackSrc = resolveP2PUrl(source.src)
console.log(`Failed to load video source. Resolving to gateway URL: ${fallbackSrc}`)
source.src = fallbackSrc
}
}

customElements.define('p2p-video', P2PVideo)
1 change: 1 addition & 0 deletions post.html
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
<script type="module" src="./sidebar.js"></script>
<script type="module" src="./timeline.js"></script>
<script type="module" src="./post.js"></script>
<script type="module" src="./p2p-media.js"></script>
<script type="module" src="./followed-accounts.js"></script>
<script type="module" src="./theme-selector.js"></script>
<script type="module" src="./error-message.js"></script>
60 changes: 28 additions & 32 deletions post.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,42 +44,39 @@ function insertImagesAndVideos (content) {
const parser = new DOMParser()
const contentDOM = parser.parseFromString(content, 'text/html')

// Replace all <img> tags with <p2p-image> tags
contentDOM.querySelectorAll('img').forEach(img => {
const originalSrc = img.getAttribute('src')
const p2pSrc = resolveP2PUrl(originalSrc)

img.onerror = () => {
console.log(`Failed to load image at ${originalSrc}. Attempting to resolve...`)
// Fallback to gateway URL only if it's a P2P URL and not already using a gateway
if (isP2P(originalSrc) && !originalSrc.includes('hypha.coop')) {
const fallbackSrc = resolveP2PUrl(originalSrc)
console.log(`Resolving to gateway URL due to error: ${fallbackSrc}`)
img.setAttribute('src', fallbackSrc)
}
}

img.setAttribute('src', p2pSrc)
console.log(`Set image src to: ${p2pSrc}`)
console.log(`Original img src: ${originalSrc}`)
const p2pImg = document.createElement('p2p-image')
p2pImg.setAttribute('src', originalSrc)
img.parentNode.replaceChild(p2pImg, img)
console.log(`Replaced img with p2p-image having src: ${p2pImg.getAttribute('src')}`)
})

contentDOM.querySelectorAll('video source').forEach(video => {
const originalSrc = video.getAttribute('src')
const p2pSrc = resolveP2PUrl(originalSrc)

video.onerror = () => {
console.log(`Failed to load video at ${originalSrc}. Attempting to resolve...`)
if (isP2P(originalSrc) && !originalSrc.includes('hypha.coop')) {
const fallbackSrc = resolveP2PUrl(originalSrc)
console.log(`Resolving to gateway URL due to error: ${fallbackSrc}`)
video.setAttribute('src', fallbackSrc)
}
// Replace all <video> tags with <p2p-video> tags
contentDOM.querySelectorAll('video').forEach(video => {
const p2pVideo = document.createElement('p2p-video')
if (video.hasAttribute('src')) {
const originalSrc = video.getAttribute('src')
p2pVideo.setAttribute('src', originalSrc)
}

video.setAttribute('src', p2pSrc)
console.log(`Set video src to: ${p2pSrc}`)
Array.from(video.children).forEach(source => {
if (source.tagName === 'SOURCE') {
const originalSrc = source.getAttribute('src')
console.log(`Original video src: ${originalSrc}`)
const srcType = source.getAttribute('type')
const p2pSource = document.createElement('source')
p2pSource.setAttribute('src', originalSrc)
p2pSource.setAttribute('type', srcType)
p2pVideo.appendChild(p2pSource)
console.log(`Replaced video with p2p-video having src: ${p2pSource.getAttribute('src')}`)
}
})
video.parentNode.replaceChild(p2pVideo, video)
})

return contentDOM.body.innerHTML // Return the modified HTML to be inserted
return contentDOM.body.innerHTML
}

// Define a class for the <distributed-post> web component
Expand All @@ -97,15 +94,14 @@ class DistributedPost extends HTMLElement {
this.renderErrorContent('No post URL provided')
return
}
postUrl = await resolveP2PUrl(postUrl)

try {
const content = await db.getNote(postUrl)
if (content && content.content) {
content.content = insertImagesAndVideos(content.content) // Resolve URLs before rendering
// Assuming JSON-LD content has a "summary" field
this.renderPostContent(content)
}
// Assuming JSON-LD content has a "summary" field
this.renderPostContent(content)
} catch (error) {
console.error(error)
this.renderErrorContent(error.message)
Expand Down
1 change: 1 addition & 0 deletions profile.html
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
<script type="module" src="./sidebar.js"></script>
<script type="module" src="./actor-profile.js"></script>
<script type="module" src="./post.js"></script>
<script type="module" src="./p2p-media.js"></script>
<script type="module" src="./outbox.js"></script>
<script type="module" src="./followed-accounts.js"></script>
<script type="module" src="./theme-selector.js"></script>
Expand Down

0 comments on commit 58366b8

Please sign in to comment.