Skip to content

Commit

Permalink
Merge pull request #135 from maximvelichko/media-log
Browse files Browse the repository at this point in the history
Media and Log functions
  • Loading branch information
dougsland committed Jan 6, 2020
2 parents 3b0457c + 8202e6a commit fdbb775
Show file tree
Hide file tree
Showing 14 changed files with 317 additions and 16 deletions.
5 changes: 3 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ matrix:
env: TOXENV=py35
- python: "3.6"
env: TOXENV=py36
- python: "3.4.2"
env: TOXENV=lint
- python: "3.7"
env: TOXENV=py37
dist: xenial
cache: pip
install: pip install -U tox coveralls
script: tox
Expand Down
2 changes: 2 additions & 0 deletions cli/amcrest-cli
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@

# pylint: disable=invalid-name

from __future__ import print_function

import os
import signal
import sys
Expand Down
35 changes: 35 additions & 0 deletions examples/get-files.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# -*- coding: utf-8 -*-
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, version 2 of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# vim:sw=4:ts=4:et

import datetime
from amcrest import AmcrestCamera

amcrest = AmcrestCamera("192.168.1.10", 80, "admin", "super_password")
camera = amcrest.camera

end_time = datetime.datetime.now()
start_time = end_time - datetime.timedelta(hours=1)

for text in camera.find_files(start_time, end_time):
for line in text.split("\r\n"):
key, value = list(line.split("=", 1) + [None])[:2]
if key.endswith(".FilePath"):
print("Found file {}".format(value))
if value.endswith(".jpg"):
file_name = value

if file_name:
print("Downloading {}...".format(file_name))
with open("snapshot.jpg", "w") as file:
file.write(camera.download_file(file_name))
print("Saved as {}!".format("snapshot.jpg"))
24 changes: 24 additions & 0 deletions examples/get-logs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, version 2 of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# vim:sw=4:ts=4:et

import datetime
from amcrest import AmcrestCamera

amcrest = AmcrestCamera("192.168.1.10", 80, "admin", "super_password")
camera = amcrest.camera

end_time = datetime.datetime.now()
start_time = end_time - datetime.timedelta(hours=1)

for text in camera.log_find(start_time, end_time):
print(text)
5 changes: 4 additions & 1 deletion src/amcrest/audio.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,14 @@
#
# vim:sw=4:ts=4:et
import shutil
from urllib3.exceptions import HTTPError
import logging

from urllib3.exceptions import HTTPError
from . import utils
from .exceptions import CommError

_LOGGER = logging.getLogger(__name__)


class Audio(object):

Expand Down
4 changes: 3 additions & 1 deletion src/amcrest/http.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from .audio import Audio
from .event import Event
from .log import Log
from .media import Media
from .motion_detection import MotionDetection
from .nas import Nas
from .network import Network
Expand All @@ -42,7 +43,7 @@
# pylint: disable=too-many-ancestors
class Http(System, Network, MotionDetection, Snapshot,
UserManagement, Event, Audio, Record, Video,
Log, Ptz, Special, Storage, Nas):
Log, Ptz, Special, Storage, Nas, Media):

def __init__(self, host, port, user,
password, verbose=True, protocol='http', ssl_verify=True,
Expand Down Expand Up @@ -145,6 +146,7 @@ def _command(self, cmd, retries=None, timeout_cmd=None, stream=False):
session = requests.Session()
session.verify = self._verify
url = self.__base_url(cmd)
_LOGGER.debug("%s HTTP query %s", self, url)
if retries is None:
retries = self._retries_default
for loop in range(1, 2 + retries):
Expand Down
39 changes: 39 additions & 0 deletions src/amcrest/log.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,42 @@ def log_show(self, start_time, end_time):
'{0}&condition.EndTime={1}'.format(start_time, end_time)
)
return ret.content.decode('utf-8')

def log_find_start(self, start_time, end_time):
ret = self.command(
'log.cgi?action=startFind'
'&condition.StartTime={0}&condition.EndTime={1}'
.format(
start_time.strftime('%Y-%m-%d %H:%M:%S'),
end_time.strftime('%Y-%m-%d %H:%M:%S')))

