Agora Live Streaming: Subscribe to existing user in a chanel? - subscribe

I would like to subscribe to the existing user in a channel.
If a user(host) was published before the audience joined and subscribe host. The remote tracks do not play.
client.getClient().on("user-published", async (user, mediaType) => {

I found a solution. If you are in this post, you can try this code below:
We can use client.remoteUsers to get the remote users.
The sample code as below:
if (client.remoteUsers.length > 0) {
const host = client.remoteUsers[0];
setState((s) => {
return {
...s,
statusLive: StatusLive.live,
isPlayed: true,
};
});
if (host.hasVideo) {
await client.subscribe(host, "video");
host.videoTrack?.play(ref.current as HTMLElement);
}
if (host.hasAudio) {
await client.subscribe(host, "audio");
host.audioTrack?.play();
}
}

Related

service-worker registered and precaching works fine, but fetch event is never firing

I'm quite new to PWA, service workers and workbox. I don't understand why and how the fetch event inside my service worker is supposed to be triggered?
I see the log of workbox which is precaching files I've provided as an array:
How would I serve from cache now and why does my fetch event handler won't be fired at all?
My service-worker file:
importScripts('https://storage.googleapis.com/workbox-cdn/releases/6.2.0/workbox-sw.js')
workbox.setConfig({
debug: true,
})
// To avoid async issues, we load strategies before we call it in the event listener
workbox.loadModule('workbox-core')
workbox.loadModule('workbox-routing')
workbox.loadModule('workbox-cacheable-response')
workbox.loadModule('workbox-strategies')
workbox.loadModule('workbox-expiration')
workbox.loadModule('workbox-precaching')
const { precacheAndRoute } = workbox.precaching
const wbManifest = self.__WB_MANIFEST
console.log(wbManifest)
precacheAndRoute(wbManifest);
const cacheNames = workbox.core.cacheNames
const { registerRoute, setCatchHandler, setDefaultHandler } = workbox.routing
const { CacheableResponsePlugin } = workbox.cacheableResponse
const {
NetworkFirst,
StaleWhileRevalidate,
NetworkOnly,
} = workbox.strategies
const { ExpirationPlugin } = workbox.expiration
const cacheName = cacheNames.runtime
const contentToCache = [
'/',
]
for (const entry of wbManifest) {
contentToCache.push(entry.url)
}
self.addEventListener('activate', e => {
e.waitUntil(self.clients.claim()) // Become available to all pages
})
self.addEventListener('install', e => {
e.waitUntil((async () => {
const cache = await caches.open(cacheName)
console.log('[Service Worker] Caching content', contentToCache)
await cache.addAll(contentToCache)
self.skipWaiting()
})())
})
self.addEventListener('push', e => {
console.log(e.data.text());
});
self.addEventListener('fetch', e => {
const { request } = e;
console.log(request)
e.respondWith(caches.match(request).then(cachedResponse => {
// This promise explicitly resolves with "undefined" when there are no matches, all other values are correct
if (cachedResponse !== undefined) {
return cachedResponse
} else {
return fetch(request).then(response => {
// Since we can use the response only once, put the clone into the cache and serve the original response
const responseClone = response.clone()
caches.open('CACHE_KEY_WHATEVER').then(cache => {
cache.put(request, responseClone)
})
return response
}).catch(() => {
// Retry logic
});
}
}))
})
precacheAndRoute(wbManifest); will both precache (during install, add the entries to the cache) and route (respond to fetch events with a cached response) for all entries that are present in wbManifest.
I see that your current code attempts to do both the precaching (in your own install handler) and routing (in your own fetch handler) for all of those URLs. If you would prefer to handle that yourself, that's fine, but in that case you shouldn't be calling precacheAndRoute() at all. You're attempting to do the same thing as that method, but the event listeners for Workbox's precacheAndRoute() ends up executing first and take precedence.

PWA problem with Vue3+service worker+keep-alive

I have a problem with Vue3+service worker+keep-alive.
I use keep-live in template
<q-page-container>
<router-view v-slot="{ Component }">
<keep-alive :include="['WorkPage']">
<component :is="Component" :key="$route.fullPath"/>
</keep-alive>
</router-view>
</q-page-container>
create queue
createWorkQueue = new Queue('createWorkQueue', {
onSync: async ( {queue} ) => {
let entry
while (entry = await queue.shiftRequest()) {
try {
await fetch(entry.request);
const channel = new BroadcastChannel('sw-messages-work');
channel.postMessage({msg: 'offline-work-uploaded'});
} catch (error) {
await queue.unshiftRequest(entry);
throw error;
}
}
}
})
addEventListener('fetch'
self.addEventListener('fetch', (event) => {
if (event.request.url.endsWith('/api/ins_new_work')) {
const bgSyncLogic = async () => {
try {
const response = await fetch(event.request.clone())
return response
} catch (error) {
await createWorkQueue.pushRequest({request: event.request})
return error
}
}
event.respondWith(bgSyncLogic())
}
})
when in offline I send form - createWorkQueue.pushRequest hangs to 5 minutes
if I delete from keep-alive - WorkPage - then pushRequest works well
but I need keep-alive page. How can I solve this?
I found!!
I use IndexedDB library and for show offline message I read from DB information
const db = await openDB('workbox-background-sync')
but in first time - table 'requests' don't create
I insert next code
const db = await openDB('workbox-background-sync', undefined, { upgrade(db) { db.createObjectStore('requests') }})
and works well

Making a welcome message an embed on discord.js

I have connected MongoDB to my discord.js code and have made a setwelcome command as per-server data so that each server can customize their own welcome message. Everything works great, I just want to know if there is any way that I can make the message appear as an embed? Here's the code:
//importing all the needed files and languages
const mongo = require('./mongo')
const command = require('./command')
const welcomeSchema = require('./schemas/welcome-schema')
const mongoose = require('mongoose')
const Discord = require('discord.js')
mongoose.set('useFindAndModify', false);
//my code is inside this export
module.exports = (client) => {
//this next line is for later
const cache = {}
command(client, 'setwelcome', async (message) => {
const { member, channel, content, guild } = message
//checking to see that only admins can do this
if (!member.hasPermissions === 'ADMINISTRATOR') {
channel.send('You do not have the permission to run this command')
return
}
//simplifying commands
let text = content
//this is to store just the command and not the prefix in mongo compass
const split = text.split(' ')
if (split.length < 2) {
channel.send('Please provide a welcome message!')
return
}
split.shift()
text = split.join(' ')
//this is to not fetch from the database after code ran once
cache[guild.id] = [channel.id, text]
//this is to store the code inside mongo compass
await mongo().then(async (mongoose) => {
try {
await welcomeSchema.findOneAndUpdate({
_id: guild.id
}, {
_id: guild.id,
channelId: channel.id,
text,
}, {
upsert: true
})
} finally {
mongoose.connection.close()
}
})
})
//this is to fetch from the database
const onJoin = async (member) => {
const { guild } = member
let data = cache[guild.id]
if (!data) {
console.log('FETCHING FROM DATABASE')
await mongo().then( async (mongoose) => {
try {
const result = await welcomeSchema.findOne({ _id: guild.id })
cache[guild.id] = data = [result.channelId, result.text]
} finally {
mongoose.connection.close()
}
})
}
//this is to simplify into variables
const channelId = data[0]
const text = data[1]
/*this is where the message sends on discord. the second of these 2 lines is what I want embedded
which is basically the welcome message itself*/
const channel = guild.channels.cache.get(channelId)
channel.send(text.replace(/<#>/g, `<#${member.id}>`))
}
//this is to test the command
command(client, 'simjoin', message => {
onJoin(message.member)
})
//this is so the command works when someone joins
client.on('guildMemberAdd', member => {
onJoin(member)
})
}
I know how to usually make an embed, but I'm just confused at the moment on what to put as .setDescription() for the embed.
Please advise.
If you just want to have the message be sent as an embed, create a MessageEmbed and use setDescription() with the description as the only argument. Then send it with channel.send(embed).
const embed = new Discord.MessageEmbed();
embed.setDescription(text.replace(/<#>/g, `<#${member.id}>`));
channel.send(embed);
By the way, if you are confused about how to use a specific method you can always search for the method name on the official discord.js documentation so you don’t have to wait for an answer here. Good luck creating your bot!

Issue Connecting to MongoDB collections

I am using axios and express.js API to connect to my mongo DB. I have a .get() request that works for one collection and doesn't work for any other collection. This currently will connect to the database and can access one of the collections called users. I have another collection setup under the same database called tasks, I have both users and tasks setup the same way and being used the same way in the code. The users can connect to the DB (get, post) and the tasks fails to connect to the collection when calling the get or the post functions. When viewing the .get() API request in the browser it just hangs and never returns anything or finishes the request.
any help would be greatly appreciated!
The project is on GitHub under SCRUM-150.
API connection
MONGO_URI=mongodb://localhost:27017/mydb
Working
methods: {
//load all users from DB, we call this often to make sure the data is up to date
load() {
http
.get("users")
.then(response => {
this.users = response.data.users;
})
.catch(e => {
this.errors.push(e);
});
},
//opens delete dialog
setupDelete(user) {
this.userToDelete = user;
this.deleteDialog = true;
},
//opens edit dialog
setupEdit(user) {
Object.keys(user).forEach(key => {
this.userToEdit[key] = user[key];
});
this.editName = user.name;
this.editDialog = true;
},
//build the alert info for us
//Will emit an alert, followed by a boolean for success, the type of call made, and the name of the
//resource we are working on
alert(success, callName, resource) {
console.log('Page Alerting')
this.$emit('alert', success, callName, resource)
this.load()
}
},
//get those users
mounted() {
this.load();
}
};
Broken
methods: {
//load all tasks from DB, we call this often to make sure the data is up to date
load() {
http
.get("tasks")
.then(response => {
this.tasks = response.data.tasks
})
.catch(e => {
this.errors.push(e);
});
},
//opens delete dialog
setupDelete(tasks) {
this.taskToDelete = tasks;
this.deleteDialog = true;
},
//opens edit dialog
setupEdit(tasks) {
Object.keys(tasks).forEach(key => {
this.taskToEdit[key] = tasks[key];
});
this.editName = tasks.name;
this.editDialog = true;
},
//build the alert info for us
//Will emit an alert, followed by a boolean for success, the type of call made, and the name of the
//resource we are working on
alert(success, callName, resource) {
console.log('Page Alerting')
this.$emit('alert', success, callName, resource)
this.load()
}
},
//get those tasks
mounted() {
this.load();
}
};
Are you setting any access controls in the code?
Also refer to mongoDB's documentation here:
https://docs.mongodb.com/manual/core/collection-level-access-control/
Here is my solution:
In your app.js, have this:
let mongoose = require('mongoose');
mongoose.connect('Your/Database/Url', {
keepAlive : true,
reconnectTries: 2,
useMongoClient: true
});
In your route have this:
let mongoose = require('mongoose');
let db = mongoose.connection;
fetchAndSendDatabase('yourCollectionName', db);
function fetchAndSendDatabase(dbName, db) {
db.collection(dbName).find({}).toArray(function(err, result) {
if( err ) {
console.log("couldn't get database items. " + err);
}
else {
console.log('Database received successfully');
}
});
}

WebRTC with PeerJS remote video not showing on Android

I'm using Ionic3 to build an android videochat application.
The videochat works perfectly between two tabs on my browser, but only shows the local video on my android device (the remote video being blank).
I'm using PeerJS for the peer-to-peer connection in my index.html:
I'm using the stunServer {url: "stun:stun.l.google.com:19302"} for the connection.
I'm using the functions shown on the home page: http://peerjs.com/
My config service:
import {Injectable} from '#angular/core';
#Injectable()
export class WebRTCConfig {
peerServerPort: number = 9000;
key:string = '<my peer id>';
stun: string = 'stun.l.google.com:19302';
stunServer = {
url: 'stun:' + this.stun
};
getPeerJSOption() {
return {
// Set API key for cloud server (you don't need this if you're running your own.
key: this.key,
// Set highest debug level (log everything!).
debug: 3,
// Set it to false because of:
// > PeerJS: ERROR Error: The cloud server currently does not support HTTPS.
// > Please run your own PeerServer to use HTTPS.
secure: false,
config: {
iceServers: [
this.stunServer/*,
this.turnServer*/
]
}
};
}
/**********************/
audio: boolean = true;
video: boolean = false;
getMediaStreamConstraints(): MediaStreamConstraints {
return <MediaStreamConstraints> {
audio: this.audio,
video: this.video
}
}
}
Snippet of my Peer WebRTC service:
createPeer(userId: string = '') {
// Create the Peer object where we create and receive connections.
this._peer = new Peer(/*userId,*/ this.config.getPeerJSOption());
setTimeout(()=> {
console.log(this._peer.id);
this.myid = this._peer.id;
}, 3000)
}
myCallId() {
return this.myid;
}
answer(call) {
call.answer(this._localStream);
this._step2(call);
}
init(myEl: HTMLMediaElement, otherEl: HTMLMediaElement, onCalling: Function) {
this.myEl = myEl;
this.otherEl = otherEl;
this.onCalling = onCalling;
// Receiving a call
this._peer.on('call', (call) => {
// Answer the call automatically (instead of prompting user) for demo purposes
this.answer(call);
});
this._peer.on('error', (err) => {
console.log(err.message);
// Return to step 2 if error occurs
if (this.onCalling) {
this.onCalling();
}
// this._step2();
});
this._step1();
}
call(otherUserId: string) {
// Initiate a call!
var call = this._peer.call(otherUserId, this._localStream);
this._step2(call);
}
endCall() {
this._existingCall.close();
// this._step2();
if (this.onCalling) {
this.onCalling();
}
}
private _step1() {
// Get audio/video stream
navigator.getUserMedia({ audio: true, video: true }, (stream) => {
// Set your video displays
this.myEl.src = URL.createObjectURL(stream);
this._localStream = stream;
// this._step2();
if (this.onCalling) {
this.onCalling();
}
}, (error) => {
console.log(error);
});
}
private _step2(call) {
// Hang up on an existing call if present
if (this._existingCall) {
this._existingCall.close();
}
// Wait for stream on the call, then set peer video display
call.on('stream', (stream) => {
this.otherEl.src = URL.createObjectURL(stream);
});
// UI stuff
this._existingCall = call;
// $('#their-id').text(call.peer);
call.on('close', () => {
// this._step2();
if (this.onCalling) {
this.onCalling();
}
});
}
In my chat.ts, I use this to call the function from the peer webrtc service:
call() {
this.webRTCService.call(this.calleeId);
}
It's likely to be a permission problem. You need to grant it permission to use the camera.
Camera Permission - Your application must request permission to use a
device camera.
<uses-permission android:name="android.permission.CAMERA" />
See
https://developer.android.com/guide/topics/media/camera.html