onepoint icon indicating copy to clipboard operation
onepoint copied to clipboard

大佬有空可以加上阿里云盘

Open naicfeng opened this issue 3 years ago • 2 comments

官方网页端完全就像公开API一样呀。

refresh_token保存在localstorage,和onedrive一样可以无限获取access_token post https://websv.aliyundrive.com/token/refresh {RefreshToken: RefreshToken}

目前还不限速,完美

naicfeng avatar Mar 19 '21 01:03 naicfeng

const { axios, RTError, IDHelper, _P } = require('../utils/node');
const logger = require('../utils/logger');
const op = require('../core/op');

const { PAGE_SIZE } = require('../conf/sys-config');

function filter(e) {

    const r = {
        type: 1,
        name: e.name,
        time: e.updated_at,
        id: e.file_id,
    };

    if (e.type !== 'folder') {
        r.type = 0;
        r.mime = e.content_type;
        r.size = Number(e.size);
        r.url = e.download_url;
        r.thumbnail = e.thumbnail;
    }

    return r;
}

class Aliyundrive extends IDHelper {
    static async build(config) {
        const g = new Aliyundrive(config);
        await g.refreshToken();
        g.service.defaults.headers.common.Authorization = 'Bearer ' + g.access_token;
        return g;
    }

    constructor({ refresh_token, root }) {
        super(root);
        this.oauth = Object.assign({}, Aliyundrive.oauth2s[0], { refresh_token });
        const service = axios.create({ baseURL: this.oauth.api_url });
        service.interceptors.response.use(
            (response) => response.data,
            (err) => {
                if (err.response && err.response.data && err.response.data.error) {
                    if (err.response.status === 404) {
                        return Promise.reject(new RTError(404, 'ItemNotExist'));
                    } else {
                        const d = err.response.data.error;
                        return Promise.reject(new RTError(400, 'ModuleError', d.message || d));
                    }
                }
                return Promise.reject(err);
            }
        );
        this.service = service;
    }

    async refreshToken() {
        if(op.config.cache && op.config.cache[this.oauth.refresh_token] && op.config.cache[this.oauth.refresh_token].exptime > Date.now()){
            this.access_token = op.config.cache[this.oauth.refresh_token].access_token;
            this.drive_id = op.config.cache[this.oauth.refresh_token].drive_id;
            logger.info('use refresh_token cache');
            return op.config.cache[this.oauth.refresh_token];
        }
        const data = await this.service.post(
            this.oauth.refresh_url,
            new URLSearchParams({
                refresh_token: this.oauth.refresh_token
            })
        );
        this.access_token = data.access_token;
        this.drive_id = data.default_drive_id
        //cache
        let exptime = Date.now() + Number(data.expires_in * 1000);
        let cache_key = this.oauth.refresh_token;
        if(!op.config.cache)op.config['cache'] = {};
        op.config.cache[cache_key] = {access_token:this.access_token,exptime:exptime,drive_id:this.drive_id};
        op.saveConfig();
//        logger.log('aliyun drive access_token:' + data.access_token);
        return data;
    }

    async itemInfo(id) {
        return this.service
            .post('file/get', {
                "drive_id": this.drive_id,
                "file_id": id,
            })
            .then(filter);
    }

    async fetchList(parentId, pageToken) {
        const params = {
            "drive_id": this.drive_id,
            "fields": '*',
            "order_by": 'updated_at',
            "order_direction": 'DESC',
            "parent_file_id": parentId?parentId:this.root,
            "limit": 3000,//PAGE_SIZE,
            "next_marker": pageToken
        };

        return this.service.post('file/list', params).then((data) => ({
            list: data.items.map(filter),
            next: data.next_marker,
        }));
    }

}

Aliyundrive.oauth2s = [
    {
        api_url: 'https://api.aliyundrive.com/v2/',
        refresh_url: 'https://websv.aliyundrive.com/token/refresh',
    },
];

module.exports = {
    params() {
        return [_P('refresh_token', '', '登录后在Local Storage获取', 7, '', true, true), _P('root', '', '根目录ID,使用根目录填root', 7, '', false, true)];
    },
    async handle(config, data, cache, ctx) {
        if ((cache.etime || 0) < Date.now()) {
            if (!config.root) {
                config.root = 'root';
            }
            if (!config.refresh_token) {
                throw new RTError(400, 'ConfigError', { fields: ['refresh_token'] });
            }

            cache.$g = await Aliyundrive.build(config);

            cache.etime = Date.now() + 3600 * 1000;
        }
        return this.ls(data, cache, ctx);
    },
    async ls({ path, id, page }, { $g }, ctx) {
        if (path.endsWith('/')) {
            if (path !=='/' && !id)throw new RTError(400, '非法请求', { msg: '访问当前页面需要携带 id 参数' });
            const { list, next } = await $g.fetchList(id, page);
            if (list.length === 0) {
                const e = await $g.itemInfo(id);
                if (e.type === 0) {
                    throw new RTError(400, 'ItemIsFile');
                }
            }
            ctx.respondList(list, next);
        } else {
            const e = await $g.itemInfo(id);
            ctx.respondOne(e);
        }
    },
};

自行适配了aliyundrive,目前存在的问题

  1. 阿里云盘自带翻页key,但是不知道如何使用(直接获取3000条记录,放进cache翻页)
  2. 所有路径都是以parent_file_id形式记录,无法直接用路径获取(没有找到相应api),所以访问必须附带id
  3. 由于以上问题,导致navsParent链接无法直接访问
  4. cf worker网络访问阿里云的api可能会超时

演示见:https://dev.cuojue.workers.dev/

naicfeng avatar Mar 20 '21 07:03 naicfeng

参考了大佬的主题,增加了alidrive,支持分页、子目录挂载

demo: https://onepoint.onesrc.workers.dev/alidrive/

受限于 referrer,视频、图片可能无法预览,但可以正常下载

如果想要预览,可以参考 https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy

ukuq avatar Apr 25 '21 10:04 ukuq