github.com/rclone/rclone@v1.66.1-0.20240517100346-7b89735ae726/backend/pikpak/api/types.go (about) 1 // Package api has type definitions for pikpak 2 // 3 // Manually obtained from the API responses using Browse Dev. Tool and https://mholt.github.io/json-to-go/ 4 package api 5 6 import ( 7 "fmt" 8 "reflect" 9 "strconv" 10 "time" 11 ) 12 13 const ( 14 // "2022-09-17T14:31:06.056+08:00" 15 timeFormat = `"` + time.RFC3339 + `"` 16 ) 17 18 // Time represents date and time information for the pikpak API, by using RFC3339 19 type Time time.Time 20 21 // MarshalJSON turns a Time into JSON (in UTC) 22 func (t *Time) MarshalJSON() (out []byte, err error) { 23 timeString := (*time.Time)(t).Format(timeFormat) 24 return []byte(timeString), nil 25 } 26 27 // UnmarshalJSON turns JSON into a Time 28 func (t *Time) UnmarshalJSON(data []byte) error { 29 if string(data) == "null" || string(data) == `""` { 30 return nil 31 } 32 newT, err := time.Parse(timeFormat, string(data)) 33 if err != nil { 34 return err 35 } 36 *t = Time(newT) 37 return nil 38 } 39 40 // Types of things in Item 41 const ( 42 KindOfFolder = "drive#folder" 43 KindOfFile = "drive#file" 44 KindOfFileList = "drive#fileList" 45 KindOfResumable = "drive#resumable" 46 KindOfForm = "drive#form" 47 ThumbnailSizeS = "SIZE_SMALL" 48 ThumbnailSizeM = "SIZE_MEDIUM" 49 ThumbnailSizeL = "SIZE_LARGE" 50 PhaseTypeComplete = "PHASE_TYPE_COMPLETE" 51 PhaseTypeRunning = "PHASE_TYPE_RUNNING" 52 PhaseTypeError = "PHASE_TYPE_ERROR" 53 PhaseTypePending = "PHASE_TYPE_PENDING" 54 UploadTypeForm = "UPLOAD_TYPE_FORM" 55 UploadTypeResumable = "UPLOAD_TYPE_RESUMABLE" 56 ListLimit = 500 57 ) 58 59 // ------------------------------------------------------------ 60 61 // Error details api error from pikpak 62 type Error struct { 63 Reason string `json:"error"` // short description of the reason, e.g. "file_name_empty" "invalid_request" 64 Code int `json:"error_code"` 65 URL string `json:"error_url,omitempty"` 66 Message string `json:"error_description,omitempty"` 67 // can have either of `error_details` or `details`` 68 ErrorDetails []*ErrorDetails `json:"error_details,omitempty"` 69 Details []*ErrorDetails `json:"details,omitempty"` 70 } 71 72 // ErrorDetails contains further details of api error 73 type ErrorDetails struct { 74 Type string `json:"@type,omitempty"` 75 Reason string `json:"reason,omitempty"` 76 Domain string `json:"domain,omitempty"` 77 Metadata struct{} `json:"metadata,omitempty"` // TODO: undiscovered yet 78 Locale string `json:"locale,omitempty"` // e.g. "en" 79 Message string `json:"message,omitempty"` 80 StackEntries []interface{} `json:"stack_entries,omitempty"` // TODO: undiscovered yet 81 Detail string `json:"detail,omitempty"` 82 } 83 84 // Error returns a string for the error and satisfies the error interface 85 func (e *Error) Error() string { 86 out := fmt.Sprintf("Error %q (%d)", e.Reason, e.Code) 87 if e.Message != "" { 88 out += ": " + e.Message 89 } 90 return out 91 } 92 93 // Check Error satisfies the error interface 94 var _ error = (*Error)(nil) 95 96 // ------------------------------------------------------------ 97 98 // Filters contains parameters for filters when listing. 99 // 100 // possible operators 101 // * in: a list of comma-separated string 102 // * eq: "true" or "false" 103 // * gt or lt: time format string, e.g. "2023-01-28T10:56:49.757+08:00" 104 type Filters struct { 105 Phase map[string]string `json:"phase,omitempty"` // "in" or "eq" 106 Trashed map[string]bool `json:"trashed,omitempty"` // "eq" 107 Kind map[string]string `json:"kind,omitempty"` // "eq" 108 Starred map[string]bool `json:"starred,omitempty"` // "eq" 109 ModifiedTime map[string]string `json:"modified_time,omitempty"` // "gt" or "lt" 110 } 111 112 // Set sets filter values using field name, operator and corresponding value 113 func (f *Filters) Set(field, operator, value string) { 114 if value == "" { 115 // UNSET for empty values 116 return 117 } 118 r := reflect.ValueOf(f) 119 fd := reflect.Indirect(r).FieldByName(field) 120 if v, err := strconv.ParseBool(value); err == nil { 121 fd.Set(reflect.ValueOf(map[string]bool{operator: v})) 122 } else { 123 fd.Set(reflect.ValueOf(map[string]string{operator: value})) 124 } 125 } 126 127 // ------------------------------------------------------------ 128 // Common Elements 129 130 // Link contains a download URL for opening files 131 type Link struct { 132 URL string `json:"url"` 133 Token string `json:"token"` 134 Expire Time `json:"expire"` 135 Type string `json:"type,omitempty"` 136 } 137 138 // Valid reports whether l is non-nil, has an URL, and is not expired. 139 func (l *Link) Valid() bool { 140 return l != nil && l.URL != "" && time.Now().Add(10*time.Second).Before(time.Time(l.Expire)) 141 } 142 143 // URL is a basic form of URL 144 type URL struct { 145 Kind string `json:"kind,omitempty"` // e.g. "upload#url" 146 URL string `json:"url,omitempty"` 147 } 148 149 // ------------------------------------------------------------ 150 // Base Elements 151 152 // FileList contains a list of File elements 153 type FileList struct { 154 Kind string `json:"kind,omitempty"` // drive#fileList 155 Files []*File `json:"files,omitempty"` 156 NextPageToken string `json:"next_page_token"` 157 Version string `json:"version,omitempty"` 158 VersionOutdated bool `json:"version_outdated,omitempty"` 159 SyncTime Time `json:"sync_time"` 160 } 161 162 // File is a basic element representing a single file object 163 // 164 // There are two types of download links, 165 // 1) one from File.WebContentLink or File.Links.ApplicationOctetStream.URL and 166 // 2) the other from File.Medias[].Link.URL. 167 // Empirically, 2) is less restrictive to multiple concurrent range-requests 168 // for a single file, i.e. supports for higher `--multi-thread-streams=N`. 169 // However, it is not generally applicable as it is only for media. 170 type File struct { 171 Apps []*FileApp `json:"apps,omitempty"` 172 Audit *FileAudit `json:"audit,omitempty"` 173 Collection string `json:"collection,omitempty"` // TODO 174 CreatedTime Time `json:"created_time,omitempty"` 175 DeleteTime Time `json:"delete_time,omitempty"` 176 FileCategory string `json:"file_category,omitempty"` // "AUDIO", "VIDEO" 177 FileExtension string `json:"file_extension,omitempty"` 178 FolderType string `json:"folder_type,omitempty"` 179 Hash string `json:"hash,omitempty"` // sha1 but NOT a valid file hash. looks like a torrent hash 180 IconLink string `json:"icon_link,omitempty"` 181 ID string `json:"id,omitempty"` 182 Kind string `json:"kind,omitempty"` // "drive#file" 183 Links *FileLinks `json:"links,omitempty"` 184 Md5Checksum string `json:"md5_checksum,omitempty"` 185 Medias []*Media `json:"medias,omitempty"` 186 MimeType string `json:"mime_type,omitempty"` 187 ModifiedTime Time `json:"modified_time,omitempty"` // updated when renamed or moved 188 Name string `json:"name,omitempty"` 189 OriginalFileIndex int `json:"original_file_index,omitempty"` // TODO 190 OriginalURL string `json:"original_url,omitempty"` 191 Params *FileParams `json:"params,omitempty"` 192 ParentID string `json:"parent_id,omitempty"` 193 Phase string `json:"phase,omitempty"` 194 Revision int `json:"revision,omitempty,string"` 195 ReferenceEvents []interface{} `json:"reference_events"` 196 ReferenceResource interface{} `json:"reference_resource"` 197 Size int64 `json:"size,omitempty,string"` 198 SortName string `json:"sort_name,omitempty"` 199 Space string `json:"space,omitempty"` 200 SpellName []interface{} `json:"spell_name,omitempty"` // TODO maybe list of something? 201 Starred bool `json:"starred,omitempty"` 202 Tags []interface{} `json:"tags"` 203 ThumbnailLink string `json:"thumbnail_link,omitempty"` 204 Trashed bool `json:"trashed,omitempty"` 205 UserID string `json:"user_id,omitempty"` 206 UserModifiedTime Time `json:"user_modified_time,omitempty"` 207 WebContentLink string `json:"web_content_link,omitempty"` 208 Writable bool `json:"writable,omitempty"` 209 } 210 211 // FileLinks includes links to file at backend 212 type FileLinks struct { 213 ApplicationOctetStream *Link `json:"application/octet-stream,omitempty"` 214 } 215 216 // FileAudit contains audit information for the file 217 type FileAudit struct { 218 Status string `json:"status,omitempty"` // "STATUS_OK" 219 Message string `json:"message,omitempty"` 220 Title string `json:"title,omitempty"` 221 } 222 223 // Media contains info about supported version of media, e.g. original, transcoded, etc 224 type Media struct { 225 MediaID string `json:"media_id,omitempty"` 226 MediaName string `json:"media_name,omitempty"` 227 Video struct { 228 Height int `json:"height,omitempty"` 229 Width int `json:"width,omitempty"` 230 Duration int64 `json:"duration,omitempty"` 231 BitRate int `json:"bit_rate,omitempty"` 232 FrameRate int `json:"frame_rate,omitempty"` 233 VideoCodec string `json:"video_codec,omitempty"` // "h264", "hevc" 234 AudioCodec string `json:"audio_codec,omitempty"` // "pcm_bluray", "aac" 235 VideoType string `json:"video_type,omitempty"` // "mpegts" 236 HdrType string `json:"hdr_type,omitempty"` 237 } `json:"video,omitempty"` 238 Link *Link `json:"link,omitempty"` 239 NeedMoreQuota bool `json:"need_more_quota,omitempty"` 240 VipTypes []interface{} `json:"vip_types,omitempty"` // TODO maybe list of something? 241 RedirectLink string `json:"redirect_link,omitempty"` 242 IconLink string `json:"icon_link,omitempty"` 243 IsDefault bool `json:"is_default,omitempty"` 244 Priority int `json:"priority,omitempty"` 245 IsOrigin bool `json:"is_origin,omitempty"` 246 ResolutionName string `json:"resolution_name,omitempty"` 247 IsVisible bool `json:"is_visible,omitempty"` 248 Category string `json:"category,omitempty"` // "category_origin" 249 Audio interface{} `json:"audio"` // TODO: undiscovered yet 250 } 251 252 // FileParams includes parameters for instant open 253 type FileParams struct { 254 Duration int64 `json:"duration,omitempty,string"` // in seconds 255 Height int `json:"height,omitempty,string"` 256 Platform string `json:"platform,omitempty"` // "Upload" 257 PlatformIcon string `json:"platform_icon,omitempty"` 258 URL string `json:"url,omitempty"` 259 Width int `json:"width,omitempty,string"` 260 } 261 262 // FileApp includes parameters for instant open 263 type FileApp struct { 264 ID string `json:"id,omitempty"` // "decompress" for rar files 265 Name string `json:"name,omitempty"` // decompress" for rar files 266 Access []interface{} `json:"access,omitempty"` 267 Link string `json:"link,omitempty"` // "https://mypikpak.com/drive/decompression/{File.Id}?gcid={File.Hash}\u0026wv-style=topbar%3Ahide" 268 RedirectLink string `json:"redirect_link,omitempty"` 269 VipTypes []interface{} `json:"vip_types,omitempty"` 270 NeedMoreQuota bool `json:"need_more_quota,omitempty"` 271 IconLink string `json:"icon_link,omitempty"` 272 IsDefault bool `json:"is_default,omitempty"` 273 Params struct{} `json:"params,omitempty"` // TODO 274 CategoryIDs []interface{} `json:"category_ids,omitempty"` 275 AdSceneType int `json:"ad_scene_type,omitempty"` 276 Space string `json:"space,omitempty"` 277 Links struct{} `json:"links,omitempty"` // TODO 278 } 279 280 // ------------------------------------------------------------ 281 282 // TaskList contains a list of Task elements 283 type TaskList struct { 284 Tasks []*Task `json:"tasks,omitempty"` // "drive#task" 285 NextPageToken string `json:"next_page_token"` 286 ExpiresIn int `json:"expires_in,omitempty"` 287 } 288 289 // Task is a basic element representing a single task such as offline download and upload 290 type Task struct { 291 Kind string `json:"kind,omitempty"` // "drive#task" 292 ID string `json:"id,omitempty"` // task id? 293 Name string `json:"name,omitempty"` // torrent name? 294 Type string `json:"type,omitempty"` // "offline" 295 UserID string `json:"user_id,omitempty"` 296 Statuses []interface{} `json:"statuses,omitempty"` // TODO 297 StatusSize int `json:"status_size,omitempty"` // TODO 298 Params *TaskParams `json:"params,omitempty"` // TODO 299 FileID string `json:"file_id,omitempty"` 300 FileName string `json:"file_name,omitempty"` 301 FileSize string `json:"file_size,omitempty"` 302 Message string `json:"message,omitempty"` // e.g. "Saving" 303 CreatedTime Time `json:"created_time,omitempty"` 304 UpdatedTime Time `json:"updated_time,omitempty"` 305 ThirdTaskID string `json:"third_task_id,omitempty"` // TODO 306 Phase string `json:"phase,omitempty"` // e.g. "PHASE_TYPE_RUNNING" 307 Progress int `json:"progress,omitempty"` 308 IconLink string `json:"icon_link,omitempty"` 309 Callback string `json:"callback,omitempty"` 310 ReferenceResource interface{} `json:"reference_resource,omitempty"` // TODO 311 Space string `json:"space,omitempty"` 312 } 313 314 // TaskParams includes parameters informing status of Task 315 type TaskParams struct { 316 Age string `json:"age,omitempty"` 317 PredictSpeed string `json:"predict_speed,omitempty"` 318 PredictType string `json:"predict_type,omitempty"` 319 URL string `json:"url,omitempty"` 320 } 321 322 // Form contains parameters for upload by multipart/form-data 323 type Form struct { 324 Headers struct{} `json:"headers"` 325 Kind string `json:"kind"` // "drive#form" 326 Method string `json:"method"` // "POST" 327 MultiParts struct { 328 OSSAccessKeyID string `json:"OSSAccessKeyId"` 329 Signature string `json:"Signature"` 330 Callback string `json:"callback"` 331 Key string `json:"key"` 332 Policy string `json:"policy"` 333 XUserData string `json:"x:user_data"` 334 } `json:"multi_parts"` 335 URL string `json:"url"` 336 } 337 338 // Resumable contains parameters for upload by resumable 339 type Resumable struct { 340 Kind string `json:"kind,omitempty"` // "drive#resumable" 341 Provider string `json:"provider,omitempty"` // e.g. "PROVIDER_ALIYUN" 342 Params *ResumableParams `json:"params,omitempty"` 343 } 344 345 // ResumableParams specifies resumable paramegers 346 type ResumableParams struct { 347 AccessKeyID string `json:"access_key_id,omitempty"` 348 AccessKeySecret string `json:"access_key_secret,omitempty"` 349 Bucket string `json:"bucket,omitempty"` 350 Endpoint string `json:"endpoint,omitempty"` 351 Expiration Time `json:"expiration,omitempty"` 352 Key string `json:"key,omitempty"` 353 SecurityToken string `json:"security_token,omitempty"` 354 } 355 356 // FileInArchive is a basic element in archive 357 type FileInArchive struct { 358 Index int `json:"index,omitempty"` 359 Filename string `json:"filename,omitempty"` 360 Filesize string `json:"filesize,omitempty"` 361 MimeType string `json:"mime_type,omitempty"` 362 Gcid string `json:"gcid,omitempty"` 363 Kind string `json:"kind,omitempty"` 364 IconLink string `json:"icon_link,omitempty"` 365 Path string `json:"path,omitempty"` 366 } 367 368 // ------------------------------------------------------------ 369 370 // NewFile is a response to RequestNewFile 371 type NewFile struct { 372 File *File `json:"file,omitempty"` 373 Form *Form `json:"form,omitempty"` 374 Resumable *Resumable `json:"resumable,omitempty"` 375 Task *Task `json:"task,omitempty"` // null in this case 376 UploadType string `json:"upload_type,omitempty"` // "UPLOAD_TYPE_FORM" or "UPLOAD_TYPE_RESUMABLE" 377 } 378 379 // NewTask is a response to RequestNewTask 380 type NewTask struct { 381 UploadType string `json:"upload_type,omitempty"` // "UPLOAD_TYPE_URL" 382 File *File `json:"file,omitempty"` // null in this case 383 Task *Task `json:"task,omitempty"` 384 URL *URL `json:"url,omitempty"` // {"kind": "upload#url"} 385 } 386 387 // About informs drive status 388 type About struct { 389 Kind string `json:"kind,omitempty"` // "drive#about" 390 Quota *Quota `json:"quota,omitempty"` 391 ExpiresAt string `json:"expires_at,omitempty"` 392 Quotas struct{} `json:"quotas,omitempty"` // maybe []*Quota? 393 } 394 395 // Quota informs drive quota 396 type Quota struct { 397 Kind string `json:"kind,omitempty"` // "drive#quota" 398 Limit int64 `json:"limit,omitempty,string"` // limit in bytes 399 Usage int64 `json:"usage,omitempty,string"` // bytes in use 400 UsageInTrash int64 `json:"usage_in_trash,omitempty,string"` // bytes in trash but this seems not working 401 PlayTimesLimit string `json:"play_times_limit,omitempty"` // maybe in seconds 402 PlayTimesUsage string `json:"play_times_usage,omitempty"` // maybe in seconds 403 IsUnlimited bool `json:"is_unlimited,omitempty"` 404 } 405 406 // Share is a response to RequestShare 407 // 408 // used in PublicLink() 409 type Share struct { 410 ShareID string `json:"share_id,omitempty"` 411 ShareURL string `json:"share_url,omitempty"` 412 PassCode string `json:"pass_code,omitempty"` 413 ShareText string `json:"share_text,omitempty"` 414 } 415 416 // User contains user account information 417 // 418 // GET https://user.mypikpak.com/v1/user/me 419 type User struct { 420 Sub string `json:"sub,omitempty"` // userid for internal use 421 Name string `json:"name,omitempty"` // Username 422 Picture string `json:"picture,omitempty"` // URL to Avatar image 423 Email string `json:"email,omitempty"` // redacted email address 424 Providers *[]UserProvider `json:"providers,omitempty"` // OAuth provider 425 PhoneNumber string `json:"phone_number,omitempty"` 426 Password string `json:"password,omitempty"` // "SET" if configured 427 Status string `json:"status,omitempty"` // "ACTIVE" 428 CreatedAt Time `json:"created_at,omitempty"` 429 PasswordUpdatedAt Time `json:"password_updated_at,omitempty"` 430 } 431 432 // UserProvider details third-party authentication 433 type UserProvider struct { 434 ID string `json:"id,omitempty"` // e.g. "google.com" 435 ProviderUserID string `json:"provider_user_id,omitempty"` 436 Name string `json:"name,omitempty"` // username 437 } 438 439 // VIP includes subscription details about premium account 440 // 441 // GET https://api-drive.mypikpak.com/drive/v1/privilege/vip 442 type VIP struct { 443 Result string `json:"result,omitempty"` // "ACCEPTED" 444 Message string `json:"message,omitempty"` 445 RedirectURI string `json:"redirect_uri,omitempty"` 446 Data struct { 447 Expire Time `json:"expire,omitempty"` 448 Status string `json:"status,omitempty"` // "invalid" or "ok" 449 Type string `json:"type,omitempty"` // "novip" or "platinum" 450 UserID string `json:"user_id,omitempty"` // same as User.Sub 451 } `json:"data,omitempty"` 452 } 453 454 // DecompressResult is a response to RequestDecompress 455 type DecompressResult struct { 456 Status string `json:"status,omitempty"` // "OK" 457 StatusText string `json:"status_text,omitempty"` 458 TaskID string `json:"task_id,omitempty"` // same as File.Id 459 FilesNum int `json:"files_num,omitempty"` // number of files in archive 460 RedirectLink string `json:"redirect_link,omitempty"` 461 } 462 463 // ------------------------------------------------------------ 464 465 // RequestShare is to request for file share 466 type RequestShare struct { 467 FileIDs []string `json:"file_ids,omitempty"` 468 ShareTo string `json:"share_to,omitempty"` // "publiclink", 469 ExpirationDays int `json:"expiration_days,omitempty"` // -1 = 'forever' 470 PassCodeOption string `json:"pass_code_option,omitempty"` // "NOT_REQUIRED" 471 } 472 473 // RequestBatch is to request for batch actions 474 type RequestBatch struct { 475 IDs []string `json:"ids,omitempty"` 476 To map[string]string `json:"to,omitempty"` 477 } 478 479 // RequestNewFile is to request for creating a new `drive#folder` or `drive#file` 480 type RequestNewFile struct { 481 // always required 482 Kind string `json:"kind"` // "drive#folder" or "drive#file" 483 Name string `json:"name"` 484 ParentID string `json:"parent_id"` 485 FolderType string `json:"folder_type"` 486 // only when uploading a new file 487 Hash string `json:"hash,omitempty"` // sha1sum 488 Resumable map[string]string `json:"resumable,omitempty"` // {"provider": "PROVIDER_ALIYUN"} 489 Size int64 `json:"size,omitempty"` 490 UploadType string `json:"upload_type,omitempty"` // "UPLOAD_TYPE_FORM" or "UPLOAD_TYPE_RESUMABLE" 491 } 492 493 // RequestNewTask is to request for creating a new task like offline downloads 494 // 495 // Name and ParentID can be left empty. 496 type RequestNewTask struct { 497 Kind string `json:"kind,omitempty"` // "drive#file" 498 Name string `json:"name,omitempty"` 499 ParentID string `json:"parent_id,omitempty"` 500 UploadType string `json:"upload_type,omitempty"` // "UPLOAD_TYPE_URL" 501 URL *URL `json:"url,omitempty"` // {"url": downloadUrl} 502 FolderType string `json:"folder_type,omitempty"` // "" if parent_id else "DOWNLOAD" 503 } 504 505 // RequestDecompress is to request for decompress of archive files 506 type RequestDecompress struct { 507 Gcid string `json:"gcid,omitempty"` // same as File.Hash 508 Password string `json:"password,omitempty"` // "" 509 FileID string `json:"file_id,omitempty"` 510 Files []*FileInArchive `json:"files,omitempty"` // can request selected files to be decompressed 511 DefaultParent bool `json:"default_parent,omitempty"` 512 } 513 514 // ------------------------------------------------------------ 515 516 // NOT implemented YET 517 518 // RequestArchiveFileList is to request for a list of files in archive 519 // 520 // POST https://api-drive.mypikpak.com/decompress/v1/list 521 type RequestArchiveFileList struct { 522 Gcid string `json:"gcid,omitempty"` // same as api.File.Hash 523 Path string `json:"path,omitempty"` // "" by default 524 Password string `json:"password,omitempty"` // "" by default 525 FileID string `json:"file_id,omitempty"` 526 } 527 528 // ArchiveFileList is a response to RequestArchiveFileList 529 type ArchiveFileList struct { 530 Status string `json:"status,omitempty"` // "OK" 531 StatusText string `json:"status_text,omitempty"` // "" 532 TaskID string `json:"task_id,omitempty"` // "" 533 CurrentPath string `json:"current_path,omitempty"` // "" 534 Title string `json:"title,omitempty"` 535 FileSize int64 `json:"file_size,omitempty"` 536 Gcid string `json:"gcid,omitempty"` // same as File.Hash 537 Files []*FileInArchive `json:"files,omitempty"` 538 }