diff --git a/frigate/api/camera.py b/frigate/api/camera.py index 936a0bb09..c26469ba3 100644 --- a/frigate/api/camera.py +++ b/frigate/api/camera.py @@ -848,9 +848,10 @@ async def onvif_probe( try: if isinstance(uri, str) and uri.startswith("rtsp://"): if username and password and "@" not in uri: - # Inject URL-encoded credentials and add only the - # authenticated version. - cred = f"{quote_plus(username)}:{quote_plus(password)}@" + # Inject raw credentials and add only the + # authenticated version. The credentials will be encoded + # later by ffprobe_stream or the config system. + cred = f"{username}:{password}@" injected = uri.replace( "rtsp://", f"rtsp://{cred}", 1 ) @@ -903,9 +904,9 @@ async def onvif_probe( "/cam/realmonitor?channel=1&subtype=0", "/11", ] - # Use URL-encoded credentials for pattern fallback URIs when provided + # Use raw credentials for pattern fallback URIs when provided auth_str = ( - f"{quote_plus(username)}:{quote_plus(password)}@" + f"{username}:{password}@" if username and password else "" ) @@ -930,7 +931,7 @@ async def onvif_probe( and uri.startswith("rtsp://") and "@" not in uri ): - cred = f"{quote_plus(username)}:{quote_plus(password)}@" + cred = f"{username}:{password}@" cred_uri = uri.replace("rtsp://", f"rtsp://{cred}", 1) if cred_uri not in to_test: to_test.append(cred_uri) diff --git a/web/src/utils/cameraUtil.ts b/web/src/utils/cameraUtil.ts index 4802c5e5f..543605ad0 100644 --- a/web/src/utils/cameraUtil.ts +++ b/web/src/utils/cameraUtil.ts @@ -81,7 +81,8 @@ export async function detectReolinkCamera( export function maskUri(uri: string): string { try { // Handle RTSP URLs with user:pass@host format - const rtspMatch = uri.match(/rtsp:\/\/([^:]+):([^@]+)@(.+)/); + // Use greedy match for password to handle passwords with @ + const rtspMatch = uri.match(/rtsp:\/\/([^:]+):(.+)@(.+)/); if (rtspMatch) { return `rtsp://${rtspMatch[1]}:${"*".repeat(4)}@${rtspMatch[3]}`; }