return ret.content.decode('utf-8')

def log_find_next(self, token, count=100):
ret = self.command('log.cgi?action=doFind&token={0}&count={1}'
.format(token, count))
return ret.content.decode('utf-8')

def log_find_stop(self, token):
ret = self.command('log.cgi?action=stopFind&token={0}'
.format(token))
return ret.content.decode('utf-8')

def log_find(self, start_time, end_time):
token = self.log_find_start(start_time, end_time).strip().split('=')[1]
to_query = True

while to_query:
content = self.log_find_next(token)
tag, count = (
list(content.split('\r\n', 1)[0]
.split('=')) + [None])[:2]

to_query = False

if (tag == 'found') and (int(count) > 0):
to_query = True

yield content

self.log_find_stop(token)
196 changes: 196 additions & 0 deletions src/amcrest/media.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
# -*- coding: utf-8 -*-
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, version 2 of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# vim:sw=4:ts=4:et

import logging

_LOGGER = logging.getLogger(__name__)
# fmt: off


class Media(object):

def factory_create(self):
ret = self.command(
'mediaFileFind.cgi?action=factory.create'
)
return ret.content.decode('utf-8')

def factory_close(self, factory_id):
ret = self.command(
'mediaFileFind.cgi?action=factory.close&object={0}'
.format(factory_id)
)
return ret.content.decode('utf-8')

def factory_destroy(self, factory_id):
ret = self.command(
'mediaFileFind.cgi?action=factory.destroy&object={0}'
.format(factory_id)
)
return ret.content.decode('utf-8')

def media_file_find_start(self, factory_id,
start_time, end_time, channel=0,
directories=(), types=(), flags=(),
events=(), stream=None):
"""
https://s3.amazonaws.com/amcrest-files/Amcrest+HTTP+API+3.2017.pdf
factory_id : returned by factory_create()
dir : in which directories you want to find the file. It is an array.
The index starts from 1. The range of dir is {"/mnt/dvr/sda0",
"/mnt/dvr/sda1"}. This condition can be omitted. If omitted,
find files in all the directories.
type : which types of the file you want to find. It is an array. The
index starts from 0. The range of type is {"dav", "jpg", "mp4"}
If omitted, find files with all the types.
flag : which flags of the file you want to find. It is an array. The
index starts from 0. The range of flag is {"Timing", "Manual",
"Marker", "Event", "Mosaic", "Cutout"}. If omitted, find files
with all the flags.
event : by which event the record file is triggered. It is an array.
The index starts from 0. The range of event is {"AlarmLocal",
"VideoMotion", "VideoLoss", "VideoBlind", "Traffic*"}. This
condition can be omitted. If omitted, find files of all the
events. stream : which video stream type you want to find.
The range of stream is {"Main", "Extra1", "Extra2", "Extra3"}.
If omitted, find files with all the stream types.
"""

c_dirs = ''.join(['&condition.Dirs[{0}]={1}'.format(k, v)
for k, v in enumerate(directories)])

c_types = ''.join(['&condition.Types[{0}]={1}'.format(k, v)
for k, v in enumerate(types)])

c_flag = ''.join(['&condition.Flag[{0}]={1}'.format(k, v)
for k, v in enumerate(flags)])

c_events = ''.join(['&condition.Events[{0}]={1}'.format(k, v)
for k, v in enumerate(events)])

c_vs = ('&condition.VideoStream={0}'.format(stream) if stream else '')

ret = self.command(
'mediaFileFind.cgi?action=findFile&object={0}&condition.Channel'
'={1}&condition.StartTime={2}&condition.EndTime={3}{4}{5}{6}{7}{8}'
.format(factory_id, channel, start_time, end_time, c_dirs, c_types,
c_flag, c_events, c_vs)
)
return ret.content.decode('utf-8')

