gh-pages 9f7632e693b1 cached
8 files
25.7 KB
8.1k tokens
13 symbols
1 requests
Download .txt
Repository: Mapaler/GetOneDriveDirectLink
Branch: gh-pages
Commit: 9f7632e693b1
Files: 8
Total size: 25.7 KB

Directory structure:
gitextract_04vsu85l/

├── LICENSE
├── README.md
├── contract.html
├── index.html
├── privacy.html
├── script.js
├── scripts/
│   └── auth.js
└── style.css

================================================
FILE CONTENTS
================================================

================================================
FILE: LICENSE
================================================
                   GNU LESSER GENERAL PUBLIC LICENSE
                       Version 3, 29 June 2007

 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.


  This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.

  0. Additional Definitions.

  As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.

  "The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.

  An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.

  A "Combined Work" is a work produced by combining or linking an
Application with the Library.  The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".

  The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.

  The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.

  1. Exception to Section 3 of the GNU GPL.

  You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.

  2. Conveying Modified Versions.

  If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:

   a) under this License, provided that you make a good faith effort to
   ensure that, in the event an Application does not supply the
   function or data, the facility still operates, and performs
   whatever part of its purpose remains meaningful, or

   b) under the GNU GPL, with none of the additional permissions of
   this License applicable to that copy.

  3. Object Code Incorporating Material from Library Header Files.

  The object code form of an Application may incorporate material from
a header file that is part of the Library.  You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:

   a) Give prominent notice with each copy of the object code that the
   Library is used in it and that the Library and its use are
   covered by this License.

   b) Accompany the object code with a copy of the GNU GPL and this license
   document.

  4. Combined Works.

  You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:

   a) Give prominent notice with each copy of the Combined Work that
   the Library is used in it and that the Library and its use are
   covered by this License.

   b) Accompany the Combined Work with a copy of the GNU GPL and this license
   document.

   c) For a Combined Work that displays copyright notices during
   execution, include the copyright notice for the Library among
   these notices, as well as a reference directing the user to the
   copies of the GNU GPL and this license document.

   d) Do one of the following:

       0) Convey the Minimal Corresponding Source under the terms of this
       License, and the Corresponding Application Code in a form
       suitable for, and under terms that permit, the user to
       recombine or relink the Application with a modified version of
       the Linked Version to produce a modified Combined Work, in the
       manner specified by section 6 of the GNU GPL for conveying
       Corresponding Source.

       1) Use a suitable shared library mechanism for linking with the
       Library.  A suitable mechanism is one that (a) uses at run time
       a copy of the Library already present on the user's computer
       system, and (b) will operate properly with a modified version
       of the Library that is interface-compatible with the Linked
       Version.

   e) Provide Installation Information, but only if you would otherwise
   be required to provide such information under section 6 of the
   GNU GPL, and only to the extent that such information is
   necessary to install and execute a modified version of the
   Combined Work produced by recombining or relinking the
   Application with a modified version of the Linked Version. (If
   you use option 4d0, the Installation Information must accompany
   the Minimal Corresponding Source and Corresponding Application
   Code. If you use option 4d1, you must provide the Installation
   Information in the manner specified by section 6 of the GNU GPL
   for conveying Corresponding Source.)

  5. Combined Libraries.

  You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:

   a) Accompany the combined library with a copy of the same work based
   on the Library, uncombined with any other library facilities,
   conveyed under the terms of this License.

   b) Give prominent notice with the combined library that part of it
   is a work based on the Library, and explaining where to find the
   accompanying uncombined form of the same work.

  6. Revised Versions of the GNU Lesser General Public License.

  The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.

  Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.

  If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.


