251 lines
8.7 KiB
Python
251 lines
8.7 KiB
Python
import io
|
|
import os
|
|
import posixpath
|
|
import re
|
|
from seafileapi.utils import querystr
|
|
|
|
ZERO_OBJ_ID = '0000000000000000000000000000000000000000'
|
|
|
|
class _SeafDirentBase(object):
|
|
"""Base class for :class:`SeafFile` and :class:`SeafDir`.
|
|
|
|
It provides implementation of their common operations.
|
|
"""
|
|
isdir = None
|
|
|
|
def __init__(self, repo, path, object_id, size=0):
|
|
"""
|
|
:param:`path` the full path of this entry within its repo, like
|
|
"/documents/example.md"
|
|
|
|
:param:`size` The size of a file. It should be zero for a dir.
|
|
"""
|
|
self.client = repo.client
|
|
self.repo = repo
|
|
self.path = path
|
|
self.id = object_id
|
|
self.size = size
|
|
|
|
@property
|
|
def name(self):
|
|
return posixpath.basename(self.path)
|
|
|
|
def list_revisions(self):
|
|
pass
|
|
|
|
def delete(self):
|
|
suffix = 'dir' if self.isdir else 'file'
|
|
url = '/api2/repos/%s/%s/' % (self.repo.id, suffix) + querystr(p=self.path)
|
|
resp = self.client.delete(url)
|
|
return resp
|
|
|
|
def rename(self, newname):
|
|
"""Change file/folder name to newname
|
|
"""
|
|
suffix = 'dir' if self.isdir else 'file'
|
|
url = '/api2/repos/%s/%s/' % (self.repo.id, suffix) + querystr(p=self.path, reloaddir='true')
|
|
postdata = {'operation': 'rename', 'newname': newname}
|
|
resp = self.client.post(url, data=postdata)
|
|
succeeded = resp.status_code == 200
|
|
if succeeded:
|
|
if self.isdir:
|
|
new_dirent = self.repo.get_dir(os.path.join(os.path.dirname(self.path), newname))
|
|
else:
|
|
new_dirent = self.repo.get_file(os.path.join(os.path.dirname(self.path), newname))
|
|
for key in list(self.__dict__.keys()):
|
|
self.__dict__[key] = new_dirent.__dict__[key]
|
|
return succeeded
|
|
|
|
def _copy_move_task(self, operation, dirent_type, dst_dir, dst_repo_id=None):
|
|
url = '/api/v2.1/copy-move-task/'
|
|
src_repo_id = self.repo.id
|
|
src_parent_dir = os.path.dirname(self.path)
|
|
src_dirent_name = os.path.basename(self.path)
|
|
dst_repo_id = dst_repo_id
|
|
dst_parent_dir = dst_dir
|
|
operation = operation
|
|
dirent_type = dirent_type
|
|
postdata = {'src_repo_id': src_repo_id, 'src_parent_dir': src_parent_dir,
|
|
'src_dirent_name': src_dirent_name, 'dst_repo_id': dst_repo_id,
|
|
'dst_parent_dir': dst_parent_dir, 'operation': operation,
|
|
'dirent_type': dirent_type}
|
|
return self.client.post(url, data=postdata)
|
|
|
|
def copyTo(self, dst_dir, dst_repo_id=None):
|
|
"""Copy file/folder to other directory (also to a different repo)
|
|
"""
|
|
if dst_repo_id is None:
|
|
dst_repo_id = self.repo.id
|
|
|
|
dirent_type = 'dir' if self.isdir else 'file'
|
|
resp = self._copy_move_task('copy', dirent_type, dst_dir, dst_repo_id)
|
|
return resp.status_code == 200
|
|
|
|
def moveTo(self, dst_dir, dst_repo_id=None):
|
|
"""Move file/folder to other directory (also to a different repo)
|
|
"""
|
|
if dst_repo_id is None:
|
|
dst_repo_id = self.repo.id
|
|
|
|
dirent_type = 'dir' if self.isdir else 'file'
|
|
resp = self._copy_move_task('move', dirent_type, dst_dir, dst_repo_id)
|
|
succeeded = resp.status_code == 200
|
|
if succeeded:
|
|
new_repo = self.client.repos.get_repo(dst_repo_id)
|
|
dst_path = os.path.join(dst_dir, os.path.basename(self.path))
|
|
if self.isdir:
|
|
new_dirent = new_repo.get_dir(dst_path)
|
|
else:
|
|
new_dirent = new_repo.get_file(dst_path)
|
|
for key in list(self.__dict__.keys()):
|
|
self.__dict__[key] = new_dirent.__dict__[key]
|
|
return succeeded
|
|
|
|
def get_share_link(self):
|
|
pass
|
|
|
|
class SeafDir(_SeafDirentBase):
|
|
isdir = True
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super(SeafDir, self).__init__(*args, **kwargs)
|
|
self.entries = None
|
|
self.entries = kwargs.pop('entries', None)
|
|
|
|
def ls(self, force_refresh=False):
|
|
"""List the entries in this dir.
|
|
|
|
Return a list of objects of class :class:`SeafFile` or :class:`SeafDir`.
|
|
"""
|
|
if self.entries is None or force_refresh:
|
|
self.load_entries()
|
|
|
|
return self.entries
|
|
|
|
def share_to_user(self, email, permission):
|
|
url = '/api2/repos/%s/dir/shared_items/' % self.repo.id + querystr(p=self.path)
|
|
putdata = {
|
|
'share_type': 'user',
|
|
'username': email,
|
|
'permission': permission
|
|
}
|
|
resp = self.client.put(url, data=putdata)
|
|
return resp.status_code == 200
|
|
|
|
def create_empty_file(self, name):
|
|
"""Create a new empty file in this dir.
|
|
Return a :class:`SeafFile` object of the newly created file.
|
|
"""
|
|
# TODO: file name validation
|
|
path = posixpath.join(self.path, name)
|
|
url = '/api2/repos/%s/file/' % self.repo.id + querystr(p=path, reloaddir='true')
|
|
postdata = {'operation': 'create'}
|
|
resp = self.client.post(url, data=postdata)
|
|
self.id = resp.headers['oid']
|
|
self.load_entries(resp.json())
|
|
return SeafFile(self.repo, path, ZERO_OBJ_ID, 0)
|
|
|
|
def mkdir(self, name):
|
|
"""Create a new sub folder right under this dir.
|
|
|
|
Return a :class:`SeafDir` object of the newly created sub folder.
|
|
"""
|
|
path = posixpath.join(self.path, name)
|
|
url = '/api2/repos/%s/dir/' % self.repo.id + querystr(p=path, reloaddir='true')
|
|
postdata = {'operation': 'mkdir'}
|
|
resp = self.client.post(url, data=postdata)
|
|
self.id = resp.headers['oid']
|
|
self.load_entries(resp.json())
|
|
return SeafDir(self.repo, path, ZERO_OBJ_ID)
|
|
|
|
def upload(self, fileobj, filename):
|
|
"""Upload a file to this folder.
|
|
|
|
:param:fileobj :class:`File` like object
|
|
:param:filename The name of the file
|
|
|
|
Return a :class:`SeafFile` object of the newly uploaded file.
|
|
"""
|
|
if isinstance(fileobj, str):
|
|
fileobj = io.BytesIO(fileobj)
|
|
upload_url = self._get_upload_link()
|
|
files = {
|
|
'file': (filename, fileobj),
|
|
'parent_dir': self.path,
|
|
}
|
|
self.client.post(upload_url, files=files)
|
|
return self.repo.get_file(posixpath.join(self.path, filename))
|
|
|
|
def upload_local_file(self, filepath, name=None):
|
|
"""Upload a file to this folder.
|
|
|
|
:param:filepath The path to the local file
|
|
:param:name The name of this new file. If None, the name of the local file would be used.
|
|
|
|
Return a :class:`SeafFile` object of the newly uploaded file.
|
|
"""
|
|
name = name or os.path.basename(filepath)
|
|
with open(filepath, 'r') as fp:
|
|
return self.upload(fp, name)
|
|
|
|
def _get_upload_link(self):
|
|
url = '/api2/repos/%s/upload-link/' % self.repo.id
|
|
resp = self.client.get(url)
|
|
return re.match(r'"(.*)"', resp.text).group(1)
|
|
|
|
def get_uploadable_sharelink(self):
|
|
"""Generate a uploadable shared link to this dir.
|
|
|
|
Return the url of this link.
|
|
"""
|
|
pass
|
|
|
|
def load_entries(self, dirents_json=None):
|
|
if dirents_json is None:
|
|
url = '/api2/repos/%s/dir/' % self.repo.id + querystr(p=self.path)
|
|
dirents_json = self.client.get(url).json()
|
|
|
|
self.entries = [self._load_dirent(entry_json) for entry_json in dirents_json]
|
|
|
|
def _load_dirent(self, dirent_json):
|
|
path = posixpath.join(self.path, dirent_json['name'])
|
|
if dirent_json['type'] == 'file':
|
|
return SeafFile(self.repo, path, dirent_json['id'], dirent_json['size'])
|
|
else:
|
|
return SeafDir(self.repo, path, dirent_json['id'], 0)
|
|
|
|
@property
|
|
def num_entries(self):
|
|
if self.entries is None:
|
|
self.load_entries()
|
|
return len(self.entries) if self.entries is not None else 0
|
|
|
|
def __str__(self):
|
|
return 'SeafDir[repo=%s,path=%s,entries=%s]' % \
|
|
(self.repo.id[:6], self.path, self.num_entries)
|
|
|
|
__repr__ = __str__
|
|
|
|
class SeafFile(_SeafDirentBase):
|
|
isdir = False
|
|
|
|
def update(self, fileobj):
|
|
"""Update the content of this file"""
|
|
pass
|
|
|
|
def __str__(self):
|
|
return 'SeafFile[repo=%s,path=%s,size=%s]' % \
|
|
(self.repo.id[:6], self.path, self.size)
|
|
|
|
def _get_download_link(self):
|
|
url = '/api2/repos/%s/file/' % self.repo.id + querystr(p=self.path)
|
|
resp = self.client.get(url)
|
|
return re.match(r'"(.*)"', resp.text).group(1)
|
|
|
|
def get_content(self):
|
|
"""Get the content of the file"""
|
|
url = self._get_download_link()
|
|
return self.client.get(url).content
|
|
|
|
__repr__ = __str__
|