Skip to content
Draft
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
3 changes: 2 additions & 1 deletion config/config.example.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ log_level: "INFO" # INFO, DEBUG, WARNING, ERROR
seafile:
host: "https://box.nju.edu.cn"
api_token: "YOUR_SEAFILE_API_TOKEN_HERE"
repo_id: "YOUR_REPO_UUID_HERE"
repo_name: "YOUR_REPO_NAME_HERE" # Optional if repo_id is provided
# repo_id: "YOUR_REPO_UUID_HERE" # Optional if repo_name is provided

# Rclone Configuration
rclone:
Expand Down
16 changes: 11 additions & 5 deletions src/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,17 @@ def main():
target_path = Path(args.path)

# Init Clients
seafile = SeafileClient(
config['seafile']['host'],
config['seafile']['api_token'],
config['seafile']['repo_id']
)
try:
seafile = SeafileClient(
config['seafile']['host'],
config['seafile']['api_token'],
repo_id=config['seafile'].get('repo_id'),
repo_name=config['seafile'].get('repo_name')
)
except Exception as e:
logging.critical(f"Failed to initialize Seafile client: {e}")
sys.exit(1)

rclone = RcloneWrapper(
config['rclone']['remote_name'],
config['rclone']['bwlimit']
Expand Down
72 changes: 70 additions & 2 deletions src/seafile_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,81 @@
import logging

class SeafileClient:
def __init__(self, host, token, repo_id):
def __init__(self, host, token, repo_id=None, repo_name=None):
self.host = host
self.headers = {"Authorization": f"Token {token}", "Accept": "application/json"}
self.repo_id = repo_id
self.repo_name = repo_name
self.repo_id = None

if self.repo_name:
try:
found_id = self.get_repo_id_by_name(self.repo_name)
if found_id:
self.repo_id = found_id
logging.info(f"Resolved repo_name '{self.repo_name}' to repo_id '{self.repo_id}'")
else:
logging.warning(f"Could not find repo with name '{self.repo_name}' and permission 'rw'.")
except Exception as e:
logging.error(f"Error during repo lookup: {e}")

# Fallback or primary use of repo_id
if not self.repo_id and repo_id:
self.repo_id = repo_id
if self.repo_name:
logging.info(f"Falling back to provided repo_id '{self.repo_id}'")

if not self.repo_id:
raise ValueError("Could not determine repo_id. Please provide a valid repo_id or a reachable repo_name.")

def get_repos(self):
"""Fetches the list of repositories from the API."""
url = urljoin(self.host, "/api/v2.1/repos/")
resp = requests.get(url, headers=self.headers)
if not resp.ok:
logging.error(f"Failed to list repos. Status: {resp.status_code}, Body: {resp.text}")
resp.raise_for_status()
return resp.json()

def get_repo_id_by_name(self, target_name):
"""
Finds a repo ID by name from the API response.
Ensures permission is 'rw'.
"""
data = self.get_repos()

# Handle if response is dict with 'repos' key or just a list
repos = []
if isinstance(data, dict):
repos = data.get('repos', [])
elif isinstance(data, list):
repos = data
else:
logging.error(f"Unexpected API response format: {type(data)}")
return None

for repo in repos:
# Check for name match and write permission
if repo.get('repo_name') == target_name and repo.get('permission') == 'rw':
return repo.get('repo_id')

return None

def get_share_link(self, remote_path):
"""Generates or retrieves a direct download link."""

# Adjust path if repo_name is known and path starts with it
# This handles the case where Rclone remote root includes the repo name
if self.repo_name:
prefix = f"/{self.repo_name}"
# Check for "/RepoName" (exact) or "/RepoName/..."
if remote_path == prefix or remote_path.startswith(f"{prefix}/"):
# Strip the prefix
original_path = remote_path
remote_path = remote_path[len(prefix):]
if not remote_path.startswith("/"):
remote_path = "/" + remote_path
logging.info(f"Adjusted path from '{original_path}' to '{remote_path}' based on repo_name")

url = urljoin(self.host, "/api/v2.1/share-links/")

# Payload for creating a link
Expand Down