Google Drive资源 从 批量转存 到 在线观看 及 直链下载 您所在的位置:网站首页 谷歌云端硬盘在线播放视频 Google Drive资源 从 批量转存 到 在线观看 及 直链下载

Google Drive资源 从 批量转存 到 在线观看 及 直链下载

2023-07-26 02:51| 来源: 网络整理| 查看: 265

Google Drive目前只有单文件转存功能,如果是多文件转存一个一个点很麻烦,即使转存了,也经常超过最大播放次数而无法在线观看,亦或是因为没有直链,难以被Internet Download Manager识别,也难以通过aria2等手段直接下载。基于此,我们打造一条完整的 Google Drive 生态,解决上述问题。

流程展示:获取资源——>批量转存——>在线观看/获取直链——>直链下载 注意:转存占用账号大小额度的,普通账号只有15G。如果账号是无限容量,那么每天的转存大小和上传流量共享750G

实现方法:

资源获取在此不再过多赘述;批量转存:使用 Copy, URL to Google Drive™ 插件在线观看/获取直链 :GoIndex直链下载:aria2一:批量转存

我们使用 Copy, URL to Google Drive™ 插件,在任意文件上右击——>打开方式——>关联更多应用——>搜索并安装Copy, URL to Google Drive应用。

搜索并安装应用

使用方法:在需要转存的文件夹上 右键-打开方式-Copy, URL to Google Drive,之后点击 Save, Copy to Google Drive,就可以看见正在转存了,如果比较多时间就比较久。(首次使用需要授权)

二:在线观看/获取直链

GoIndex是一款部署在Cloudflare Workers的Google Drive目录索引程序,无需提供服务器,可以直接列出你谷歌网盘的所有文件,下载和访问不需要挂梯子,也可以在线观看某些格式的视频文件,获取直链。

出于安全和速度考虑,我们用自己的google api部署。

(1)首先下载 GoIndex源码

