Documentation
¶
Overview ¶
Package cloud115 – raw HTTP client for the 115 cloud API.
This file implements the API struct: a thin HTTP layer that handles cookies, rate-limiting, retry, 429/405 recovery, and M115/EC115 encryption. Higher-level caching lives in client.go / cached_client.go.
Package cloud115 – CachedClient: cache-first reads + write-through writes.
Client wraps API and Cache into a single high-level facade. Every read checks the SQLite cache first (respecting TTL); every write calls the API then updates the cache so subsequent reads stay warm.
Package cloud115 implements M115 (RSA+XOR) and EC115 (ECDH-P224+AES-CBC+LZ4) encryption for the 115 cloud storage API.
Ported from py115 (MIT license, github.com/deadblue/py115).
Index ¶
- Constants
- func DefaultCachePath() string
- func GenerateM115Key() []byte
- func M115Decode(key []byte, ciphertext string) string
- func M115Encode(key []byte, plaintext string) string
- type API
- func (a *API) BatchRename(renames map[string]string) (map[string]any, error)
- func (a *API) CheckLogin() bool
- func (a *API) Delete(fileIDs []string) (map[string]any, error)
- func (a *API) DownloadURL(pickCode, userAgent string) (string, error)
- func (a *API) ExportTree(dirID string) (string, error)
- func (a *API) GetCookies() string
- func (a *API) GetDirID(path string) (string, error)
- func (a *API) ListFiles(dirID string, limit, offset int) ([]map[string]any, error)
- func (a *API) ListFilesAll(dirID string) ([]map[string]any, error)
- func (a *API) Mkdir(parentID, name string) (map[string]any, error)
- func (a *API) Move(fileIDs []string, targetDirID string) (map[string]any, error)
- func (a *API) QRGetToken(app string) (*QRSession, error)
- func (a *API) QRLogin(app string) error
- func (a *API) QRWaitAndLogin(sess *QRSession) error
- func (a *API) RapidUpload(dirID, filename string, fileSize int64, fileSHA1 string, ...) (map[string]any, error)
- func (a *API) Rename(fileID, newName string) (map[string]any, error)
- func (a *API) RenewCookies(app string) bool
- func (a *API) SaveCookies(path string) error
- func (a *API) Search(keyword, dirID string) ([]map[string]any, error)
- func (a *API) UploadFile(localPath, targetDirID, filename string) (map[string]any, error)
- func (a *API) UploadInfo() (userID, userKey string, err error)
- type BatchRenameItem
- type Cache
- func (c *Cache) AddEntry(parentCID string, entry Entry)
- func (c *Cache) Clear()
- func (c *Cache) ClearMetadata()
- func (c *Cache) ClearTree()
- func (c *Cache) Close() error
- func (c *Cache) DeleteDirMeta(cid string)
- func (c *Cache) DeletePathPrefix(prefix string)
- func (c *Cache) FindEntries(parentCID, name string) []Entry
- func (c *Cache) FindEntry(parentCID, name string) *Entry
- func (c *Cache) GetDirEntries(cid string) []Entry
- func (c *Cache) GetDirTS(cid string) (ts int64, ok bool)
- func (c *Cache) GetPath(path string) (cid string, ts int64, ok bool)
- func (c *Cache) GetRateLimit(name string) RateLimitState
- func (c *Cache) GetSnapshotMeta() SnapshotMeta
- func (c *Cache) GetTreeEntries(category string) []TreeEntry
- func (c *Cache) IncrementMinuteCount(name string)
- func (c *Cache) InvalidateDir(cid string)
- func (c *Cache) MoveEntry(oldParentCID, nodeID, newParentCID string)
- func (c *Cache) RemoveEntry(parentCID, name string)
- func (c *Cache) RemoveEntryByID(parentCID, nodeID string)
- func (c *Cache) RenameEntry(parentCID, nodeID, newName string)
- func (c *Cache) SetDirListing(cid string, entries []Entry)
- func (c *Cache) SetPath(path, cid string)
- func (c *Cache) SetRateLimit(name string, state RateLimitState)
- func (c *Cache) SetTree(entries []TreeEntry, rootPath string)
- func (c *Cache) StaleDirCount(listingTTL int) int
- func (c *Cache) Stats() CacheStats
- func (c *Cache) TreeStats() (total, videos, nfos int)
- func (c *Cache) TryAcquireSlot(name string, now, minInterval float64, qpm int) bool
- type CacheStats
- type Client
- func (c *Client) BatchRename(renames []BatchRenameItem) error
- func (c *Client) CacheClear() error
- func (c *Client) CacheStatus() (*CacheStats, error)
- func (c *Client) CheckLogin() bool
- func (c *Client) Close() error
- func (c *Client) Delete(paths []string) error
- func (c *Client) DeleteByIDs(fids []string, refreshDirs []string) error
- func (c *Client) DownloadURL(pickCode, userAgent string) (string, error)
- func (c *Client) ExportTree(path string) (string, error)
- func (c *Client) FindFile(path string) (*Entry, error)
- func (c *Client) GetCookies() string
- func (c *Client) Invalidate(path string) error
- func (c *Client) ListDir(path string) ([]Entry, error)
- func (c *Client) ListDirUncached(path string) ([]Entry, error)
- func (c *Client) Mkdir(path string, parents bool) (string, error)
- func (c *Client) Move(srcPaths []string, destPath string) error
- func (c *Client) QRGetToken(app string) (*QRSession, error)
- func (c *Client) QRLogin(app string) error
- func (c *Client) QRWaitAndLogin(sess *QRSession) error
- func (c *Client) RapidUpload(localPath, remoteDir string) (*RapidResult, error)
- func (c *Client) RefreshDir(path string) ([]Entry, error)
- func (c *Client) RefreshPaths(paths []string) error
- func (c *Client) Rename(path, newName string) error
- func (c *Client) RenewCookies() bool
- func (c *Client) ResolvePath(path string) (string, error)
- func (c *Client) SaveCookies(path string) error
- func (c *Client) SaveTree(text string, videoExts []string, rootPath string) (int, error)
- func (c *Client) Search(keyword, path string) ([]Entry, error)
- func (c *Client) Stat(path string) (*Entry, error)
- func (c *Client) StreamURL(path, userAgent string) (string, error)
- func (c *Client) TreeEntries(category string) ([]TreeEntry, error)
- func (c *Client) TreeStats() (*TreeStats, error)
- func (c *Client) Upload(localPath, remoteDir, filename string) (map[string]any, error)
- func (c *Client) Walk(path string, maxDepth int, fn func(Entry, string, int) error) error
- func (c *Client) Warm(path string, depth int, progress func(string, int)) error
- type ClientOption
- type EC115Cipher
- type Entry
- type QRSession
- type RapidResult
- type RateLimitState
- type RateLimiter
- type SnapshotMeta
- type TreeEntry
- type TreeStats
Constants ¶
const ( WebAPI = "https://webapi.115.com" ProAPI = "https://proapi.115.com" QRAPI = "https://qrcodeapi.115.com" PassportAPI = "https://passportapi.115.com" )
API endpoint base URLs.
const TokenSalt = "Qclm8MGWUv59TnrR0XPg"
TokenSalt is exported for callers that need the HMAC-style token salt.
Variables ¶
This section is empty.
Functions ¶
func DefaultCachePath ¶
func DefaultCachePath() string
DefaultCachePath returns the default path for the SQLite cache database.
func GenerateM115Key ¶
func GenerateM115Key() []byte
GenerateM115Key returns 16 cryptographically random bytes suitable for use as an M115 session key.
func M115Decode ¶
M115Decode decodes a base64 ciphertext from the M115 download channel.
Pipeline: base64 → RSA decrypt → extract serverKey(16) → XOR(deriveKey(serverKey,12)) → reverse → XOR(deriveKey(key,4)).
func M115Encode ¶
M115Encode encodes a plaintext string for the M115 download channel.
Pipeline: XOR(deriveKey(key,4)) → reverse → XOR(clientKey) → prepend key → RSA encrypt → base64.
Types ¶
type API ¶
type API struct {
// contains filtered or unexported fields
}
API is a low-level 115 cloud HTTP client (cookie mode only). Every public method maps to one HTTP endpoint.
func NewAPI ¶
NewAPI constructs an API client from a cookie string. cache may be nil (disables rate-limit persistence).
func (*API) BatchRename ¶
BatchRename renames multiple files in one API call. renames maps fileID newName.
func (*API) CheckLogin ¶
CheckLogin returns true if the current cookies are valid.
func (*API) DownloadURL ¶
DownloadURL returns the download URL for the given pick code. userAgent is sent in the request (affects whether CDN gives a direct URL).
func (*API) ExportTree ¶
ExportTree exports a directory tree and returns the UTF-16LE decoded text.
func (*API) GetCookies ¶
GetCookies returns the current cookie string.
func (*API) GetDirID ¶
GetDirID resolves an absolute path string to a directory ID. Returns "" when the path does not exist (including when the API returns id=0).
func (*API) ListFilesAll ¶
ListFilesAll returns all entries in dirID, handling pagination automatically.
func (*API) QRGetToken ¶
QRGetToken fetches a QR login token and returns the session (phase 1). Call QRWaitAndLogin with the returned session to complete the login (phase 2).
func (*API) QRWaitAndLogin ¶
QRWaitAndLogin polls for QR scan completion and finalizes login (phase 2). Updates the API cookie string on success.
func (*API) RapidUpload ¶
func (a *API) RapidUpload(dirID, filename string, fileSize int64, fileSHA1 string, fileStream io.ReadSeeker) (map[string]any, error)
RapidUpload attempts an instant (hash-match) upload. Returns status 2 on success.
func (*API) RenewCookies ¶
RenewCookies attempts to silently refresh the session cookies via QR token flow.
func (*API) SaveCookies ¶
SaveCookies updates the TOML config file at path, replacing the cookies value.
func (*API) UploadFile ¶
UploadFile uploads a local file to targetDirID via OSS.
func (*API) UploadInfo ¶
UploadInfo returns the userID and userKey required for rapid-upload requests.
type BatchRenameItem ¶
BatchRenameItem pairs a cloud path with its desired new name.
type Cache ¶
type Cache struct {
// contains filtered or unexported fields
}
Cache is a SQLite-backed file/directory cache for cloud115. Every public method commits immediately (or runs in its own transaction).
func NewCache ¶
NewCache opens (or creates) the SQLite cache at dbPath. Pass an empty string to use the default path.
func (*Cache) ClearMetadata ¶
func (c *Cache) ClearMetadata()
ClearMetadata clears all cache data except rate_limit.
func (*Cache) DeleteDirMeta ¶
DeleteDirMeta marks directory cid as stale (removes meta, keeps entries).
func (*Cache) DeletePathPrefix ¶
DeletePathPrefix deletes path itself and every entry whose path starts with prefix + "/".
func (*Cache) FindEntries ¶
FindEntries returns all entries matching name inside parentCID.
func (*Cache) GetDirEntries ¶
GetDirEntries returns cached entries for cid, dirs first then by name.
func (*Cache) GetRateLimit ¶
func (c *Cache) GetRateLimit(name string) RateLimitState
GetRateLimit returns the rate-limit state for name (defaults all to 0).
func (*Cache) GetSnapshotMeta ¶
func (c *Cache) GetSnapshotMeta() SnapshotMeta
GetSnapshotMeta returns the snapshot metadata recorded by SetTree.
func (*Cache) GetTreeEntries ¶
GetTreeEntries returns all tree entries, optionally filtered by a category path prefix (entries whose path starts with category + "/" or equals category).
func (*Cache) IncrementMinuteCount ¶
IncrementMinuteCount atomically bumps minute_count for name (creates row if needed).
func (*Cache) InvalidateDir ¶
InvalidateDir removes both the dir_meta row and all dir_entry rows for cid.
func (*Cache) MoveEntry ¶
MoveEntry moves the entry identified by nodeID from oldParentCID to newParentCID.
func (*Cache) RemoveEntry ¶
RemoveEntry deletes all entries with the given name from parentCID.
func (*Cache) RemoveEntryByID ¶
RemoveEntryByID deletes a specific entry identified by nodeID within parentCID.
func (*Cache) RenameEntry ¶
RenameEntry renames the entry identified by nodeID within parentCID.
func (*Cache) SetDirListing ¶
SetDirListing atomically replaces the cached listing for directory cid.
func (*Cache) SetRateLimit ¶
func (c *Cache) SetRateLimit(name string, state RateLimitState)
SetRateLimit upserts rate-limit fields, always overwriting all columns.
func (*Cache) StaleDirCount ¶
StaleDirCount returns the number of directories whose listing has expired.
func (*Cache) Stats ¶
func (c *Cache) Stats() CacheStats
Stats returns a summary of cache contents and database file size.
type CacheStats ¶
type CacheStats struct {
PathCount int `json:"path_count"`
DirCount int `json:"dir_count"`
EntryCount int `json:"entry_count"`
TreeEntries int `json:"tree_entries"`
DBSizeBytes int64 `json:"db_size_bytes"`
RateLimit map[string]RateLimitState `json:"rate_limit"`
}
CacheStats summarises the current state of the cache database.
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client is a high-level 115 cloud client with transparent SQLite caching.
func NewClient ¶
func NewClient(cookies string, opts ...ClientOption) (*Client, error)
NewClient creates a Client. cookies is the 115 cookie string.
func (*Client) BatchRename ¶
func (c *Client) BatchRename(renames []BatchRenameItem) error
BatchRename renames multiple files in a single API call.
func (*Client) CacheClear ¶
CacheClear clears all cache metadata (keeps rate-limit state).
func (*Client) CacheStatus ¶
func (c *Client) CacheStatus() (*CacheStats, error)
CacheStatus returns a summary of the current cache state.
func (*Client) CheckLogin ¶
CheckLogin returns true if the current cookies are valid.
func (*Client) DeleteByIDs ¶
DeleteByIDs deletes files by their file IDs. optionally refreshing dirs after the deletion.
func (*Client) DownloadURL ¶
DownloadURL returns the download URL for a pick code (direct API, no cache).
func (*Client) ExportTree ¶
ExportTree exports the directory tree at path as text.
func (*Client) GetCookies ¶
GetCookies returns the current cookie string.
func (*Client) Invalidate ¶
Invalidate removes cache entries for path and everything beneath it.
func (*Client) ListDirUncached ¶
ListDirUncached bypasses the cache and fetches the listing directly from the API. Useful for dedup scenarios where stale cache must not be trusted.
func (*Client) Mkdir ¶
Mkdir creates a directory at path. If parents is true, intermediate directories are created as needed. Returns the new directory cid.
func (*Client) QRGetToken ¶
QRGetToken fetches a QR login token (phase 1 of two-phase login).
func (*Client) QRLogin ¶
QRLogin performs an interactive QR-code login and refreshes client cookies.
func (*Client) QRWaitAndLogin ¶
QRWaitAndLogin polls for QR scan and finalizes login (phase 2).
func (*Client) RapidUpload ¶
func (c *Client) RapidUpload(localPath, remoteDir string) (*RapidResult, error)
RapidUpload attempts an instant (hash-match) upload.
func (*Client) RefreshDir ¶
RefreshDir forces a refresh of path's listing regardless of TTL.
func (*Client) RefreshPaths ¶
RefreshPaths refreshes multiple directories, deduplicating first.
func (*Client) RenewCookies ¶
RenewCookies attempts a silent cookie refresh and returns true on success.
func (*Client) ResolvePath ¶
ResolvePath resolves a cloud path to a directory cid. Returns "0" for root. Returns an error wrapping fs.ErrNotExist if missing.
func (*Client) SaveCookies ¶
SaveCookies updates the TOML config file at path with the current cookies.
func (*Client) SaveTree ¶
SaveTree parses the 115 export-tree text and saves entries to the cache. videoExts is the set of lowercase extensions (e.g. {".mkv", ".mp4"}). Returns the number of entries saved.
func (*Client) TreeEntries ¶
TreeEntries returns tree entries, optionally filtered by category path prefix.
func (*Client) Upload ¶
Upload uploads a local file to remoteDir. If filename is empty the local file's base name is used. Returns API result data or nil.
type ClientOption ¶
type ClientOption func(*Client)
ClientOption configures a Client.
func WithCacheDir ¶
func WithCacheDir(dir string) ClientOption
WithCacheDir overrides the directory used for cache.db.
func WithListingTTL ¶
func WithListingTTL(d time.Duration) ClientOption
WithListingTTL overrides the directory-listing TTL (default 1 h).
func WithPathTTL ¶
func WithPathTTL(d time.Duration) ClientOption
WithPathTTL overrides the path→cid TTL (default 24 h).
func WithStats ¶
func WithStats(s *logging.Stats) ClientOption
WithStats attaches a Stats tracker to the client for call counting.
type EC115Cipher ¶
type EC115Cipher struct {
// contains filtered or unexported fields
}
EC115Cipher holds all state for a single EC115 session.
func NewEC115Cipher ¶
func NewEC115Cipher() (*EC115Cipher, error)
NewEC115Cipher generates a fresh P-224 key pair, performs ECDH with the 115 server public key, and derives the AES session key and IV.
func (*EC115Cipher) Decode ¶
func (c *EC115Cipher) Decode(data []byte) ([]byte, error)
Decode AES-128-CBC decrypts then LZ4-block-decompresses the server response.
Format: ciphertext (all but last 12 bytes) | trailer (12 bytes) Trailer bytes 0–3 XOR'd with byte 7 give the total uncompressed size (uint32 LE). The decrypted plaintext is a sequence of chunks:
uint16 LE src_size | src_size bytes of lz4 block
Chunks are decompressed in at most 8 KiB steps until dst_size bytes are recovered.
func (*EC115Cipher) Encode ¶
func (c *EC115Cipher) Encode(data []byte) []byte
Encode AES-128-CBC encrypts data. Input is zero-padded to a 16-byte boundary if not already aligned (no padding added when len(data)%16 == 0).
func (*EC115Cipher) EncodeToken ¶
func (c *EC115Cipher) EncodeToken() string
EncodeToken builds the k_ec query parameter from the client public key.
Pack layout (little-endian uint32):
pubKey[:15] | 0x00 | uint32(115) | uint32(timestamp) | pubKey[15:] | 0x00 | uint32(1) = 15 + 1 + 4 + 4 + 15 + 1 + 4 = 44 bytes
The first 24 bytes are XOR'd with random r1; the remaining bytes with r2. A CRC32 of (crcSalt + token) is appended as a little-endian uint32.
type Entry ¶
type Entry struct {
Name string `json:"name"`
Type string `json:"type"` // "dir" or "file"
NodeID string `json:"node_id"`
Size int64 `json:"size,omitempty"`
PickCode string `json:"pick_code,omitempty"`
CID string `json:"cid,omitempty"` // convenience: same as NodeID for dirs
FID string `json:"fid,omitempty"` // convenience: same as NodeID for files
}
Entry represents a single file or directory returned by the cloud API.
type QRSession ¶
type QRSession struct {
UID string `json:"uid"`
Time string `json:"time"`
Sign string `json:"sign"`
App string `json:"app"`
QRURL string `json:"qr_url"`
}
QRSession holds the state needed for a two-phase QR login.
type RapidResult ¶
RapidResult holds the result of a rapid-upload attempt.
type RateLimitState ¶
type RateLimitState struct {
CooldownUntil float64 `json:"cooldown_until"`
LastRequest float64 `json:"last_request"`
MinuteStart float64 `json:"minute_start"`
MinuteCount int `json:"minute_count"`
}
RateLimitState holds per-endpoint rate-limit counters.
type RateLimiter ¶
type RateLimiter struct {
// contains filtered or unexported fields
}
RateLimiter enforces QPS and QPM limits, persisting state to SQLite via Cache. When cache is nil the limiter still tracks request counts but does not block.
func NewRateLimiter ¶
func NewRateLimiter(name string, qps float64, qpm int, cache *Cache) *RateLimiter
NewRateLimiter creates a RateLimiter. name – key used in the rate_limit table (e.g. "api", "download"). qps – target queries per second (e.g. 0.5 means one request every 2 s). qpm – maximum requests per rolling 60-second window. cache – pass nil to disable persistence (no blocking beyond mutex).
func (*RateLimiter) Acquire ¶
func (r *RateLimiter) Acquire() error
Acquire blocks until a slot is available, then increments the request count. Returns an error only when a cooldown is active. The mutex is released during any sleep so other goroutines are not blocked.
func (*RateLimiter) SetCooldown ¶
func (r *RateLimiter) SetCooldown(seconds float64)
SetCooldown persists a cooldown_until timestamp for this limiter. After a 429 response the caller sets a cooldown so the next Acquire will fail immediately instead of retrying.
type SnapshotMeta ¶
type SnapshotMeta struct {
RootPath string `json:"root_path"`
ExportedAt string `json:"exported_at"`
EntryCount string `json:"entry_count"`
}
SnapshotMeta holds metadata recorded alongside a tree snapshot.