Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions homeassistant/components/stream/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
to always keep workers active.
"""
import logging
import re
import secrets
import threading
import time
Expand All @@ -38,6 +39,8 @@

_LOGGER = logging.getLogger(__name__)

STREAM_SOURCE_RE = re.compile("//(.*):(.*)@")


def create_stream(hass, stream_source, options=None):
"""Create a stream with the specified identfier based on the source url.
Expand Down Expand Up @@ -173,7 +176,9 @@ def start(self):
target=self._run_worker,
)
self._thread.start()
_LOGGER.info("Started stream: %s", self.source)
_LOGGER.info(
"Started stream: %s", STREAM_SOURCE_RE.sub("//", str(self.source))
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to show that information has been redacted?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could do that, but I don't think that the fact that it is redacted has any importance. Having the username + password or maybe just the username would add more information, but aside from that just having the rest of the url is enough to identify the camera which is probably the point of the log message.

)

def update_source(self, new_source):
"""Restart the stream with a new stream source."""
Expand Down Expand Up @@ -239,7 +244,9 @@ def _stop(self):
self._thread_quit.set()
self._thread.join()
self._thread = None
_LOGGER.info("Stopped stream: %s", self.source)
_LOGGER.info(
"Stopped stream: %s", STREAM_SOURCE_RE.sub("//", str(self.source))
)

async def async_record(self, video_path, duration=30, lookback=5):
"""Make a .mp4 recording from a provided stream."""
Expand Down
5 changes: 4 additions & 1 deletion homeassistant/components/stream/worker.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import av

from . import STREAM_SOURCE_RE
from .const import (
AUDIO_CODECS,
MAX_MISSING_DTS,
Expand Down Expand Up @@ -127,7 +128,9 @@ def stream_worker(source, options, segment_buffer, quit_event):
try:
container = av.open(source, options=options, timeout=STREAM_TIMEOUT)
except av.AVError:
_LOGGER.error("Error opening stream %s", source)
_LOGGER.error(
"Error opening stream %s", STREAM_SOURCE_RE.sub("//", str(source))
)
return
try:
video_stream = container.streams.video[0]
Expand Down
10 changes: 10 additions & 0 deletions tests/components/stream/test_recorder.py
Original file line number Diff line number Diff line change
Expand Up @@ -257,3 +257,13 @@ async def test_record_stream_audio(
# Verify that the save worker was invoked, then block until its
# thread completes and is shutdown completely to avoid thread leaks.
await record_worker_sync.join()


async def test_recorder_log(hass, caplog):
"""Test starting a stream to record logs the url without username and password."""
await async_setup_component(hass, "stream", {"stream": {}})
stream = create_stream(hass, "https://abcd:efgh@foo.bar")
with patch.object(hass.config, "is_allowed_path", return_value=True):
await stream.async_record("/example/path")
assert "https://abcd:efgh@foo.bar" not in caplog.text
assert "https://foo.bar" in caplog.text
15 changes: 15 additions & 0 deletions tests/components/stream/test_worker.py
Original file line number Diff line number Diff line change
Expand Up @@ -574,3 +574,18 @@ def blocking_open(stream_source, *args, **kwargs):

# Ccleanup
stream.stop()


async def test_worker_log(hass, caplog):
"""Test that the worker logs the url without username and password."""
stream = Stream(hass, "https://abcd:efgh@foo.bar")
stream.add_provider(STREAM_OUTPUT_FORMAT)
with patch("av.open") as av_open:
av_open.side_effect = av.error.InvalidDataError(-2, "error")
segment_buffer = SegmentBuffer(stream.outputs)
stream_worker(
"https://abcd:efgh@foo.bar", {}, segment_buffer, threading.Event()
)
await hass.async_block_till_done()
assert "https://abcd:efgh@foo.bar" not in caplog.text
assert "https://foo.bar" in caplog.text