Skip to content

Commit

Permalink
add file upload support for nextcloud
Browse files Browse the repository at this point in the history
  • Loading branch information
manzari authored and MichaIng committed Apr 12, 2022
1 parent 07e6e5e commit 327fab0
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 4 deletions.
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">S3 (AWS/MinIO/...)</option>
<option value="nextcloud">Nextcloud</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|nextcloud)">
<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 IP-adreso de la servilo (ekz. ftp.example.com 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|nextcloud)">
<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|nextcloud)">
<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|nextcloud)">
<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
82 changes: 82 additions & 0 deletions motioneye/uploadservices.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@

import boto3
import pycurl
import base64

from motioneye import settings, utils

Expand Down Expand Up @@ -881,6 +882,87 @@ def _refresh_credentials(self, refresh_token):
}


class Nextcloud(UploadService):
NAME = 'nextcloud'

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 _get_base_url(self):
scheme = 'http://'
if self._port == 443:
scheme = 'https://'
url = scheme + self._server + ':' + str(self._port) + '/remote.php/dav/files/' + self._username + '/'
return url

def _request(self, url, method, data=None):
self.debug('request: ' + method + ' ' + url)
request = urllib2.Request(url, data=data)
request.get_method = lambda: method
base64string = base64.b64encode('%s:%s' % (self._username, self._password))
request.add_header("Authorization", "Basic %s" % base64string)
if data is not None:
request.add_header('Content-Length', '%d' % len(data))
try:
utils.urlopen(request)
except urllib2.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._get_base_url()
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._get_base_url() + 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('uploading %s of %s bytes' % (filename, len(data)))
self._request(self._get_base_url() + path + filename, 'PUT', bytearray(data))
self.debug('upload done')

def dump(self):
return {
'server': self._server,
'port': self._port,
'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('port') is not None:
self._port = int(data['port'])
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

0 comments on commit 327fab0

Please sign in to comment.