mirror of
https://github.com/HumanAIGC-Engineering/gradio-webrtc.git
synced 2026-02-05 18:09:23 +08:00
Set border radius (#84)
Co-authored-by: Freddy Boulton <freddyboulton@hf-freddy.local>
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -17,4 +17,5 @@ demo/scratch
|
|||||||
.vscode
|
.vscode
|
||||||
.DS_Store
|
.DS_Store
|
||||||
test/
|
test/
|
||||||
.venv*
|
.venv*
|
||||||
|
.env
|
||||||
@@ -42,6 +42,8 @@ class UIArgs(TypedDict):
|
|||||||
"""Color of the icon button. Default is var(--color-accent) of the demo theme."""
|
"""Color of the icon button. Default is var(--color-accent) of the demo theme."""
|
||||||
pulse_color: NotRequired[str]
|
pulse_color: NotRequired[str]
|
||||||
"""Color of the pulse animation. Default is var(--color-accent) of the demo theme."""
|
"""Color of the pulse animation. Default is var(--color-accent) of the demo theme."""
|
||||||
|
icon_radius: NotRequired[int]
|
||||||
|
"""Border radius of the icon button expressed as a percentage of the button size. Default is 50%."""
|
||||||
|
|
||||||
|
|
||||||
class Stream(WebRTCConnectionMixin):
|
class Stream(WebRTCConnectionMixin):
|
||||||
@@ -322,6 +324,7 @@ class Stream(WebRTCConnectionMixin):
|
|||||||
icon=ui_args.get("icon"),
|
icon=ui_args.get("icon"),
|
||||||
icon_button_color=ui_args.get("icon_button_color"),
|
icon_button_color=ui_args.get("icon_button_color"),
|
||||||
pulse_color=ui_args.get("pulse_color"),
|
pulse_color=ui_args.get("pulse_color"),
|
||||||
|
icon_radius=ui_args.get("icon_radius"),
|
||||||
)
|
)
|
||||||
for component in additional_output_components:
|
for component in additional_output_components:
|
||||||
if component not in same_components:
|
if component not in same_components:
|
||||||
@@ -358,6 +361,10 @@ class Stream(WebRTCConnectionMixin):
|
|||||||
rtc_configuration=self.rtc_configuration,
|
rtc_configuration=self.rtc_configuration,
|
||||||
mode="send-receive",
|
mode="send-receive",
|
||||||
modality="audio",
|
modality="audio",
|
||||||
|
icon=ui_args.get("icon"),
|
||||||
|
icon_button_color=ui_args.get("icon_button_color"),
|
||||||
|
pulse_color=ui_args.get("pulse_color"),
|
||||||
|
icon_radius=ui_args.get("icon_radius"),
|
||||||
)
|
)
|
||||||
for component in additional_input_components:
|
for component in additional_input_components:
|
||||||
if component not in same_components:
|
if component not in same_components:
|
||||||
@@ -400,6 +407,7 @@ class Stream(WebRTCConnectionMixin):
|
|||||||
icon=ui_args.get("icon"),
|
icon=ui_args.get("icon"),
|
||||||
icon_button_color=ui_args.get("icon_button_color"),
|
icon_button_color=ui_args.get("icon_button_color"),
|
||||||
pulse_color=ui_args.get("pulse_color"),
|
pulse_color=ui_args.get("pulse_color"),
|
||||||
|
icon_radius=ui_args.get("icon_radius"),
|
||||||
)
|
)
|
||||||
for component in additional_input_components:
|
for component in additional_input_components:
|
||||||
if component not in same_components:
|
if component not in same_components:
|
||||||
@@ -443,6 +451,7 @@ class Stream(WebRTCConnectionMixin):
|
|||||||
icon=ui_args.get("icon"),
|
icon=ui_args.get("icon"),
|
||||||
icon_button_color=ui_args.get("icon_button_color"),
|
icon_button_color=ui_args.get("icon_button_color"),
|
||||||
pulse_color=ui_args.get("pulse_color"),
|
pulse_color=ui_args.get("pulse_color"),
|
||||||
|
icon_radius=ui_args.get("icon_radius"),
|
||||||
)
|
)
|
||||||
for component in additional_input_components:
|
for component in additional_input_components:
|
||||||
if component not in same_components:
|
if component not in same_components:
|
||||||
@@ -552,6 +561,7 @@ class Stream(WebRTCConnectionMixin):
|
|||||||
port: int = 8000,
|
port: int = 8000,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
):
|
):
|
||||||
|
import atexit
|
||||||
import secrets
|
import secrets
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
@@ -563,7 +573,6 @@ class Stream(WebRTCConnectionMixin):
|
|||||||
from gradio.networking import setup_tunnel
|
from gradio.networking import setup_tunnel
|
||||||
from gradio.tunneling import CURRENT_TUNNELS
|
from gradio.tunneling import CURRENT_TUNNELS
|
||||||
from huggingface_hub import get_token
|
from huggingface_hub import get_token
|
||||||
import atexit
|
|
||||||
|
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
import fractions
|
import fractions
|
||||||
|
import functools
|
||||||
|
import inspect
|
||||||
import io
|
import io
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import tempfile
|
import tempfile
|
||||||
|
import traceback
|
||||||
from contextvars import ContextVar
|
from contextvars import ContextVar
|
||||||
from typing import Any, Callable, Literal, Protocol, TypedDict, cast
|
from typing import Any, Callable, Literal, Protocol, TypedDict, cast
|
||||||
import functools
|
|
||||||
import traceback
|
|
||||||
import inspect
|
|
||||||
import av
|
import av
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from numpy.typing import NDArray
|
from numpy.typing import NDArray
|
||||||
|
|||||||
@@ -87,6 +87,7 @@ class WebRTC(Component, WebRTCConnectionMixin):
|
|||||||
icon: str | None = None,
|
icon: str | None = None,
|
||||||
icon_button_color: str | None = None,
|
icon_button_color: str | None = None,
|
||||||
pulse_color: str | None = None,
|
pulse_color: str | None = None,
|
||||||
|
icon_radius: int | None = None,
|
||||||
button_labels: dict | None = None,
|
button_labels: dict | None = None,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
@@ -119,6 +120,7 @@ class WebRTC(Component, WebRTCConnectionMixin):
|
|||||||
icon_button_color: Color of the icon button. Default is var(--color-accent) of the demo theme.
|
icon_button_color: Color of the icon button. Default is var(--color-accent) of the demo theme.
|
||||||
pulse_color: Color of the pulse animation. Default is var(--color-accent) of the demo theme.
|
pulse_color: Color of the pulse animation. Default is var(--color-accent) of the demo theme.
|
||||||
button_labels: Text to display on the audio or video start, stop, waiting buttons. Dict with keys "start", "stop", "waiting" mapping to the text to display on the buttons.
|
button_labels: Text to display on the audio or video start, stop, waiting buttons. Dict with keys "start", "stop", "waiting" mapping to the text to display on the buttons.
|
||||||
|
icon_radius: Border radius of the icon button expressed as a percentage of the button size. Default is 50%
|
||||||
"""
|
"""
|
||||||
self.time_limit = time_limit
|
self.time_limit = time_limit
|
||||||
self.height = height
|
self.height = height
|
||||||
@@ -129,6 +131,7 @@ class WebRTC(Component, WebRTCConnectionMixin):
|
|||||||
self.mode = mode
|
self.mode = mode
|
||||||
self.modality = modality
|
self.modality = modality
|
||||||
self.icon_button_color = icon_button_color
|
self.icon_button_color = icon_button_color
|
||||||
|
self.icon_radius = icon_radius
|
||||||
self.pulse_color = pulse_color
|
self.pulse_color = pulse_color
|
||||||
self.rtp_params = rtp_params or {}
|
self.rtp_params = rtp_params or {}
|
||||||
self.button_labels = {
|
self.button_labels = {
|
||||||
|
|||||||
@@ -149,14 +149,14 @@ class WebRTCConnectionMixin:
|
|||||||
|
|
||||||
if isinstance(self.event_handler, StreamHandlerBase):
|
if isinstance(self.event_handler, StreamHandlerBase):
|
||||||
handler = self.event_handler.copy()
|
handler = self.event_handler.copy()
|
||||||
handler.emit = webrtc_error_handler(handler.emit)
|
handler.emit = webrtc_error_handler(handler.emit) # type: ignore
|
||||||
handler.receive = webrtc_error_handler(handler.receive)
|
handler.receive = webrtc_error_handler(handler.receive) # type: ignore
|
||||||
handler.start_up = webrtc_error_handler(handler.start_up)
|
handler.start_up = webrtc_error_handler(handler.start_up) # type: ignore
|
||||||
handler.shutdown = webrtc_error_handler(handler.shutdown)
|
handler.shutdown = webrtc_error_handler(handler.shutdown) # type: ignore
|
||||||
if hasattr(handler, "video_receive"):
|
if hasattr(handler, "video_receive"):
|
||||||
handler.video_receive = webrtc_error_handler(handler.video_receive)
|
handler.video_receive = webrtc_error_handler(handler.video_receive) # type: ignore
|
||||||
if hasattr(handler, "video_emit"):
|
if hasattr(handler, "video_emit"):
|
||||||
handler.video_emit = webrtc_error_handler(handler.video_emit)
|
handler.video_emit = webrtc_error_handler(handler.video_emit) # type: ignore
|
||||||
else:
|
else:
|
||||||
handler = webrtc_error_handler(cast(Callable, self.event_handler))
|
handler = webrtc_error_handler(cast(Callable, self.event_handler))
|
||||||
|
|
||||||
|
|||||||
@@ -5,16 +5,16 @@ import time
|
|||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
import gradio as gr
|
import gradio as gr
|
||||||
from gradio.utils import get_space
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from google import genai
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from fastrtc import (
|
from fastrtc import (
|
||||||
AsyncAudioVideoStreamHandler,
|
AsyncAudioVideoStreamHandler,
|
||||||
Stream,
|
Stream,
|
||||||
get_twilio_turn_credentials,
|
|
||||||
WebRTC,
|
WebRTC,
|
||||||
|
get_twilio_turn_credentials,
|
||||||
)
|
)
|
||||||
|
from google import genai
|
||||||
|
from gradio.utils import get_space
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
load_dotenv()
|
load_dotenv()
|
||||||
|
|||||||
@@ -38,6 +38,7 @@
|
|||||||
export let icon: string | undefined = undefined;
|
export let icon: string | undefined = undefined;
|
||||||
export let icon_button_color: string = "var(--color-accent)";
|
export let icon_button_color: string = "var(--color-accent)";
|
||||||
export let pulse_color: string = "var(--color-accent)";
|
export let pulse_color: string = "var(--color-accent)";
|
||||||
|
export let icon_radius: number = 50;
|
||||||
|
|
||||||
const on_change_cb = (msg: "change" | "tick" | any) => {
|
const on_change_cb = (msg: "change" | "tick" | any) => {
|
||||||
if (
|
if (
|
||||||
@@ -124,6 +125,7 @@
|
|||||||
{icon}
|
{icon}
|
||||||
{icon_button_color}
|
{icon_button_color}
|
||||||
{pulse_color}
|
{pulse_color}
|
||||||
|
{icon_radius}
|
||||||
i18n={gradio.i18n}
|
i18n={gradio.i18n}
|
||||||
on:tick={() => gradio.dispatch("tick")}
|
on:tick={() => gradio.dispatch("tick")}
|
||||||
on:error={({ detail }) => gradio.dispatch("error", detail)}
|
on:error={({ detail }) => gradio.dispatch("error", detail)}
|
||||||
@@ -178,6 +180,7 @@
|
|||||||
{icon}
|
{icon}
|
||||||
{reject_cb}
|
{reject_cb}
|
||||||
{icon_button_color}
|
{icon_button_color}
|
||||||
|
{icon_radius}
|
||||||
{pulse_color}
|
{pulse_color}
|
||||||
{button_labels}
|
{button_labels}
|
||||||
on:tick={() => gradio.dispatch("tick")}
|
on:tick={() => gradio.dispatch("tick")}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
export let icon_button_color: string = "var(--color-accent)";
|
export let icon_button_color: string = "var(--color-accent)";
|
||||||
export let pulse_color: string = "var(--color-accent)";
|
export let pulse_color: string = "var(--color-accent)";
|
||||||
export let pending: boolean = false;
|
export let pending: boolean = false;
|
||||||
|
export let icon_radius: number = 50;
|
||||||
|
|
||||||
let audioContext: AudioContext;
|
let audioContext: AudioContext;
|
||||||
let analyser: AnalyserNode;
|
let analyser: AnalyserNode;
|
||||||
@@ -34,6 +35,7 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
function setupAudioContext() {
|
function setupAudioContext() {
|
||||||
|
// @ts-ignore
|
||||||
audioContext = new (window.AudioContext || window.webkitAudioContext)();
|
audioContext = new (window.AudioContext || window.webkitAudioContext)();
|
||||||
analyser = audioContext.createAnalyser();
|
analyser = audioContext.createAnalyser();
|
||||||
const source = audioContext.createMediaStreamSource(
|
const source = audioContext.createMediaStreamSource(
|
||||||
@@ -58,6 +60,7 @@
|
|||||||
);
|
);
|
||||||
for (let i = 0; i < bars.length; i++) {
|
for (let i = 0; i < bars.length; i++) {
|
||||||
const barHeight = (dataArray[i] / 255) * 2;
|
const barHeight = (dataArray[i] / 255) * 2;
|
||||||
|
// @ts-ignore
|
||||||
bars[i].style.transform = `scaleY(${Math.max(0.1, barHeight)})`;
|
bars[i].style.transform = `scaleY(${Math.max(0.1, barHeight)})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,6 +81,7 @@
|
|||||||
{pulse_color}
|
{pulse_color}
|
||||||
{icon}
|
{icon}
|
||||||
{icon_button_color}
|
{icon_button_color}
|
||||||
|
{icon_radius}
|
||||||
{audio_source_callback}
|
{audio_source_callback}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -33,6 +33,7 @@
|
|||||||
export let icon: string | undefined = undefined;
|
export let icon: string | undefined = undefined;
|
||||||
export let icon_button_color: string = "var(--color-accent)";
|
export let icon_button_color: string = "var(--color-accent)";
|
||||||
export let pulse_color: string = "var(--color-accent)";
|
export let pulse_color: string = "var(--color-accent)";
|
||||||
|
export let icon_radius: number = 50;
|
||||||
export let button_labels: { start: string; stop: string; waiting: string };
|
export let button_labels: { start: string; stop: string; waiting: string };
|
||||||
let pending = false;
|
let pending = false;
|
||||||
|
|
||||||
@@ -291,6 +292,7 @@
|
|||||||
{icon_button_color}
|
{icon_button_color}
|
||||||
{pulse_color}
|
{pulse_color}
|
||||||
{pending}
|
{pending}
|
||||||
|
{icon_radius}
|
||||||
/>
|
/>
|
||||||
<StreamingBar time_limit={_time_limit} />
|
<StreamingBar time_limit={_time_limit} />
|
||||||
<div class="button-wrap" class:pulse={stopword_recognized}>
|
<div class="button-wrap" class:pulse={stopword_recognized}>
|
||||||
|
|||||||
@@ -63,6 +63,7 @@
|
|||||||
{icon_button_color}
|
{icon_button_color}
|
||||||
{pulse_color}
|
{pulse_color}
|
||||||
{button_labels}
|
{button_labels}
|
||||||
|
{icon_radius}
|
||||||
on:error
|
on:error
|
||||||
on:start_recording
|
on:start_recording
|
||||||
on:stop_recording
|
on:stop_recording
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
export let icon: string | ComponentType = undefined;
|
export let icon: string | ComponentType = undefined;
|
||||||
export let icon_button_color: string = "var(--color-accent)";
|
export let icon_button_color: string = "var(--color-accent)";
|
||||||
export let pulse_color: string = "var(--color-accent)";
|
export let pulse_color: string = "var(--color-accent)";
|
||||||
|
export let icon_radius: number = 50;
|
||||||
|
|
||||||
let audioContext: AudioContext;
|
let audioContext: AudioContext;
|
||||||
let analyser: AnalyserNode;
|
let analyser: AnalyserNode;
|
||||||
@@ -27,6 +28,7 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
function setupAudioContext() {
|
function setupAudioContext() {
|
||||||
|
// @ts-ignore
|
||||||
audioContext = new (window.AudioContext || window.webkitAudioContext)();
|
audioContext = new (window.AudioContext || window.webkitAudioContext)();
|
||||||
analyser = audioContext.createAnalyser();
|
analyser = audioContext.createAnalyser();
|
||||||
const source = audioContext.createMediaStreamSource(
|
const source = audioContext.createMediaStreamSource(
|
||||||
@@ -77,7 +79,12 @@
|
|||||||
style:background={icon_button_color}
|
style:background={icon_button_color}
|
||||||
>
|
>
|
||||||
{#if typeof icon === "string"}
|
{#if typeof icon === "string"}
|
||||||
<img src={icon} alt="Audio visualization icon" class="icon-image" />
|
<img
|
||||||
|
src={icon}
|
||||||
|
alt="Audio visualization icon"
|
||||||
|
class="icon-image"
|
||||||
|
style:border-radius={`${icon_radius}%`}
|
||||||
|
/>
|
||||||
{:else if icon === undefined}
|
{:else if icon === undefined}
|
||||||
<div></div>
|
<div></div>
|
||||||
{:else}
|
{:else}
|
||||||
@@ -123,7 +130,6 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
border-radius: 50%;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.pulse-ring {
|
.pulse-ring {
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
export let icon: string | undefined = undefined;
|
export let icon: string | undefined = undefined;
|
||||||
export let icon_button_color: string = "var(--color-accent)";
|
export let icon_button_color: string = "var(--color-accent)";
|
||||||
export let pulse_color: string = "var(--color-accent)";
|
export let pulse_color: string = "var(--color-accent)";
|
||||||
|
export let icon_radius: number = 50;
|
||||||
|
|
||||||
export let server: {
|
export let server: {
|
||||||
offer: (body: any) => Promise<any>;
|
offer: (body: any) => Promise<any>;
|
||||||
@@ -65,6 +66,7 @@
|
|||||||
});
|
});
|
||||||
let stream = null;
|
let stream = null;
|
||||||
const timeoutId = setTimeout(() => {
|
const timeoutId = setTimeout(() => {
|
||||||
|
// @ts-ignore
|
||||||
on_change_cb({ type: "connection_timeout" });
|
on_change_cb({ type: "connection_timeout" });
|
||||||
}, 5000);
|
}, 5000);
|
||||||
|
|
||||||
@@ -120,6 +122,7 @@
|
|||||||
{icon}
|
{icon}
|
||||||
{icon_button_color}
|
{icon_button_color}
|
||||||
{pulse_color}
|
{pulse_color}
|
||||||
|
{icon_radius}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ build-backend = "hatchling.build"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "fastrtc"
|
name = "fastrtc"
|
||||||
version = "0.0.5.post2"
|
version = "0.0.6"
|
||||||
description = "The realtime communication library for Python"
|
description = "The realtime communication library for Python"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
license = "apache-2.0"
|
license = "apache-2.0"
|
||||||
@@ -83,3 +83,4 @@ packages = ["/backend/fastrtc"]
|
|||||||
|
|
||||||
[tool.ruff]
|
[tool.ruff]
|
||||||
target-version = "py310"
|
target-version = "py310"
|
||||||
|
extend-exclude = ["demo/phonic_chat"]
|
||||||
|
|||||||
Reference in New Issue
Block a user