Skip to content

Magnets and Instant Create

NihilDigit edited this page Sep 30, 2026 · 3 revisions

Magnets and Instant Create

PikPak stores content by hash, and both halves of that are reachable: a magnet can be resolved to the hash of every file in it without creating anything, and a hash creates a file object in one request with no bytes transferred. So a magnet reaches playable bytes in about a second, where an offline download takes five to ten on content PikPak already holds.

val resource = client.resolveMagnet("magnet:?xt=urn:btih:...") ?: return   // null: PikPak has never seen it
val episode = resource.files.first { it.path == "specials/S00E01.mkv" }
val gcid = episode.gcid ?: return                                          // null: this one file is unindexed
val fileId = client.instantCreate(episode, parentId = folderId)

resolveMagnet

POST /drive/v1/resource/list, body {"urls": magnet, "page_size": 500, "thumbnail_type": "FROM_HASH"}.

Fact (2026-09-11). It parses a magnet into a file tree without creating an offline task and without touching the drive, and every leaf carries meta.hash — the gcid. Subfolders arrive inlined in dir.resources, no cursor between levels, and the SDK walks them to any depth. 150–314 ms.

Fact. Content the index has never seen comes back as one placeholder: the info hash as the name, file_size "0", empty meta.hash, no status. The SDK returns null for it, in 146 ms, because "PikPak does not have this" is an answer, not a failure. Third-party code claims a meta.error of 406:E_BT_FILE_NOT_EXIST marks the case; it was absent from every miss observed, so the empty gcids are the signal.

  • files keeps entries with a null gcid: a caller matches an episode by name across the whole tree, and hiding the unindexed ones would turn "not in the index" into "not in the torrent".
  • A torrent whose top level is a single folder has it dropped from the paths, so specials/S00E01.mkv has the same shape as a single-file torrent's bare name. Several top-level entries keep their names.
  • truncated is set when the server paged the top level (more than 500 entries at the root). Following its page token has not been tried.

Shape. There is no fallback to an offline download on a miss. A caller with another source should use it; one without is better served by createUrlFile directly than by a wait hidden inside a call that usually returns in a second.

instantCreate

The upload's create request with the hash and size supplied by the caller. It can only succeed on content PikPak already stores.

Fact (2026-09-11). A response with file.phase == PHASE_TYPE_COMPLETE and no resumable node means it worked: a file object, in the folder and under the name you chose, about 200 ms. This is new sha in pikpakcli and the gcid early return in rclone's upload.

Fact. Any other phase means the server wants the bytes. It has created a pending node by then, which would sit in the folder forever (observed 2026-09-26) with a retry adding a "(1)" copy beside it; the SDK deletes it and throws InstantContentUnavailableException, so a caller can fall back to an offline task.

Fact. The three hashes agree: meta.hash from resolving a magnet, the hash of the file an offline download of that magnet produced, and the hash of a file instant-created from the first — A3C62CB1…12DF for the Arch ISO. Offline products carried a gcid in 96 of 96 files checked. Hashes are reported in upper case and the server accepts either case in a request; the SDK hands out upper case everywhere, its own hashing included.

Known flake. Creating the same gcid twice in one folder once returned a file node with an id that did not yet resolve: an immediate getFile had no link. The SDK does not check for it; a caller about to read the detail anyway would pay the check twice.

What it costs

Fact, and a correction. An instant upload takes its full size of storage. A single-file run once read no change in quota.usage, but /drive/v1/about lags a write by more than one file is worth; ten files of distinct content, 9.13 GiB created at once, moved usage by 9.13 GiB within 15 s and gave it all back within 15 s of a permanent delete (2026-09-11, InstantCreateQuotaProbeTest).

Fact (2026-09-29, free account). An instant upload is not a cloud download: the daily count in about.quotas.cloud_download read the same before and after, and no task appeared. A create PikPak cannot serve from its index, answered PENDING and then deleted, is charged nothing.

Fact, and a correction (2026-09-24). It is not free. Through GET /vip/v1/quantity/list?type=transfer on a premium account, an instant upload is charged 15 % of the file's size against the monthly upload allowance — 4.6 GB drew 0.69 GB, 3.8 GB drew 0.57 GB — and content the account already held was charged the same as content it did not. An offline download is charged its full size against the offline allowance. With premium limits of 1 TiB upload and 40 TiB offline a month, the instant path costs about six times more per byte of content. It wins on latency for a file or two, not on a whole pack. getTransferQuota reports all of this; the SDK's requests count against the account, not the connected-apps share.

Links and file objects

Fact. A signed link outlives the file object it came from — readable after trashing and after permanent deletion. Only tested immediately; how long it survives is unknown.

Shape. By default the SDK leaves the file objects it creates in place. Keeping one makes a later refresh one request instead of two and a second playback skip resolution entirely; the drive fills up, and eviction is the caller's.

Leases are the other choice, since 1.3.0: leaseDetail and fileHandle(leased = true) delete each object as soon as its link is in hand, and LeaseBudget bounds the storage the objects in between take (Playback). What they cost: every refresh after an expiry is a rebuild — an instant create, 15 % of the size from the upload allowance, and the size in storage until the delete lands. What they risk: an earlier version dropped the mode because rclone's tracker reports the server's record of a deleted file's hash changing within the hour, which would make the rebuild fail exactly when it is needed. That has not been measured; LeaseRebuildProbeTest does (Development). A caller that deletes objects its own way gets every one reported through onObjectMinted.

Closed questions

Tried, and do not work:

  • Reading content with no file object. Every link field (web_content_link, medias[].link.url) comes from two endpoints, both taking a file_id. resource/list's list_id and per-resource id have no follow-up endpoint — resource/link, /detail, /play, /info, /file all 404.
  • Suppressing the folder an offline task creates, or picking files. The create-task body is kind / name / parent_id / upload_type / url / folder_type and nothing else, across four independent implementations. The gcid path sidesteps both by creating no task; for an offline download, see pruneOfflineOutput in Uploads and Offline Tasks.

Clone this wiki locally