也可以直接复制源码 // =======Options START======= var authConfig = { siteName: "goindex", // 网站名称 version: "1.1.1", // 程序版本 theme: "acrou", // 强烈推荐使用自己的 client_id 和 client_secret client_id: "888992377280-9e9hk4m0ns5e4ms4pvpgp9dn9eahdjqr.apps.googleusercontent.com", client_secret: "k-iBrHk-DHNphgwCjwJHmmDD", refresh_token: "1//0ecvjlboYby7GCgYIARAAGA4SNwF-L9Ir7Y_uLJwaGSuexVaskThtsfSUaGH2vAIjU2Dej80N6uilBv3sJcYuIE3yksh7BHm_8yk", // 授权 token /** * 设置要显示的多个云端硬盘;按格式添加多个 * [id]: 可以是 团队盘id、子文件夹id、或者"root"(代表个人盘根目录); * [name]: 显示的名称 * [user]: Basic Auth 的用户名 * [pass]: Basic Auth 的密码 * [protect_file_link]: Basic Auth 是否用于保护文件链接,默认值(不设置时)为 false,即不保护文件链接(方便 直链下载/外部播放 等) * 每个盘的 Basic Auth 都可以单独设置。Basic Auth 默认保护该盘下所有文件夹/子文件夹路径 * 【注意】默认不保护文件链接,这样可以方便 直链下载/外部播放; * 如果要保护文件链接,需要将 protect_file_link 设置为 true,此时如果要进行外部播放等操作,需要将 host 替换为 user:pass@host 的 形式 * 不需要 Basic Auth 的盘,保持 user 和 pass 同时为空即可。(直接不设置也可以) * 【注意】对于id设置为为子文件夹id的盘将不支持搜索功能(不影响其他盘)。 */ roots: [ { id: "1C54GgACc1wrGgvFXhMgeo6xAvAfpDiNE", name: "direct_link", pass: "", }, { id: "root", name: "PrivateDrive", user: "", pass: "", protect_file_link: true, }, { id: "", name: "folder1", pass: "", }, ], default_gd: 0, /** * 文件列表页面每页显示的数量。【推荐设置值为 100 到 1000 之间】; * 如果设置大于1000,会导致请求 drive api 时出错; * 如果设置的值过小,会导致文件列表页面滚动条增量加载(分页加载)失效; * 此值的另一个作用是,如果目录内文件数大于此设置值(即需要多页展示的),将会对首次列目录结果进行缓存。 */ files_list_page_size: 50, /** * 搜索结果页面每页显示的数量。【推荐设置值为 50 到 1000 之间】; * 如果设置大于1000,会导致请求 drive api 时出错; * 如果设置的值过小,会导致搜索结果页面滚动条增量加载(分页加载)失效; * 此值的大小影响搜索操作的响应速度。 */ search_result_list_page_size: 50, // 确认有 cors 用途的可以开启 enable_cors_file_down: false, /** * 上面的 basic auth 已经包含了盘内全局保护的功能。所以默认不再去认证 .password 文件内的密码; * 如果在全局认证的基础上,仍需要给某些目录单独进行 .password 文件内的密码验证的话,将此选项设置为 true; * 【注意】如果开启了 .password 文件密码验证,每次列目录都会额外增加查询目录内 .password 文件是否存在的开销。 */ enable_password_file_verify: false, }; var themeOptions = { cdn: "https://cdn.jsdelivr.net/gh/alx-xlx/goindex", // 主题版本号 version: "2.0.5-darkmode-0.1", //可选默认系统语言:en/zh-chs/zh-cht languages: "en", render: { /** * 是否渲染HEAD.md文件 * Render HEAD.md file */ head_md: false, /** * 是否渲染README.md文件 * Render README.md file */ readme_md: false, /** * 是否渲染文件/文件夹描述 * Render file/folder description or not */ desc: false, }, /** * 播放器选项 * Player options */ player: { /** * 播放器api(不指定则使用浏览器默认播放器) * Player api(Use browser default player if not specified) */ api: "https://api.jsonpop.cn/demo/blplyaer/?url=", }, }; // =======Options END======= /** * global functions */ const FUNCS = { /** * 转换成针对谷歌搜索词法相对安全的搜索关键词 */ formatSearchKeyword: function (keyword) { let nothing = ""; let space = " "; if (!keyword) return nothing; return keyword .replace(/(!=)|['"=/\\:]/g, nothing) .replace(/[,,|(){}]/g, space) .trim(); }, }; /** * global consts * @type {{folder_mime_type: string, default_file_fields: string, gd_root_type: {share_drive: number, user_drive: number, sub_folder: number}}} */ const CONSTS = new (class { default_file_fields = "parents,id,name,mimeType,modifiedTime,createdTime,fileExtension,size"; gd_root_type = { user_drive: 0, share_drive: 1, sub_folder: 2, }; folder_mime_type = "application/vnd.google-apps.folder"; })(); // gd instances var gds = []; function html(current_drive_order = 0, model = {}) { return ` ${authConfig.siteName} @import url(${themeOptions.cdn}@${themeOptions.version}/goindex-acrou/dist/style.min.css); window.gdconfig = JSON.parse('${JSON.stringify({ version: authConfig.version, themeOptions: themeOptions, })}'); window.themeOptions = JSON.parse('${JSON.stringify(themeOptions)}'); window.gds = JSON.parse('${JSON.stringify( authConfig.roots.map((it) => it.name) )}'); window.MODEL = JSON.parse('${JSON.stringify(model)}'); window.current_drive_order = ${current_drive_order}; `; } addEventListener("fetch", (event) => { event.respondWith(handleRequest(event.request)); }); /** * Fetch and log a request * @param {Request} request */ async function handleRequest(request) { if (gds.length === 0) { for (let i = 0; i < authConfig.roots.length; i++) { const gd = new googleDrive(authConfig, i); await gd.init(); gds.push(gd); } // 这个操作并行,提高效率 let tasks = []; gds.forEach((gd) => { tasks.push(gd.initRootType()); }); for (let task of tasks) { await task; } } // 从 path 中提取 drive order // 并根据 drive order 获取对应的 gd instance let gd; let url = new URL(request.url); let path = decodeURI(url.pathname); /** * 重定向至起始页 * @returns {Response} */ function redirectToIndexPage() { return new Response("", { status: 301, headers: { Location: `/${authConfig.default_gd}:/` }, }); } if (path == "/") return redirectToIndexPage(); if (path.toLowerCase() == "/favicon.ico") { // 后面可以找一个 favicon return new Response("", { status: 404 }); } // 特殊命令格式 const command_reg = /^\/(?\d+):(?[a-zA-Z0-9]+)(\/.*)?$/g; const match = command_reg.exec(path); let command; if (match) { const num = match.groups.num; const order = Number(num); if (order >= 0 && order < gds.length) { gd = gds[order]; } else { return redirectToIndexPage(); } // basic auth for (const r = gd.basicAuthResponse(request); r; ) return r; command = match.groups.command; // 搜索 if (command === "search") { if (requesthod === "POST") { // 搜索结果 return handleSearch(request, gd); } else { const params = url.searchParams; // 搜索页面 return new Response( html(gd.order, { q: params.get("q") || "", is_search_page: true, root_type: gd.root_type, }), { status: 200, headers: { "Content-Type": "text/html; " }, } ); } } else if (command === "id2path" && requesthod === "POST") { return handleId2Path(request, gd); } else if (command === "view") { const params = url.searchParams; return gd.view(params.get("url"), request.headers.get("Range")); } else if (command !== "down" && requesthod === "GET") { return new Response(html(gd.order, { root_type: gd.root_type }), { status: 200, headers: { "Content-Type": "text/html; charset=utf-8" }, }); } } const reg = new RegExp(`^(/\\d+:)${command}/`, "g"); path = path.replace(reg, (p1, p2) => { return p2 + "/"; }); // 期望的 path 格式 const common_reg = /^\/\d+:\/.*$/g; try { if (!path.match(common_reg)) { return redirectToIndexPage(); } let split = path.split("/"); let order = Number(split[1].slice(0, -1)); if (order >= 0 && order < gds.length) { gd = gds[order]; } else { return redirectToIndexPage(); } } catch (e) { return redirectToIndexPage(); } // basic auth // for (const r = gd.basicAuthResponse(request); r;) return r; const basic_auth_res = gd.basicAuthResponse(request); path = path.replace(gd.url_path_prefix, "") || "/"; if (requesthod == "POST") { return basic_auth_res || apiRequest(request, gd); } let action = url.searchParams.get("a"); if (path.substr(-1) == "/" || action != null) { return ( basic_auth_res || new Response(html(gd.order, { root_type: gd.root_type }), { status: 200, headers: { "Content-Type": "text/html; charset=utf-8" }, }) ); } else { if (path.split("/").pop().toLowerCase() == ".password") { return basic_auth_res || new Response("", { status: 404 }); } let file = await gd.file(path); let range = request.headers.get("Range"); if (gd.root.protect_file_link && basic_auth_res) return basic_auth_res; const is_down = !(command && command == "down"); return gd.down(file.id, range, is_down); } } async function apiRequest(request, gd) { let url = new URL(request.url); let path = url.pathname; path = path.replace(gd.url_path_prefix, "") || "/"; let option = { status: 200, headers: { "Access-Control-Allow-Origin": "*" } }; if (path.substr(-1) == "/") { let deferred_pass = gd.password(path); let body = await request.text(); body = JSON.parse(body); // 这样可以提升首次列目录时的速度。缺点是,如果password验证失败,也依然会产生列目录的开销 let deferred_list_result = gd.list( path, body.page_token, Number(body.page_index) ); // check .password file, if `enable_password_file_verify` is true if (authConfig["enable_password_file_verify"]) { let password = await gd.password(path); // console.log("dir password", password); if (password && password.replace("\n", "") !== body.password) { let html = `{"error": {"code": 401,"message": "password error."}}`; return new Response(html, option); } } let list_result = await deferred_list_result; return new Response(JSON.stringify(list_result), option); } else { let file = await gd.file(path); let range = request.headers.get("Range"); return new Response(JSON.stringify(file)); } } // 处理 search async function handleSearch(request, gd) { const option = { status: 200, headers: { "Access-Control-Allow-Origin": "*" }, }; let body = await request.text(); body = JSON.parse(body); let search_result = await gd.search( body.q || "", body.page_token, Number(body.page_index) ); return new Response(JSON.stringify(search_result), option); } /** * 处理 id2path * @param request 需要 id 参数 * @param gd * @returns {Promise} 【注意】如果从前台接收的id代表的项目不在目标gd盘下,那么response会返回给前台一个空字符串"" */ async function handleId2Path(request, gd) { const option = { status: 200, headers: { "Access-Control-Allow-Origin": "*" }, }; let body = await request.text(); body = JSON.parse(body); let path = await gd.findPathById(body.id); return new Response(path || "", option); } class googleDrive { constructor(authConfig, order) { // 每个盘对应一个order,对应一个gd实例 this.order = order; this.root = authConfig.roots[order]; this.root.protect_file_link = this.root.protect_file_link || false; this.url_path_prefix = `/${order}:`; this.authConfig = authConfig; // TODO: 这些缓存的失效刷新策略,后期可以制定一下 // path id this.paths = []; // path file this.files = []; // path pass this.passwords = []; // id path this.id_path_cache = {}; this.id_path_cache[this.root["id"]] = "/"; this.paths["/"] = this.root["id"]; /*if (this.root['pass'] != "") { this.passwords['/'] = this.root['pass']; }*/ // this.init(); } /** * 初次授权;然后获取 user_drive_real_root_id * @returns {Promise} */ async init() { await this.accessToken(); /*await (async () => { // 只获取1次 if (authConfig.user_drive_real_root_id) return; const root_obj = await (gds[0] || this).findItemById('root'); if (root_obj && root_obj.id) { authConfig.user_drive_real_root_id = root_obj.id } })();*/ // 等待 user_drive_real_root_id ,只获取1次 if (authConfig.user_drive_real_root_id) return; const root_obj = await (gds[0] || this).findItemById("root"); if (root_obj && root_obj.id) { authConfig.user_drive_real_root_id = root_obj.id; } } /** * 获取根目录类型,设置到 root_type * @returns {Promise} */ async initRootType() { const root_id = this.root["id"]; const types = CONSTS.gd_root_type; if (root_id === "root" || root_id === authConfig.user_drive_real_root_id) { this.root_type = types.user_drive; } else { const obj = await this.getShareDriveObjById(root_id); this.root_type = obj ? types.share_drive : types.sub_folder; } } /** * Returns a response that requires authorization, or null * @param request * @returns {Response|null} */ basicAuthResponse(request) { const user = this.root.user || "", pass = this.root.pass || "", _401 = new Response("Unauthorized", { headers: { "WWW-Authenticate": `Basic realm="goindex:drive:${this.order}"`, }, status: 401, }); if (user || pass) { const auth = request.headers.get("Authorization"); if (auth) { try { const [received_user, received_pass] = atob( auth.split(" ").pop() ).split(":"); return received_user === user && received_pass === pass ? null : _401; } catch (e) {} } } else return null; return _401; } async view(url, range = "", inline = true) { let requestOption = await this.requestOption(); requestOption.headers["Range"] = range; let res = await fetch(url, requestOption); const { headers } = (res = new Response(res.body, res)); this.authConfig.enable_cors_file_down && headers.append("Access-Control-Allow-Origin", "*"); inline === true && headers.set("Content-Disposition", "inline"); return res; } async down(id, range = "", inline = false) { let url = `https://www.googleapis.com/drive/v3/files/${id}?alt=media`; let requestOption = await this.requestOption(); requestOption.headers["Range"] = range; let res = await fetch(url, requestOption); const { headers } = (res = new Response(res.body, res)); this.authConfig.enable_cors_file_down && headers.append("Access-Control-Allow-Origin", "*"); inline === true && headers.set("Content-Disposition", "inline"); return res; } async file(path) { if (typeof this.files[path] == "undefined") { this.files[path] = await this._file(path); } return this.files[path]; } async _file(path) { let arr = path.split("/"); let name = arr.pop(); name = decodeURIComponent(name).replace(/\'/g, "\\'"); let dir = arr.join("/") + "/"; // console.log(name, dir); let parent = await this.findPathId(dir); // console.log(parent); let url = "https://www.googleapis.com/drive/v3/files"; let params = { includeItemsFromAllDrives: true, supportsAllDrives: true }; params.q = `'${parent}' in parents and name = '${name}' and trashed = false`; params.fields = "files(id, name, mimeType, size ,createdTime, modifiedTime, iconLink, thumbnailLink)"; url += "?" + this.enQuery(params); let requestOption = await this.requestOption(); let response = await fetch(url, requestOption); let obj = await response.json(); // console.log(obj); return obj.files[0]; } // 通过reqeust cache 来缓存 async list(path, page_token = null, page_index = 0) { if (this.path_children_cache == undefined) { // { :[ {nextPageToken:'',data:{}}, {nextPageToken:'',data:{}} ...], ...} this.path_children_cache = {}; } if ( this.path_children_cache[path] && this.path_children_cache[path][page_index] && this.path_children_cache[path][page_index].data ) { let child_obj = this.path_children_cache[path][page_index]; return { nextPageToken: child_obj.nextPageToken || null, curPageIndex: page_index, data: child_obj.data, }; } let id = await this.findPathId(path); let result = await this._ls(id, page_token, page_index); let data = result.data; // 对有多页的,进行缓存 if (result.nextPageToken && data.files) { if (!Array.isArray(this.path_children_cache[path])) { this.path_children_cache[path] = []; } this.path_children_cache[path][Number(result.curPageIndex)] = { nextPageToken: result.nextPageToken, data: data, }; } return result; } async _ls(parent, page_token = null, page_index = 0) { // console.log("_ls", parent); if (parent == undefined) { return null; } let obj; let params = { includeItemsFromAllDrives: true, supportsAllDrives: true }; params.q = `'${parent}' in parents and trashed = false AND name !='.password'`; params.orderBy = "folder,name,modifiedTime desc"; params.fields = "nextPageToken, files(id, name, mimeType, size , modifiedTime, thumbnailLink, description)"; params.pageSize = this.authConfig.files_list_page_size; if (page_token) { params.pageToken = page_token; } let url = "https://www.googleapis.com/drive/v3/files"; url += "?" + this.enQuery(params); let requestOption = await this.requestOption(); let response = await fetch(url, requestOption); obj = await response.json(); return { nextPageToken: obj.nextPageToken || null, curPageIndex: page_index, data: obj, }; /*do { if (pageToken) { params.pageToken = pageToken; } let url = 'https://www.googleapis.com/drive/v3/files'; url += '?' + this.enQuery(params); let requestOption = await this.requestOption(); let response = await fetch(url, requestOption); obj = await response.json(); files.push(...obj.files); pageToken = obj.nextPageToken; } while (pageToken);*/ } async password(path) { if (this.passwords[path] !== undefined) { return this.passwords[path]; } // console.log("load", path, ".password", this.passwords[path]); let file = await this.file(path + ".password"); if (file == undefined) { this.passwords[path] = null; } else { let url = `https://www.googleapis.com/drive/v3/files/${file.id}?alt=media`; let requestOption = await this.requestOption(); let response = await this.fetch200(url, requestOption); this.passwords[path] = await response.text(); } return this.passwords[path]; } /** * 通过 id 获取 share drive 信息 * @param any_id * @returns {Promise} 任何非正常情况都返回 null */ async getShareDriveObjById(any_id) { if (!any_id) return null; if ("string" !== typeof any_id) return null; let url = `https://www.googleapis.com/drive/v3/drives/${any_id}`; let requestOption = await this.requestOption(); let res = await fetch(url, requestOption); let obj = await res.json(); if (obj && obj.id) return obj; return null; } /** * 搜索 * @returns {Promise} */ async search(origin_keyword, page_token = null, page_index = 0) { const types = CONSTS.gd_root_type; const is_user_drive = this.root_type === types.user_drive; const is_share_drive = this.root_type === types.share_drive; const empty_result = { nextPageToken: null, curPageIndex: page_index, data: null, }; if (!is_user_drive && !is_share_drive) { return empty_result; } let keyword = FUNCS.formatSearchKeyword(origin_keyword); if (!keyword) { // 关键词为空,返回 return empty_result; } let words = keyword.split(/\s+/); let name_search_str = `name contains '${words.join( "' AND name contains '" )}'`; // corpora 为 user 是个人盘 ,为 drive 是团队盘。配合 driveId let params = {}; if (is_user_drive) { params.corpora = "user"; } if (is_share_drive) { params.corpora = "drive"; params.driveId = this.root.id; // This parameter will only be effective until June 1, 2020. Afterwards shared drive items will be included in the results. params.includeItemsFromAllDrives = true; params.supportsAllDrives = true; } if (page_token) { params.pageToken = page_token; } params.q = `trashed = false AND name !='.password' AND (${name_search_str})`; params.fields = "nextPageToken, files(id, name, mimeType, size , modifiedTime, thumbnailLink, description)"; params.pageSize = this.authConfig.search_result_list_page_size; // params.orderBy = 'folder,name,modifiedTime desc'; let url = "https://www.googleapis.com/drive/v3/files"; url += "?" + this.enQuery(params); // console.log(params) let requestOption = await this.requestOption(); let response = await fetch(url, requestOption); let res_obj = await response.json(); return { nextPageToken: res_obj.nextPageToken || null, curPageIndex: page_index, data: res_obj, }; } /** * 一层一层的向上获取这个文件或文件夹的上级文件夹的 file 对象。注意:会很慢!!! * 最多向上寻找到当前 gd 对象的根目录 (root id) * 只考虑一条单独的向上链。 * 【注意】如果此id代表的项目不在目标gd盘下,那么此函数会返回null * * @param child_id * @param contain_myself * @returns {Promise} */ async findParentFilesRecursion(child_id, contain_myself = true) { const gd = this; const gd_root_id = gd.root.id; const user_drive_real_root_id = authConfig.user_drive_real_root_id; const is_user_drive = gd.root_type === CONSTS.gd_root_type.user_drive; // 自下向上查询的终点目标id const target_top_id = is_user_drive ? user_drive_real_root_id : gd_root_id; const fields = CONSTS.default_file_fields; // [{},{},...] const parent_files = []; let meet_top = false; async function addItsFirstParent(file_obj) { if (!file_obj) return; if (!file_obj.parents) return; if (file_obj.parents.length < 1) return; // ['','',...] let p_ids = file_obj.parents; if (p_ids && p_ids.length > 0) { // its first parent const first_p_id = p_ids[0]; if (first_p_id === target_top_id) { meet_top = true; return; } const p_file_obj = await gd.findItemById(first_p_id); if (p_file_obj && p_file_obj.id) { parent_files.push(p_file_obj); await addItsFirstParent(p_file_obj); } } } const child_obj = await gd.findItemById(child_id); if (contain_myself) { parent_files.push(child_obj); } await addItsFirstParent(child_obj); return meet_top ? parent_files : null; } /** * 获取相对于本盘根目录的path * @param child_id * @returns {Promise} 【注意】如果此id代表的项目不在目标gd盘下,那么此方法会返回空字符串"" */ async findPathById(child_id) { if (this.id_path_cache[child_id]) { return this.id_path_cache[child_id]; } const p_files = await this.findParentFilesRecursion(child_id); if (!p_files || p_files.length < 1) return ""; let cache = []; // 把查出来的每一级的path和id都缓存一下 p_files.forEach((value, idx) => { const is_folder = idx === 0 ? p_files[idx].mimeType === CONSTS.folder_mime_type : true; let path = "/" + p_files .slice(idx) .map((it) => it.name) .reverse() .join("/"); if (is_folder) path += "/"; cache.push({ id: p_files[idx].id, path: path }); }); cache.forEach((obj) => { this.id_path_cache[obj.id] = obj.path; this.paths[obj.path] = obj.id; }); /*const is_folder = p_files[0].mimeType === CONSTS.folder_mime_type; let path = '/' + p_files.map(it => it.name).reverse().join('/'); if (is_folder) path += '/';*/ return cache[0].path; } // 根据id获取file item async findItemById(id) { const is_user_drive = this.root_type === CONSTS.gd_root_type.user_drive; let url = `https://www.googleapis.com/drive/v3/files/${id}?fields=${ CONSTS.default_file_fields }${is_user_drive ? "" : "&supportsAllDrives=true"}`; let requestOption = await this.requestOption(); let res = await fetch(url, requestOption); return await res.json(); } async findPathId(path) { let c_path = "/"; let c_id = this.paths[c_path]; let arr = path.trim("/").split("/"); for (let name of arr) { c_path += name + "/"; if (typeof this.paths[c_path] == "undefined") { let id = await this._findDirId(c_id, name); this.paths[c_path] = id; } c_id = this.paths[c_path]; if (c_id == undefined || c_id == null) { break; } } // console.log(this.paths); return this.paths[path]; } async _findDirId(parent, name) { name = decodeURIComponent(name).replace(/\'/g, "\\'"); // console.log("_findDirId", parent, name); if (parent == undefined) { return null; } let url = "https://www.googleapis.com/drive/v3/files"; let params = { includeItemsFromAllDrives: true, supportsAllDrives: true }; params.q = `'${parent}' in parents and mimeType = 'application/vnd.google-apps.folder' and name = '${name}' and trashed = false`; params.fields = "nextPageToken, files(id, name, mimeType)"; url += "?" + this.enQuery(params); let requestOption = await this.requestOption(); let response = await fetch(url, requestOption); let obj = await response.json(); if (obj.files[0] == undefined) { return null; } return obj.files[0].id; } async accessToken() { console.log("accessToken"); if ( this.authConfig.expires == undefined || this.authConfig.expires < Date.now() ) { const obj = await this.fetchAccessToken(); if (obj.access_token != undefined) { this.authConfig.accessToken = obj.access_token; this.authConfig.expires = Date.now() + 3500 * 1000; } } return this.authConfig.accessToken; } async fetchAccessToken() { console.log("fetchAccessToken"); const url = "https://www.googleapis.com/oauth2/v4/token"; const headers = { "Content-Type": "application/x-www-form-urlencoded", }; const post_data = { client_id: this.authConfig.client_id, client_secret: this.authConfig.client_secret, refresh_token: this.authConfig.refresh_token, grant_type: "refresh_token", }; let requestOption = { method: "POST", headers: headers, body: this.enQuery(post_data), }; const response = await fetch(url, requestOption); return await response.json(); } async fetch200(url, requestOption) { let response; for (let i = 0; i < 3; i++) { response = await fetch(url, requestOption); console.log(response.status); if (response.status != 403) { break; } await this.sleep(800 * (i + 1)); } return response; } async requestOption(headers = {}, method = "GET") { const accessToken = await this.accessToken(); headers["authorization"] = "Bearer " + accessToken; return { method: method, headers: headers }; } enQuery(data) { const ret = []; for (let d in data) { ret.push(encodeURIComponent(d) + "=" + encodeURIComponent(data[d])); } return ret.join("&"); } sleep(ms) { return new Promise(function (resolve, reject) { let i = 0; setTimeout(function () { console.log("sleep" + ms); i++; if (i >= 2) reject(new Error("i>=2")); else resolve(i); }, ms); }); } } String.prototype.trim = function (char) { if (char) { return this.replace( new RegExp("^\\" + char + "+|\\" + char + "+$", "g"), "" ); } return this.replace(/^\s+|\s+$/g, ""); };

