diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index f4c92a597..768de88f6 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -33,7 +33,7 @@ jobs: - uses: docker/setup-buildx-action@v1 - name: Cache Docker layers - uses: actions/cache@v2 + uses: actions/cache@v3.0.1 with: path: /tmp/.buildx-cache key: ${{ runner.os }}-buildx-${{ github.sha }} diff --git a/motioneye/templates/main.html b/motioneye/templates/main.html index 5d39a9e86..5340d831e 100644 --- a/motioneye/templates/main.html +++ b/motioneye/templates/main.html @@ -454,16 +454,17 @@ + ? - + {{ _("Servila Adreso") }} ? - + {{ _("Servila haveno") }} ? @@ -493,12 +494,12 @@ ? - + {{ _("Uzantnomo") }} ? - + {{ _("Pasvorto") }} ? diff --git a/motioneye/uploadservices.py b/motioneye/uploadservices.py index 9bc800638..3250e48bb 100644 --- a/motioneye/uploadservices.py +++ b/motioneye/uploadservices.py @@ -26,6 +26,7 @@ import urllib.error import urllib.parse import urllib.request +from base64 import b64encode import boto3 import pycurl @@ -845,6 +846,79 @@ def _request_credentials(self, authorization_key): return {'access_token': data['access_token']} +class Webdav(UploadService): + NAME = 'webdav' + + def __init__(self, camera_id): + self._server = None + self._port = None + self._username = None + self._password = None + self._location = None + + UploadService.__init__(self, camera_id) + + def _request(self, url, method, body=None): + base64string = b64encode(f'{self._username}:{self._password}') + headers = {'Authorization': 'Basic %s' % base64string} + if body is not None: + headers.update('Content-Length', '%d' % len(body)) + self.debug('request: ' + method + ' ' + url) + request = urllib.request.Request(url, data=body, headers=headers) + request.get_method = lambda: method + try: + utils.urlopen(request) + except urllib.HTTPError as e: + if method == 'MKCOL' and e.code == 405: + self.debug( + 'MKCOL failed with code 405, this is normal if the folder exists' + ) + else: + raise e + + def _make_dirs(self, path): + dir_url = self._server + for folder in path.strip('/').split('/'): + dir_url = dir_url + folder + '/' + self._request(dir_url, 'MKCOL') + + def test_access(self): + try: + test_path = self._location.strip('/') + '/' + str(time.time()) + self._make_dirs(test_path) + self._request(self._server + test_path, 'DELETE') + return True + except Exception as e: + self.error(str(e), exc_info=True) + return str(e) + + def upload_data(self, filename, mime_type, data, ctime, camera_name): + path = self._location.strip('/') + '/' + os.path.dirname(filename) + '/' + filename = os.path.basename(filename) + self._make_dirs(path) + self.debug(f'uploading {filename} of {len(data)} bytes') + self._request(self._server + path + filename, 'PUT', bytearray(data)) + self.debug('upload done') + + def dump(self): + return { + 'server': self._server, + 'username': self._username, + 'password': self._password, + 'location': self._location, + } + + def load(self, data): + if data.get('server') is not None: + self._server = data['server'] + if data.get('username') is not None: + self._username = data['username'] + if data.get('password') is not None: + self._password = data['password'] + if data.get('location'): + self._location = data['location'] + + class FTP(UploadService): NAME = 'ftp' CONN_LIFE_TIME = 60 # don't keep an FTP connection for more than 1 minute