kopia lustrzana https://codeberg.org/nmkj/audon
fix annoying avatar change
rodzic
44e0b2b3d7
commit
cc4337ebcb
|
@ -20,7 +20,7 @@ webhook:
|
|||
|
||||
room:
|
||||
auto_create: false
|
||||
empty_timeout: 30
|
||||
empty_timeout: 3600
|
||||
max_participants: 0
|
||||
max_metadata_size: 0
|
||||
|
||||
|
|
|
@ -0,0 +1,173 @@
|
|||
<script>
|
||||
import { Room } from "livekit-client";
|
||||
import { useMastodonStore } from "../stores/mastodon";
|
||||
import { mdiArrowRightBold } from "@mdi/js";
|
||||
import { pushNotFound } from "../assets/utils";
|
||||
import axios from "axios";
|
||||
|
||||
export default {
|
||||
setup() {
|
||||
return {
|
||||
mdiArrowRightBold,
|
||||
donStore: useMastodonStore(),
|
||||
};
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
roomID: this.$route.params.id,
|
||||
isLoading: true,
|
||||
roomToken: null,
|
||||
dialogEnabled: true,
|
||||
uploading: false,
|
||||
};
|
||||
},
|
||||
emits: ["connect"],
|
||||
props: {
|
||||
roomId: String,
|
||||
roomClient: Room,
|
||||
},
|
||||
computed: {
|
||||
uploadEnabled() {
|
||||
return this.roomToken?.original && this.roomToken?.indicator;
|
||||
},
|
||||
},
|
||||
async mounted() {
|
||||
try {
|
||||
await this.donStore.fetchToken();
|
||||
const resp = await axios.post(
|
||||
`/api/room/${this.roomID}`,
|
||||
this.donStore.userinfo
|
||||
);
|
||||
this.roomToken = resp.data;
|
||||
} catch (error) {
|
||||
this.dialogEnabled = false;
|
||||
if (error.response?.status === 401) {
|
||||
return;
|
||||
}
|
||||
let message = "";
|
||||
switch (error.response?.status) {
|
||||
case 403:
|
||||
switch (error.response?.data) {
|
||||
case "following":
|
||||
message = this.$t("errors.restriction.following");
|
||||
break;
|
||||
case "follower":
|
||||
message = this.$t("errors.restriction.follower");
|
||||
break;
|
||||
case "knowing":
|
||||
message = this.$t("errors.restriction.knowing");
|
||||
break;
|
||||
case "mutual":
|
||||
message = this.$t("errors.restriction.mutual");
|
||||
break;
|
||||
case "private":
|
||||
message = this.$t("errors.restriction.private");
|
||||
break;
|
||||
default:
|
||||
message = this.$t("errors.restriction.default");
|
||||
}
|
||||
alert(message);
|
||||
break;
|
||||
case 404:
|
||||
pushNotFound(this.$route);
|
||||
break;
|
||||
case 406:
|
||||
alert(this.$t("errors.alreadyConnected"));
|
||||
break;
|
||||
case 410:
|
||||
alert(this.$t("errors.alreadyClosed"));
|
||||
break;
|
||||
default:
|
||||
alert(error);
|
||||
}
|
||||
this.$router.push({ name: "home" });
|
||||
} finally {
|
||||
this.isLoading = false;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async joining(indicator) {
|
||||
try {
|
||||
await this.roomClient.startAudio();
|
||||
if (indicator && this.uploadEnabled) {
|
||||
this.uploading = true;
|
||||
this.donStore.avatar = this.roomToken.original;
|
||||
try {
|
||||
await this.donStore.updateAvatar(this.roomToken.indicator);
|
||||
} finally {
|
||||
this.uploading = false;
|
||||
this.dialogEnabled = false;
|
||||
this.$emit("connect", this.roomToken);
|
||||
}
|
||||
} else {
|
||||
this.dialogEnabled = false;
|
||||
this.$emit("connect", this.roomToken);
|
||||
}
|
||||
} catch {
|
||||
alert(this.$t("errors.connectionFailed"));
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-overlay
|
||||
:model-value="isLoading || uploading"
|
||||
persistent
|
||||
class="align-center justify-center"
|
||||
>
|
||||
<v-progress-circular indeterminate size="40"></v-progress-circular>
|
||||
</v-overlay>
|
||||
<v-dialog
|
||||
v-if="!isLoading"
|
||||
v-model="dialogEnabled"
|
||||
max-width="500"
|
||||
persistent
|
||||
>
|
||||
<v-alert v-if="uploadEnabled" color="deep-purple-darken-2">
|
||||
<div>
|
||||
{{ $t("onlineIndicator.message") }}
|
||||
</div>
|
||||
<div class="mt-3 text-center">
|
||||
<v-avatar class="rounded" size="80">
|
||||
<v-img :src="roomToken.indicator"></v-img>
|
||||
</v-avatar>
|
||||
</div>
|
||||
<v-alert
|
||||
class="mt-3"
|
||||
density="compact"
|
||||
type="success"
|
||||
color="white"
|
||||
variant="tonal"
|
||||
>
|
||||
{{ $t("onlineIndicator.hint") }}
|
||||
</v-alert>
|
||||
<v-alert
|
||||
class="mt-3"
|
||||
border="start"
|
||||
density="compact"
|
||||
type="warning"
|
||||
variant="outlined"
|
||||
>
|
||||
{{ $t("onlineIndicator.warning") }}
|
||||
</v-alert>
|
||||
<div class="mt-3 mb-1 d-flex align-center justify-space-around">
|
||||
<v-btn @click="joining(false)">{{ $t("onlineIndicator.nope") }}</v-btn>
|
||||
<v-btn color="indigo" @click="joining(true)">{{
|
||||
$t("onlineIndicator.sure")
|
||||
}}</v-btn>
|
||||
</div>
|
||||
</v-alert>
|
||||
<v-alert v-else color="indigo">
|
||||
<div class="mb-5">
|
||||
{{ $t("browserMuted") }}
|
||||
</div>
|
||||
<div class="text-center mb-1">
|
||||
<v-btn color="gray" @click="joining(false)">{{
|
||||
$t("startListening")
|
||||
}}</v-btn>
|
||||
</div>
|
||||
</v-alert>
|
||||
</v-dialog>
|
||||
</template>
|
|
@ -1,3 +1,4 @@
|
|||
<!-- eslint-disable vue/multi-word-component-names -->
|
||||
<script>
|
||||
import { mdiMicrophone, mdiMicrophoneOff } from "@mdi/js";
|
||||
import { webfinger } from "../assets/utils";
|
||||
|
@ -103,7 +104,7 @@ export default {
|
|||
:icon="muted ? mdiMicrophoneOff : mdiMicrophone"
|
||||
></v-icon>
|
||||
<a :href="data?.url" class="plain" target="_blank">{{
|
||||
!data?.displayName ? data?.acct : data?.displayName
|
||||
!data?.displayName ? webfinger(data) : data?.displayName
|
||||
}}</a>
|
||||
</h4>
|
||||
</v-col>
|
||||
|
|
|
@ -6,6 +6,8 @@ logoutConfirm: "Are you sure you want to sign out from Audon?"
|
|||
loginRequired: "You need to sign in to view this page."
|
||||
create: "Create"
|
||||
cancel: "Cancel"
|
||||
sure: "Yes"
|
||||
nope: "No"
|
||||
edit: "Edit"
|
||||
save: "Save"
|
||||
share: "Share"
|
||||
|
@ -21,6 +23,8 @@ addressRequired: "Enter your instance address"
|
|||
createNewRoom: "Create a New Room"
|
||||
editRoom: "Room Edit"
|
||||
comingFuture: "Coming with future update!"
|
||||
processing: "Processing now...<br />Keep this window open!"
|
||||
lostWarning: "Unsaved data will be lost if you leave the page, are you sure?"
|
||||
form:
|
||||
title: "Title"
|
||||
titleRequired: "Room title required"
|
||||
|
@ -57,7 +61,13 @@ errors:
|
|||
private: "Only cohosts can join."
|
||||
default: "You are not allowed to join."
|
||||
startListening: "Start Listening"
|
||||
browserMuted: "Your sound is muted by the browser. Press @:startListening to continue."
|
||||
browserMuted: "To protect your ears, sound is muted by the browser. Press @:startListening to continue."
|
||||
onlineIndicator:
|
||||
message: "Do you want to add the online indicator to your account's avatar like this?"
|
||||
hint: "Audon will remove the indicator after this room is closed."
|
||||
warning: "Your instance may take a while to reflect the indicator."
|
||||
sure: "Yes"
|
||||
nope: "No"
|
||||
speakRequest:
|
||||
label: "Speaker Requests"
|
||||
dialog: "Are you sure you want to send a request to be a speaker?"
|
||||
|
|
|
@ -6,6 +6,8 @@ logoutConfirm: "Audon からログアウトしますか?"
|
|||
loginRequired: "続行するにはログインする必要があります。"
|
||||
create: "作成"
|
||||
cancel: "キャンセル"
|
||||
sure: "はい"
|
||||
nope: "いいえ"
|
||||
edit: "編集"
|
||||
save: "保存"
|
||||
share: "シェア"
|
||||
|
@ -21,6 +23,8 @@ addressRequired: "アドレスを入力してください"
|
|||
createNewRoom: "部屋を作成"
|
||||
editRoom: "部屋の編集"
|
||||
comingFuture: "今後のアップデートで追加予定"
|
||||
processing: "処理中です。<br />画面を閉じないでください。"
|
||||
lostWarning: "この画面を閉じると保存前の内容が失われます。構いませんか?"
|
||||
form:
|
||||
title: "タイトル"
|
||||
titleRequired: "部屋の名前を入力してください"
|
||||
|
@ -57,7 +61,13 @@ errors:
|
|||
private: "この部屋は共同ホスト限定です。"
|
||||
default: "入室が許可されていません。"
|
||||
startListening: "視聴を始める"
|
||||
browserMuted: "ブラウザの設定により無音になっています。続行するには @:startListening ボタンを押してください。"
|
||||
browserMuted: "大きな音であなたが驚かないよう、無音になっています。続行するには @:startListening ボタンを押してください。"
|
||||
onlineIndicator:
|
||||
message: "あなたが部屋をホスト中であることを表示しますか?「表示する」を選ぶとアカウントのアバターがこのようになります。"
|
||||
hint: "部屋を閉じた後、アバターは自動で元に戻ります。"
|
||||
warning: "サーバーが変更を反映するまで時間がかかることがあります。"
|
||||
sure: "表示する"
|
||||
nope: "表示しない"
|
||||
speakRequest:
|
||||
label: "発言リクエスト"
|
||||
dialog: "発言をリクエストしますか?"
|
||||
|
|
|
@ -64,7 +64,7 @@ router.beforeEach(async (to) => {
|
|||
}
|
||||
}
|
||||
});
|
||||
router.afterEach((to, from) => {
|
||||
router.afterEach((to) => {
|
||||
const donStore = useMastodonStore();
|
||||
if (!to.meta.noauth && !donStore.authorized) {
|
||||
const query = to.name !== "home" ? { l: to.path } : {};
|
||||
|
|
|
@ -14,6 +14,7 @@ export const useMastodonStore = defineStore("mastodon", {
|
|||
},
|
||||
client: null,
|
||||
userinfo: null,
|
||||
avatar: "",
|
||||
};
|
||||
},
|
||||
getters: {
|
||||
|
@ -38,30 +39,19 @@ export const useMastodonStore = defineStore("mastodon", {
|
|||
this.userinfo = user;
|
||||
this.authorized = true;
|
||||
},
|
||||
async updateAvatar(img) {
|
||||
async updateAvatar(img, filename) {
|
||||
if (this.client === null) return;
|
||||
const avatarBlob = await (await fetch(img)).blob();
|
||||
this.userinfo = await this.client.v1.accounts.updateCredentials({
|
||||
avatar: new File([avatarBlob], `${Date.now()}.gif`),
|
||||
avatar: new File([avatarBlob], `${Date.now()}_${filename}`),
|
||||
});
|
||||
},
|
||||
async revertAvatar() {
|
||||
const t = setTimeout(async () => {
|
||||
const token = await axios.get("/api/token");
|
||||
const oldAvatar = sessionStorage.getItem("avatar_old_data");
|
||||
sessionStorage.removeItem("avatar_old_data");
|
||||
sessionStorage.removeItem("avatar_timeout");
|
||||
if (this.client === null || !oldAvatar || !token.data.audon.avatar)
|
||||
return;
|
||||
const resp = await axios.delete("/api/room");
|
||||
if (resp.status === 200) {
|
||||
const avatarBlob = await (await fetch(oldAvatar)).blob();
|
||||
this.userinfo = await this.client.v1.accounts.updateCredentials({
|
||||
avatar: new File([avatarBlob], token.data.audon.avatar),
|
||||
});
|
||||
}
|
||||
}, 1500);
|
||||
sessionStorage.setItem("avatar_timeout", t.toString());
|
||||
const token = await axios.get("/api/token");
|
||||
if (this.avatar && token.data.audon.avatar) {
|
||||
await this.updateAvatar(this.avatar, token.data.audon.avatar);
|
||||
};
|
||||
await axios.delete("/api/room");
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
@ -13,6 +13,12 @@ export default {
|
|||
query: "",
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
removeEventListener("beforeunload", (event) => {
|
||||
event.preventDefault();
|
||||
return (event.returnValue = "");
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
async onLogout() {
|
||||
// if (!confirm(this.$t("logoutConfirm"))) return;
|
||||
|
|
|
@ -88,9 +88,7 @@ export default {
|
|||
alt="Branding Wordmark"
|
||||
style="width: 100%; max-width: 200px"
|
||||
/>
|
||||
<p class="mt-2">
|
||||
Audio spaces for Mastodon
|
||||
</p>
|
||||
<p class="mt-2">Audio spaces for Mastodon</p>
|
||||
</div>
|
||||
<v-alert v-if="$route.query.l" type="warning" variant="text">
|
||||
<div>{{ $t("loginRequired") }}</div>
|
||||
|
|
|
@ -7,6 +7,7 @@ import { darkTheme } from "picmo";
|
|||
import { createPopup } from "@picmo/popup-picker";
|
||||
import { Howl } from "howler";
|
||||
import Participant from "../components/Participant.vue";
|
||||
import JoinDialog from "../components/JoinDialog.vue";
|
||||
import {
|
||||
mdiMicrophone,
|
||||
mdiMicrophoneOff,
|
||||
|
@ -28,7 +29,6 @@ import {
|
|||
DataPacket_Kind,
|
||||
AudioPresets,
|
||||
} from "livekit-client";
|
||||
import { createClient } from "masto";
|
||||
import { useVuelidate } from "@vuelidate/core";
|
||||
import { helpers, maxLength, required } from "@vuelidate/validators";
|
||||
import NoSleep from "@uriopass/nosleep.js";
|
||||
|
@ -96,6 +96,7 @@ export default {
|
|||
},
|
||||
components: {
|
||||
Participant,
|
||||
JoinDialog,
|
||||
},
|
||||
validations() {
|
||||
return {
|
||||
|
@ -116,7 +117,7 @@ export default {
|
|||
data() {
|
||||
return {
|
||||
roomID: this.$route.params.id,
|
||||
loading: true,
|
||||
loading: false,
|
||||
mainHeight: 700,
|
||||
roomInfo: {
|
||||
title: this.$t("connecting"),
|
||||
|
@ -126,6 +127,7 @@ export default {
|
|||
cohosts: [],
|
||||
speakers: [],
|
||||
created_at: null,
|
||||
accounts: {},
|
||||
},
|
||||
editingRoomInfo: {
|
||||
title: "",
|
||||
|
@ -146,12 +148,12 @@ export default {
|
|||
activeSpeakerIDs: new Set(),
|
||||
mutedSpeakerIDs: new Set(),
|
||||
micGranted: false,
|
||||
autoplayDisabled: false,
|
||||
speakRequests: new Set(),
|
||||
showRequestNotification: false,
|
||||
showRequestDialog: false,
|
||||
showRequestedNotification: false,
|
||||
isEditLoading: false,
|
||||
closeLoading: false,
|
||||
showEditDialog: false,
|
||||
timeElapsed: "",
|
||||
preview: false,
|
||||
|
@ -172,7 +174,7 @@ export default {
|
|||
this.mutedSpeakerIDs = new Set(Object.keys(this.participants));
|
||||
for (const [key, value] of Object.entries(this.participants)) {
|
||||
if (value !== null) {
|
||||
this.fetchMastoData(key, value);
|
||||
this.fetchMastoData(key);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
|
@ -198,13 +200,6 @@ export default {
|
|||
}
|
||||
}
|
||||
}
|
||||
if (!this.preview) {
|
||||
try {
|
||||
await this.joinRoom();
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
}
|
||||
setInterval(this.refreshRemoteMuteStatus, 100);
|
||||
setInterval(this.refreshTimeElapsed, 1000);
|
||||
},
|
||||
|
@ -258,219 +253,175 @@ export default {
|
|||
const delta = now.diff(createdAt);
|
||||
this.timeElapsed = delta.toFormat("hh:mm:ss");
|
||||
},
|
||||
async joinRoom() {
|
||||
if (!this.donStore.authorized) return;
|
||||
async joinRoom(token) {
|
||||
if (!this.donStore.authorized) {
|
||||
this.$router.replace({ name: "home" });
|
||||
}
|
||||
try {
|
||||
const timeout = sessionStorage.getItem("avatar_timeout");
|
||||
if (timeout) {
|
||||
const timeoutID = parseInt(timeout);
|
||||
clearTimeout(timeoutID);
|
||||
sessionStorage.removeItem("avatar_timeout");
|
||||
}
|
||||
const token = await axios.get("/api/token");
|
||||
this.donStore.oauth = token.data;
|
||||
let avatarURL = this.donStore.userinfo.avatar;
|
||||
if (this.donStore.oauth.audon?.avatar) {
|
||||
avatarURL = "";
|
||||
}
|
||||
const resp = await axios.postForm(`/api/room/${this.roomID}`, {
|
||||
avatar: avatarURL,
|
||||
});
|
||||
sessionStorage.setItem("avatar_old_data", resp.data.original);
|
||||
if (resp.data.indicator && !timeout) {
|
||||
try {
|
||||
await this.donStore.updateAvatar(resp.data.indicator);
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
this.loading = true;
|
||||
await this.connectLivekit(token);
|
||||
} catch (error) {
|
||||
alert(this.$t("errors.connectionFailed"));
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
async connectLivekit(payload) {
|
||||
const self = this;
|
||||
this.roomClient
|
||||
.on(RoomEvent.TrackSubscribed, (track) => {
|
||||
if (track.kind === Track.Kind.Audio) {
|
||||
const element = track.attach();
|
||||
self.$refs.audioDOM.appendChild(element);
|
||||
}
|
||||
}
|
||||
const room = new Room();
|
||||
const self = this;
|
||||
room
|
||||
.on(RoomEvent.TrackSubscribed, (track) => {
|
||||
if (track.kind === Track.Kind.Audio) {
|
||||
const element = track.attach();
|
||||
self.$refs.audioDOM.appendChild(element);
|
||||
}
|
||||
})
|
||||
.on(RoomEvent.TrackUnsubscribed, (track) => {
|
||||
track.detach();
|
||||
})
|
||||
.on(RoomEvent.LocalTrackPublished, () => {
|
||||
self.micGranted = true;
|
||||
})
|
||||
.on(RoomEvent.LocalTrackUnpublished, (publication) => {
|
||||
publication.track?.detach();
|
||||
})
|
||||
.on(RoomEvent.ActiveSpeakersChanged, (speakers) => {
|
||||
self.activeSpeakerIDs = new Set(map(speakers, (p) => p.identity));
|
||||
})
|
||||
.on(RoomEvent.ParticipantConnected, (participant) => {
|
||||
if (self.iamHost || self.iamCohost) self.sounds.boop.play();
|
||||
const metadata = self.addParticipant(participant);
|
||||
if (metadata !== null) {
|
||||
self.fetchMastoData(participant.identity, metadata);
|
||||
}
|
||||
})
|
||||
.on(RoomEvent.ParticipantDisconnected, (participant) => {
|
||||
self.participants = omit(self.participants, participant.identity);
|
||||
})
|
||||
.on(RoomEvent.AudioPlaybackStatusChanged, () => {
|
||||
if (!room.canPlaybackAudio) {
|
||||
self.autoplayDisabled = true;
|
||||
}
|
||||
})
|
||||
.on(RoomEvent.Disconnected, (reason) => {
|
||||
// TODO: change this from alert to a vuetify thing
|
||||
self.noSleep.disable();
|
||||
if (reason === DisconnectReason.PARTICIPANT_REMOVED) {
|
||||
alert(self.$t("roomEvent.removed"));
|
||||
self.$router.push({ name: "home" });
|
||||
} else {
|
||||
self.donStore.revertAvatar().finally(() => {
|
||||
let message = "";
|
||||
switch (reason) {
|
||||
case DisconnectReason.ROOM_DELETED:
|
||||
message = self.$t("roomEvent.closedByHost");
|
||||
break;
|
||||
case DisconnectReason.CLIENT_INITIATED:
|
||||
break;
|
||||
default:
|
||||
message = "Disconnected due to unknown reasons";
|
||||
})
|
||||
.on(RoomEvent.TrackUnsubscribed, (track) => {
|
||||
track.detach();
|
||||
})
|
||||
.on(RoomEvent.LocalTrackPublished, () => {
|
||||
self.micGranted = true;
|
||||
})
|
||||
.on(RoomEvent.LocalTrackUnpublished, (publication) => {
|
||||
publication.track?.detach();
|
||||
})
|
||||
.on(RoomEvent.ActiveSpeakersChanged, (speakers) => {
|
||||
self.activeSpeakerIDs = new Set(map(speakers, (p) => p.identity));
|
||||
})
|
||||
.on(RoomEvent.ParticipantConnected, (participant) => {
|
||||
if (self.iamHost || self.iamCohost) self.sounds.boop.play();
|
||||
const metadata = self.addParticipant(participant);
|
||||
if (metadata !== null) {
|
||||
self.fetchMastoData(participant.identity);
|
||||
}
|
||||
})
|
||||
.on(RoomEvent.ParticipantDisconnected, (participant) => {
|
||||
self.participants = omit(self.participants, participant.identity);
|
||||
})
|
||||
.on(RoomEvent.Disconnected, async (reason) => {
|
||||
// TODO: change this from alert to a vuetify thing
|
||||
self.noSleep.disable();
|
||||
if (reason === DisconnectReason.PARTICIPANT_REMOVED) {
|
||||
alert(self.$t("roomEvent.removed"));
|
||||
self.$router.push({ name: "home" });
|
||||
} else {
|
||||
let message = "";
|
||||
switch (reason) {
|
||||
case DisconnectReason.ROOM_DELETED:
|
||||
if (self.iamHost || self.iamCohost) {
|
||||
self.closeLoading = true;
|
||||
try {
|
||||
await self.donStore.revertAvatar();
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
} finally {
|
||||
self.closeLoading = false;
|
||||
}
|
||||
}
|
||||
if (message !== "") {
|
||||
alert(message);
|
||||
message = self.$t("roomEvent.closedByHost");
|
||||
break;
|
||||
case DisconnectReason.CLIENT_INITIATED:
|
||||
if (self.iamCohost) {
|
||||
self.closeLoading = true;
|
||||
try {
|
||||
await self.donStore.revertAvatar();
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
} finally {
|
||||
self.closeLoading = false;
|
||||
}
|
||||
}
|
||||
self.$router.push({ name: "home" });
|
||||
});
|
||||
break;
|
||||
default:
|
||||
message = "Disconnected due to unknown reasons";
|
||||
}
|
||||
})
|
||||
.on(RoomEvent.DataReceived, (payload, participant) => {
|
||||
try {
|
||||
/* data should be like
|
||||
if (message !== "") {
|
||||
alert(message);
|
||||
}
|
||||
self.$router.push({ name: "home" });
|
||||
}
|
||||
})
|
||||
.on(RoomEvent.DataReceived, (payload, participant) => {
|
||||
try {
|
||||
/* data should be like
|
||||
{ "kind": "speak_request" }
|
||||
{ "kind": "chat", "data": "..." }
|
||||
{ "kind": "request_declined", "audon_id": "..."}
|
||||
{ "kind": "emoji", "emoji": "..." }
|
||||
*/
|
||||
const strData = self.decoder.decode(payload);
|
||||
const jsonData = JSON.parse(strData);
|
||||
const metadata = JSON.parse(participant.metadata);
|
||||
switch (jsonData?.kind) {
|
||||
case "emoji":
|
||||
self.addEmojiReaction(participant.identity, jsonData.emoji);
|
||||
break;
|
||||
case "speak_request": // someone is wanting to be a speaker
|
||||
self.onSpeakRequestReceived(participant);
|
||||
break;
|
||||
case "request_declined":
|
||||
if (
|
||||
self.isHost(participant.identity) ||
|
||||
self.isCohost(metadata)
|
||||
) {
|
||||
self.speakRequests.delete(jsonData.audon_id);
|
||||
if (self.speakRequests.size < 1)
|
||||
self.showRequestNotification = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(
|
||||
"invalida data received from: ",
|
||||
participant.identity
|
||||
);
|
||||
const strData = self.decoder.decode(payload);
|
||||
const jsonData = JSON.parse(strData);
|
||||
const metadata = JSON.parse(participant.metadata);
|
||||
switch (jsonData?.kind) {
|
||||
case "emoji":
|
||||
self.addEmojiReaction(participant.identity, jsonData.emoji);
|
||||
break;
|
||||
case "speak_request": // someone is wanting to be a speaker
|
||||
self.onSpeakRequestReceived(participant);
|
||||
break;
|
||||
case "request_declined":
|
||||
if (
|
||||
self.isHost(participant.identity) ||
|
||||
self.isCohost(metadata)
|
||||
) {
|
||||
self.speakRequests.delete(jsonData.audon_id);
|
||||
if (self.speakRequests.size < 1)
|
||||
self.showRequestNotification = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
})
|
||||
.on(RoomEvent.RoomMetadataChanged, (metadata) => {
|
||||
self.roomInfo = JSON.parse(metadata);
|
||||
self.editingRoomInfo = clone(self.roomInfo);
|
||||
if (!self.roomInfo.speakers) return;
|
||||
for (const speakers of self.roomInfo.speakers) {
|
||||
self.speakRequests.delete(speakers.audon_id);
|
||||
if (self.speakRequests.size < 1)
|
||||
self.showRequestNotification = false;
|
||||
}
|
||||
if (self.iamSpeaker && !self.micGranted) {
|
||||
self.roomClient.localParticipant
|
||||
.setMicrophoneEnabled(true, captureOpts, publishOpts)
|
||||
.then(() => {
|
||||
self.micGranted = true;
|
||||
})
|
||||
.finally(() => {
|
||||
self.roomClient.localParticipant.setMicrophoneEnabled(false);
|
||||
});
|
||||
}
|
||||
});
|
||||
await room.connect(resp.data.url, resp.data.token);
|
||||
this.roomClient = room;
|
||||
this.roomInfo = JSON.parse(room.metadata);
|
||||
this.editingRoomInfo = clone(this.roomInfo);
|
||||
this.addParticipant(room.localParticipant);
|
||||
for (const part of room.participants.values()) {
|
||||
this.addParticipant(part);
|
||||
}
|
||||
this.mutedSpeakerIDs.add(this.donStore.oauth.audon.audon_id);
|
||||
this.activeSpeakerIDs = new Set(
|
||||
map(room.activeSpeakers, (p) => p.identity)
|
||||
);
|
||||
// cache mastodon data of current participants
|
||||
for (const [key, value] of Object.entries(this.participants)) {
|
||||
if (value !== null) {
|
||||
this.fetchMastoData(key, value);
|
||||
} catch (error) {
|
||||
console.log("invalida data received from: ", participant.identity);
|
||||
}
|
||||
}
|
||||
if (this.iamHost || this.iamCohost || this.iamSpeaker) {
|
||||
try {
|
||||
await room.localParticipant.setMicrophoneEnabled(
|
||||
true,
|
||||
captureOpts,
|
||||
publishOpts
|
||||
);
|
||||
} catch {
|
||||
alert(this.$t("microphoneBlocked"));
|
||||
} finally {
|
||||
await room.localParticipant.setMicrophoneEnabled(false);
|
||||
})
|
||||
.on(RoomEvent.RoomMetadataChanged, (metadata) => {
|
||||
self.roomInfo = JSON.parse(metadata);
|
||||
self.editingRoomInfo = clone(self.roomInfo);
|
||||
if (!self.roomInfo.speakers) return;
|
||||
for (const speakers of self.roomInfo.speakers) {
|
||||
self.speakRequests.delete(speakers.audon_id);
|
||||
if (self.speakRequests.size < 1)
|
||||
self.showRequestNotification = false;
|
||||
}
|
||||
if (self.iamSpeaker && !self.micGranted) {
|
||||
self.roomClient.localParticipant
|
||||
.setMicrophoneEnabled(true, captureOpts, publishOpts)
|
||||
.then(() => {
|
||||
self.micGranted = true;
|
||||
})
|
||||
.finally(() => {
|
||||
self.roomClient.localParticipant.setMicrophoneEnabled(false);
|
||||
});
|
||||
}
|
||||
});
|
||||
await this.roomClient.connect(payload.url, payload.token);
|
||||
this.roomInfo = JSON.parse(this.roomClient.metadata);
|
||||
this.editingRoomInfo = clone(this.roomInfo);
|
||||
this.addParticipant(this.roomClient.localParticipant);
|
||||
for (const part of this.roomClient.participants.values()) {
|
||||
this.addParticipant(part);
|
||||
}
|
||||
this.mutedSpeakerIDs.add(this.donStore.oauth.audon.audon_id);
|
||||
this.activeSpeakerIDs = new Set(
|
||||
map(this.roomClient.activeSpeakers, (p) => p.identity)
|
||||
);
|
||||
// cache mastodon data of current participants
|
||||
for (const [key, value] of Object.entries(this.participants)) {
|
||||
if (value !== null) {
|
||||
this.fetchMastoData(key);
|
||||
}
|
||||
} catch (error) {
|
||||
let message = "";
|
||||
switch (error.response?.status) {
|
||||
case 403:
|
||||
switch (error.response?.data) {
|
||||
case "following":
|
||||
message = this.$t("errors.restriction.following");
|
||||
break;
|
||||
case "follower":
|
||||
message = this.$t("errors.restriction.follower");
|
||||
break;
|
||||
case "knowing":
|
||||
message = this.$t("errors.restriction.knowing");
|
||||
break;
|
||||
case "mutual":
|
||||
message = this.$t("errors.restriction.mutual");
|
||||
break;
|
||||
case "private":
|
||||
message = this.$t("errors.restriction.private");
|
||||
break;
|
||||
default:
|
||||
message = this.$t("errors.restriction.default");
|
||||
}
|
||||
alert(message);
|
||||
break;
|
||||
case 404:
|
||||
pushNotFound(this.$route);
|
||||
break;
|
||||
case 406:
|
||||
alert(this.$t("errors.alreadyConnected"));
|
||||
break;
|
||||
case 410:
|
||||
alert(this.$t("errors.alreadyClosed"));
|
||||
break;
|
||||
default:
|
||||
alert(error);
|
||||
}
|
||||
if (this.iamHost || this.iamCohost || this.iamSpeaker) {
|
||||
try {
|
||||
await this.roomClient.localParticipant.setMicrophoneEnabled(
|
||||
true,
|
||||
captureOpts,
|
||||
publishOpts
|
||||
);
|
||||
} catch {
|
||||
alert(this.$t("microphoneBlocked"));
|
||||
} finally {
|
||||
await this.roomClient.localParticipant.setMicrophoneEnabled(false);
|
||||
}
|
||||
this.noSleep.disable();
|
||||
this.$router.push({ name: "home" });
|
||||
}
|
||||
},
|
||||
refreshRemoteMuteStatus() {
|
||||
|
@ -612,17 +563,24 @@ export default {
|
|||
}
|
||||
return metadata;
|
||||
},
|
||||
async fetchMastoData(identity, { remote_id, remote_url }) {
|
||||
if (this.cachedMastoData[identity] !== undefined) return;
|
||||
async fetchMastoData(identity) {
|
||||
if (
|
||||
this.cachedMastoData[identity] !== undefined ||
|
||||
this.roomInfo.accounts[identity] === undefined
|
||||
)
|
||||
return;
|
||||
try {
|
||||
const url = new URL(remote_url);
|
||||
const mastoClient = createClient({
|
||||
url: url.origin,
|
||||
disableVersionCheck: true,
|
||||
});
|
||||
const info = await mastoClient.v1.accounts.fetch(remote_id);
|
||||
const resp = await axios.get(`/app/user/${identity}`);
|
||||
info.avatar = `/storage/${resp.data.audon_id}/avatar/${resp.data.avatar}`;
|
||||
const account = this.roomInfo.accounts[identity];
|
||||
const info = {
|
||||
acct: account.acct,
|
||||
displayName: account.displayName,
|
||||
avatar: account.avatar,
|
||||
url: account.url,
|
||||
};
|
||||
if (resp.data.avatar) {
|
||||
info.avatar = `/storage/${resp.data.audon_id}/avatar/${resp.data.avatar}`;
|
||||
}
|
||||
this.cachedMastoData[identity] = info;
|
||||
} catch (error) {
|
||||
// FIXME: display error snackbar
|
||||
|
@ -680,15 +638,6 @@ export default {
|
|||
async onLeave() {
|
||||
await this.roomClient.disconnect();
|
||||
},
|
||||
async onStartListening() {
|
||||
try {
|
||||
await this.roomClient.startAudio();
|
||||
this.autoplayDisabled = false;
|
||||
} catch {
|
||||
alert(this.$t("errors.connectionFailed"));
|
||||
await this.roomClient.disconnect();
|
||||
}
|
||||
},
|
||||
async onEditSubmit() {
|
||||
this.editingRoomInfo.title = trim(this.editingRoomInfo.title);
|
||||
this.editingRoomInfo.description = trim(this.editingRoomInfo.description);
|
||||
|
@ -718,6 +667,20 @@ export default {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<v-overlay
|
||||
:model-value="closeLoading"
|
||||
persistent
|
||||
class="align-center justify-center"
|
||||
>
|
||||
<div class="mb-8 text-center">
|
||||
<v-progress-circular indeterminate size="40"></v-progress-circular>
|
||||
</div>
|
||||
<div>
|
||||
<v-alert variant="flat" class="text-center">
|
||||
<span v-html="$t('processing')"></span>
|
||||
</v-alert>
|
||||
</div>
|
||||
</v-overlay>
|
||||
<v-dialog v-model="showEditDialog" max-width="500" persistent>
|
||||
<v-card :loading="isEditLoading">
|
||||
<v-card-title>{{ $t("editRoom") }}</v-card-title>
|
||||
|
@ -760,23 +723,12 @@ export default {
|
|||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
<v-dialog v-model="autoplayDisabled" max-width="500" persistent>
|
||||
<v-alert color="indigo">
|
||||
<div class="mb-5">
|
||||
{{ $t("browserMuted") }}
|
||||
</div>
|
||||
<div class="text-center mb-3">
|
||||
<v-btn color="gray" @click="onStartListening">{{
|
||||
$t("startListening")
|
||||
}}</v-btn>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<v-btn variant="text" @click="roomClient.disconnect()">{{
|
||||
$t("leaveRoom")
|
||||
}}</v-btn>
|
||||
</div>
|
||||
</v-alert>
|
||||
</v-dialog>
|
||||
<JoinDialog
|
||||
v-if="!preview"
|
||||
:room-id="roomID"
|
||||
:room-client="roomClient"
|
||||
@connect.once="joinRoom"
|
||||
></JoinDialog>
|
||||
<v-dialog v-model="showRequestDialog" max-width="500">
|
||||
<v-card max-height="600" class="d-flex flex-column">
|
||||
<v-card-title>{{ $t("speakRequest.label") }}</v-card-title>
|
||||
|
|
2
auth.go
2
auth.go
|
@ -177,7 +177,7 @@ func oauthHandler(c echo.Context) (err error) {
|
|||
// return c.Redirect(http.StatusFound, "http://localhost:5173")
|
||||
}
|
||||
|
||||
func getOAuthTokenHandler(c echo.Context) (err error) {
|
||||
func getUserTokenHandler(c echo.Context) (err error) {
|
||||
data, ok := c.Get("data").(*SessionData)
|
||||
if !ok {
|
||||
return ErrInvalidSession
|
||||
|
|
|
@ -47,9 +47,6 @@ func (u *AudonUser) GetIndicator(ctx context.Context, fnew []byte) ([]byte, erro
|
|||
if err := os.WriteFile(saved, fnew, 0664); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if u.AvatarFile != "" {
|
||||
// os.Remove(u.getAvatarImagePath(u.AvatarFile))
|
||||
}
|
||||
isAvatarNew = true
|
||||
}
|
||||
|
||||
|
@ -94,7 +91,7 @@ func (u *AudonUser) createGIF(avatar image.Image) ([]byte, error) {
|
|||
baseFrame := image.NewRGBA(avatarPNG.Bounds())
|
||||
draw.Draw(baseFrame, baseFrame.Bounds(), image.Black, image.Point{}, draw.Src)
|
||||
draw.Copy(baseFrame, image.Point{}, avatarPNG, avatarPNG.Bounds(), draw.Over, nil)
|
||||
draw.Draw(baseFrame, baseFrame.Bounds(), mainConfig.LogoImageBack, image.Point{-35, -35}, draw.Over)
|
||||
draw.Draw(baseFrame, baseFrame.Bounds(), mainConfig.LogoImageBack, image.Point{-55, -105}, draw.Over)
|
||||
|
||||
anim := webpanimation.NewWebpAnimation(150, 150, 0)
|
||||
defer anim.ReleaseMemory()
|
||||
|
@ -115,7 +112,7 @@ func (u *AudonUser) createGIF(avatar image.Image) ([]byte, error) {
|
|||
}
|
||||
|
||||
mask := image.NewUniform(color.Alpha{alpha})
|
||||
draw.DrawMask(frame, frame.Bounds(), mainConfig.LogoImageFront, image.Point{-35, -35}, mask, image.Point{}, draw.Over)
|
||||
draw.DrawMask(frame, frame.Bounds(), mainConfig.LogoImageFront, image.Point{-55, -105}, mask, image.Point{}, draw.Over)
|
||||
|
||||
if err := anim.AddFrame(frame, 1000/count*i, webpConf); err != nil {
|
||||
return nil, err
|
||||
|
|
Plik binarny nie jest wyświetlany.
Przed Szerokość: | Wysokość: | Rozmiar: 5.9 KiB Po Szerokość: | Wysokość: | Rozmiar: 5.6 KiB |
Plik binarny nie jest wyświetlany.
Przed Szerokość: | Wysokość: | Rozmiar: 16 KiB Po Szerokość: | Wysokość: | Rozmiar: 11 KiB |
68
room.go
68
room.go
|
@ -299,7 +299,7 @@ func joinRoomHandler(c echo.Context) (err error) {
|
|||
}
|
||||
}
|
||||
|
||||
roomMetadata := &RoomMetadata{Room: room}
|
||||
roomMetadata := &RoomMetadata{Room: room, MastodonAccounts: make(map[string]*MastodonAccount)}
|
||||
|
||||
// Allows the user to talk if the user is a speaker
|
||||
lkRoom, _ := getRoomInLivekit(c.Request().Context(), room.RoomID) // lkRoom will be nil if it doesn't exist
|
||||
|
@ -326,21 +326,43 @@ func joinRoomHandler(c echo.Context) (err error) {
|
|||
Token: token,
|
||||
Audon: user,
|
||||
}
|
||||
if user.AvatarFile != "" {
|
||||
orig, err := os.ReadFile(user.getAvatarImagePath(user.AvatarFile))
|
||||
if err == nil && orig != nil {
|
||||
resp.Original = fmt.Sprintf("data:%s;base64,%s", mimetype.Detect(orig), base64.StdEncoding.EncodeToString(orig))
|
||||
}
|
||||
|
||||
mastoAccount := new(MastodonAccount)
|
||||
if err := c.Bind(&mastoAccount); err != nil {
|
||||
c.Logger().Error(err)
|
||||
return ErrInvalidRequestFormat
|
||||
}
|
||||
|
||||
avatarLink := c.FormValue("avatar")
|
||||
if avatarLink != "" {
|
||||
roomMetadata.MastodonAccounts[user.AudonID] = mastoAccount
|
||||
|
||||
// Get ready to change avatar if user is host or cohost
|
||||
if room.IsHost(user) || room.IsCoHost(user) {
|
||||
// Get user's stored avatar if exists
|
||||
if user.AvatarFile != "" {
|
||||
orig, err := os.ReadFile(user.getAvatarImagePath(user.AvatarFile))
|
||||
if err == nil && orig != nil {
|
||||
resp.Original = fmt.Sprintf("data:%s;base64,%s", mimetype.Detect(orig), base64.StdEncoding.EncodeToString(orig))
|
||||
} else if orig == nil {
|
||||
user.AvatarFile = ""
|
||||
}
|
||||
// icon, err := os.ReadFile(user.GetGIFAvatarPath())
|
||||
// if err == nil && icon != nil {
|
||||
// resp.Indicator = fmt.Sprintf("data:image/gif;base64,%s", base64.StdEncoding.EncodeToString(icon))
|
||||
// }
|
||||
}
|
||||
avatarLink := mastoAccount.Avatar
|
||||
if err := mainValidator.Var(&avatarLink, "required"); err != nil {
|
||||
return wrapValidationError(err)
|
||||
}
|
||||
avatarURL, err := url.Parse(avatarLink)
|
||||
if err != nil {
|
||||
c.Logger().Error(err)
|
||||
return ErrInvalidRequestFormat
|
||||
}
|
||||
|
||||
if online, err := user.InLivekit(c.Request().Context()); !online && err == nil {
|
||||
// Retrieve user's current avatar if the old one doesn't exist in Audon.
|
||||
// Skips if user is still in another room.
|
||||
if already, err := user.InLivekit(c.Request().Context()); !already && err == nil && user.AvatarFile == "" {
|
||||
// Download user's avatar
|
||||
req, err := http.NewRequest(http.MethodGet, avatarURL.String(), nil)
|
||||
if err != nil {
|
||||
|
@ -362,12 +384,14 @@ func joinRoomHandler(c echo.Context) (err error) {
|
|||
return echo.NewHTTPError(http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
// Generate indicator GIF
|
||||
indicator, err := user.GetIndicator(c.Request().Context(), fnew)
|
||||
if err != nil {
|
||||
c.Logger().Warn(err)
|
||||
c.Logger().Error(err)
|
||||
return echo.NewHTTPError(http.StatusInternalServerError)
|
||||
}
|
||||
resp.Indicator = fmt.Sprintf("data:image/gif;base64,%s", base64.StdEncoding.EncodeToString(indicator))
|
||||
resp.Original = fmt.Sprintf("data:%s;base64,%s", mimetype.Detect(fnew), base64.StdEncoding.EncodeToString(fnew))
|
||||
resp.Indicator = fmt.Sprintf("data:image/gif;base64,%s", base64.StdEncoding.EncodeToString(indicator))
|
||||
} else if err != nil {
|
||||
c.Logger().Error(err)
|
||||
}
|
||||
|
@ -392,6 +416,26 @@ func joinRoomHandler(c echo.Context) (err error) {
|
|||
c.Logger().Error(err)
|
||||
return echo.NewHTTPError(http.StatusConflict)
|
||||
}
|
||||
} else {
|
||||
currentMeta, err := getRoomMetadataFromLivekitRoom(lkRoom)
|
||||
if err != nil {
|
||||
c.Logger().Error(err)
|
||||
return echo.NewHTTPError(http.StatusInternalServerError)
|
||||
}
|
||||
currentMeta.MastodonAccounts[user.AudonID] = mastoAccount
|
||||
newMetadata, err := json.Marshal(currentMeta)
|
||||
if err != nil {
|
||||
c.Logger().Error(err)
|
||||
return echo.NewHTTPError(http.StatusInternalServerError)
|
||||
}
|
||||
_, err = lkRoomServiceClient.UpdateRoomMetadata(c.Request().Context(), &livekit.UpdateRoomMetadataRequest{
|
||||
Room: roomID,
|
||||
Metadata: string(newMetadata),
|
||||
})
|
||||
if err != nil {
|
||||
c.Logger().Error(err)
|
||||
return echo.NewHTTPError(http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
// Store user's session data in cache
|
||||
|
@ -444,7 +488,7 @@ func leaveRoomHandler(c echo.Context) error {
|
|||
c.Logger().Error(err)
|
||||
return echo.NewHTTPError(http.StatusInternalServerError)
|
||||
} else if still {
|
||||
return c.NoContent(http.StatusAccepted)
|
||||
return c.NoContent(http.StatusConflict)
|
||||
}
|
||||
if err := user.ClearUserAvatar(c.Request().Context()); err != nil {
|
||||
c.Logger().Error(err)
|
||||
|
|
|
@ -30,7 +30,8 @@ type (
|
|||
|
||||
RoomMetadata struct {
|
||||
*Room
|
||||
Speakers []*AudonUser `json:"speakers"`
|
||||
Speakers []*AudonUser `json:"speakers"`
|
||||
MastodonAccounts map[string]*MastodonAccount `json:"accounts"`
|
||||
}
|
||||
|
||||
Room struct {
|
||||
|
|
|
@ -169,7 +169,8 @@ func main() {
|
|||
e.POST("/app/webhook", livekitWebhookHandler)
|
||||
|
||||
api := e.Group("/api", authMiddleware)
|
||||
api.GET("/token", getOAuthTokenHandler)
|
||||
api.GET("/token", getUserTokenHandler)
|
||||
// api.GET("/room", getStatusHandler)
|
||||
api.POST("/room", createRoomHandler)
|
||||
api.DELETE("/room", leaveRoomHandler)
|
||||
api.POST("/room/:id", joinRoomHandler)
|
||||
|
|
54
user.go
54
user.go
|
@ -3,11 +3,37 @@ package main
|
|||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
mastodon "github.com/mattn/go-mastodon"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
)
|
||||
|
||||
type MastodonAccount struct {
|
||||
ID mastodon.ID `json:"id"`
|
||||
Username string `json:"username"`
|
||||
Acct string `json:"acct"`
|
||||
DisplayName string `json:"displayName"`
|
||||
Locked bool `json:"locked"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
FollowersCount int64 `json:"followersCount"`
|
||||
FollowingCount int64 `json:"followingCount"`
|
||||
StatusesCount int64 `json:"statusesCount"`
|
||||
Note string `json:"note"`
|
||||
URL string `json:"url"`
|
||||
Avatar string `json:"avatar"`
|
||||
AvatarStatic string `json:"avatarStatic"`
|
||||
Header string `json:"header"`
|
||||
HeaderStatic string `json:"headerStatic"`
|
||||
Emojis []mastodon.Emoji `json:"emojis"`
|
||||
Moved *MastodonAccount `json:"moved"`
|
||||
Fields []mastodon.Field `json:"fields"`
|
||||
Bot bool `json:"bot"`
|
||||
Discoverable bool `json:"discoverable"`
|
||||
Source *mastodon.AccountSource `json:"source"`
|
||||
}
|
||||
|
||||
func getUserHandler(c echo.Context) error {
|
||||
audonID := c.Param("id")
|
||||
if err := mainValidator.Var(&audonID, "required,printascii"); err != nil {
|
||||
|
@ -22,6 +48,18 @@ func getUserHandler(c echo.Context) error {
|
|||
return c.JSON(http.StatusOK, user)
|
||||
}
|
||||
|
||||
func getStatusHandler(c echo.Context) error {
|
||||
u := c.Get("user").(*AudonUser)
|
||||
|
||||
ids, err := u.GetCurrentRoomIDs(c.Request().Context())
|
||||
if err != nil {
|
||||
c.Logger().Error(err)
|
||||
return echo.NewHTTPError(http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
return c.JSON(http.StatusOK, ids)
|
||||
}
|
||||
|
||||
func (a *AudonUser) Equal(u *AudonUser) bool {
|
||||
if a == nil {
|
||||
return false
|
||||
|
@ -40,12 +78,26 @@ func (a *AudonUser) InLivekit(ctx context.Context) (bool, error) {
|
|||
}
|
||||
|
||||
func (a *AudonUser) ClearUserAvatar(ctx context.Context) error {
|
||||
// os.Remove(a.getAvatarImagePath(a.AvatarFile))
|
||||
coll := mainDB.Collection(COLLECTION_USER)
|
||||
_, err := coll.UpdateOne(ctx,
|
||||
bson.D{{Key: "audon_id", Value: a.AudonID}},
|
||||
bson.D{
|
||||
{Key: "$set", Value: bson.D{{Key: "avatar", Value: ""}}},
|
||||
})
|
||||
// if err == nil {
|
||||
// os.Remove(a.getAvatarImagePath(a.AvatarFile))
|
||||
// }
|
||||
return err
|
||||
}
|
||||
|
||||
func (a *AudonUser) GetCurrentRoomIDs(ctx context.Context) ([]string, error) {
|
||||
rooms, err := a.GetCurrentLivekitRooms(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
roomIDs := make([]string, len(rooms))
|
||||
for i, r := range rooms {
|
||||
roomIDs[i] = r.GetName()
|
||||
}
|
||||
return roomIDs, nil
|
||||
}
|
||||
|
|
53
webhooks.go
53
webhooks.go
|
@ -25,7 +25,8 @@ func livekitWebhookHandler(c echo.Context) error {
|
|||
}
|
||||
|
||||
if event.GetEvent() == webhook.EventRoomFinished {
|
||||
room, err := findRoomByID(c.Request().Context(), event.GetRoom().GetName())
|
||||
lkRoom := event.GetRoom()
|
||||
room, err := findRoomByID(c.Request().Context(), lkRoom.GetName())
|
||||
if err != nil {
|
||||
c.Logger().Error(err)
|
||||
return echo.NewHTTPError(http.StatusNotFound)
|
||||
|
@ -66,34 +67,36 @@ func livekitWebhookHandler(c echo.Context) error {
|
|||
countdown := time.NewTimer(10 * time.Second)
|
||||
webhookTimerCache.Set(audonID, countdown, ttlcache.DefaultTTL)
|
||||
|
||||
<-countdown.C
|
||||
webhookTimerCache.Delete(audonID)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
||||
defer cancel()
|
||||
// ctx := context.TODO()
|
||||
go func() {
|
||||
<-countdown.C
|
||||
webhookTimerCache.Delete(audonID)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
||||
defer cancel()
|
||||
|
||||
stillAgain, err := user.InLivekit(ctx)
|
||||
if stillAgain || err != nil {
|
||||
return c.NoContent(http.StatusOK)
|
||||
}
|
||||
user, err = findUserByID(ctx, audonID)
|
||||
if err == nil && user.AvatarFile != "" {
|
||||
log.Printf("restoring avatar: %s\n", audonID)
|
||||
stillAgain, err := user.InLivekit(ctx)
|
||||
if err != nil {
|
||||
c.Logger().Error(err)
|
||||
return echo.NewHTTPError(http.StatusInternalServerError)
|
||||
log.Println(err)
|
||||
}
|
||||
avatar := user.getAvatarImagePath(user.AvatarFile)
|
||||
_, err = updateAvatar(ctx, mastoClient, avatar)
|
||||
if err != nil {
|
||||
c.Logger().Warn(err)
|
||||
if stillAgain {
|
||||
return
|
||||
}
|
||||
user.ClearUserAvatar(ctx)
|
||||
// os.Remove(avatar)
|
||||
} else if err != nil {
|
||||
c.Logger().Error(err)
|
||||
return echo.NewHTTPError(http.StatusInternalServerError)
|
||||
}
|
||||
nextUser, err := findUserByID(ctx, audonID)
|
||||
if err == nil && nextUser.AvatarFile != "" {
|
||||
log.Printf("restoring avatar: %s\n", audonID)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
avatar := nextUser.getAvatarImagePath(nextUser.AvatarFile)
|
||||
_, err = updateAvatar(ctx, mastoClient, avatar)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
nextUser.ClearUserAvatar(ctx)
|
||||
} else if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
return c.NoContent(http.StatusOK)
|
||||
} else if event.GetEvent() == webhook.EventRoomStarted {
|
||||
|
|
Ładowanie…
Reference in New Issue