其中我们需要修改的值分别是:client_id 、client_secret、refresh_token和 roots(究竟访问网盘中哪些内容),这些值如何获得后文详细说。 (2)API部署

1、获取GDrive客户端先启用Google Drive API,点击进入,注意这里使用个人账号操作。

再创建一个OAuth client ID。应用类型选择其他(Other),名称自己填。记录客户端ID和客户端密钥(这两个值在Rclone部署时需要用到)。如果之前有创建过没有其他选项,可以前往API凭据查看。

2、部署Rclone获取client_id 、client_secret、refresh_token值

#装依赖 yum install p7zip unzip -y #CentOS系统 apt install -y p7zip-full #Debian/Ubuntu系统 #装Rclone curl https://rclone.org/install.sh | bash #部署 rclone config

按照下面的操作方式操作,其中需要填入上文中所获取的Google Drive客户端ID和客户端密匙:

n) New remote s) Set configuration password q) Quit config n/s/q> n name> link #配置名称,随便填 Type of storage to configure. Enter a string value. Press Enter for the default (""). Choose a number from below, or type in your own value 1 / 1Fichier \ "fichier" 2 / Alias for an existing remote \ "alias" 3 / Amazon Drive \ "amazon cloud drive" 4 / Amazon S3 Compliant Storage Provider (AWS, Alibaba, Ceph, Digital Ocean, Dreamhost, IBM COS, Minio, etc) \ "s3" 5 / Backblaze B2 \ "b2" 6 / Box \ "box" 7 / Cache a remote \ "cache" 8 / Dropbox \ "dropbox" 9 / Encrypt/Decrypt a remote \ "crypt" 10 / FTP Connection \ "ftp" 11 / Google Cloud Storage (this is not Google Drive) \ "google cloud storage" 12 / Google Drive \ "drive" 13 / Google Photos \ "google photos" 14 / Hubic \ "hubic" 15 / JottaCloud \ "jottacloud" 16 / Koofr \ "koofr" 17 / Local Disk \ "local" 18 / Mega \ "mega" 19 / Microsoft Azure Blob Storage \ "azureblob" 20 / Microsoft OneDrive \ "onedrive" 21 / OpenDrive \ "opendrive" 22 / Openstack Swift (Rackspace Cloud Files, Memset Memstore, OVH) \ "swift" 23 / Pcloud \ "pcloud" 24 / Put.io \ "putio" 25 / QingCloud Object Storage \ "qingstor" 26 / SSH/SFTP Connection \ "sftp" 27 / Union merges the contents of several remotes \ "union" 28 / Webdav \ "webdav" 29 / Yandex Disk \ "yandex" 30 / http Connection \ "http" 31 / premiumize.me \ "premiumizeme" Storage> 12 #选择12,Google Drive,注意该序列号会随时变化,看清楚再填 client_id> 850428 #填上你的Google Drive客户端ID client_secret> D72gPc #填上你的Google Drive客户端密匙 Choose a number from below, or type in your own value 1 / Full access all files, excluding Application Data Folder. \ "drive" 2 / Read-only access to file metadata and file contents. \ "drive.readonly" / Access to files created by rclone only. 3 | These are visible in the drive website. | File authorization is revoked when the user deauthorizes the app. \ "drive.file" / Allows read and write access to the Application Data folder. 4 | This is not visible in the drive website. \ "drive.appfolder" / Allows read-only access to file metadata but 5 | does not allow any access to read or download file content. \ "drive.metadata.readonly" scope> 1 ID of the root folder Leave blank normally. Fill in to access "Computers" folders. (see docs). Enter a string value. Press Enter for the default (""). root_folder_id> Service Account Credentials JSON file path Leave blank normally. Needed only if you want use SA instead of interactive login. Enter a string value. Press Enter for the default (""). service_account_file> Edit advanced config? (y/n) y) Yes n) No y/n> n #输入n Remote config Use auto config? * Say Y if not sure * Say N if you are working on a remote or headless machine y) Yes n) No y/n> n #输入n If your browser doesn't open automatically go to the following link: https://accounts.google.com/o/ #打开该地址获取code Log in and authorize rclone for access Enter verification code>hjdd #输入你获取到的code Configure this as a team drive? y) Yes n) No y/n> n #输入n -------------------- [Rats] type = drive client_id = 85042871 client_secret = D72gPc scope = drive token = {"access_token":"y902Z"} #记住里面的refresh_token参数 -------------------- y) Yes this is OK e) Edit this remote d) Delete this remote y/e/d> y #输入y Current remotes: Name Type ==== ==== link drive e) Edit existing remote n) New remote d) Delete remote r) Rename remote c) Copy remote s) Set configuration password q) Quit config e/n/d/r/c/s/q> q #输入q保存退出