def media_file_find_next(self, factory_id, count=100):
ret = self.command(
'mediaFileFind.cgi?action=findNextFile&object={0}&count={1}'
.format(factory_id, count)
)

return ret.content.decode('utf-8')

def find_files(self, start_time, end_time, channel=0,
directories=(), types=(), flags=(), events=(), stream=None):
"""
https://s3.amazonaws.com/amcrest-files/Amcrest+HTTP+API+3.2017.pdf
dir : in which directories you want to find the file. It is an array.
The index starts from 1. The range of dir is {"/mnt/dvr/sda0",
"/mnt/dvr/sda1"}. This condition can be omitted. If omitted,
find files in all the directories.
type : which types of the file you want to find. It is an array. The
index starts from 0. The range of type is {"dav", "jpg", "mp4"}
If omitted, find files with all the types.
flag : which flags of the file you want to find. It is an array. The
index starts from 0. The range of flag is {"Timing", "Manual",
"Marker", "Event", "Mosaic", "Cutout"}. If omitted, find files
with all the flags.
event : by which event the record file is triggered. It is an array.
The index starts from 0. The range of event is {"AlarmLocal",
"VideoMotion", "VideoLoss", "VideoBlind", "Traffic*"}. This
condition can be omitted. If omitted, find files of all the
events. stream : which video stream type you want to find.
The range of stream is {"Main", "Extra1", "Extra2", "Extra3"}.
If omitted, find files with all the stream types.
"""
factory_id = self.factory_create().strip().split('=')[1]
_LOGGER.debug("%s findFile for factory_id=%s", self, factory_id)

search = self.media_file_find_start(
factory_id=factory_id,
start_time=start_time,
end_time=end_time,
channel=channel,
directories=directories,
types=types,
flags=flags,
events=events,
stream=stream)

if "ok" in search.lower():
count = 100

while count and count > 0:
_LOGGER.debug("%s findNextFile", self)
content = self.media_file_find_next(factory_id)

# The first line is 'found=N'.
# However, it can be 'Error' if e.g. no more files found
tag, count = (list(content.split('\r\n', 1)[0]
.split('=')) + [None])[:2]
_LOGGER.debug("%s returned %s %s", self, tag, count)

if tag == 'found':
count = int(count)
else:
count = None

yield content

self.factory_close(factory_id)
self.factory_destroy(factory_id)
else:
_LOGGER.debug("%s returned error: %s", self, search)

def download_file(self, file_path, timeout=None, stream=False):
"""
file_path: File location like returned by FilePath from find_files()
Example: /mnt/sd/2019-12-31/001/dav/00/00.12.00-00.20.00.mp4
timeout: Use default if None
stream: If True use streaming download instead of
reading content into memory
"""
ret = self.command(
'RPC_Loadfile/{0}'.format(file_path),
timeout_cmd=timeout,
stream=stream
)
return ret.content

def download_time(self, start_time, end_time, channel=0, stream=0):
"""
start_time and end_time are formatted as yyyy-mm-dd hh:mm:ss
'%Y-%m-%d%%20%H:%M:%S'
"""
ret = self.command(
'loadfile.cgi?action=startLoad&channel={0}&startTime={1}'
'&endTime={2}&subtype={3}'
.format(channel, start_time, end_time, stream)
)
return ret.content
# fmt: on
7 changes: 0 additions & 7 deletions src/amcrest/record.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,6 @@ def record_capability(self):
)
return ret.content.decode('utf-8')

@property
def factory_create(self):
ret = self.command(
'mediaFileFind.cgi?action=factory.create'
)
return ret.content.decode('utf-8')

@property
def record_config(self):
ret = self.command(
Expand Down
4 changes: 3 additions & 1 deletion src/amcrest/snapshot.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@
#
# vim:sw=4:ts=4:et
import shutil
import logging
from urllib3.exceptions import HTTPError

from .exceptions import CommError

_LOGGER = logging.getLogger(__name__)


class Snapshot(object):
def __get_config(self, config_name):
Expand Down

0 comments on commit fdbb775

Please sign in to comment.