Skip to content
This repository was archived by the owner on Nov 20, 2024. It is now read-only.
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
7 changes: 6 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,29 @@
"python.languageServer": "Pylance",
"python.sortImports.path": "isort",
"cSpell.words": [
"aiohttp",
"asyncio",
"autouse",
"coro",
"Docstrings",
"dotenv",
"gazedata",
"isort",
"keepalive",
"pylib",
"pyright",
"pytest",
"PYTHONDEVMODE",
"rtsp",
"scenecamera",
"scenevideo",
"setuptools",
"subprotocol",
"subprotocols",
"tobii",
"unsubscription",
"websockets",
"zeroconf"
],
],
"cSpell.language": "en,sv-SE",
}
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ dependencies = [
"websockets ~= 10.3",
"zeroconf ~= 0.38.7",
"aiortsp @ git+https://github.com/m4reko/aiortsp@master",
"av ~= 9.2.0"
"av ~= 9.2.0",
"aiohttp ~= 3.8.1"
]
dynamic = ["version", "description"]

Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ websockets ~= 10.3
zeroconf ~= 0.38.7
aiortsp @ git+https://github.com/m4reko/aiortsp@master
av ~= 9.2.0
aiohttp ~= 3.8.1
2 changes: 2 additions & 0 deletions src/g3pylib/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
class InvalidResponseError(Exception):
"""Raised when the server responds with an invalid message."""
38 changes: 38 additions & 0 deletions src/g3pylib/recordings/recording.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import json
import logging
from datetime import datetime, timedelta
from typing import List, Optional, cast

import aiohttp

from g3pylib._utils import APIComponent, EndpointKind
from g3pylib.exceptions import InvalidResponseError
from g3pylib.g3typing import URI
from g3pylib.websocket import G3WebSocketClientProtocol

Expand All @@ -12,6 +17,7 @@ def __init__(
):
self._connection = connection
self._uuid = uuid
self.logger: logging.Logger = logging.getLogger(__name__)
super().__init__(URI(f"{api_base_uri}/{uuid}"))

async def get_created(self) -> datetime:
Expand Down Expand Up @@ -159,3 +165,35 @@ async def move(self, folder: str) -> bool:
def uuid(self) -> str:
"""The uuid of the recording."""
return self._uuid

async def get_scenevideo_url(self) -> str:
"""Returns a URL to the recording's video file."""
host_address = self._connection.remote_address[0]
data_url = f"http://{host_address}{await self.get_http_path()}"
async with aiohttp.ClientSession() as session:
async with session.get(data_url) as response:
data = json.loads(await response.text())
try:
scenevideo_file_name = data["scenecamera"]["file"]
except KeyError:
self.logger.warning(
f"Could not retrieve file name for recording from recording data collected from {data_url}."
)
raise InvalidResponseError
return f"{data_url}/{scenevideo_file_name}"

async def get_gazedata_url(self) -> str:
"""Returns a URL to the recording's decompressed gaze data file."""
host_address = self._connection.remote_address[0]
data_url = f"http://{host_address}{await self.get_http_path()}"
async with aiohttp.ClientSession() as session:
async with session.get(data_url) as response:
data = json.loads(await response.text())
try:
gaze_file_name = data["gaze"]["file"]
except KeyError:
self.logger.warning(
f"Could not retrieve file name for gaze data from recording data collected from {data_url}."
)
raise InvalidResponseError
return f"{data_url}/{gaze_file_name}?use-content-encoding=true"
8 changes: 2 additions & 6 deletions src/g3pylib/websocket/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from websockets.typing import Subprotocol

from g3pylib import _utils
from g3pylib.exceptions import InvalidResponseError
from g3pylib.g3typing import (
URI,
JSONDict,
Expand All @@ -23,12 +24,7 @@
SignalId,
SubscriptionId,
)
from g3pylib.websocket.exceptions import (
GlassesError,
InvalidResponseError,
SubscribeError,
UnsubscribeError,
)
from g3pylib.websocket.exceptions import GlassesError, SubscribeError, UnsubscribeError


def connect(ws_url: str) -> websockets.legacy.client.Connect:
Expand Down
4 changes: 0 additions & 4 deletions src/g3pylib/websocket/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,6 @@ class UnsubscribeError(Exception):
"""Raised when unsubscribing to a signal is unsuccessful."""


class InvalidResponseError(Exception):
"""Raised when the server responds with an invalid message."""


class GlassesError(Exception):
"""Raised when the glasses responds with an error websocket message."""

Expand Down
15 changes: 15 additions & 0 deletions tests/api_components/test_recording.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from datetime import datetime, timedelta
from typing import cast

import aiohttp
import pytest

from g3pylib import Glasses3
Expand Down Expand Up @@ -96,3 +97,17 @@ async def test_meta_data(recording: Recording):
assert meta_keys == ["RuVersion", "HuSerial", "RuSerial", "key1"]
non_existing_message = await recording.meta_lookup("key3")
assert non_existing_message == None


async def test_get_scenevideo_url(recording: Recording):
url = await recording.get_scenevideo_url()
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
assert response.status == 200


async def test_get_gazedata_url(recording: Recording):
url = await recording.get_gazedata_url()
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
assert response.status == 200