Skip to content
Closed
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
2 changes: 1 addition & 1 deletion .github/workflows/docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }}
Expand Down
9 changes: 5 additions & 4 deletions motioneye/templates/main.html
Original file line number Diff line number Diff line change
Expand Up @@ -454,16 +454,17 @@
<option value="gphoto">Google Photo</option>
<option value="dropbox">Dropbox</option>
<option value="s3">AWS S3</option>
<option value="webdav">WebDAV</option>
</select>
</td>
<td><span class="help-mark" title="{{ _("elektu servon, al kiu la mediadosieroj estu alŝutitaj") }}">?</span></td>
</tr>
<tr class="settings-item" required="true" depends="uploadEnabled uploadService=(ftp|sftp|http|https)">
<tr class="settings-item" required="true" depends="uploadEnabled uploadService=(ftp|sftp|http|https|webdav)">
<td class="settings-item-label"><span class="settings-item-label">{{ _("Servila Adreso") }}</span></td>
<td class="settings-item-value"><input type="text" class="styled storage camera-config" id="uploadServerEntry"></td>
<td><span class="help-mark" title="{{ _("la domajna nomo aŭ IP-adreso de la servilo (ekz. ftp.example.com aŭ 192.168.1.3)") }}">?</span></td>
</tr>
<tr class="settings-item" required="true" min="1" max="65535" depends="uploadEnabled uploadService=(ftp|sftp|http|https)">
<tr class="settings-item" required="true" min="1" max="65535" depends="uploadEnabled uploadService=(ftp|sftp|http|https|webdav)">
<td class="settings-item-label"><span class="settings-item-label">{{ _("Servila haveno") }}</span></td>
<td class="settings-item-value"><input type="text" class="number styled storage camera-config" id="uploadPortEntry"></td>
<td><span class="help-mark" title="{{ _("la haveno por uzi konekti al la servo (lasu ĉi tiun kampon malplena por uzi la defaŭltan valoron)") }}">?</span></td>
Expand Down Expand Up @@ -493,12 +494,12 @@
<td class="settings-item-value"><input type="checkbox" class="styled storage camera-config" id="cleanCloudEnabledSwitch"></td>
<td><span class="help-mark" title="{{ _("ebligu ĉi tion forigi amaskomunikilaĵojn dosierojn alŝutitajn al la nubo ankaŭ kiam loka amaskomunikilaro estas forigita konforme al la agordo de persistemo de dosieroj. Nuntempe ĉi tiu opcio nur haveblas por gdrivo.") }}">?</span></td>
</tr>
<tr class="settings-item" depends="uploadEnabled uploadService=(ftp|sftp|http|https)">
<tr class="settings-item" depends="uploadEnabled uploadService=(ftp|sftp|http|https|webdav)">
<td class="settings-item-label"><span class="settings-item-label">{{ _("Uzantnomo") }}</span></td>
<td class="settings-item-value"><input type="text" autocapitalize="none" class="styled storage camera-config" id="uploadUsernameEntry"></td>
<td><span class="help-mark" title="{{ _("la uzantnomo por la alŝuta servo-konto") }}">?</span></td>
</tr>
<tr class="settings-item" depends="uploadEnabled uploadService=(ftp|sftp|http|https)">
<tr class="settings-item" depends="uploadEnabled uploadService=(ftp|sftp|http|https|webdav)">
<td class="settings-item-label"><span class="settings-item-label">{{ _("Pasvorto") }}</span></td>
<td class="settings-item-value"><input type="password" autocomplete="new-password" class="styled storage camera-config" id="uploadPasswordEntry"></td>
<td><span class="help-mark" title="{{ _("la pasvorto por la alŝuta servo-konto") }}">?</span></td>
Expand Down
74 changes: 74 additions & 0 deletions motioneye/uploadservices.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import urllib.error
import urllib.parse
import urllib.request
from base64 import b64encode

import boto3
import pycurl
Expand Down Expand Up @@ -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
Expand Down