Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Don't hardcode file directory #397

Merged
merged 2 commits into from
Sep 21, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ and this project adheres to `Semantic Versioning <https://semver.org/spec/v2.0.0
Unreleased
----------
* Fix BLE notifications not being routed correctly
* Don't hardcode media directory. Also append directory to filenames in media list.

0.14.0 (September-13-2022)
--------------------------
Expand Down
51 changes: 34 additions & 17 deletions demos/python/sdk_wireless_camera_control/open_gopro/api/builders.py
Original file line number Diff line number Diff line change
Expand Up @@ -801,25 +801,15 @@ def __init__(

super().__init__(endpoint, identifier, components, arguments, parser, rules)


class HttpGetJsonCommand(HttpCommand):
"""An HTTP command that performs a GET operation and receives JSON as response"""

async def __call__(
self, __communicator__: GoProHttp, rules: list[MessageRules] | None = None, **kwargs: Any
) -> GoProResp:
"""Execute the command by sending it via HTTP
def build_url(self, **kwargs: Any) -> str:
"""Build the URL string from the passed in components and arguments

Args:
__communicator__ (GoProHttp): HTTP communicator
rules (Optional[dict[MessageRules, RuleSignature]], optional): rules to apply when executing this
message. Defaults to None.
**kwargs (Any): arguments to message
**kwargs (Any): additional entries for the dict

Returns:
GoProResp: Response received via HTTP
str: built URL
"""
# Append components
url = self._endpoint
for component in self._components or []:
url += "/" + kwargs.pop(component)
Expand All @@ -835,7 +825,30 @@ async def __call__(
)
):
url += "?" + arg_part
return url


class HttpGetJsonCommand(HttpCommand):
"""An HTTP command that performs a GET operation and receives JSON as response"""

async def __call__(
self,
__communicator__: GoProHttp,
rules: list[MessageRules] | None = None,
**kwargs: Any,
) -> GoProResp:
"""Execute the command by sending it via HTTP

Args:
__communicator__ (GoProHttp): HTTP communicator
rules (Optional[dict[MessageRules, RuleSignature]], optional): rules to apply when executing this
message. Defaults to None.
**kwargs (Any): arguments to message

Returns:
GoProResp: Response received via HTTP
"""
url = self.build_url(**kwargs)
# Send to camera
logger.info(Logger.build_log_tx_str(pretty_print(self._as_dict(**kwargs, endpoint=url))))
response = await __communicator__._http_get(url, self._parser, rules=rules)
Expand All @@ -849,7 +862,11 @@ class HttpGetBinary(HttpCommand):
"""An HTTP command that performs a GET operation and receives a binary stream as response"""

async def __call__( # type: ignore
self, __communicator__: GoProHttp, *, camera_file: str, local_file: Path | None = None
self,
__communicator__: GoProHttp,
*,
camera_file: str,
local_file: Path | None = None,
) -> GoProResp:
"""Execute the command by getting the binary data from the communicator

Expand All @@ -863,8 +880,8 @@ async def __call__( # type: ignore
GoProResp: location on local device that file was written to
"""
# The method that will actually send the command and receive the stream
local_file = local_file or Path(".") / camera_file
url = self._endpoint + "/" + camera_file
local_file = local_file or Path(".") / Path(camera_file).name
url = self.build_url(path=camera_file)
logger.info(
Logger.build_log_tx_str(
pretty_print(self._as_dict(endpoint=url, camera_file=camera_file, local_file=local_file))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,6 @@ async def get_media_metadata(self, *, file: str) -> GoProResp[MediaMetadata]:
Returns:
GoProResp: Media metadata JSON structure
"""
return {"path": f"100GOPRO/{file}"} # type: ignore

@http_get_json_command(
endpoint="gopro/media/list",
Expand Down Expand Up @@ -249,7 +248,6 @@ async def set_date_time(
"dst": int(is_dst),
}

# TODO
@http_get_json_command(endpoint="gopro/camera/get_date_time")
async def get_date_time(self) -> GoProResp[datetime.datetime]:
"""Get the date and time of the camera (Non timezone / DST aware)
Expand Down Expand Up @@ -285,7 +283,7 @@ async def add_file_hilight(
Returns:
GoProResp: command status
"""
return {"path": f"100GOPRO/{file}", "ms": offset or None} # type: ignore
return {"path": file, "ms": offset or None} # type: ignore

@http_get_json_command(
endpoint="gopro/media/hilight/remove",
Expand All @@ -306,7 +304,7 @@ async def remove_file_hilight(
Returns:
GoProResp: command status
"""
return {"path": f"100GOPRO/{file}", "ms": offset} # type: ignore
return {"path": file, "ms": offset} # type: ignore

@http_get_json_command(
endpoint="gopro/webcam/exit",
Expand Down Expand Up @@ -396,7 +394,7 @@ async def wired_usb_control(self, *, control: Params.Toggle) -> GoProResp[None]:
# HTTP GET BINARY COMMANDS
######################################################################################################

@http_get_binary_command(endpoint="gopro/media/gpmf?path=100GOPRO")
@http_get_binary_command(endpoint="gopro/media/gpmf", arguments=["path"])
async def get_gpmf_data(self, *, camera_file: str, local_file: Path | None = None) -> GoProResp[Path]:
"""Get GPMF data for a file.

Expand All @@ -410,7 +408,7 @@ async def get_gpmf_data(self, *, camera_file: str, local_file: Path | None = Non
Path: Path to local_file that output was written to
"""

@http_get_binary_command(endpoint="gopro/media/screennail?path=100GOPRO")
@http_get_binary_command(endpoint="gopro/media/screennail", arguments=["path"])
async def get_screennail__call__(self, *, camera_file: str, local_file: Path | None = None) -> GoProResp[Path]:
"""Get screennail for a file.

Expand All @@ -424,7 +422,7 @@ async def get_screennail__call__(self, *, camera_file: str, local_file: Path | N
Path: Path to local_file that output was written to
"""

@http_get_binary_command(endpoint="gopro/media/thumbnail?path=100GOPRO")
@http_get_binary_command(endpoint="gopro/media/thumbnail", arguments=["path"])
async def get_thumbnail(self, *, camera_file: str, local_file: Path | None = None) -> GoProResp[Path]:
"""Get thumbnail for a file.

Expand All @@ -438,7 +436,7 @@ async def get_thumbnail(self, *, camera_file: str, local_file: Path | None = Non
Path: Path to local_file that output was written to
"""

@http_get_binary_command(endpoint="gopro/media/telemetry?path=100GOPRO")
@http_get_binary_command(endpoint="gopro/media/telemetry", arguments=["path"])
async def get_telemetry(self, *, camera_file: str, local_file: Path | None = None) -> GoProResp[Path]:
"""Download the telemetry data for a camera file and store in a local file.

Expand All @@ -452,7 +450,7 @@ async def get_telemetry(self, *, camera_file: str, local_file: Path | None = Non
Path: Path to local_file that output was written to
"""

@http_get_binary_command(endpoint="videos/DCIM/100GOPRO", identifier="Download File")
@http_get_binary_command(endpoint="videos/DCIM", components=["path"], identifier="Download File")
async def download_file(self, *, camera_file: str, local_file: Path | None = None) -> GoProResp[Path]:
"""Download a video from the camera to a local file.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ async def main(args: argparse.Namespace) -> None:
# The photo is (most likely) the difference between the two sets
photo = media_set_after.difference(media_set_before).pop()
# Download the photo
console.print("Downloading the photo...")
console.print(f"Downloading {photo.filename}...")
await gopro.http_command.download_file(camera_file=photo.filename, local_file=args.output)
console.print(f"Success!! :smiley: File has been downloaded to {args.output}")
except Exception as e: # pylint: disable = broad-except
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
from __future__ import annotations

from abc import ABC
from typing import Optional
from typing import Any, Optional

from pydantic import Field, validator
from pydantic import Field, PrivateAttr, validator

from open_gopro import types
from open_gopro.models.bases import CustomBaseModel
Expand Down Expand Up @@ -139,6 +139,16 @@ class MediaList(CustomBaseModel):

identifier: str = Field(alias="id") #: String identifier of this media list
media: list[MediaFileSystem] #: Media filesystem(s)
_files: list[MediaItem] = PrivateAttr(default_factory=list)

def __init__(self, *args: Any, **kwargs: Any) -> None:
super().__init__(*args, **kwargs)
# self.thing = 1
# Modify each file name to use full path
for directory in self.media:
for media in directory.file_system:
media.filename = f"{directory.directory}/{media.filename}"
self._files.append(media)

@property
def files(self) -> list[MediaItem]:
Expand All @@ -147,4 +157,4 @@ def files(self) -> list[MediaItem]:
Returns:
list[MediaItem]: all media items in this media list
"""
return [item for media in self.media for item in media.file_system]
return self._files
2 changes: 1 addition & 1 deletion demos/python/sdk_wireless_camera_control/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ log_file_level = "DEBUG"
log_file_format = "%(threadName)13s: %(name)40s:%(lineno)5d %(asctime)s.%(msecs)03d %(levelname)-8s | %(message)s"
log_file_date_format = "%H:%M:%S"
filterwarnings = "ignore::DeprecationWarning"
timeout = 10
# timeout = 10
tcamise-gpsw marked this conversation as resolved.
Show resolved Hide resolved
addopts = [
"-s",
"--capture=tee-sys",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,39 +7,46 @@

import pytest

from open_gopro.communicator_interface import GoProWifi
from open_gopro.gopro_base import GoProBase

camera_file = "100GOPRO/XXX.mp4"


@pytest.mark.asyncio
async def test_get_with_no_params(mock_wifi_communicator: GoProWifi):
async def test_get_with_no_params(mock_wifi_communicator: GoProBase):
response = await mock_wifi_communicator.http_command.get_media_list()
assert response.url == "gopro/media/list"


@pytest.mark.asyncio
async def test_get_with_params(mock_wifi_communicator: GoProWifi):
async def test_get_with_params(mock_wifi_communicator: GoProBase):
zoom = 99
response = await mock_wifi_communicator.http_command.set_digital_zoom(percent=zoom)
assert response.url == f"gopro/camera/digital_zoom?percent={zoom}"


@pytest.mark.asyncio
async def test_with_multiple_params(mock_wifi_communicator: GoProWifi):
media_file = "XXX.mp4"
offset_ms = 2500
response = await mock_wifi_communicator.http_command.add_file_hilight(file=media_file, offset=offset_ms)
assert response.url == "gopro/media/hilight/file?path=100GOPRO/XXX.mp4&ms=2500"
async def test_get_binary(mock_wifi_communicator: GoProBase):
url, file = await mock_wifi_communicator.http_command.get_gpmf_data(camera_file=camera_file)
assert url == f"gopro/media/gpmf?path={camera_file}"
assert file == Path("XXX.mp4")


@pytest.mark.asyncio
async def test_get_binary_with_component(mock_wifi_communicator: GoProBase):
url, file = await mock_wifi_communicator.http_command.download_file(camera_file=camera_file)
assert url == f"videos/DCIM/{camera_file}"
assert file == Path("XXX.mp4")


@pytest.mark.asyncio
async def test_get_binary(mock_wifi_communicator: GoProWifi):
file = await mock_wifi_communicator.http_command.download_file(
camera_file="test_file", local_file=Path("local_file")
)
assert str(file[1]) == "local_file"
async def test_with_multiple_params(mock_wifi_communicator: GoProBase):
offset_ms = 2500
response = await mock_wifi_communicator.http_command.add_file_hilight(file=camera_file, offset=offset_ms)
assert response.url == f"gopro/media/hilight/file?path={camera_file}&ms=2500"


def test_ensure_no_positional_args(mock_wifi_communicator: GoProWifi):
def test_ensure_no_positional_args(mock_wifi_communicator: GoProBase):
for command in mock_wifi_communicator.http_command.values():
if inspect.getfullargspec(command).args != ["self"]:
logging.error("All arguments to commands must be keyword-only")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ def test_media_list():
items = media_list.files
assert len(items) == 12
assert len([item for item in items if isinstance(item, GroupedMediaItem)]) == 2
assert media_list.files[0].filename == "100GOPRO/GX010001.MP4"


VIDEO_METADATA: Final = {
Expand Down
Loading