部署完后我们可以看到client_id 、client_secret、refresh_token参数,如果忘了的可以查看配置文件。

rclone config file | grep rclone.conf

3、修改GoIndex源码中的client_id 、client_secret、refresh_token值4、修改GoIndex源码中其他值

#如修改站点名称 siteName: "goindex", // 网站名称 #修改访问目录 * 设置要显示的多个云端硬盘;按格式添加多个 * [id]: 可以是 团队盘id、子文件夹id、或者"root"(代表个人盘根目录); * [name]: 显示的名称 * [user]: Basic Auth 的用户名 * [pass]: Basic Auth 的密码 * [protect_file_link]: Basic Auth 是否用于保护文件链接,默认值(不设置时)为 false,即不保护文件链接(方便 直链下载/外部播放 等)

文件夹ID获取方法,点击根目录某个文件夹,地址栏为:https://drive.google.com/drive/folders/1C4Aro,则1C4Aro为文件夹ID。 (3)部署到Workers首先登录cloudflare,然后首页右侧可以看到Workers入口。点进去后新建一个Workers子域名,也就是你的专属域名,填入确认后就更改不了了,所以别乱填。接下来点击Create a Worker。 把代码粘贴进去,然后可以在左上角双击修改域名,再点击Save and Deploy即可。最后打开分配的子域名即可,此后修改配置可以自行修改Workers代码。(4)绑定域名

如果想绑定自己的域名,就需要先将自己的域名转到cloudflare,然后点自己的域名,解析一个A记录,IP地址随意。可以参见 这篇博文

(5)使用

对于较小文件可以获取直链后在线播放,对于较大文件会触发idm下载。

这时候问题来了,如果想在线播放大文件,或者需要离线下载,如何办?上aria2即可。

三:aria2

这一部分可以参考我的博文,进行部署。



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有