|
1 | | -"""Shared utility for reading URL list files from local paths or S3.""" |
| 1 | +"""Shared utility for reading URL list files from local paths, S3, or WebDAV.""" |
2 | 2 |
|
3 | 3 | import logging |
| 4 | +from io import BytesIO |
4 | 5 | from pathlib import Path |
5 | 6 |
|
6 | 7 | import aiofiles |
|
12 | 13 | logger = logging.getLogger(__name__) |
13 | 14 |
|
14 | 15 |
|
| 16 | +def is_webdav_url(path: str) -> bool: |
| 17 | + """Return True if *path* looks like an HTTP(S) URL.""" |
| 18 | + return path.startswith("http://") or path.startswith("https://") |
| 19 | + |
| 20 | + |
15 | 21 | def resolve_local_path( |
16 | 22 | urls_file: str, |
17 | 23 | base_dir: str | None = None, |
@@ -42,25 +48,73 @@ def resolve_local_path( |
42 | 48 | return urls_file |
43 | 49 |
|
44 | 50 |
|
| 51 | +def read_text_from_webdav( |
| 52 | + url: str, |
| 53 | + webdav_url: str | None = None, |
| 54 | + webdav_username: str | None = None, |
| 55 | + webdav_password: str | None = None, |
| 56 | +) -> str: |
| 57 | + """Download a text file from a WebDAV server. |
| 58 | +
|
| 59 | + The full file URL is split into a base URL (scheme + host) and a |
| 60 | + path component. Authentication credentials fall back to the |
| 61 | + global settings when not provided explicitly. Client creation is |
| 62 | + delegated to :func:`~soliplex.agents.webdav.app.create_webdav_client` |
| 63 | + so that timeout, header, and TLS settings stay consistent. |
| 64 | +
|
| 65 | + Args: |
| 66 | + url: Full HTTP(S) URL to the file on the WebDAV server. |
| 67 | + webdav_url: Optional override for the WebDAV base URL. |
| 68 | + When *None* the base URL is derived from *url*. |
| 69 | + webdav_username: Optional WebDAV username. |
| 70 | + webdav_password: Optional WebDAV password. |
| 71 | +
|
| 72 | + Returns: |
| 73 | + The file contents decoded as UTF-8 text. |
| 74 | + """ |
| 75 | + from urllib.parse import urlparse |
| 76 | + |
| 77 | + from soliplex.agents.webdav.app import create_webdav_client |
| 78 | + |
| 79 | + parsed = urlparse(url) |
| 80 | + base_url = webdav_url or f"{parsed.scheme}://{parsed.netloc}" |
| 81 | + path = parsed.path |
| 82 | + |
| 83 | + client = create_webdav_client(base_url, webdav_username, webdav_password) |
| 84 | + |
| 85 | + buffer = BytesIO() |
| 86 | + client.download_fileobj(path, buffer) |
| 87 | + return buffer.getvalue().decode("utf-8") |
| 88 | + |
| 89 | + |
45 | 90 | async def read_urls_file( |
46 | 91 | urls_file: str, |
47 | 92 | base_dir: str | None = None, |
| 93 | + webdav_url: str | None = None, |
| 94 | + webdav_username: str | None = None, |
| 95 | + webdav_password: str | None = None, |
48 | 96 | ) -> list[str]: |
49 | 97 | """Read a URL list file and return non-empty, stripped lines. |
50 | 98 |
|
51 | | - Supports S3 URLs (``s3://bucket/key``) and local filesystem paths. |
52 | | - For local paths, relative paths are resolved against *base_dir* |
53 | | - when provided (see :func:`resolve_local_path`). |
| 99 | + Supports S3 URLs (``s3://bucket/key``), WebDAV URLs |
| 100 | + (``http(s)://...``), and local filesystem paths. For local paths, |
| 101 | + relative paths are resolved against *base_dir* when provided (see |
| 102 | + :func:`resolve_local_path`). |
54 | 103 |
|
55 | 104 | Args: |
56 | | - urls_file: Path or S3 URL to the URL list file. |
| 105 | + urls_file: Path, S3 URL, or WebDAV URL to the URL list file. |
57 | 106 | base_dir: Optional directory for resolving relative local paths. |
| 107 | + webdav_url: Optional WebDAV base URL override (for WebDAV URLs). |
| 108 | + webdav_username: Optional WebDAV username (for WebDAV URLs). |
| 109 | + webdav_password: Optional WebDAV password (for WebDAV URLs). |
58 | 110 |
|
59 | 111 | Returns: |
60 | 112 | List of non-empty, whitespace-stripped lines. |
61 | 113 | """ |
62 | 114 | if is_s3_url(urls_file): |
63 | 115 | content = await read_text_from_s3(urls_file, settings.s3_endpoint_url) |
| 116 | + elif is_webdav_url(urls_file): |
| 117 | + content = read_text_from_webdav(urls_file, webdav_url, webdav_username, webdav_password) |
64 | 118 | else: |
65 | 119 | resolved = resolve_local_path(urls_file, base_dir) |
66 | 120 | async with aiofiles.open(resolved) as f: |
|
0 commit comments