kcfinder-client 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- kcfinder_client-0.2.0/.github/workflows/ci.yml +34 -0
- kcfinder_client-0.2.0/.github/workflows/publish.yml +42 -0
- kcfinder_client-0.2.0/.gitignore +15 -0
- kcfinder_client-0.2.0/.markdownlint.rb +8 -0
- kcfinder_client-0.2.0/CLAUDE.md +146 -0
- kcfinder_client-0.2.0/LICENSE +21 -0
- kcfinder_client-0.2.0/PKG-INFO +175 -0
- kcfinder_client-0.2.0/README.md +154 -0
- kcfinder_client-0.2.0/docs/auth.md +178 -0
- kcfinder_client-0.2.0/docs/bulk.md +130 -0
- kcfinder_client-0.2.0/docs/directories.md +172 -0
- kcfinder_client-0.2.0/docs/exceptions.md +223 -0
- kcfinder_client-0.2.0/docs/files.md +171 -0
- kcfinder_client-0.2.0/docs/sync.md +132 -0
- kcfinder_client-0.2.0/examples/async_usage.py +42 -0
- kcfinder_client-0.2.0/examples/sync_usage.py +64 -0
- kcfinder_client-0.2.0/live.py +459 -0
- kcfinder_client-0.2.0/pyproject.toml +60 -0
- kcfinder_client-0.2.0/src/kcfinder_client/__init__.py +42 -0
- kcfinder_client-0.2.0/src/kcfinder_client/_core.py +177 -0
- kcfinder_client-0.2.0/src/kcfinder_client/async_client.py +214 -0
- kcfinder_client-0.2.0/src/kcfinder_client/auth.py +160 -0
- kcfinder_client-0.2.0/src/kcfinder_client/client.py +212 -0
- kcfinder_client-0.2.0/src/kcfinder_client/exceptions.py +96 -0
- kcfinder_client-0.2.0/src/kcfinder_client/models.py +36 -0
- kcfinder_client-0.2.0/src/kcfinder_client/sync.py +93 -0
- kcfinder_client-0.2.0/tasks.py +73 -0
- kcfinder_client-0.2.0/tests/__init__.py +0 -0
- kcfinder_client-0.2.0/tests/conftest.py +13 -0
- kcfinder_client-0.2.0/tests/test_async_client.py +326 -0
- kcfinder_client-0.2.0/tests/test_auth.py +186 -0
- kcfinder_client-0.2.0/tests/test_client.py +203 -0
- kcfinder_client-0.2.0/tests/test_core.py +225 -0
- kcfinder_client-0.2.0/tests/test_exceptions.py +133 -0
- kcfinder_client-0.2.0/tests/test_models.py +55 -0
- kcfinder_client-0.2.0/tests/test_sync.py +266 -0
- kcfinder_client-0.2.0/uv.lock +639 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
check:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
|
|
15
|
+
- name: Install uv
|
|
16
|
+
uses: astral-sh/setup-uv@v6
|
|
17
|
+
|
|
18
|
+
- name: Set up Python
|
|
19
|
+
run: uv python install 3.14
|
|
20
|
+
|
|
21
|
+
- name: Install dependencies
|
|
22
|
+
run: uv sync
|
|
23
|
+
|
|
24
|
+
- name: Lint
|
|
25
|
+
run: uv run ruff check .
|
|
26
|
+
|
|
27
|
+
- name: Format check
|
|
28
|
+
run: uv run ruff format --check .
|
|
29
|
+
|
|
30
|
+
- name: Typecheck
|
|
31
|
+
run: uv run pyrefly check
|
|
32
|
+
|
|
33
|
+
- name: Test
|
|
34
|
+
run: uv run pytest -v
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
id-token: write
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
publish:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
environment: pypi
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
|
|
17
|
+
- name: Install uv
|
|
18
|
+
uses: astral-sh/setup-uv@v6
|
|
19
|
+
|
|
20
|
+
- name: Set up Python
|
|
21
|
+
run: uv python install 3.14
|
|
22
|
+
|
|
23
|
+
- name: Install dependencies
|
|
24
|
+
run: uv sync
|
|
25
|
+
|
|
26
|
+
- name: Lint
|
|
27
|
+
run: uv run ruff check .
|
|
28
|
+
|
|
29
|
+
- name: Format check
|
|
30
|
+
run: uv run ruff format --check .
|
|
31
|
+
|
|
32
|
+
- name: Typecheck
|
|
33
|
+
run: uv run pyrefly check
|
|
34
|
+
|
|
35
|
+
- name: Test
|
|
36
|
+
run: uv run pytest -v
|
|
37
|
+
|
|
38
|
+
- name: Build
|
|
39
|
+
run: uv build
|
|
40
|
+
|
|
41
|
+
- name: Publish to PyPI
|
|
42
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# https://github.com/markdownlint/markdownlint/blob/main/docs/creating_styles.md
|
|
2
|
+
# https://github.com/markdownlint/markdownlint/blob/main/docs/RULES.md
|
|
3
|
+
all
|
|
4
|
+
rule 'MD007', :indent => 3
|
|
5
|
+
rule 'MD013', :ignore_code_blocks => true, :tables => false
|
|
6
|
+
rule 'MD029', :style => :ordered
|
|
7
|
+
exclude_rule 'MD033'
|
|
8
|
+
exclude_rule 'MD034'
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with
|
|
4
|
+
code in this repository.
|
|
5
|
+
|
|
6
|
+
## Project Overview
|
|
7
|
+
|
|
8
|
+
A standalone Python client library for KCFinder, a PHP web file manager.
|
|
9
|
+
Wraps KCFinder's HTTP action-dispatch API (`browse.php?act=<action>`) into a
|
|
10
|
+
clean Python interface with both sync and async clients. Designed to work with
|
|
11
|
+
any KCFinder instance, with built-in support for the HarmonySite auth
|
|
12
|
+
extension.
|
|
13
|
+
|
|
14
|
+
## Build & Development
|
|
15
|
+
|
|
16
|
+
Python package managed with `uv` and `pyproject.toml`. Requires Python 3.14+.
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
uv sync # Install dependencies
|
|
20
|
+
uv run pytest # Run all tests
|
|
21
|
+
uv run pytest tests/test_client.py::test_list_files # Run a single test
|
|
22
|
+
uv run pytest -x # Stop on first failure
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### Invoke Tasks
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
inv test # Run pytest
|
|
29
|
+
inv lint # Run ruff linter
|
|
30
|
+
inv format # Run ruff formatter
|
|
31
|
+
inv tc # Run pyrefly type checker
|
|
32
|
+
inv mdlint # Run markdownlint on docs (requires Docker)
|
|
33
|
+
inv check # Lint + format + typecheck + mdlint (run this frequently)
|
|
34
|
+
inv scan # Security scan (bandit + pip-audit)
|
|
35
|
+
inv build # Build package with uv
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Live Testing
|
|
39
|
+
|
|
40
|
+
Atomic live tests for every client method against a real KCFinder server.
|
|
41
|
+
Requires env vars (see `live.py` for details). Credentials are loaded via
|
|
42
|
+
`python-dotenv` from a `.env` file.
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
inv live.auth # Authenticate, confirm session
|
|
46
|
+
inv live.list # List files in root (--dir for subdirs)
|
|
47
|
+
inv live.tree # Root directory node with immediate subdirs
|
|
48
|
+
inv live.expand # List subdirectories (--dir for subdirs)
|
|
49
|
+
inv live.upload # Upload a test JPEG
|
|
50
|
+
inv live.download # Upload, download, verify bytes match
|
|
51
|
+
inv live.thumb # Upload image, get thumbnail
|
|
52
|
+
inv live.rename # Upload, rename, verify
|
|
53
|
+
inv live.delete # Upload, delete, verify gone
|
|
54
|
+
inv live.mkdir # Create test directory
|
|
55
|
+
inv live.list-dir # Create dir with file, list contents
|
|
56
|
+
inv live.rename-dir # Create dir, rename, verify
|
|
57
|
+
inv live.delete-dir # Create dir, delete, verify gone
|
|
58
|
+
inv live.copy # Upload, copy to dest dir
|
|
59
|
+
inv live.move # Upload, move to dest dir
|
|
60
|
+
inv live.bulk-delete # Upload 2 files, bulk delete
|
|
61
|
+
inv live.download-dir # Download dir as ZIP
|
|
62
|
+
inv live.download-sel # Download selected files as ZIP
|
|
63
|
+
inv live.cleanup # Remove all _test_* artifacts
|
|
64
|
+
inv live.all # Run all tests in order
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Package Structure
|
|
68
|
+
|
|
69
|
+
```text
|
|
70
|
+
src/kcfinder_client/
|
|
71
|
+
├── __init__.py # Public exports
|
|
72
|
+
├── _core.py # Shared request building, response parsing, error checking
|
|
73
|
+
├── async_client.py # AsyncKCFinderClient (httpx.AsyncClient)
|
|
74
|
+
├── client.py # KCFinderClient (httpx.Client, sync)
|
|
75
|
+
├── auth.py # BaseAuth, HarmonySiteAuth, SessionAuth, harmonysite_auth_from_env
|
|
76
|
+
├── models.py # FileInfo, DirTree, SyncResult dataclasses
|
|
77
|
+
├── exceptions.py # KCFinderError hierarchy
|
|
78
|
+
└── sync.py # SyncManager, SyncManagerSync (one-way push)
|
|
79
|
+
|
|
80
|
+
live.py # Live test tasks (inv live.*)
|
|
81
|
+
tasks.py # Invoke task definitions + live namespace registration
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Architecture
|
|
85
|
+
|
|
86
|
+
### Dual Client Pattern
|
|
87
|
+
|
|
88
|
+
Two client classes share core logic via private helpers in `_core.py`:
|
|
89
|
+
|
|
90
|
+
- **`AsyncKCFinderClient`** — async methods, uses `httpx.AsyncClient`
|
|
91
|
+
- **`KCFinderClient`** — sync methods, uses `httpx.Client`
|
|
92
|
+
|
|
93
|
+
Both are context managers that handle auth and cleanup. All request building,
|
|
94
|
+
response parsing, and error detection lives in `_core.py`.
|
|
95
|
+
|
|
96
|
+
### KCFinder Protocol
|
|
97
|
+
|
|
98
|
+
All operations POST to `browse.php?act=<action>` with form data (`dir`,
|
|
99
|
+
`file`, `files[]`). Key protocol details:
|
|
100
|
+
|
|
101
|
+
- **Session-based auth**: `PHPSESSID` cookie from login step
|
|
102
|
+
- **Required headers**: `Referer` (full browse URL) and
|
|
103
|
+
`X-Requested-With: XMLHttpRequest`
|
|
104
|
+
- **Auth query params**: HarmonySite's fork requires `bros_config` and
|
|
105
|
+
`brosseccheck` as query params on every request, not just the session init.
|
|
106
|
+
This is handled by `BaseAuth.get_query_params()`.
|
|
107
|
+
- **Dir param type prefix**: All `dir` params must include the file type
|
|
108
|
+
prefix (e.g., `images/subfolder`). The client handles this internally via
|
|
109
|
+
`prefix_dir()` — callers pass bare paths like `"subfolder"`.
|
|
110
|
+
- **Error handling**: KCFinder returns HTTP 200 even on errors — response body
|
|
111
|
+
must be inspected. Success is `{}` for mutating actions, JSON for queries.
|
|
112
|
+
Bulk action errors are arrays joined with `"; "`.
|
|
113
|
+
- **Thumbnail via GET**: `act=thumb` uses GET query params (dir, file), not
|
|
114
|
+
POST form data.
|
|
115
|
+
- **File scoping**: The `type=images` param scopes to `images/` under
|
|
116
|
+
`uploadDir` — this is the filesystem ceiling
|
|
117
|
+
|
|
118
|
+
### Auth Strategy Pattern
|
|
119
|
+
|
|
120
|
+
Auth is pluggable via `BaseAuth` subclasses. Each strategy implements
|
|
121
|
+
`authenticate(session)`, `authenticate_sync(session)`, `get_referer()`, and
|
|
122
|
+
optionally `get_query_params()`.
|
|
123
|
+
|
|
124
|
+
- **`HarmonySiteAuth`**: Logs in via `dbaction.php`, then initializes KCFinder
|
|
125
|
+
session with `bros_config`/`brosseccheck` query params. These params are
|
|
126
|
+
also sent on every subsequent request via `get_query_params()`.
|
|
127
|
+
- **`SessionAuth`**: For standard KCFinder installs where the session is
|
|
128
|
+
pre-established
|
|
129
|
+
- **`harmonysite_auth_from_env()`**: Factory that reads credentials from
|
|
130
|
+
environment variables
|
|
131
|
+
|
|
132
|
+
### SyncManager
|
|
133
|
+
|
|
134
|
+
One-way push sync on top of the client. Compares local vs remote files by
|
|
135
|
+
name + size. Supports dry run. Available in both async (`SyncManager`) and
|
|
136
|
+
sync (`SyncManagerSync`) variants.
|
|
137
|
+
|
|
138
|
+
## Design Principles
|
|
139
|
+
|
|
140
|
+
- Dual sync/async clients sharing logic via `_core.py` (same pattern as
|
|
141
|
+
anthropic/openai SDKs)
|
|
142
|
+
- Auth classes take explicit args — no credential sourcing; consumers handle
|
|
143
|
+
that
|
|
144
|
+
- `harmonysite_auth_from_env()` factory for deployment contexts (n8n, CI)
|
|
145
|
+
- Primary consumer is the `witnessmusic` project via local path dependency
|
|
146
|
+
(`uv` source)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 cyberops7
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: kcfinder-client
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Python client library for KCFinder file manager
|
|
5
|
+
Project-URL: Changelog, https://github.com/cyberops7/kcfinder-client/releases
|
|
6
|
+
Project-URL: Issues, https://github.com/cyberops7/kcfinder-client/issues
|
|
7
|
+
Project-URL: Repository, https://github.com/cyberops7/kcfinder-client
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Keywords: file-manager,httpx,kcfinder,php
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
16
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
17
|
+
Classifier: Typing :: Typed
|
|
18
|
+
Requires-Python: >=3.14
|
|
19
|
+
Requires-Dist: httpx>=0.28
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
|
|
22
|
+
# kcfinder-client
|
|
23
|
+
|
|
24
|
+
[](https://github.com/cyberops7/kcfinder-client/actions/workflows/ci.yml)
|
|
25
|
+

|
|
26
|
+
[](https://opensource.org/licenses/MIT)
|
|
27
|
+
|
|
28
|
+
A Python client library for [KCFinder](https://github.com/sunhater/kcfinder),
|
|
29
|
+
a PHP web file manager. Wraps KCFinder's HTTP action-dispatch API into a clean
|
|
30
|
+
Python interface with both async and sync clients.
|
|
31
|
+
|
|
32
|
+
> [!NOTE]
|
|
33
|
+
> Designed for and tested against HarmonySite's KCFinder fork.
|
|
34
|
+
> May work with other KCFinder instances via `SessionAuth`, but this
|
|
35
|
+
> has not been tested.
|
|
36
|
+
|
|
37
|
+
## Table of Contents
|
|
38
|
+
|
|
39
|
+
- [Install](#install)
|
|
40
|
+
- [Quick Start](#quick-start)
|
|
41
|
+
- [Auth from Environment Variables](#auth-from-environment-variables)
|
|
42
|
+
- [Sync Files with SyncManager](#sync-files-with-syncmanager)
|
|
43
|
+
- [Documentation](#documentation)
|
|
44
|
+
|
|
45
|
+
## Install
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
# uv
|
|
49
|
+
uv add kcfinder-client
|
|
50
|
+
|
|
51
|
+
# pip
|
|
52
|
+
pip install kcfinder-client
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Quick Start
|
|
56
|
+
|
|
57
|
+
### Async (recommended)
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
import asyncio
|
|
61
|
+
import os
|
|
62
|
+
from kcfinder_client import AsyncKCFinderClient, HarmonySiteAuth
|
|
63
|
+
|
|
64
|
+
auth = HarmonySiteAuth(
|
|
65
|
+
login_url="https://example.harmonysong.com/dbaction.php",
|
|
66
|
+
browse_url="https://example.harmonysong.com/kcfinder/browse.php",
|
|
67
|
+
username="admin",
|
|
68
|
+
password="secret",
|
|
69
|
+
bros_config=(
|
|
70
|
+
'a:3:{s:9:"uploadDir";s:6:"files/";'
|
|
71
|
+
's:9:"thumbsDir";s:14:"files/.thumbs/";'
|
|
72
|
+
's:9:"uploadURL";s:0:"";}'
|
|
73
|
+
),
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
async def main():
|
|
77
|
+
browse_url = "https://example.harmonysong.com/kcfinder/browse.php"
|
|
78
|
+
async with AsyncKCFinderClient(browse_url, auth) as client:
|
|
79
|
+
files = await client.list_files("banners")
|
|
80
|
+
for f in files:
|
|
81
|
+
print(f.name, f.size)
|
|
82
|
+
|
|
83
|
+
asyncio.run(main())
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Sync
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
from kcfinder_client import KCFinderClient, HarmonySiteAuth
|
|
90
|
+
|
|
91
|
+
auth = HarmonySiteAuth(
|
|
92
|
+
login_url="https://example.harmonysong.com/dbaction.php",
|
|
93
|
+
browse_url="https://example.harmonysong.com/kcfinder/browse.php",
|
|
94
|
+
username="admin",
|
|
95
|
+
password="secret",
|
|
96
|
+
bros_config=(
|
|
97
|
+
'a:3:{s:9:"uploadDir";s:6:"files/";'
|
|
98
|
+
's:9:"thumbsDir";s:14:"files/.thumbs/";'
|
|
99
|
+
's:9:"uploadURL";s:0:"";}'
|
|
100
|
+
),
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
browse_url = "https://example.harmonysong.com/kcfinder/browse.php"
|
|
104
|
+
with KCFinderClient(browse_url, auth) as client:
|
|
105
|
+
files = client.list_files("banners")
|
|
106
|
+
for f in files:
|
|
107
|
+
print(f.name, f.size)
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Auth from Environment Variables
|
|
111
|
+
|
|
112
|
+
Load credentials from the environment rather than hardcoding them:
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
export KCFINDER_LOGIN_URL=https://example.harmonysong.com/dbaction.php
|
|
116
|
+
export KCFINDER_BROWSE_URL=https://example.harmonysong.com/kcfinder/browse.php
|
|
117
|
+
export KCFINDER_USERNAME=admin
|
|
118
|
+
export KCFINDER_PASSWORD=secret
|
|
119
|
+
export KCFINDER_BROS_CONFIG='a:3:{s:9:"uploadDir";s:6:"files/";s:9:"thumbsDir";s:14:"files/.thumbs/";s:9:"uploadURL";s:0:"";}'
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
```python
|
|
123
|
+
import os
|
|
124
|
+
from kcfinder_client import AsyncKCFinderClient, harmonysite_auth_from_env
|
|
125
|
+
import asyncio
|
|
126
|
+
|
|
127
|
+
async def main():
|
|
128
|
+
auth = harmonysite_auth_from_env()
|
|
129
|
+
browse_url = os.environ["KCFINDER_BROWSE_URL"]
|
|
130
|
+
async with AsyncKCFinderClient(browse_url, auth) as client:
|
|
131
|
+
files = await client.list_files()
|
|
132
|
+
print(f"Found {len(files)} files")
|
|
133
|
+
|
|
134
|
+
asyncio.run(main())
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Sync Files with SyncManager
|
|
138
|
+
|
|
139
|
+
One-way push sync to make a remote directory match a local one:
|
|
140
|
+
|
|
141
|
+
```python
|
|
142
|
+
import os
|
|
143
|
+
from pathlib import Path
|
|
144
|
+
from kcfinder_client import KCFinderClient, SyncManagerSync, harmonysite_auth_from_env
|
|
145
|
+
|
|
146
|
+
auth = harmonysite_auth_from_env()
|
|
147
|
+
browse_url = os.environ["KCFINDER_BROWSE_URL"]
|
|
148
|
+
|
|
149
|
+
with KCFinderClient(browse_url, auth) as client:
|
|
150
|
+
sync = SyncManagerSync(client)
|
|
151
|
+
|
|
152
|
+
# Dry run first to preview changes
|
|
153
|
+
result = sync.push("banners", Path("./banners"), dry_run=True)
|
|
154
|
+
print(f"Would upload: {result.uploaded}")
|
|
155
|
+
print(f"Would delete: {result.deleted}")
|
|
156
|
+
print(f"Would skip: {result.skipped}")
|
|
157
|
+
|
|
158
|
+
# Apply the sync
|
|
159
|
+
result = sync.push("banners", Path("./banners"))
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## Documentation
|
|
163
|
+
|
|
164
|
+
- [Auth setup](docs/auth.md) — HarmonySiteAuth, SessionAuth, env vars
|
|
165
|
+
- [File operations](docs/files.md) — list, upload, download, delete, rename,
|
|
166
|
+
thumbnails
|
|
167
|
+
- [Directory operations](docs/directories.md) — tree, expand, create, rename,
|
|
168
|
+
delete
|
|
169
|
+
- [Bulk operations](docs/bulk.md) — copy, move, bulk delete, download selected
|
|
170
|
+
- [Sync manager](docs/sync.md) — push sync, dry run, SyncResult
|
|
171
|
+
- [Exceptions](docs/exceptions.md) — error handling, exception hierarchy
|
|
172
|
+
|
|
173
|
+
## License
|
|
174
|
+
|
|
175
|
+
MIT
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# kcfinder-client
|
|
2
|
+
|
|
3
|
+
[](https://github.com/cyberops7/kcfinder-client/actions/workflows/ci.yml)
|
|
4
|
+

|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
|
|
7
|
+
A Python client library for [KCFinder](https://github.com/sunhater/kcfinder),
|
|
8
|
+
a PHP web file manager. Wraps KCFinder's HTTP action-dispatch API into a clean
|
|
9
|
+
Python interface with both async and sync clients.
|
|
10
|
+
|
|
11
|
+
> [!NOTE]
|
|
12
|
+
> Designed for and tested against HarmonySite's KCFinder fork.
|
|
13
|
+
> May work with other KCFinder instances via `SessionAuth`, but this
|
|
14
|
+
> has not been tested.
|
|
15
|
+
|
|
16
|
+
## Table of Contents
|
|
17
|
+
|
|
18
|
+
- [Install](#install)
|
|
19
|
+
- [Quick Start](#quick-start)
|
|
20
|
+
- [Auth from Environment Variables](#auth-from-environment-variables)
|
|
21
|
+
- [Sync Files with SyncManager](#sync-files-with-syncmanager)
|
|
22
|
+
- [Documentation](#documentation)
|
|
23
|
+
|
|
24
|
+
## Install
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
# uv
|
|
28
|
+
uv add kcfinder-client
|
|
29
|
+
|
|
30
|
+
# pip
|
|
31
|
+
pip install kcfinder-client
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Quick Start
|
|
35
|
+
|
|
36
|
+
### Async (recommended)
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
import asyncio
|
|
40
|
+
import os
|
|
41
|
+
from kcfinder_client import AsyncKCFinderClient, HarmonySiteAuth
|
|
42
|
+
|
|
43
|
+
auth = HarmonySiteAuth(
|
|
44
|
+
login_url="https://example.harmonysong.com/dbaction.php",
|
|
45
|
+
browse_url="https://example.harmonysong.com/kcfinder/browse.php",
|
|
46
|
+
username="admin",
|
|
47
|
+
password="secret",
|
|
48
|
+
bros_config=(
|
|
49
|
+
'a:3:{s:9:"uploadDir";s:6:"files/";'
|
|
50
|
+
's:9:"thumbsDir";s:14:"files/.thumbs/";'
|
|
51
|
+
's:9:"uploadURL";s:0:"";}'
|
|
52
|
+
),
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
async def main():
|
|
56
|
+
browse_url = "https://example.harmonysong.com/kcfinder/browse.php"
|
|
57
|
+
async with AsyncKCFinderClient(browse_url, auth) as client:
|
|
58
|
+
files = await client.list_files("banners")
|
|
59
|
+
for f in files:
|
|
60
|
+
print(f.name, f.size)
|
|
61
|
+
|
|
62
|
+
asyncio.run(main())
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Sync
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
from kcfinder_client import KCFinderClient, HarmonySiteAuth
|
|
69
|
+
|
|
70
|
+
auth = HarmonySiteAuth(
|
|
71
|
+
login_url="https://example.harmonysong.com/dbaction.php",
|
|
72
|
+
browse_url="https://example.harmonysong.com/kcfinder/browse.php",
|
|
73
|
+
username="admin",
|
|
74
|
+
password="secret",
|
|
75
|
+
bros_config=(
|
|
76
|
+
'a:3:{s:9:"uploadDir";s:6:"files/";'
|
|
77
|
+
's:9:"thumbsDir";s:14:"files/.thumbs/";'
|
|
78
|
+
's:9:"uploadURL";s:0:"";}'
|
|
79
|
+
),
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
browse_url = "https://example.harmonysong.com/kcfinder/browse.php"
|
|
83
|
+
with KCFinderClient(browse_url, auth) as client:
|
|
84
|
+
files = client.list_files("banners")
|
|
85
|
+
for f in files:
|
|
86
|
+
print(f.name, f.size)
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Auth from Environment Variables
|
|
90
|
+
|
|
91
|
+
Load credentials from the environment rather than hardcoding them:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
export KCFINDER_LOGIN_URL=https://example.harmonysong.com/dbaction.php
|
|
95
|
+
export KCFINDER_BROWSE_URL=https://example.harmonysong.com/kcfinder/browse.php
|
|
96
|
+
export KCFINDER_USERNAME=admin
|
|
97
|
+
export KCFINDER_PASSWORD=secret
|
|
98
|
+
export KCFINDER_BROS_CONFIG='a:3:{s:9:"uploadDir";s:6:"files/";s:9:"thumbsDir";s:14:"files/.thumbs/";s:9:"uploadURL";s:0:"";}'
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
import os
|
|
103
|
+
from kcfinder_client import AsyncKCFinderClient, harmonysite_auth_from_env
|
|
104
|
+
import asyncio
|
|
105
|
+
|
|
106
|
+
async def main():
|
|
107
|
+
auth = harmonysite_auth_from_env()
|
|
108
|
+
browse_url = os.environ["KCFINDER_BROWSE_URL"]
|
|
109
|
+
async with AsyncKCFinderClient(browse_url, auth) as client:
|
|
110
|
+
files = await client.list_files()
|
|
111
|
+
print(f"Found {len(files)} files")
|
|
112
|
+
|
|
113
|
+
asyncio.run(main())
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Sync Files with SyncManager
|
|
117
|
+
|
|
118
|
+
One-way push sync to make a remote directory match a local one:
|
|
119
|
+
|
|
120
|
+
```python
|
|
121
|
+
import os
|
|
122
|
+
from pathlib import Path
|
|
123
|
+
from kcfinder_client import KCFinderClient, SyncManagerSync, harmonysite_auth_from_env
|
|
124
|
+
|
|
125
|
+
auth = harmonysite_auth_from_env()
|
|
126
|
+
browse_url = os.environ["KCFINDER_BROWSE_URL"]
|
|
127
|
+
|
|
128
|
+
with KCFinderClient(browse_url, auth) as client:
|
|
129
|
+
sync = SyncManagerSync(client)
|
|
130
|
+
|
|
131
|
+
# Dry run first to preview changes
|
|
132
|
+
result = sync.push("banners", Path("./banners"), dry_run=True)
|
|
133
|
+
print(f"Would upload: {result.uploaded}")
|
|
134
|
+
print(f"Would delete: {result.deleted}")
|
|
135
|
+
print(f"Would skip: {result.skipped}")
|
|
136
|
+
|
|
137
|
+
# Apply the sync
|
|
138
|
+
result = sync.push("banners", Path("./banners"))
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Documentation
|
|
142
|
+
|
|
143
|
+
- [Auth setup](docs/auth.md) — HarmonySiteAuth, SessionAuth, env vars
|
|
144
|
+
- [File operations](docs/files.md) — list, upload, download, delete, rename,
|
|
145
|
+
thumbnails
|
|
146
|
+
- [Directory operations](docs/directories.md) — tree, expand, create, rename,
|
|
147
|
+
delete
|
|
148
|
+
- [Bulk operations](docs/bulk.md) — copy, move, bulk delete, download selected
|
|
149
|
+
- [Sync manager](docs/sync.md) — push sync, dry run, SyncResult
|
|
150
|
+
- [Exceptions](docs/exceptions.md) — error handling, exception hierarchy
|
|
151
|
+
|
|
152
|
+
## License
|
|
153
|
+
|
|
154
|
+
MIT
|