================================================
FILE: README.md
================================================
获取OneDrive直链
===========
本应用的目的是为了**批量**获取 OneDrive 直链,方便在其他站点外链。前身为[提取OneDrive直链地址工具](http://bbs.comicdd.com/thread-354826-1-1.html)的网页版,因为原来的工具失效了,软件版也不是那么好用了,因此决定使用 OneDrive 官方 SDK 来进行获取,确保不失效。 

2024年7月8日 以前的 SDK v7.2 好像用不了了,切换 SDK 到 v8 版本,只能获取到基本信息了。找了很久也不知道怎么获取更多的文件信息,就这样将就着用吧。

# 马上使用
https://mapaler.github.io/GetOneDriveDirectLink/

> 因为使用官方 API 直接访问 onedrive.live.com 选择文件,因此可能需要翻墙。

# 隐私声明

使用微软官方 [文件选取器 v8](https://learn.microsoft.com/zh-cn/onedrive/developer/controls/file-pickers/),本应用不会得到你的账号密码和其他用户资料。
目前仅申请了 `Files.Read`、`Files.Read.Selected` 两个权限,SDK 只会返回用户选择的文件的信息,不会获得未授权的其他内容。  

# 如何自行搭建

## 文件选取器 v8 教程

我不想注册收费的 Azure,所以只提到在原有 v7.2 上的变化。注册 Azure 的流程区别请自己想办法了。

1. 身份验证不能用 Web ,得改成单页应用程序,不然会报错:[The provided request must include a 'client_secret' input parameter in the sample project](https://github.com/Azure-Samples/ms-identity-javascript-react-spa-dotnetcore-webapi-obo/issues/30)  
![身份验证](document/authentication-v8.png)
2. 应用 ID,替换本程序 `scripts/auth.js` 内的 `msalParams.auth.clientId`。

## JavaScript SDK v7.2 教程

2024年7月8日 目前时间点登录会显示以下警告,也就是需要迁移到收费的 Azure,我不想搞,所以以下自行搭建教程已不适用于目前的内容,就这样将就着用吧。

> 自 2020 年 6 月 30 日起,我们将不再向 Azure Active Directory 身份验证库(ADAL)和 Azure Active Directory Graph 添加任何新功能。我们将继续提供技术支持和安全更新程序,但将不再提供功能更新。应用程序将需要升级到 Microsoft 身份验证库(MSAL)和 Microsoft Graph。[了解更多信息](https://go.microsoft.com/fwlink/?linkid=2132805)

> 这些应用程序与帐户 xxxx@outlook.com 关联,但不包含在任何目录中。在目录外部创建应用程序的功能已被弃用。你可通过加入 [M365 开发人员计划](https://aka.ms/joinM365DeveloperProgram)或[注册 Azure](https://aka.ms/signUpForAzure) 来获取新目录。[了解详细信息](https://aka.ms/MsaDeprecateInfo)

### 原内容

>按照[OneDrive file picker SDK](https://docs.microsoft.com/onedrive/developer/controls/file-pickers/js-v72/)内的说明进行,由于旧有的*Microsoft 应用注册门户*已经迁移到*Azure门户*,特此做出更新,此版本更新于2020年2月6日。

1. 应用注册  
在 [Azure 门户](https://portal.azure.com/#blade/Microsoft_AAD_RegisteredApps/ApplicationsListBlade) 创建应用,自己申请一个应用 ID,然后替换本程序 `script.js` 内底部的 `clientId`。  
![客户端ID](document/clientID.png)  
其中 `odOptions.advanced.queryParameters` 内容参见 [DriveItem 资源类型](https://docs.microsoft.com/onedrive/developer/rest-api/resources/driveitem?view=odsp-graph-online),若不设定,将只返回基本信息。
1. 设置**身份验证**  
将网页的各种东西上传到你申请 ID 时设置的 重定向 URL 即可,页面 URL 不在重定向内会发生错误。注:这个页面必须为 https(仅 localhost 可为 http,且 localhost 也需添加到 重定向 URL)  
需要勾选**隐式授权**的**访问令牌**、**ID 令牌**。*讨论见[#9](//github.com/Mapaler/GetOneDriveDirectLink/issues/9)*
![身份验证](document/authentication.png)
1. 设置**API 权限**  
添加 `Microsoft Graph` ▶ `委托的权限` ▶ `Files.Read`、`Files.Read.Selected`  
![需求的权限](document/permission.png) 

## 原理
`http://storage.live.com/items/文件ID` 是很早之前就流传的 SkyDrive 官方的真实直链重定向地址,出处不可考。  
开始一般是用`http://storage.live.com/items/文件ID?filename=xxx.jpg`来外链图片,后面的文件名用来欺骗DiscuzX论坛系统,但是浏览器下载该文件还是没有正确文件名。  
后来有高手发现的`http://storage.live.com/items/文件ID:/xxx.jpg`这个地址格式不会影响 SkyDrive 识别ID,还同时可以欺骗浏览器为普通文件地址,识别出文件的文件名与扩展名。  
访问 `onedrive.live.com` 域名需要翻墙,但是生成的 `storage.live.com` 链接不需要。

### 1drv.ws 原理

> 由于 *1drv.ws* 项目自身停止运行,已删除对此功能的支持

**1drv.ws** 是 *[The OneDrive Direct Download Link Helper](//github.com/aploium/OneDrive-Direct-Link)* 项目的实现。  
原理为
1. 获取OneDrive的分享链接 `https://1drv.ms/u/分享ID`
2. 重定向到 `https://onedrive.live.com/redir?resid=文件ID&authkey=通行证`
3. 将访问网页的地址修改为下载地址 `https://onedrive.live.com/download?resid=文件ID&authkey=通行证`
4. 再次重定向到真实CDN地址 `https://public.ch.files.1drv.com/很长一串字符/文件名?download&psid=1`

其中,对 `onedrive.live.com` 的重定向需要墙外后端支持。

在 OneDrive SDK 中,使用 `share` 操作才能新增并获取到分享链接,因此添加了“额外创建分享链接”按钮。  
掩码将获取到的分享链接中的 1drv.**m**s 修改为 1drv.**w**s ,即可得到下载链接。但是这种方式的 http header `content-Dispositong` 值为 `attachemen` 会弹出下载窗口,而不是内部预览。

================================================
FILE: contract.html
================================================
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>服务条款</title>
</head>

<body>
	<h1>获取OneDrive直链 服务条款</h1>
	<p>你怎么用不关我事哈</p>
</body>
</html>


================================================
FILE: index.html
================================================
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>获取OneDrive直链</title>
<link rel="icon" href="icon.png" type="image/x-png" />
<link rel="shortcut icon" href="icon.png" type="image/x-png" />
<link href="style.css" rel="stylesheet" type="text/css">
<script type="text/javascript" src="https://alcdn.msauth.net/browser/2.35.0/js/msal-browser.min.js"></script>
<!--
官方示例代码是 MSAL 2.x 的,切换到 MSAL 3.x 无法使用
想迁移 v3 的自己看 https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/v2-migration.md
找 v2 最新的自己看 https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/cdn-usage.md
-->
<script type="text/javascript" src="scripts/auth.js"></script>
<script type="text/javascript" src="script.js"></script>
</head>

<body>
	<h1>获取OneDrive直链</h1>
	<p>本应用的目的是为了批量获取OneDrive直链,方便在其他站点外链。前身为<a href="http://bbs.comicdd.com/thread-354826-1-1.html" target="_blank">提取OneDrive直链地址工具</a>的网页版,因为原来的工具失效了,软件版也不是那么好用了,因此决定使用OneDrive官方API来进行获取,确保不失效。
	</p>
	<h3>隐私声明</h3>
	<p>使用微软官方API,这里不会得到你的账号密码和其他用户资料。<br>
	目前仅申请了Files.Read、Files.Read.Selected两个权限,API只会返回用户选择的文件的信息,不会获得其他内容。
	</p>
	<h3>使用方法</h3>
	<ol class="howtouse">
		<li>点击<span class="openbtn" title="不是点我,真的按钮在下面呢" onclick="document.getElementById('launchPicker').click();">从OneDrive选择文件</span>按钮,等待打开新窗口。</li>
		<li>第一次可能被浏览器广告拦截阻断,需要手动允许浏览器打开新窗口。</li>
		<li>在新窗口中登陆 OneDrive 对应的账户并授予需要的权限。</li>
		<li>进入 OneDrive “公开”文件夹内选择需要获取直链的文件(同一文件夹内可多选),点击打开。<a href="#no-public">没有“公开”怎么办</a></li>
		<li>选择需要输出的掩码格式,输出面板内会自动生成对应的格式。</li>
	</ol>
	<div class="splitContainer">
		<div class="picker">
			<h3>控制面板</h3>
			<button class="openbtn" id="launchPicker">从OneDrive选择文件</button>
			<!--<label><input type="checkbox" class="add-permission">主动添加访问权限(无所有人查看权限的文件)</label>-->
			<div>输出字符串模板</div>
			<select class="mask-list" size="5" onclick="mask_select();"></select>
			<div class="mask-bar">
				<input class="mask-name" type="text" placeholder="自定义名称" />
				<input class="mask-content" type="text" placeholder="掩码内容" />
				<input class="mask-add" value="+" type="button" onclick="mask_add();" />
				<input class="mask-remove" value="-" type="button" onclick="mask_remove();" />
			</div>
			<div class="tip">▲以上掩码设置保存在浏览器本地</div>
		</div>
		<div class="output">
			<h3>输出面板</h3>
			<div class="outinfo"></div>
			<textarea class="outcontent"></textarea>
		</div>
	</div>

	<h3>F&amp;Q</h3>
	<dl>
		<dt>如何书写掩码?</dt>
		<dd>使用 ES6 原生<a href="https://developer.mozilla.org/docs/Web/JavaScript/Reference/Template_literals">模板字符串</a> ${表达式} 书写方式。
			“file”表示该文件信息,“index”表示在获取列表中的序号,具体的JSON对象会在获取数据后发送到控制台(或查看全局变量 redata ),需要更多信息的的自己去看吧。</dd>
		<dt>为什么要把文件放在“公开”文件夹?</dt>
		<dd>新版OneDrive只有“公开”文件夹(含子文件夹)的权限才是“所有人”,其他文件夹分享必须登录才能看到,无法成功外链。</dd>
		<dt id="no-public">没有“公开”文件夹怎么办?</dt>
		<dd>好像现在的新账号已经没有公开文件夹了。就需要麻烦点,添加访问通行证。<br>
			<details>
				<summary>有<strong>嵌入</strong>相册按钮</summary>
				选中需要共享的文件夹,点击嵌入<br>
				<img src="images/Image 29.png" alt="点击嵌入"/><br>
				在右方复制authkey及其值。将来这个文件夹内的文件及子文件夹都只需要这同一个通行码。<br>
				<img src="images/Image 30.png" alt="复制authkey"/><br>
				如果忘记,可以点击右上角信息符号,查看共享链接<br>
				<img src="images/Image 31.png" alt="查看信息"/>
			</details>
			<details>
				<summary>通过<strong>共享</strong>连接获取</summary>
				选中需要共享的文件夹,点击共享<br>
				<img src="images/share-1.webp" alt="点击共享"/><br>
				因为不希望被其他人编辑,修改权限。<br>
				<img src="images/share-2.webp" alt="编辑权限"/><br>
				设定为与任何人共享,仅查看,点击应用<br>
				<img src="images/share-3.webp" alt="设定权限"/><br>
				复制生成的共享连接<br>
				<img src="images/share-4.webp" alt="复制链接"/><br>
				如果忘记了复制,可以在<strong>文件夹详细信息-管理访问权限-链接</strong>可以重新获得之前生成的链接<br>
				<img src="images/share-4-2.webp" alt="重新找回链接"/><br>
				直接在地址栏访问链接<br>
				<img src="images/share-5.webp" alt="访问链接"/><br>
				在跳转到的新地址里复制 <strong>authkey</strong><br>
				<img src="images/share-6.webp" alt="复制authkey"/><br>
			</details>
			把authkey及其值加入到x外链地址掩码最后面,作为链接的参数,格式为“?authkey=xxxxxxx”<br>
			<img src="images/Image 32.png" alt="修改掩码"/><br>
		</dd>
		<dt>OneDrive窗口里如何全选?</dt>
		<dd>磁贴模式下:选中第一个,拉到底下,按住Shift点击最后一个,中间的就会自动选上了。</dd>
		<dd>列表模式下:点左上角那个选中全部的复选框就行了。</dd>
		<dt>如何收回我授予的权限?</dt>
		<dd>访问微软账户<a href="https://account.live.com/consent/Manage" target="_blank">你已授予访问权限的应用和服务</a>删除即可。</dd>
	</dl>
	</p>

<div class="copyright">Copyright © 2024 本程序源代码使用GPLv3协议公开<br >Source public by GPLv3.</div>

</body>
</html>


================================================
FILE: privacy.html
================================================
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>隐私声明</title>
</head>

<body>
	<h1>获取OneDrive直链 隐私声明</h1>
	<p>使用微软官方API,我这里不会得到你的账号密码和其他用户信息。<br>
	目前仅要求Files.Read、Files.Read.Selected权限,API只会返回选择的文件的信息。</p>
</body>
</html>


================================================
FILE: script.js
================================================
const curMaskVersion = 3; //当前的掩码设置版本,用于检测是否更新

//↓Code from https://github.com/OneDrive/samples/tree/master/samples/file-picking/javascript-basic-consumer
const baseUrl = "https://onedrive.live.com/picker";

// the options we pass to the picker page through the querystring
const params = {
	sdk: "8.0",
	entry: {
		oneDrive: {
			files: {},
		}
	},
	authentication: {},
	messaging: {
		origin: location.toString(),
		channelId: "27"
	},
	typesAndSources: {
		mode: "files",
		pivots: {
			oneDrive: true,
			recent: true,
		},
	},
	selection: {
	  mode: "multiple",
	  enablePersistence: true,
	},
};

let win = null;
let port = null;

async function launchPicker(e) {

	e.preventDefault();

	win = window.open("", "Picker", "width=800,height=600")

	const authToken = await getToken();

	const queryString = new URLSearchParams({
		filePicker: JSON.stringify(params),
	});

	const url = `${baseUrl}?${queryString}`;

	const form = win.document.createElement("form");
	form.setAttribute("action", url);
	form.setAttribute("method", "POST");
	win.document.body.append(form);

	const input = win.document.createElement("input");
	input.setAttribute("type", "hidden")
	input.setAttribute("name", "access_token");
	input.setAttribute("value", authToken);
	form.appendChild(input);

	form.submit();

	window.addEventListener("message", (event) => {

		if (event.source && event.source === win) {

			const message = event.data;

			if (message.type === "initialize" && message.channelId === params.messaging.channelId) {

				port = event.ports[0];

				port.addEventListener("message", messageListener);

				port.start();

				port.postMessage({
					type: "activate",
				});
			}
		}
	});
}

async function messageListener(message) {
	switch (message.data.type) {

		case "notification":
			console.log(`notification: %o`, message.data);
			break;

		case "command":

			port.postMessage({
				type: "acknowledge",
				id: message.data.id,
			});

			const command = message.data.data;

			switch (command.command) {

				case "authenticate":

					// getToken is from scripts/auth.js
					const token = await getToken();

					if (typeof token !== "undefined" && token !== null) {

						port.postMessage({
							type: "result",
							id: message.data.id,
							data: {
								result: "token",
								token,
							}
						});

					} else {
						console.error(`Could not get auth token for command: ${JSON.stringify(command)}`);
					}

					break;

				case "close":

					win.close();
					break;

				case "pick":

					console.log(`Picked: %o`, command);

					redata = command.items; //存入全局数组
					console.log("本次返回 %d 个文件,数据为 %o",
						redata.length,
						redata
					);
					generate_output(redata);

					port.postMessage({
						type: "result",
						id: message.data.id,
						data: {
							result: "success",
						},
					});

					win.close();

					break;

				default:

					console.warn(`Unsupported command: ${JSON.stringify(command)}`, 2);

					port.postMessage({
						result: "error",
						error: {
							code: "unsupportedCommand",
							message: command.command
						},
						isExpected: true,
					});
					break;
			}

			break;
	}
}
//↑Code from OneDrive/samples

class maskObj
{
	name = '';
	content = '';
	constructor(name,content) //一个掩码对象
	{
		this.name = name;
		this.content = content;
	};
}

const masks = []; //储存掩码数组
let mask_list = null; //掩码列表框
let mask_name = null;
let mask_content = null;
let outinfo = null;
let outcontent = null;

function mask_add()
{
	if (mask_name.value.length>0 && mask_content.value.length>0)
	{
		addNewMask(mask_name.value,mask_content.value);
		mask_name.value = "";
		mask_content.value = "";
	}else
	{
		alert("掩码名或内容为空");
	}
	mask_list.selectedIndex = mask_list.options.length - 1;
	save_mask_local();
}
//从文本添加一个新的掩码
function addNewMask(name,content)
{
	const mask = new maskObj(name,content);
	masks.push(mask);
	const opt = new Option(name + " : " + content, content);
	mask_list.options.add(opt);
}
function mask_remove()
{
	if(mask_list.selectedIndex>=0)
	{
		let lastSelectedIndex = mask_list.selectedIndex;
		masks.splice(mask_list.selectedIndex, 1);
		mask_list.remove(mask_list.selectedIndex);
		mask_list.selectedIndex = (lastSelectedIndex<mask_list.options.length) ?
									lastSelectedIndex :
									(mask_list.options.length-1);
	}else
	{
		alert("没有选中掩码");
	}
	save_mask_local();
}
function mask_select()
{
	mask_name.value = masks[mask_list.selectedIndex].name;
	mask_content.value = masks[mask_list.selectedIndex].content;
	if (redata) generate_output(redata); //重新生成
	localStorage.setItem("godl-mask-index",mask_list.selectedIndex);
}
function save_mask_local() //把掩码设置保存到本地
{
	const maskstr = JSON.stringify(masks);
	localStorage.setItem("godl-masks",maskstr);
	localStorage.setItem("godl-mask-index",mask_list.selectedIndex);
}
function load_mask_local() //从空白加载设置
{
	const masksCfg = ((maskStr)=>{
		try {
			return JSON.parse(maskStr);
		} catch (e) {
			return null;
		}
	})(localStorage.getItem("godl-masks"));
	
	if (!Array.isArray(masksCfg) ||
		((parseInt(localStorage.getItem("new-mask-version"),10) || 1) < curMaskVersion)
	) //没有掩码数据,初始化默认配置。
	{
		addNewMask("普通外链","http://storage.live.com/items/${file.id}:/${file.name}");
		addNewMask("最短链接","http://storage.live.com/items/${file.id}");
		addNewMask("UBB代码外链图片","[img]http://storage.live.com/items/${file.id}:/${file.name}[/img]");
		addNewMask("模板字符串基本使用示例","在OneDrive里查看 ${file.name} 的地址是:${file.webUrl}");
		addNewMask("表达式使用示例","${index+1}号文件的尺寸是:${file.size>1024?Math.round(file.size/1024)+\"K\":file.size}B");
		addNewMask("自动选择img/mp3 UBB代码","[${file.image?\"img\":(file.audio?\"mp3\":\"file\")}]http://storage.live.com/items/${file.id}:/${file.name}[/${file.image?\"img\":(file.audio?\"mp3\":\"file\")}]");
		addNewMask("ES6完整文件尺寸换算示例","${index+1}号文件的尺寸是:${(size=>{const bArr = [\"B\",\"KiB\",\"MiB\",\"GiB\",\"TiB\"];for(let idx=0;idx<bArr.length;idx++){if(idx<bArr.length && size/Math.pow(1024,idx+1)>1)continue;else return (size/Math.pow(1024,idx)).toFixed(2) + \" \" + bArr[idx];}})(file.size)}");

		if (Array.isArray(masksCfg))
		{addNewMask("▲以上为版本更新,重新添加的掩码示例","");}
		localStorage.setItem("new-mask-version",curMaskVersion);
	}else
	{
		masksCfg.forEach(function(item){
			addNewMask(item.name,item.content);
		});
	}

	mask_list.selectedIndex = parseInt(localStorage.getItem("godl-mask-index"), 10) || 0;
}

function generate_output(files)
{
	const mask = masks[mask_list.selectedIndex] || masks[0];
	
	outinfo.innerHTML = "共选择 " + files.length + " 个文件。"

	const outStrArr = files.map((item,index)=>
		showMask(mask.content,item,index)
	);
	outcontent.value = outStrArr.join("\n");
}

//显示掩码用
function showMask(str,file,index) {
	const newTxt = eval("`" + str +"`");
	return newTxt;
}

let redata = null;//储存返回的数据

window.onload = function() //网页加载初始化
{
	mask_list = document.querySelector(".mask-list");
	mask_name = document.querySelector(".mask-name");
	mask_content = document.querySelector(".mask-content");
	outinfo = document.querySelector(".outinfo");
	outcontent = document.querySelector(".outcontent");

	// if (location.protocol !="https:" && (location.hostname !="localhost" && location.hostname !="127.0.0.1") && location.hostname != "")
	// {
	// 	const goto = confirm("检测到你正在使用http模式,本应用要求使用https模式。\n是否自动跳转?");
	// 	if (goto) {
	// 		location.protocol = "https:";
	// 	}
	// }
	
	load_mask_local();

	document.getElementById("launchPicker").onclick = launchPicker;
}

================================================
FILE: scripts/auth.js
================================================
const msalParams = {
    auth: {
        authority: "https://login.microsoftonline.com/consumers",
        clientId: "d7b41a52-5bb3-43df-a20c-6259cb6a1886",
        redirectUri: location.toString()
    },
}

const app = new msal.PublicClientApplication(msalParams);

async function getToken() {

    let accessToken = "";

    authParams = { scopes: ["OneDrive.ReadOnly"] };

    try {

        // see if we have already the idtoken saved
        const resp = await app.acquireTokenSilent(authParams);
        accessToken = resp.accessToken;

    } catch (e) {

        // per examples we fall back to popup
        const resp = await app.loginPopup(authParams);
        app.setActiveAccount(resp.account);

        if (resp.idToken) {

            const resp2 = await app.acquireTokenSilent(authParams);
            accessToken = resp2.accessToken;

        }
    }

    return accessToken;
}


================================================
FILE: style.css
================================================
@charset "utf-8";
img {
		border-width: 1px;
		border-color: black;
		border-style: solid;
}
details>summary {
	cursor: pointer;
}
details>summary:hover {
	background-color: #bbb;
}
h1 {
	text-align: center;
}
dt {
	font-weight: bold;
	background-color: rgba(0,0,0,0.2);
}
.splitContainer {
	display: grid;
	grid-template-columns: 1fr 2fr;
}
.picker
{
	padding: 10px;
	padding-top: 0;
	background-color: rgba(200,200,200,0.5);
	font-size: 0.9em;
}
.openbtn
{
	font-weight: bold;
	color: white;
	border: 1px solid rgb(9, 74, 178);
	height: 24px;
	padding-left: 4px;
	padding-right: 4px;
	padding-bottom: 2px;
	text-align: center;
	cursor: pointer;
	background-color: rgb(9, 74, 178);
}
.openbtn::before {
	content: "";
	display: inline-block;
	width: 1em;
	height: 1em;
	background-image: url("images/cloud-white-18dp.svg");
	background-size: contain;
}
.mask-list
{
	width: 100%;
	height: 300px;
	margin-bottom: 5px;
}
.mask-bar {
	display: grid;
	grid-template-columns: 100px auto 30px 30px;
	gap: 3px;
}
.mask-add,.mask-remove
{
	margin: 0;
}
.tip
{
	font-style: italic;
	color: #666;
}
.output
{
	padding: 10px;
	padding-top: 0;
	background-color: rgba(253,224,180,0.5);
}
.outinfo
{
	height: 25px;
	line-height: 25px;
	background-color:lightblue;
	margin-bottom: 10px;
}
.outcontent
{
	box-sizing: border-box;
	width: 100%;
	resize: vertical;
	height: 340px;
	min-height:300px;
}
.copyright
{
	width: 100%;
	text-align: center;
}
Download .txt
gitextract_04vsu85l/

├── LICENSE
├── README.md
├── contract.html
├── index.html
├── privacy.html
├── script.js
├── scripts/
│   └── auth.js
└── style.css
Download .txt
SYMBOL INDEX (13 symbols across 2 files)

FILE: script.js
  function launchPicker (line 35) | async function launchPicker(e) {
  function messageListener (line 84) | async function messageListener(message) {
  class maskObj (line 172) | class maskObj
    method constructor (line 176) | constructor(name,content) //一个掩码对象
  function mask_add (line 190) | function mask_add()
  function addNewMask (line 205) | function addNewMask(name,content)
  function mask_remove (line 212) | function mask_remove()
  function mask_select (line 228) | function mask_select()
  function save_mask_local (line 235) | function save_mask_local() //把掩码设置保存到本地
  function load_mask_local (line 241) | function load_mask_local() //从空白加载设置
  function generate_output (line 276) | function generate_output(files)
  function showMask (line 289) | function showMask(str,file,index) {

FILE: scripts/auth.js
  function getToken (line 11) | async function getToken() {
Condensed preview — 8 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (34K chars).
[
  {
    "path": "LICENSE",
    "chars": 7651,
    "preview": "                   GNU LESSER GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007"
  },
  {
    "path": "README.md",
    "chars": 3633,
    "preview": "获取OneDrive直链\r\n===========\r\n本应用的目的是为了**批量**获取 OneDrive 直链,方便在其他站点外链。前身为[提取OneDrive直链地址工具](http://bbs.comicdd.com/thread-3"
  },
  {
    "path": "contract.html",
    "chars": 163,
    "preview": "<!doctype html>\r\n<html>\r\n<head>\r\n<meta charset=\"utf-8\">\r\n<title>服务条款</title>\r\n</head>\r\n\r\n<body>\r\n\t<h1>获取OneDrive直链 服务条款<"
  },
  {
    "path": "index.html",
    "chars": 4464,
    "preview": "<!doctype html>\r\n<html>\r\n<head>\r\n<meta charset=\"utf-8\">\r\n<title>获取OneDrive直链</title>\r\n<link rel=\"icon\" href=\"icon.png\" t"
  },
  {
    "path": "privacy.html",
    "chars": 246,
    "preview": "<!doctype html>\r\n<html>\r\n<head>\r\n<meta charset=\"utf-8\">\r\n<title>隐私声明</title>\r\n</head>\r\n\r\n<body>\r\n\t<h1>获取OneDrive直链 隐私声明<"
  },
  {
    "path": "script.js",
    "chars": 7786,
    "preview": "const curMaskVersion = 3; //当前的掩码设置版本,用于检测是否更新\r\n\r\n//↓Code from https://github.com/OneDrive/samples/tree/master/samples/f"
  },
  {
    "path": "scripts/auth.js",
    "chars": 894,
    "preview": "const msalParams = {\n    auth: {\n        authority: \"https://login.microsoftonline.com/consumers\",\n        clientId: \"d7"
  },
  {
    "path": "style.css",
    "chars": 1529,
    "preview": "@charset \"utf-8\";\r\nimg {\r\n\t\tborder-width: 1px;\r\n\t\tborder-color: black;\r\n\t\tborder-style: solid;\r\n}\r\ndetails>summary {\r\n\tc"
  }
]

About this extraction

This page contains the full source code of the Mapaler/GetOneDriveDirectLink GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 8 files (25.7 KB), approximately 8.1k tokens, and a symbol index with 13 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!