alist-org / alist

🗂️A file list/WebDAV program that supports multiple storages, powered by Gin and Solidjs. / 一个支持多存储的文件列表/WebDAV程序,使用 Gin 和 Solidjs。
https://alist.nn.ci
GNU Affero General Public License v3.0
42.44k stars 5.49k forks source link

增加PikPak网盘的离线下载功能 #6632

Closed Muione closed 2 months ago

Muione commented 3 months ago

Please make sure of the following things

Description of the feature / 需求描述

因为PikPak网盘离线下载的速度比较快,能不能在/api/fs/add_offline_download API下增加对PikPak离线下载的支持

Suggested solution / 实现思路

这段代码可以通过PikPak离线下载任务的接口实现(可能有些小问题):

// 离线下载文件
func (d *PikPak) OfflineDownload(ctx context.Context, fileUrl string, parentId string, name string) error {
    requestBody := base.Json{
        "kind":        "drive#file",
        "name":        name,
        "upload_type": "UPLOAD_TYPE_URL",
        "url": base.Json{
            "url": fileUrl,
        },
        "folder_type": "DOWNLOAD",
    }
    if parentId != "" {
        requestBody["folder_type"] = ""
        requestBody["parent_id"] = parentId
    }
    _, err := d.request("https://api-drive.mypikpak.com/drive/v1/files", http.MethodPost, func(req *resty.Request) {
        req.SetBody(requestBody)
    }, nil)
    return err
}

// OfflineList 获取离线下载列表
func (d *PikPak) OfflineList(ctx context.Context, size int, nextPageToken string, phase []string) (*OfflineListResp, error) {
    if size <= 0 {
        size = 10000
    }
    url := "https://api-drive.mypikpak.com/drive/v1/tasks"
    data := map[string]interface{}{
        "type":            "offline",
        "thumbnail_size": "SIZE_SMALL",
        "limit":           size,
        "filters":         fmt.Sprintf(`{"phase": {"in": "%s"}}`, strings.Join(phase, ",")),
        "with":            "reference_resource",
    }
    if nextPageToken != "" {
        data["page_token"] = nextPageToken
    }
    var resp OfflineListResp
    _, err := d.request(url, http.MethodGet, func(req *resty.Request) {
        req.SetQueryParams(data)
    }, &resp)
    return &resp, err
}

// GetTaskStatus 获取离线下载任务状态
func (d *PikPak) GetTaskStatus(ctx context.Context, taskId string, fileId string) (DownloadStatus, error) {
    // 首先尝试从任务列表中查找
    listResp, err := d.OfflineList(ctx, 10000, "", []string{"PHASE_TYPE_RUNNING", "PHASE_TYPE_ERROR"})
    if err != nil {
        return DownloadStatusError, err
    }
    for _, task := range listResp.Tasks {
        if task.ID == taskId {
            return DownloadStatusDownloading, nil
        }
    }

    // 如果在任务列表中找不到,则尝试获取文件信息
    fileInfo, err := d.offlineFileInfo(fileId)
    if err != nil {
        if err.(*resty.Response).StatusCode() == http.StatusNotFound {
            return DownloadStatusNotFound, nil
        }
        return DownloadStatusError, err
    }
    if fileInfo != nil {
        return DownloadStatusDone, nil
    }

    return DownloadStatusNotFound, nil
}

// offlineFileInfo 获取离线下载文件信息
func (d *PikPak) offlineFileInfo(fileId string) (*File, error) {
    url := fmt.Sprintf("https://api-drive.mypikpak.com/drive/v1/files/%s", fileId)
    var resp File
    _, err := d.request(url, http.MethodGet, func(req *resty.Request) {
        req.SetQueryParam("thumbnail_size", "SIZE_LARGE")
    }, &resp)
    if err != nil {
        return nil, err
    }
    return &resp, nil
}

// DownloadStatus 表示下载状态
type DownloadStatus string

const (
    DownloadStatusDownloading DownloadStatus = "Downloading"
    DownloadStatusDone        DownloadStatus = "Done"
    DownloadStatusNotFound    DownloadStatus = "NotFound"
    DownloadStatusError       DownloadStatus = "Error"
)

// OfflineListResp 离线下载列表响应
type OfflineListResp struct {
    Tasks []struct {
        ID string `json:"id"`
    } `json:"tasks"`
}

Additional context / 附件

No response

welcome[bot] commented 3 months ago

Thanks for opening your first issue here! Be sure to follow the issue template!